| 
 
	积分4154贡献 精华在线时间 小时注册时间2017-2-19最后登录1970-1-1 
 | 
 
| 
花了几天时间研究了下python版本的reof,抛砖引玉,时间仓促还没用真实气象数据验证,鉴于本人水平有限可能许多地方理解有出入,重在理解。
x
登录后查看更多精彩内容~您需要 登录 才可以下载或查看,没有帐号?立即注册 
  复制代码import numpy as np 
from tqdm import tqdm
#生成一个玩具数据集,840个站点1200个时次的数据
x  = np.random.randint(1,20,size=(840,1200))
def standardization(data):
    """
    标准化资料阵
    """
    mu = np.mean(data, axis=1,keepdims = True)
    #print(mu.shape)
    m = np.tile(mu,(1,data.shape[1]))
    #print(m.shape)
    sigma = np.std(data, axis=1,keepdims = True)
    s = np.tile(sigma,(1,data.shape[1]))
    fengzi = (data - m)
    fengmu = s
    return fengzi / fengmu
#对资料阵标准化
data = standardization(x)
#计算协方差阵,也时相关阵
covx = 1.0/x.shape[1]*np.matmul(x,x.T)
#covx = np.cov(x,rowvar=1)
print(covx.shape)
#计算特征值和特征向量
eigenvalues,eigenvecter =  np.linalg.eig(covx)
#print(np.matmul(eigenvecter.T,eigenvecter))
print(eigenvecter[:,0].var())
#根据特征值(方差贡献排序)
idx = np.argsort(eigenvalues)[::-1]
eigenvecter = eigenvecter [:,idx]
-----------------------------------------------------------------
#根据累计方差贡献大于85%来确定待旋转的特征向量
totalvar = np.sum(eigenvalues)
cumsum = 0
#eigvalue_sort  = sorted(eigenvalues, reverse=True)
eigvalue_sort = eigenvalues[idx]
#print(eigvalue_sort)
for number,i in enumerate(eigvalue_sort):
    cumsum += i/totalvar
    if cumsum > 0.85:
        break
#print(number) 
-------------------------------------------------
rotate_eigvector = eigenvecter[:,:number+1]
eigvalue_sort = eigvalue_sort[:number+1]
#极大方差旋转,刚性旋转,轴是正交的
#1.使用wiki里的迭代解法,但无论用哪个版本都需要将特征向量转变成载荷矩阵
loadings = np.dot(rotate_eigvector, np.diag(np.sqrt(eigvalue_sort)))
#loadings = eVec_corr3*np.sqrt(eVal_corr3)
n_rows, n_cols = loadings.shape
#对loadings标准化,这里是横向给每个元素除共同度的开根也就是h
normalized_mtx = np.apply_along_axis(lambda x: np.sqrt(np.sum(x**2)), 1, loadings.copy())
normlize_loadings = (loadings.T / normalized_mtx).T
#print(normlize_loadings[:,0].mean())
#print((loadings[:,0]*loadings[:,1]).mean())
#assert loadings == n]ormlize_loadings
#初始化旋转矩阵,为1的对角矩阵,意味着第一次旋转相当于原地不动
rotation_mtx = np.eye(n_cols)
d = 0
for i in range(1000):
  print('process-->',i)
  old_d = d
  #basis应该是旋转后的载荷矩阵
  basis = np.dot(loadings, rotation_mtx)
  """
  下面这个地方最难理解并且是最核心的地方,因为这个公式实在太抽象了我试着猜一下大概是怎么回事,这里参考我下面贴的svd旋转矩阵的链接
  首先套公式argmax(x.T*y),如果载荷矩阵的转置是x的话,那么很容易猜到这个后面一长串的公式就是这个y,根据帖子中x,y都是多维空间中的点 
  群,x是原来的载荷矩阵,那么y就应该是更具最大方差原则处理后的载荷矩阵,并且我猜这里没有正交的约束,因为svd分解在后面,它的跳出循环的 
  条件应该就是不满足正交矩阵了。这个basis3次方困惑了我很久,根据
  黄嘉佑老师书中的公式,可以确定的最大化方差的计算中每一个因子(特征向量)(列)中每个值都是平方的,在求方差应该
  是有四次方项的,但这个y肯定包含了方差最大化的条件,所以我从公式的右半侧开始看np.diag(np.diag(np.dot(basis.T, basis)这个
  最好理解它的作用是先求每个因子的方差然后对角化在除于因子(特征向量)的长度,相当于因子方差平均到每个feature(站点)上的大小
  接下来就纯属于我个人理解(猜测),因子旋转的目的是让因子中的某几个feature(站点或格点)具有高载荷,它不同于pca(或eof)只需要让
  方差集中在前几个因子上就行了,那么旋转的目的就是让大的更大小的更小,所以根据这个思路这个basis**3应该拆开来看,这里的三次方是逐元素
  的幂,将其拆成basis*(basis**2)这里的*是哈达玛逐元素乘积,那么这个公式应该可以合并成basis*(basis**2 - 每一个因子元素的平均方差)
  那么根据这个思路(如果正确的话)就比较好理解了,大于平均方差的项朝着更大的方向转,小于的则更小这也符合旋转的原则,最后在这个方向上根 
  据svd求出正交的部分(旋转矩阵)直到按照这个方向旋转求不出正交部分为止,这根家园fortran版本里的解法应该是相反的(我只是简单的看了 
  下),fortran的版本是先固定正交的条件,然后两两排列组合旋转,所以也非常好理解。
  
  """
  transformed = np.dot(loadings.T, basis**3 - (1.0 / n_rows) * np.dot(basis, np.diag(np.diag(np.dot(basis.T, basis)))))
  U, S, V = np.linalg.svd(transformed)
  #下面参考svd求旋转矩阵的连接wiki里也有简要的推导
  rotation_mtx = np.dot(U, V)
  d = np.sum(S)
  #这里的意思应该是保证对角线上奇异值的和保持不变从而保证旋转的矩阵是正交的
  if old_d != 0 and d / old_d < 1 + 1e-05 :
      break
new_loadings = np.dot(loadings, rotation_mtx)
print(new_loadings)
#通过旋转后的载荷矩阵计算新的方差贡献,载荷矩阵对应的因子(模态)的总方差跟未旋转的总方差相同
new_variance = np.apply_along_axis(lambda x: np.sum(x**2), 0, new_loadings)
old_variance = np.apply_along_axis(lambda x: np.sum(x**2), 0, loadings)
new_variance = new_variance/new_variance.sum()
old_variance = old_variance/old_variance.sum()
#参考链接
#wiki源码链接:https://en.wikipedia.org/wiki/Talk%3aVarimax_rotation
#因子分析:https://blog.csdn.net/mengjizhiyou/article/details/83241469
#varimax :https://github.com/odeviron/varimax/blob/master/varimax_lib.py
#https://github.com/trhgu/Varimax_Rotation_and_Thereafter_module/blob/master/Varimax_Rotation_and_Thereafter.ipynb
#https://github.com/EducationalTestingService/factor_analyzer/blob/master/factor_analyzer/factor_analyzer.py
#https://github.com/EducationalTestingService/factor_analyzer/blob/master/factor_analyzer/rotator.py
#svd求解旋转矩阵
#https://blog.csdn.net/dfdfdsfdfdfdf/article/details/53213240
 | 
 |