Velicer's MAP Test results in 0 factors for Dynamic Factor Model notebook
martinvonk opened this issue · 1 comments
martinvonk commented
David and I get different results dependent on our machines. They get 0 factors with Velicer's MAP test while I get 1 (as intended originally). Velicer's MAP test code:
metran/metran/factoranalysis.py
Lines 220 to 312 in d8aef35
def _maptest(cov, eigvec, eigval): | |
"""Internal method to run Velicer's MAP test. | |
Determines the number of factors to be used. This method includes | |
two variations of the MAP test: the orginal and the revised MAP test. | |
Parameters | |
---------- | |
cov : numpy.ndarray | |
Covariance matrix. | |
eigvec : numpy.ndarray | |
Matrix with columns eigenvectors associated with eigenvalues. | |
eigval : numpy.ndarray | |
Vector with eigenvalues in descending order. | |
Returns | |
------- | |
nfacts : integer | |
Number factors according to MAP test. | |
nfacts4 : integer | |
Number factors according to revised MAP test. | |
References | |
---------- | |
The original MAP test: | |
Velicer, W. F. (1976). Determining the number of components | |
from the matrix of partial correlations. Psychometrika, 41, 321-327. | |
The revised (2000) MAP test i.e., with the partial correlations | |
raised to the 4rth power (rather than squared): | |
Velicer, W. F., Eaton, C. A., and Fava, J. L. (2000). Construct | |
explication through factor or component analysis: A review and | |
evaluation of alternative procedures for determining the number | |
of factors or components. Pp. 41-71 in R. D. Goffin and | |
E. Helmes, eds., Problems and solutions in human assessment. | |
Boston: Kluwer. | |
""" | |
nvars = len(eigval) | |
fm = np.array([np.arange(nvars, dtype=float), np.arange(nvars, dtype=float)]).T | |
np.put( | |
fm, | |
[0, 1], | |
((np.sum(np.sum(np.square(cov))) - nvars) / (nvars * (nvars - 1))), | |
) | |
fm4 = np.copy(fm) | |
np.put( | |
fm4, | |
[0, 1], | |
( | |
(np.sum(np.sum(np.square(np.square(cov)))) - nvars) | |
/ (nvars * (nvars - 1)) | |
), | |
) | |
for m in range(nvars - 1): | |
biga = np.atleast_2d(eigvec[:, : m + 1]) | |
partcov = cov - np.dot(biga, biga.T) | |
# exit function with nfacts=1 if diag partcov contains negatives | |
if np.amin(np.diag(partcov)) < 0: | |
return 1, 1 | |
d = np.diag((1 / np.sqrt(np.diag(partcov)))) | |
pr = np.dot(d, np.dot(partcov, d)) | |
np.put( | |
fm, | |
[m + 1, 1], | |
((np.sum(np.sum(np.square(pr))) - nvars) / (nvars * (nvars - 1))), | |
) | |
np.put( | |
fm4, | |
[m + 1, 1], | |
( | |
(np.sum(np.sum(np.square(np.square(pr)))) - nvars) | |
/ (nvars * (nvars - 1)) | |
), | |
) | |
minfm = fm[0, 1] | |
nfacts = 0 | |
minfm4 = fm4[0, 1] | |
nfacts4 = 0 | |
for s in range(nvars): | |
fm[s, 0] = s | |
fm4[s, 0] = s | |
if fm[s, 1] < minfm: | |
minfm = fm[s, 1] | |
nfacts = s | |
if fm4[s, 1] < minfm4: | |
minfm4 = fm4[s, 1] | |
nfacts4 = s | |
return nfacts, nfacts4 |
On my device:
eigvec = array([[ 0.96750358, -0.25285732], [ 0.96750358, 0.25285732]])
eigvec[0,0] = 0.9675035797467857
eigvec[0,1] = -0.25285731782401605
eigvec[1,0] = 0.9675035797467855
eigvec[1,1] = 0.2528573178240161
Later on this results in:
minfm = 1.000000000000007
minfm4 = 1.0000000000000142
which yields True for (if s = 1):
if fm[s, 1] < minfm:`
with fm[s, 1]
= 1.0
martinvonk commented
Resolved. Reinstalling the environment on Davíd's machine solved the issue.