def gradient(self): ranefs = self.mm.random_effects y, P = self.mm.y, self.P Py = P * y PVis = [P * rf.V_i for rf in ranefs] return 0.5 * np.array([-1 * np.trace(PVi) + matrix.item(y.T * PVi * Py) for PVi in PVis])
def average_information_matrix(self): y = self.mm.y P = self.P Py = P * y varmats = [x.V_i for x in self.mm.random_effects] nrf = len(varmats) def is_residual(idx): return idx == nrf - 1 residual_variance = self.parameters[-1] mat = np.zeros((nrf, nrf)) for i, V_i in enumerate(varmats): for j, V_j in enumerate(varmats): if j < i: continue element = 1/2 * y.T * P * V_i * P * V_j * P * y element = matrix.item(element) mat[i, j] = element mat[j, i] = element return 1*np.matrix(mat)
def hessian_element(self, PV_i, PV_j): "Second derivative of REML function w.r.t two variance components" y, P = self.mm.y, self.P PViPVj = PV_i * PV_j a = 0.5 * np.trace(PViPVj) b = y.T * PViPVj * P * y return matrix.item(a - b)
def loglikelihood(self): n = self.mm.nobs() y, X, beta = self.mm.y, self.mm.X, self.beta resid = y - X * beta llik = -0.5 * (n*l2pi + logdet(self.V) + resid.T * self.Vinv * resid) return matrix.item(llik)
def loglikelihood(self): "Full loglikelihood of the model" n = self.mm.nobs() y, X, beta = self.mm.y, self.mm.X, self.beta resid = y - X * beta llik = -0.5 * (n*l2pi + logdet(self.V) + resid.T * self.Vinv * resid) return matrix.item(llik)
def full_loglikelihood(y, V, X, beta, Vinv=None): """ Returns the full loglikelihood of a mixed model Ref: SAS documentation for PROC MIXED """ if Vinv is None: Vinv = makeVinv(V) n = X.shape[0] fixefresids = y - X * beta llik = -0.5 * (n * l2pi + logdet(V) + fixefresids.T * Vinv * fixefresids) return matrix.item(llik)
def expectation_maximization(self): "Performs a round of Expectation-Maximization ML" resid, Vinv = self.resid, self.Vinv n = self.mm.nobs() coefficients = np.array([ matrix.item(resid.T * Vinv * rf.V_i * Vinv * resid - np.trace(Vinv * rf.V_i)) for rf in self.mm.random_effects]) levelsizes = np.array([x.nlevels for x in self.mm.random_effects]) delta = (self.parameters ** 2 / levelsizes) * coefficients return self.parameters + delta
def hessian_element(self, V_i, V_j): """ Second derivative of the full loglikelihood with regard to two variance components :param V_i: variance-covariance matrix of the first component :param V_i: matrix :param V_j: variance-covariance matrix of the second component :param V_j: matrix :returns: derivative :rtype: float """ resid, Vinv = self.resid, self.Vinv term1 = 0.5 * np.trace(Vinv * V_i * Vinv * V_j) term2 = resid.T * Vinv * V_i * Vinv * V_j * Vinv * resid return matrix.item(term1 - term2)
def loglikelihood(self): """ Returns the restricted loglikelihood for mixed model variance component estimation. References: Harville. 'Maximum Likelihood Approaches to Variance Component Estimation and to Related Problems' Journal of the American Statistical Association. (1977) (72):258 """ y, V, X, P, Vinv = self.mm.y, self.V, self.mm.X, self.P, self.Vinv n = X.shape[0] rank = np.linalg.matrix_rank(X) llik_restricted = -0.5 * (logdet(V.todense()) + logdet(X.transpose() * Vinv * X) + y.T * P * y + (n - rank) * l2pi) return matrix.item(llik_restricted)
def average_information_matrix(self): y = self.mm.y P = self.P Py = P * y varmats = [x.V_i for x in self.mm.random_effects] nrf = len(varmats) mat = np.zeros((nrf, nrf)) for i, V_i in enumerate(varmats): for j, V_j in enumerate(varmats): if j < i: continue element = 0.5 * y.T * P * V_i * P * V_j * Py element = matrix.item(element) mat[i, j] = element mat[j, i] = element return np.matrix(mat)
def minque(mm, starts=None, value=0, maxiter=200, tol=1e-4, verbose=False, return_after=1e300, return_vcs=False): """ MINQUE (MInimum Norm Quadratic Unbiansed Estimation). Only used for historical purposes or getting starting variance components for another maximization scheme. MINQUE gets variance component estimates by solving the equation Cz=t For d random effects z is a vector of variance compnents C is a dxd matrix with element C_ij trace(P * V_i * P * V_j) t is a column vector with row element i = y' * P * V_i * P * y M = I_n - (1/n) * ONES_n * ONES_n' (Ones_n is a row vector of all ones) Useful reference: J.W. Keele & W.R. Harvey (1988) "Estimation of components of variance and covariance by symmetric difference squaredand minimum norm quadratic unbiased estimation: a comparison" Journal of Animal Science Vol 67. No.2 p348-356 doi:10.2134/jas1989.672348x """ d = len(mm.random_effects) # the number of random effects if verbose: print('Maximizing model by MINQUE') if starts is not None: weights = np.array(starts) elif value == 0: # MINQUE(0) weights = np.zeros(d) weights[-1] = 1 elif value == 1: # MINQUE(1) weights = np.ones(d) vcs = np.var(mm.y - mm.X * mm.beta) * weights n = mm.nobs() y = mm.y if verbose: print(vcs) for i in range(maxiter): if i + 1 > return_after: return vcs V = mm._makeV(vcs=vcs.tolist()) Vinv = makeVinv(V) P = makeP(mm.X, Vinv) t = [matrix.item(y.T * P * ranef.V_i * P * y) for ranef in mm.random_effects] t = np.matrix(t).T # Make C C = [] for ranef_i in mm.random_effects: row = [np.trace(P * ranef_i.V_i * P * ranef_j.V_i) for ranef_j in mm.random_effects] C.append(row) C = np.matrix(C) new_vcs = scipy.linalg.solve(C, t).T[0] delta = (new_vcs / new_vcs.sum()) - (vcs / vcs.sum()) llik = restricted_loglikelihood(mm.y, V, mm.X, P, Vinv) if all(delta < tol): if return_vcs: return new_vcs mle = MLEResult(new_vcs, llik, 'MINQUE') return mle if verbose: print((i, llik, vcs)) vcs = new_vcs weights = vcs
def get_coef(rf): V_i = rf.V_i PVi = P * V_i return matrix.item(y.T * PVi * Py - np.trace(PVi))
def element(PVi): return 0.5 * np.trace(PVi) + matrix.item(y.T * PVi * Py)
def coef(rf): "gets the coefficients for em estimation" VinvV_i = Vinv * rf.V_i c1 = resid.T * VinvV_i * Vinvresid - np.trace(VinvV_i) return matrix.item(c1)
def hessian_element(self, V_i, V_j): resid, Vinv = self.resid, self.Vinv term1 = 0.5 * np.trace(Vinv * V_i * Vinv * V_j) term2 = resid.T * Vinv * V_i * Vinv * V_j * Vinv * resid return matrix.item(term1 - term2)
def hessian_element(self, PV_i, PV_j): y, P = self.mm.y, self.P PViPVj = PV_i * PV_j a = 0.5 * np.trace(PViPVj) b = y.T * PViPVj * P * y return matrix.item(a - b)