def _fa_paf(self, smc = True): cor = self.cor.copy() if smc: np.fill_diagonal(cor, utils.SMC(cor)) eig_val1 = la.eigvalsh(self.cor) eig_val1.sort() comm = np.sum(np.diag(cor)) err = comm comm_list = [] i = 0 while err > self.lower: eig_val, eig_vec = utils.eigenh_sorted(cor) if (self.n_factors > 1): loadings = eig_vec[:, :self.n_factors].dot( np.diag(np.sqrt(eig_val[:self.n_factors]))) else: loadings = eig_vec[:, 0] * np.sqrt(eig_val[0]) model = loadings.dot(loadings.T) new = np.diag(model) comm1 = np.sum(new) np.fill_diagonal(cor, new) err = np.abs(comm - comm1) if np.iscomplex(err): print('Imaginary eigenvalue condition' ' occurred!') break comm = comm1 comm_list.append(comm1) i += 1 if i > 1000: print('maximum iteration exceeded') err = 0 self.loadings = loadings
def _fa_out(self, Psi, S, q): sc = np.diag(1 / np.sqrt(Psi)) Sstar = sc.dot(S).dot(sc) eig_val, eig_vec = utils.eigenh_sorted(Sstar) L = eig_vec[:, :q] load = L.dot(np.diag(np.sqrt(np.maximum(eig_val[:q] - 1, 0)))) return np.diag(np.sqrt(Psi)).dot(load)
def ml_gradient(Psi, S, q): sc = np.diag(1 / np.sqrt(Psi)) Sstar = sc.dot(S).dot(sc) eig_val, eig_vec = utils.eigenh_sorted(Sstar) L = eig_vec[:, :q] load = L.dot(np.diag(np.sqrt(np.maximum(eig_val[:q] - 1, 0)))) load = np.diag(np.sqrt(Psi)).dot(load) g = load.dot(load.T) + np.diag(Psi) - S return np.diag(g) / (Psi ** 2)
def residual_gradient(Psi, S, n_factors, Sinv, method, obs): sc = np.diag(1 / np.sqrt(Psi)) Sstar = sc.dot(S).dot(sc) eig_val, eig_vec = utils.eigenh_sorted(Sstar) L = eig_vec[:, :n_factors] load = L.dot(np.diag(np.sqrt(np.fmax(eig_val[:n_factors] - 1, 0)))) load = np.diag(np.sqrt(Psi)).dot(load) g = load.dot(load.T) + np.diag(Psi) - S return np.diag(g) / Psi ** 2
def _fa_stats(self): X = self.cor.copy() loadings = self.loadings.copy() if self.Phi is None: model = loadings.dot(loadings.T) else: Phi = self.Phi.copy() model = loadings.dot(Phi).dot(loadings.T) obs = self.n_obs pairwise_obs = self.pairwise_obs alpha = self.conf_int var = X.shape[1] n_factors = loadings.shape[1] residual = X - model self.residual = residual.copy() X2 = np.sum(X ** 2) Xstar2 = np.sum(residual ** 2) self.dof = var * (var - 1) / 2 - var * n_factors + ( n_factors * (n_factors - 1) / 2) X2_off = X2 - np.trace(X) np.fill_diagonal(residual, 0) if self.pairwise_obs is None: Xstar_off = np.sum(residual ** 2) self.ENull = X2_off * obs self.chi = Xstar_off * obs self.rms = np.sqrt(Xstar_off / (var * (var - 1))) self.harmonic = obs else: Xstar_off = np.sum(residual ** 2 * pairwise_obs) X2_off = (X * X * pairwise_obs) X2_off = np.sum(X2_off) - np.trace(X2_off) self.chi = Xstar_off self.harmonic = utils.harmonic_mean(np.hstack(pairwise_obs.T)) self.rms = np.sqrt(Xstar_off / (self.harmonic * var * (var - 1))) if self.dof > 0: self.EPVAL = sp.stats.chi2.sf(self.chi, self.dof) self.crms = np.sqrt(Xstar_off / (2 * self.harmonic * self.dof)) self.EBIC = self.chi - self.dof * np.log(obs) self.ESABIC = self.chi - self.dof * np.log( (self.harmonic + 2) / 24) else: self.EPVAL = None self.crms = None self.EBIC = None self.ESABIC = None self.fit_result = 1 - Xstar2 / X2 self.fit_off = 1 - Xstar_off / X2_off self.sd = np.std(residual, ddof = 1) self.complexity = np.apply_along_axis( lambda x: np.sum(x ** 2), 1, loadings) ** 2 \ / np.apply_along_axis(lambda x: np.sum(x ** 4), 1, loadings) model[np.diag_indices_from(model)] = np.diag(X) model = utils.smooth_corrcoef(model) X = utils.smooth_corrcoef(X) model_inv = la.solve(model, X) self.objective = np.sum(np.diag(model_inv)) \ - np.log(la.det(model_inv)) - var chisq = self.objective * ((obs - 1) - (2 * var + 5) / 6 \ - (2 * n_factors) / 3) if chisq < 0: self.statistic = 0 else: self.statistic = chisq if self.dof > 0: self.PVAL = sp.stats.chi2.sf(self.statistic, self.dof) else: self.PVAL = None F0 = np.sum(np.diag(X)) - np.log(la.det(X)) - var Fm = self.objective Mm = Fm / (var * (var - 1) / 2 - var * n_factors + \ (n_factors * (n_factors - 1) / 2)) M0 = F0 * 2 / (var * (var - 1)) nm = (obs - 1) - (2 * var + 5) / 6 - (2 * n_factors) / 3 self.null_model = F0 self.null_dof = var * (var - 1) / 2 self.null_chi2 = F0 * ((obs - 1) - (2 * var + 5) / 6) self.TLI = (M0 - Mm) / (M0 - 1 / nm) if not np.isnan(self.TLI) and self.TLI > 1: self.F0 = 1 if self.dof > 0 and not np.isnan(self.objective): RMSEA = np.sqrt( np.max(self.objective / self.dof - 1 / (obs - 1), 0)) tail = alpha / 2 chi_max = max(obs, chisq) + 2 * obs while chi_max > 1: opt_res = sp.optimize.minimize_scalar( lambda x: \ (tail - sp.stats.ncx2.cdf(chisq, self.dof, x)) ** 2, bracket = (0, chi_max)) if np.sqrt(opt_res.fun) < tail / 100: break chi_max = chi_max / 2 if chi_max <= 1: lamU = np.nan chi_max = np.nan else: lamU = opt_res.x chi_max = lamU while (chi_max > 1): opt_res = sp.optimize.minimize_scalar( lambda x: \ (1 - tail - sp.stats.ncx2.cdf(chisq, self.dof, x)) ** 2, bracket = (0, chi_max)) if np.sqrt(opt_res.fun) < tail / 100: break chi_max = chi_max / 2 if chi_max <= 1: lamL = np.nan else: lamL = opt_res.x RMSEA_U = np.sqrt(lamU / (obs * self.dof)) RMSEA_L = min(np.sqrt(lamL / (obs * self.dof)), RMSEA) self.RMSEA = [RMSEA, RMSEA_L, RMSEA_U, alpha] self.BIC = chisq - self.dof * np.log(obs) self.SABIC = chisq - self.dof * np.log((obs + 2) / 24) if self.Phi is not None: loadings = loadings.dot(Phi) try: W = la.solve(X, loadings) except la.LinAlgError: print('Correlation matrix is singular; approximation used') eigval, eigvec = utils.eigenh_sorted(X) if np.sum(np.iscomplex()) == 0: print('Complex eigenvalues are detected. results are suspect.') else: eigval[eigval < np.finfo(np.float64).eps] = 100 * np.finfo( np.float64).eps X = eigvec.dot(np.diag(eigval)).dot(eigvec.T) np.fill_diagonal(X, 1) try: W = la.solve(X, loadings) except la.LinAlgError: print('Failed to calculate the beta weights' ' for factor score estimates') W = np.diag(np.ones((var, ))) R2 = np.diag(W.T.dot(loadings)) if np.prod(R2) < 0: print('Factor scoring weights matrix is probably singular;' ' Factor score estimate results are likely to incorrect.' ' Try a different factor extraction method.') R2[np.abs(R2) > 1] = np.nan R2[R2 <= 0] = np.nan if np.nanmax(R2) > (1 + np.finfo(np.float64).eps): print('The estimated weights for the factor scores' ' are probably incorrect.' 'Try a different factor extraction method.') self.Rscores = utils.cov_to_cor(W.T.dot(X).dot(W)) self.R2 = R2 keys = utils.factor_to_cluster(loadings) covar = keys.T.dot(X).dot(keys) if n_factors > 1 and covar.shape[1] > 1: sdinv = np.diag(1 / np.sqrt(np.diag(covar))) cluster_corr = sdinv.dot(covar).dot(sdinv) valid = loadings.T.dot(keys).dot(sdinv) self.valid = np.diag(valid) self.score_corr = cluster_corr else: sdinv = 1 / np.sqrt(covar) if (sdinv.shape[0] == 1): sdinv = np.diag(sdinv) self.valid = loadings.T.dot(keys).dot(sdinv) self.weights = W