Não está totalmente claro para mim que o que você está perguntando é o que realmente precisa: uma etapa comum de pré-processamento no aprendizado de máquina é redução de dimensionalidade + clareamento, o que significa fazer PCA e padronizar os componentes, nada mais. No entanto, vou me concentrar na sua pergunta conforme ela é formulada, porque é mais interessante.
Seja a matriz de dados centralizada com pontos de dados em linhas e variáveis em colunas. O PCA equivale a decomposição de valor singular onde executar a redução de dimensionalidade mantemos apenas componentes. Uma "rotação fatorial" ortogonal desses componentes implica escolher uma matriz ortogonal e conectá-lo à decomposição:Xn×d
X=USV⊤≈UkSkV⊤k,
kk×kRX≈UkSkV⊤k=UkRR⊤SkV⊤k=n−1−−−−−√U⊤kRRotatedstandardized scores⋅R⊤SkV⊤k/n−1−−−−−√Rotated loadings⊤.
Aqui são componentes padronizados rotacionados e o segundo termo representa cargas rotacionadas transpostas. A variação de cada componente após a rotação é dada pela soma dos quadrados do vetor de carregamento correspondente; antes da rotação é simplesmente . Após a rotação, é outra coisa.
n−1−−−−−√UkRs2i/(n−1)
Agora, estamos prontos para formular o problema em termos matemáticos: considerando cargas não rotacionadas , encontre a matriz de rotação modo que as cargas rotadas, , possui soma igual de quadrados em cada coluna.L=VkSk/n−1−−−−−√RLR
Vamos resolver isso. As somas de quadrados da coluna após a rotação são iguais aos elementos diagonais de Isso faz sentido: a rotação simplesmente redistribui as variações dos componentes, que são originalmente fornecidos por , entre eles, de acordo com esta fórmula. Precisamos redistribuí-los para que todos se tornem iguais ao seu valor médio .
(LR)⊤LR=R⊤S2n−1R.
s2i/(n−1)μ
Não acho que exista uma solução de formulário fechado para isso e, de fato, existem muitas soluções diferentes. Mas uma solução pode ser facilmente criada de maneira sequencial:
- Pegue o primeiro componente e o ésimo componente. O primeiro possui a variação e o último possui a variação .kσmax>μσmin<μ
- Gire apenas esses dois para que a variação do primeiro se torne igual a . A matriz de rotação em 2D depende apenas de um parâmetro e é fácil escrever a equação e calcular o necessário . De fato, e após a transformação, o primeiro PC terá variação do qual obtemos imediatamenteμθθ
R2D=(cosθ−sinθsinθcosθ)
cos2θ⋅σmax+sin2θ⋅σmin=cos2θ⋅σmax+(1−cos2θ)⋅σmin=μ,
cos2θ=μ−σminσmax−σmin.
- O primeiro componente está pronto, possui variação .μ
- Prossiga para o próximo par, levando o componente com a maior variação e o componente com a menor variação. Vá para o 2.
Isso redistribuirá todas as variações igualmente por uma sequência de rotações 2D. A multiplicação de todas essas matrizes de rotação resultará no geral .(k−1)R
Exemplo
Considere a seguinte matriz :A variação média é . Meu algoritmo continuará da seguinte maneira:S2/(n−1)
⎛⎝⎜⎜⎜10000060000300001⎞⎠⎟⎟⎟.
5
Etapa 1: gire PC1 e PC4 para que PC1 obtenha variação . Como resultado, PC4 obtém a variação .51+(10−5)=6
Etapa 2: gire PC2 (nova variação máxima) e PC3 para que PC2 obtenha a variação . Como resultado, PC3 obtém variação .53+(6−5)=4
Etapa 3: gire PC4 (nova variação máxima) e PC3 para que o PC4 obtenha a variação . Como resultado, PC3 obtém variação .54+(6−1)=5
Feito.
Eu escrevi o script Matlab que implementa esse algoritmo (veja abaixo). Para esta matriz de entrada, a sequência dos ângulos de rotação é:
48.1897 35.2644 45.0000
Desvios de componentes após cada etapa (em linhas):
10 6 3 1
5 6 3 6
5 5 4 6
5 5 5 5
A matriz de rotação final (produto de três matrizes de rotação 2D):
0.6667 0 0.5270 0.5270
0 0.8165 0.4082 -0.4082
0 -0.5774 0.5774 -0.5774
-0.7454 0 0.4714 0.4714
E a matriz final é:(LR)⊤LR
5.0000 0 3.1623 3.1623
0 5.0000 1.0000 -1.0000
3.1623 1.0000 5.0000 1.0000
3.1623 -1.0000 1.0000 5.0000
Aqui está o código:
S = diag([10 6 3 1]);
mu = mean(diag(S));
R = eye(size(S));
vars(1,:) = diag(S);
Supdated = S;
for i = 1:size(S,1)-1
[~, maxV] = max(diag(Supdated));
[~, minV] = min(diag(Supdated));
w = (mu-Supdated(minV,minV))/(Supdated(maxV,maxV)-Supdated(minV,minV));
cosTheta = sqrt(w);
sinTheta = sqrt(1-w);
R2d = eye(size(S));
R2d([maxV minV], [maxV minV]) = [cosTheta sinTheta; -sinTheta cosTheta];
R = R * R2d;
Supdated = transpose(R2d) * Supdated * R2d;
vars(i+1,:) = diag(Supdated);
angles(i) = acosd(cosTheta);
end
angles %// sequence of 2d rotation angles
round(vars) %// component variances on each step
R %// final rotation matrix
transpose(R)*S*R %// final S matrix
Aqui está o código em Python fornecido pelo @feilong:
def amoeba_rotation(s2):
"""
Parameters
----------
s2 : array
The diagonal of the matrix S^2.
Returns
-------
R : array
The rotation matrix R.
Examples
--------
>>> amoeba_rotation(np.array([10, 6, 3, 1]))
[[ 0.66666667 0. 0.52704628 0.52704628]
[ 0. 0.81649658 0.40824829 -0.40824829]
[ 0. -0.57735027 0.57735027 -0.57735027]
[-0.74535599 0. 0.47140452 0.47140452]]
http://stats.stackexchange.com/a/177555/87414
"""
n = len(s2)
mu = s2.mean()
R = np.eye(n)
for i in range(n-1):
max_v, min_v = np.argmax(s2), np.argmin(s2)
w = (mu - s2[min_v]) / (s2[max_v] - s2[min_v])
cos_theta, sin_theta = np.sqrt(w), np.sqrt(1-w)
R[:, [max_v, min_v]] = np.dot(
R[:, [max_v, min_v]],
np.array([[cos_theta, sin_theta], [-sin_theta, cos_theta]]))
s2[[max_v, min_v]] = [mu, s2[max_v] + s2[min_v] - mu]
return R
Observe que esse problema é completamente equivalente ao seguinte: considerando variáveis não correlacionadas com variâncias , encontre uma rotação (isto é, uma nova base ortogonal) que produzirá variáveis com variâncias iguais (mas é claro que não estão mais correlacionadas).kσ2ik