Source code for statsmodels.multivariate.factor_rotation._analytic_rotation

# -*- coding: utf-8 -*-
"""
This file contains analytic implementations of rotation methods.
"""

from __future__ import division
from __future__ import print_function

import numpy as np
import scipy as sp


[docs]def target_rotation(A, H, full_rank=False): r""" Analytically performs orthogonal rotations towards a target matrix, i.e., we minimize: .. math:: \phi(L) =\frac{1}{2}\|AT-H\|^2. where :math:`T` is an orthogonal matrix. This problem is also known as an orthogonal Procrustes problem. Under the assumption that :math:`A^*H` has full rank, the analytical solution :math:`T` is given by: .. math:: T = (A^*HH^*A)^{-\frac{1}{2}}A^*H, see Green (1952). In other cases the solution is given by :math:`T = UV`, where :math:`U` and :math:`V` result from the singular value decomposition of :math:`A^*H`: .. math:: A^*H = U\Sigma V, see Schonemann (1966). Parameters ---------- A : numpy matrix (default None) non rotated factors H : numpy matrix target matrix full_rank : boolean (default FAlse) if set to true full rank is assumed Returns ------- The matrix :math:`T`. References ---------- [1] Green (1952, Psychometrika) - The orthogonal approximation of an oblique structure in factor analysis [2] Schonemann (1966) - A generalized solution of the orthogonal procrustes problem [3] Gower, Dijksterhuis (2004) - Procustes problems """ ATH = A.T.dot(H) if full_rank or np.linalg.matrix_rank(ATH) == A.shape[1]: T = sp.linalg.fractional_matrix_power(ATH.dot(ATH.T), -1/2).dot(ATH) else: U, D, V = np.linalg.svd(ATH, full_matrices=False) T = U.dot(V) return T
[docs]def procrustes(A, H): r""" Analytically solves the following Procrustes problem: .. math:: \phi(L) =\frac{1}{2}\|AT-H\|^2. (With no further conditions on :math:`H`) Under the assumption that :math:`A^*H` has full rank, the analytical solution :math:`T` is given by: .. math:: T = (A^*HH^*A)^{-\frac{1}{2}}A^*H, see Navarra, Simoncini (2010). Parameters ---------- A : numpy matrix non rotated factors H : numpy matrix target matrix full_rank : boolean (default False) if set to true full rank is assumed Returns ------- The matrix :math:`T`. References ---------- [1] Navarra, Simoncini (2010) - A guide to emprirical orthogonal functions for climate data analysis """ return np.linalg.inv(A.T.dot(A)).dot(A.T).dot(H)
[docs]def promax(A, k=2): r""" Performs promax rotation of the matrix :math:`A`. This method was not very clear to me from the literature, this implementation is as I understand it should work. Promax rotation is performed in the following steps: * Deterine varimax rotated patterns :math:`V`. * Construct a rotation target matrix :math:`|V_{ij}|^k/V_{ij}` * Perform procrustes rotation towards the target to obtain T * Determine the patterns First, varimax rotation a target matrix :math:`H` is determined with orthogonal varimax rotation. Then, oblique target rotation is performed towards the target. Parameters ---------- A : numpy matrix non rotated factors k : float parameter, should be positive References ---------- [1] Browne (2001) - An overview of analytic rotation in exploratory factor analysis [2] Navarra, Simoncini (2010) - A guide to emprirical orthogonal functions for climate data analysis """ assert k > 0 # define rotation target using varimax rotation: from ._wrappers import rotate_factors V, T = rotate_factors(A, 'varimax') H = np.abs(V)**k/V # solve procrustes problem S = procrustes(A, H) # np.linalg.inv(A.T.dot(A)).dot(A.T).dot(H); # normalize d = np.sqrt(np.diag(np.linalg.inv(S.T.dot(S)))) D = np.diag(d) T = np.linalg.inv(S.dot(D)).T return A.dot(T), T