因子旋转的理论基础

  • 1. varimax 方法
  • 1.1 varimax的理论基础
  • 1.2 varimax的缺点
  • 2. promax方法
  • 2.1 promax方法的理论基础

  • 3. Transformation of correlated factors using a pattern matrix
  • 4. MATLAB 实现


写本博客的目的是:在使用varimax方法时不知道原理到底是什么?查了好多教材和网上的资料,都没有具体的理论推导,无法真正理解此方法的内核计算。最后终于在factor analysis as a statistical method书中的chapter6找到相关内容,并进行整理,最后给出相关matlab代码。

1. varimax 方法

1.1 varimax的理论基础

在varimax旋转方法出现之前,通常使用旋转的图形方法,目的是消除负的因子载荷(loading)并且尽可能少的非0因子载荷来描述数据。但这种方法具有一定的主观性。

varimax方法与图形方法在某种程度上具有相似的特点。 与原始的因子载荷相比,用这种方法对因子进行旋转得到的新的因子载荷要么相对大要么相对小。正如我们所看到的,这种旋转的方法是通过最大化loading的平方的一个特定函数并且迭代完成的。在Kaiser’s的原始旋转方法中,因子是成对地旋转(两个两个的旋转)直到loadings收敛到最终值。下面我们描述的是"The simultaneously factor varimax solution"(同时因子varimax解),显然,这个方法的最主要特点是在每一次迭代时,所有因子同时被旋转,并非Kaiser’s的成对成对地旋转。它比原始的varimax方法更快,并且没有累积的舍入误差(因为原始方法是成对旋转的)。

将 矩阵因子旋转的目的 python_因子旋转的目的 python 看做是未旋转的因子载荷,则因子旋转的目的 python_迭代_02的第因子旋转的目的 python_旋转矩阵_03列(即因子旋转的目的 python_因子旋转的目的 python_04 的第因子旋转的目的 python_旋转矩阵_03行)是一个因子旋转的目的 python_旋转矩阵_06维的列向量,用因子旋转的目的 python_因子旋转的目的 python_07表示。 定义因子旋转的目的 python_因子旋转的目的 python_08是一个因子旋转的目的 python_理论基础_09的正交旋转矩阵,它的第因子旋转的目的 python_迭代_10th列用因子旋转的目的 python_因子旋转的目的 python_11。新的旋转的因子载荷矩阵因子旋转的目的 python_迭代_12,可以通过如下计算:
因子旋转的目的 python_理论基础_13
因此,
因子旋转的目的 python_理论基础_14

定义一个标量因子旋转的目的 python_迭代_15

因子旋转的目的 python_理论基础_16

因子旋转的目的 python_迭代_17表示的是因子旋转的目的 python_迭代_18的第因子旋转的目的 python_迭代_10th列的因子载荷的平方和。

在同时varimax方法中的标准因子旋转的目的 python_因子旋转的目的 python_20被最大化,形式如下:

因子旋转的目的 python_理论基础_21
从这个定义中,我们可以看出:因子旋转的目的 python_因子旋转的目的 python_20代表的是因子旋转的目的 python_旋转矩阵_23与其对应的列均值的偏差平方和。这个因子旋转的目的 python_因子旋转的目的 python_20的最大化与因子旋转的目的 python_因子旋转的目的 python_08有关。因为因子旋转的目的 python_因子旋转的目的 python_08是正交阵,所以它的列满足如下条件:

因子旋转的目的 python_旋转矩阵_27

使用拉格朗日乘子的方法,表达式如下:

因子旋转的目的 python_因子旋转的目的 python_28

因子旋转的目的 python_旋转矩阵_29系数代表的是不确定的乘子,且因子旋转的目的 python_旋转矩阵_30.

因子旋转的目的 python_迭代_31关于因子旋转的目的 python_因子旋转的目的 python_32求导并令其等于0,求解如下:

因子旋转的目的 python_理论基础_33
其中,

因子旋转的目的 python_理论基础_34

经检查发现:因子旋转的目的 python_理论基础_35是下面矩阵的第因子旋转的目的 python_旋转矩阵_36th列:

因子旋转的目的 python_理论基础_37
其中,因子旋转的目的 python_因子旋转的目的 python_38是一个因子旋转的目的 python_旋转矩阵_39矩阵,其元素为因子旋转的目的 python_旋转矩阵_40因子旋转的目的 python_理论基础_41因子旋转的目的 python_理论基础_09的对称矩阵,且元素为因子旋转的目的 python_因子旋转的目的 python_43
因子旋转的目的 python_理论基础_44是对角矩阵,对角元为因子旋转的目的 python_理论基础_45.因此将因子旋转的目的 python_旋转矩阵_36的所有值都考虑进去,有:

因子旋转的目的 python_因子旋转的目的 python_47
其中,
因子旋转的目的 python_旋转矩阵_48

因此,最大化因子旋转的目的 python_因子旋转的目的 python_20的正交矩阵因子旋转的目的 python_因子旋转的目的 python_08满足如下等式:

因子旋转的目的 python_旋转矩阵_51

可以证明:如果因子旋转的目的 python_理论基础_41是正定的,由于因子旋转的目的 python_理论基础_41也是对称的,那么上述的等式对应于因子旋转的目的 python_因子旋转的目的 python_20的一个最大值。对上式左乘一个因子旋转的目的 python_理论基础_55,有:

因子旋转的目的 python_迭代_56
因此,当因子旋转的目的 python_因子旋转的目的 python_20被最大化时,等式右边应该是对称矩阵。因子旋转的目的 python_理论基础_41的第因子旋转的目的 python_迭代_10th个对角元为:

因子旋转的目的 python_迭代_60

基于因子旋转的目的 python_因子旋转的目的 python_20的定义,我们发现:因子旋转的目的 python_旋转矩阵_62就是因子旋转的目的 python_因子旋转的目的 python_20的最大值。

满足因子旋转的目的 python_理论基础_64因子旋转的目的 python_因子旋转的目的 python_08,因子旋转的目的 python_理论基础_41因子旋转的目的 python_迭代_67可以通过迭代获得。我们首先用一个近似的初始矩阵因子旋转的目的 python_因子旋转的目的 python_68,这将产生一个因子旋转的目的 python_迭代_18初始的近似因子旋转的目的 python_理论基础_70。并且,从因子旋转的目的 python_因子旋转的目的 python_71中,我们获得因子旋转的目的 python_因子旋转的目的 python_38,因子旋转的目的 python_理论基础_44因子旋转的目的 python_迭代_67的近似因子旋转的目的 python_因子旋转的目的 python_75,因子旋转的目的 python_因子旋转的目的 python_76因子旋转的目的 python_理论基础_77,x现在我们来求对称和正定矩阵因子旋转的目的 python_因子旋转的目的 python_78和第二个正交旋转矩阵因子旋转的目的 python_理论基础_79:

因子旋转的目的 python_迭代_80

注意对上述方程的等式两边分别左乘自己的转置矩阵,有如下等式:

因子旋转的目的 python_旋转矩阵_81
现在,因子旋转的目的 python_迭代_82因子旋转的目的 python_理论基础_09的对称和正定矩阵,因此我们可以用如下形式来表示:因子旋转的目的 python_旋转矩阵_84,其中因子旋转的目的 python_理论基础_85是正交矩阵,且因子旋转的目的 python_因子旋转的目的 python_86是具有正的对角元素的对角矩阵。因此我们可以用以下形式来表示因子旋转的目的 python_理论基础_87:

因子旋转的目的 python_理论基础_88

因子旋转的目的 python_迭代_89为:

因子旋转的目的 python_因子旋转的目的 python_90

然后用因子旋转的目的 python_迭代_89替代因子旋转的目的 python_理论基础_92,在对上述程序进行重复,我们会得到一个因子旋转的目的 python_因子旋转的目的 python_93的矩阵序列。可以证明:当因子旋转的目的 python_理论基础_92因子旋转的目的 python_因子旋转的目的 python_08充分接近时,序列收敛到因子旋转的目的 python_因子旋转的目的 python_08。且对应的矩阵序列因子旋转的目的 python_因子旋转的目的 python_97,它的因子旋转的目的 python_因子旋转的目的 python_98是不断上升并且最终达到一个稳定值,这个稳定值便是标准因子旋转的目的 python_因子旋转的目的 python_20的最大值。

在实际中,选择一个好的因子旋转的目的 python_因子旋转的目的 python_08的初始近似并不是必须的,通常情况下,因子旋转的目的 python_旋转矩阵_101 并且因子旋转的目的 python_理论基础_102,通常只需要很少的几次迭代就可以了。

Horst建议,在最大化过程开始时,最好对原始的因子载荷矩阵因子旋转的目的 python_因子旋转的目的 python_04按行标准化,这样每一个行向量因子旋转的目的 python_因子旋转的目的 python_104就被尺度化为单位长度了。在最大化结束时,当获得因子旋转的目的 python_因子旋转的目的 python_08后,最终的因子旋转的目的 python_迭代_18也需要重新按照行进行标准化,这样communalities的值才是正确的。

1.2 varimax的缺点

varimax有两个主要的缺点:

  • 第一个缺点:如果在旋转过程中加入其他额外的因子,则通过varimax方法获得的因子载荷的pattern可能会发生很大的变化。
  • 第二个缺点:当在因子中出现一个非常重要的一般因子(general factor)情况下,varimax方法可能并不work;

2. promax方法

在许多情况下,因子载荷的pattern可以通过进一步将因子转化为倾斜或者是相关的因子来进一步简化。可是这种简化需要付出一定的代价因为我们必须估计因子之间的相关系数,在对结果进行解释时也必须考虑这些因素。最终,人们的注意力转向了分析标准的发现,这些标准可以用来产生唯一的、不那么主观的解。有很多这样的方法,我们集中注意力介绍promax方法,因为它在实际中似乎效果更好。

2.1 promax方法的理论基础

promax方法的起始以varimax旋转的结果为起始。假设varimax方法产生的loadings是因子旋转的目的 python_旋转矩阵_107,并且构造另外一个因子旋转的目的 python_旋转矩阵_39的矩阵因子旋转的目的 python_旋转矩阵_109,它的元素按如下公式定义:

因子旋转的目的 python_旋转矩阵_110

其中,因子旋转的目的 python_理论基础_111是满足因子旋转的目的 python_迭代_112的某个整数。因此:如果因子旋转的目的 python_因子旋转的目的 python_113因子旋转的目的 python_旋转矩阵_114 ,否则,因子旋转的目的 python_理论基础_115因子旋转的目的 python_因子旋转的目的 python_116有相同的符号和相同的绝对值。

现在,我们向寻找一个变换矩阵因子旋转的目的 python_理论基础_85(此矩阵不必是正交的),对于因子旋转的目的 python_迭代_118因子旋转的目的 python_因子旋转的目的 python_119的第因子旋转的目的 python_迭代_10列与因子旋转的目的 python_理论基础_121的第因子旋转的目的 python_迭代_10列在最小二乘的意义上保持最大的一致性。一般来说,是当loadings相对较大时,因子旋转的目的 python_因子旋转的目的 python_119的目的使其更大;当loadings相对较小时,因子旋转的目的 python_因子旋转的目的 python_119使其更小。

因子旋转的目的 python_理论基础_125值的选择是一个反复试错的过程,但经验证明当简化loadings的pattern时,m大于4经常生成高度相关的因子,这在实际中是不被期望的。

因子旋转的目的 python_理论基础_121因子旋转的目的 python_理论基础_85的第因子旋转的目的 python_迭代_10列分别表示为因子旋转的目的 python_因子旋转的目的 python_129因子旋转的目的 python_因子旋转的目的 python_130。然后,对于每一个因子旋转的目的 python_迭代_10,我们选择的因子旋转的目的 python_因子旋转的目的 python_130使其最小化以下的表达式:
因子旋转的目的 python_旋转矩阵_133
上式关于因子旋转的目的 python_迭代_134求导得到:

因子旋转的目的 python_迭代_135

令其等于0得到:
因子旋转的目的 python_理论基础_136

因此,考虑所有的因子旋转的目的 python_迭代_10,有:

因子旋转的目的 python_因子旋转的目的 python_138

事实上,重新尺度化因子旋转的目的 python_理论基础_85的列是非常方便的,以至于转换后的因子具有单位方差。这可以通过找对角矩阵因子旋转的目的 python_理论基础_44来实现,且它的对角元素是正的,且满足:
因子旋转的目的 python_理论基础_141

代替因子旋转的目的 python_理论基础_85的变换矩阵为:
因子旋转的目的 python_旋转矩阵_143
转换的因子载荷矩阵为:

因子旋转的目的 python_因子旋转的目的 python_144

所以基于上述讨论有以下公式:

因子旋转的目的 python_旋转矩阵_145
其中,
因子旋转的目的 python_因子旋转的目的 python_146

因子旋转的目的 python_理论基础_147是变换矩阵的协方差矩阵。由因子旋转的目的 python_理论基础_44的定义,很明显,因子旋转的目的 python_理论基础_147具有单位对角元素,因此因子旋转的目的 python_理论基础_147是新因子(被标准化后)的相关矩阵。

3. Transformation of correlated factors using a pattern matrix

4. MATLAB 实现

function [B,T] = rotateFactors(A,varargin)
if nargin > 1
    [varargin{:}] = convertStringsToChars(varargin{:});
end

names = {'method' 'normalize' 'reltol' 'maxit'};
dflts = {'varimax' [] [] []};
[method,normalize,reltol,maxit] = internal.stats.parseArgs(names, dflts, varargin{:});

methodNames = {'varimax'};
[method,~] = internal.stats.getParamVal(method,methodNames,'Method');

switch method
    case 'varimax'
        [B, T] = varimax(A, normalize, reltol, maxit);
end

end

%-----------------------------------------------------------------------

function [B, T] = varimax(A, normalize, reltol, maxit)
% VARIMAX method of orthogonal rotation of FA or PCA loadings.

[d, m] = size(A);

% Defaults to normalized varimax rotation
if nargin < 2 || isempty(normalize), normalize = 'on'; end
if nargin < 3 || isempty(reltol), reltol = sqrt(eps(class(A))); end
if nargin < 4 || isempty(maxit), maxit = 250; end

% Normalize the factor loadings
switch normalize
case {'on',1}
    h = sqrt(sum(A.^2, 2));
    A = bsxfun(@rdivide, A, h);
case {'off',0}
%     A = A;
otherwise
    error(message('stats:rotatefactors:BadNormalize'));
end

T = eye(m); % the intial rotation matrix is identity.
R = zeros(m); % the coefficient matrix

converged = false;
for n = 1:maxit
    Aold = A;
    A = Aold * T;
    D = sum(A.^2,1); % the sum of square of the column of B
    C = A.^3;
    P = Aold'*(C-(1/d).*A.*D);
    [U,L] = svd(P'*P);
    Rold = R;
    R = U.*sqrt(diag(L))'*U'; 
    T = P/R;
    
    if (trace(R)-trace(Rold)< reltol)
        B = A;
        converged = true;
        break;
    end
end  

if ~converged
    error(message('stats:rotatefactors:IterationLimit'));
end

% Unnormalize the rotated loadings
switch normalize
    case {'on',1}
        B = bsxfun(@times, B, h);
end
end