Bug 15848 - Factanal Print function semi-random signs for inter-factor correlations
Summary: Factanal Print function semi-random signs for inter-factor correlations
Status: NEW
Alias: None
Product: R
Classification: Unclassified
Component: Analyses (show other bugs)
Version: R 3.1.0
Hardware: All Windows 64-bit
: P5 normal
Assignee: R-core
URL:
Depends on:
Blocks:
 
Reported: 2014-07-02 02:01 UTC by j.braeken
Modified: 2014-07-02 15:23 UTC (History)
2 users (show)

See Also:


Attachments
SampleOutput: Misprint & Regular (3.34 KB, text/plain)
2014-07-02 02:01 UTC, j.braeken
Details

Note You need to log in before you can comment on or make changes to this bug.
Description j.braeken 2014-07-02 02:01:14 UTC
Created attachment 1621 [details]
SampleOutput:  Misprint & Regular

For any oblique rotation factanal, 
the sign of the interfactor correlations being printed is sometimes wrong, 
and yes REGARDLESS of any additional rotational freedom (scale/sign inversion of factors) 


I have added a .txt file with sample output to illustrate what I mean and will summarize the issue below:


EXAMPLE DATA
- Simple structure 2 factors
- Sample correlations are ALL positive

Factanal results can be one of two possibilities: The correct result OR a MISPRINT

MISPRINT
1) ALL defining factor loadings are positive BUT negative interfactor correlation
=> this is clearly impossible as some of the model predicted correlations would become negative!
2) Funnily enough the model-implied correlations reported by factanal are correct (so all positive) and also the correlation between factor score estimates is correct



=> so it seems that the issue is merely with the print.method :)
Comment 1 Peter Dalgaard 2014-07-02 10:56:39 UTC
There is not enough information in the uploaded output. 

What was the original data, which commands were executed, and which oblique rotation was performed?
Comment 2 j.braeken 2014-07-02 11:35:20 UTC
##################################################################################
###factanal: bug with printing sign interfactor correlations
###Might need to rerun the example code a couple of times, 
###because the bug is not always occuring!
##################################################################################
	###Simulate data for a homogeneous simple structure oblique factormodel
	n = 250
	J = 10
	Q = 2
	S = matrix(c(1,.4,.4,1),2,2)
	A = matrix(c(rep(c(.7,0),each=J/2),rep(c(0,.7),each=J/2)),J,Q)
	###R is the resulting correlation matrix showing a positive manifold
	R=A%*%S%*%t(A);diag(R)=1;R
	Y=matrix(rnorm(n*J),n,J)%*%chol(R); 
	Y=sweep(sweep(Y,2,apply(Y,2,mean)),2,apply(Y,2,sd),"/")
	###GPA for wide list of rotations: same issue would pop up for geominQ, promax or any other oblique rotation
	library(GPArotation)  
	EFA <-factanal(Y,Q,rotation="oblimin",scores = "Bartlett")
	EFA
	###It's merely a print issue, because model-implied statistics are still correct and not affected by misprint of the correlation sign
	EFA$correlation
	cor(EFA$scores)
Comment 3 Peter Dalgaard 2014-07-02 13:15:29 UTC
OK, I can reproduce it. I don't think it is random, nor misprint, though.

In stats:::factanal we have

    if (rotation != "none") {
        rot <- do.call(rotation, c(list(load), cn$rotate))
        load <- if (is.list(rot)) {
            load <- rot$loadings
            fit$rotmat <- if (inherits(rot, "GPArotation")) 
                t(solve(rot$Th))
            else rot$rotmat
            rot$loadings
        }
        else rot
    }
    fit$loadings <- sortLoadings(load)

with sortLoadings being

function (Lambda) 
{
    cn <- colnames(Lambda)
    Phi <- attr(Lambda, "covariance")
    ssq <- apply(Lambda, 2L, function(x) -sum(x^2))
    Lambda <- Lambda[, order(ssq), drop = FALSE]
    colnames(Lambda) <- cn
    neg <- colSums(Lambda) < 0
    Lambda[, neg] <- -Lambda[, neg]
    if (!is.null(Phi)) {
        unit <- ifelse(neg, -1, 1)
        attr(Lambda, "covariance") <- unit %*% Phi[order(ssq), 
            order(ssq)] %*% unit
    }
    Lambda
}



and I believe that the issue is that sortLoadings can switch the signs on and order of factors without modifying the rotation matrix, hence getting the loadings out of sync with the correlations.

A potential fix is to sort before rotating, but I am unsure whether that defeats the purpose of  sortLoadings.
Comment 4 Peter Dalgaard 2014-07-02 14:19:12 UTC
There's more:

sortLoadings appears to keep track of 

attr(Lambda, "covariance")

but as far as I can tell, this attribute is only ever set if it was set already! Instead, print.factanal uses $rotmat, which is _not_ tracked.
Comment 5 j.braeken 2014-07-02 14:33:52 UTC
(In reply to Peter Dalgaard from comment #4)
> There's more:
> 
> sortLoadings appears to keep track of 
> 
> attr(Lambda, "covariance")
> 
> but as far as I can tell, this attribute is only ever set if it was set
> already! Instead, print.factanal uses $rotmat, which is _not_ tracked.

Not sure whethere I understand correctly, but the part on

attr(Lambda, "covariance") <- unit %*% Phi[order(ssq), order(ssq)] %*% unit

seems to be intended to "correct" the correlation signs, but it tries to store this in an attribute that does not exist?
Comment 6 Peter Dalgaard 2014-07-02 15:23:59 UTC
Yup, that's what it looks like, but at this point I'd like someone who knows their way around the code to take a look.

The rotmat business was the content of 

----
r54989 | ripley | 2011-03-24 11:38:05 +0100 (Thu, 24 Mar 2011) | 1 line

fulfill wish of PR#12754
----

and the rotate-before-sort issue seems to have been present in the original contributed code. 

Incidentally, the obvious workaround for now is to just run the analysis with an orthogonal rotation (e.g. none) and then oblimin(loadings(EFA)).