懂了因子分析的原理,再来根据实际问题设计算法。
- 样本数据标准化—公式如下:
java实现代码如下:
// 计算期望与标准差,标准化数据
public double[][] getStandard(double[][] array) {
int h = array.length; // 行号--h
int l = array[0].length;// 列号--l
double[] average = new double[l];// 每个变量的均值
double[][] result = new double[h][l];// 标准化后的向量
double[] standardDevition = new double[l];// 每个变量的标准差
// 求均值
for (int i = 0; i < l; i++) {
double temp = 0.0;
for (int j = 0; j < h; j++) {
temp += array[j][i];
}
average[i] = temp / h;
}
// 求标准差
for (int i = 0; i < l; i++) {
double temp = 0.0;
for (int j = 0; j < h; j++) {
temp += Math.pow((array[j][i] - average[i]), 2);
}
standardDevition[i] = Math.sqrt(temp / (h - 1));
}
// 求标准化后的矩阵
for (int i = 0; i < h; i++) {
for (int j = 0; j < l; j++) {
result[i][j] = (array[i][j] - average[j]) / standardDevition[j];
}
}
Matrix m = new Matrix(result);
System.out.println("标准化后的矩阵:");
m.print(6, 5);
return result;
}
- 要注意的是并不是所有数据都需要标准化处理,对原始数据进行标准化处理后倾向于各个指标的作用在主成份的构成中相等。
- 对于取值范围相差不大或是度量相同的指标进行标准化处理后,其主成份分析的结果仍与由协方差阵出发求得的结果有较大区别。其原因是由于对数据进行标准化的过程中实际上也就是抹杀原始变量离散程度差异的过程,标准化后的各变量方差相等均为1,而实际上方差也是对数据信息的重要概括形式,也就是说,对原始数据进行标准化后抹杀了一部分重要信息,因此才使得标准化后各变量在主成份构成中的作用趋于相等。由此看来,对同度量或是取值范围在同量级的数据,还是直接从协方差矩阵求解主成份为宜。
- 计算相关矩阵:因子分析是对原变量的浓缩和信息提取,要求原有变量具有较强的线性相关性,若原有变量是不相关或弱相关,也就无法浓缩了。因子分析前提的判定:计算原有变量的相关系数矩阵,一般小于0.3就不适合作因子分析。
// 计算相关矩阵---方阵
public double[][] getCorrelationMatrix(double[][] array) {
int h = array.length;
int l = array[0].length;
double[][] result = new double[l][l];
for (int i = 0; i < l; i++) {
for (int j = 0; j < l; j++) {
double temp = 0;
for (int k = 0; k < h; k++) {
temp += array[k][i] * array[k][j];
}
result[i][j] = temp / (h - 1);
}
}
Matrix m = new Matrix(result);
System.out.println("相关矩阵:");
m.print(6, 5);
return result;
}
- 计算协方差矩阵
// 计算协方差矩阵--方阵
public double[][] getCovMatrix(double[][] array) {
int h = array.length;
int l = array[0].length;
double[] average = new double[l];// 每个变量的均值
double[][] temp = new double[h][l];// 保存计算协方差的中间矩阵
double[][] result = new double[l][l];
// 求均值
for (int i = 0; i < l; i++) {
double t = 0.0;
for (int j = 0; j < h; j++) {
t += array[j][i];
}
average[i] = t / h;
}
for (int i = 0; i < h; i++) {
for (int j = 0; j < l; j++) {
temp[i][j] = array[i][j] - average[j];
}
}
for (int i = 0; i < l; i++) {
for (int j = 0; j < l; j++) {
double t = 0;
for (int k = 0; k < h; k++) {
t += temp[k][i] * temp[k][j];
}
result[i][j] = t / (h - 1);
}
}
Matrix m = new Matrix(result);
System.out.println("协方差矩阵:");
m.print(6, 5);
return result;
}
- 计算相关矩阵或协方差矩阵的特征值
public double[][] getEigenvalue(double[][] array) {
int h = array.length;// 行号等于列号
double[][] result = new double[h][h];
Matrix m = new Matrix(array);
Matrix m1 = m.eig().getD(); // 由特征值组成的对角矩阵
System.out.println("由特征值组成的对角矩阵:");
m1.print(6, 5);
result = m1.getArray();
return result;
}
- 计算相关矩阵或协方差矩阵的特征向量
public double[][] getEigenvector(double[][] array) {
int h = array.length;// 行号等于列号
double[][] result = new double[h][h];
Matrix m = new Matrix(array);
Matrix m1 = m.eig().getV(); // 该矩阵的每一列对应的是一个单位正交特征向量
System.out.println("特征向量矩阵:");
m1.print(6, 5);
result = m1.getArray();
return result;
}
- 特征值按从大到小排序保存在TreeMap中
// 特征值从大到小排序并保存成键值对形式
public TreeMap<Integer, Double> getMap(double[][] array) {
int h = array.length;
Map<Integer, Double> map = new HashMap<Integer, Double>();
ValueComparator bvc = new ValueComparator(map);
TreeMap<Integer, Double> sorted_map = new TreeMap<Integer, Double>(bvc);
double[] temp = new double[h]; // 存储特征值
for (int i = 0; i < h; i++) {
for (int j = 0; j < h; j++) {
if (i == j)
temp[i] = array[i][j];
}
}
for (int i = 0; i < temp.length; i++) {
map.put(i, temp[i]);
}
System.out.println("排序前键值对:" + map);
System.out.println();
sorted_map.putAll(map);
System.out.println("排序后的键值对: " + sorted_map);
System.out.println();
return sorted_map;
}
// map按值进行排序
class ValueComparator implements Comparator<Integer> {
Map<Integer, Double> base;
public ValueComparator(Map<Integer, Double> base) {
this.base = base;
}
public int compare(Integer a, Integer b) {
if (base.get(a) >= base.get(b)) {
return -1;
} else {
return 1;
}
}
}
// 计算方差贡献率
public double[] getContibutionRatio
(TreeMap<Integer, Double> tm)
// 计算方差累积贡献率
public double[] getCumuContributionRatio
(double[] array)
// 确定公共因子的个数
public int getNum(double[] array)
// 获取特征值对应的特征向量
public double[][] getVVector
(TreeMap<Integer, Double> tm, double[][] array)
// 获取因子载荷矩阵
public double[][] getMatrixA
(TreeMap<Integer, Double> tm, double[][] array, int num)
// 进行因子旋转--方差最大正交旋转
public double[][] getVarimax(double[][] array, int num)
// 计算因子载荷的方差
public double getA_Var(double[][] array)
- 例子: 研究影响火柴销售的变量之间的关系。火柴是人民生活的必需品,商品虽小,但与日常生活关系十分密切,该商品易燃、易潮,较难储存,经营面广、量大,价格低,易受其他日用市场冲击的影响,供应脱节并影响其他旧用工业品销售,因此为了研究火柴需求量的变化趋势,需研究影响火柴销售量的变量之间的关系。
影响火柴销售量的主要指标有:煤气、液化气户数、卷烟销售量、蚁香销售量、打火石钥售量。调查从1963年至1982年共20个年头的数据,即选择的变量数p=4、样本数N=20,进行因子分析。
原始数据见表10-1。
用二维数组存储数据:
double [][] array = {
{11.11, 14.34, 5.10, 20.41},
{13.63, 17.15, 4.90, 26.94},
{16.99, 17.45, 5.06, 25.75},
{21.12, 18.05, 5.32, 25.98},
{24.86, 20.42, 7.48, 16.18},
{25.68, 23.60, 10.10, 4.18},
{25.77, 23.42, 13.31, 2.43},
{25.88, 22.09, 9.49, 6.50},
{27.43, 21.43, 11.09, 25.78},
{29.95, 24.96, 14.48, 28.16},
{33.53, 28.37, 16.97, 24.26},
{37.31, 42.57, 20.16, 30.18},
{41.16, 45.16, 26.39, 17.08},
{45.73, 52.46, 27.04, 7.39},
{50.59, 45.30, 23.08, 3.88},
{58.82, 46.80, 24.46, 10.53},
{65.28, 51.11, 33.82, 20.09},
{71.25, 53.29, 33.57, 21.22},
{73.37, 55.36, 39.59, 12.63},
{76.68, 45.00, 48.49, 11.17}};
运行结果:
标准化后的矩阵:
-1.34086 -1.30147 -1.08994 0.37106
-1.21886 -1.10976 -1.10563 1.08941
-1.05620 -1.08929 -1.09308 0.95850
-0.85626 -1.04836 -1.07268 0.98380
-0.67520 -0.88667 -0.90325 -0.09428
-0.63550 -0.66972 -0.69773 -1.41437
-0.63114 -0.68200 -0.44594 -1.60689
-0.62582 -0.77273 -0.74558 -1.15916
-0.55078 -0.81776 -0.62008 0.96180
-0.42878 -0.57693 -0.35416 1.22362
-0.25547 -0.34429 -0.15884 0.79459
-0.07247 0.62448 0.09138 1.44584
0.11391 0.80118 0.58007 0.00473
0.33515 1.29921 0.63106 -1.06125
0.57044 0.81073 0.32043 -1.44738
0.96886 0.91307 0.42868 -0.71582
1.28160 1.20711 1.16289 0.33585
1.57062 1.35584 1.14328 0.46016
1.67325 1.49706 1.61549 -0.48481
1.83350 0.79027 2.31362 -0.64542
相关矩阵:
1.00000 0.91205 0.96408 -0.26610
0.91205 1.00000 0.89836 -0.26403
0.96408 0.89836 1.00000 -0.25686
-0.26610 -0.26403 -0.25686 1.00000
由特征值组成的对角矩阵:
0.03508 0.00000 0.00000 0.00000
0.00000 0.11488 0.00000 0.00000
0.00000 0.00000 0.89453 0.00000
0.00000 0.00000 0.00000 2.95552
排序前键值对:{0=0.03507556817435512, 1=0.11487582930368767, 2=0.894530732984345, 3=2.955517869537612}
排序后的键值对: {3=2.955517869537612, 2=0.894530732984345, 1=0.11487582930368767, 0=0.03507556817435512}
键3 值2.95552
键2 值0.89453
键1 值0.11488
键0 值0.03508
特征值 2.95552 的所占比例: 0.73888
特征值 0.89453 的所占比例: 0.22363
特征值 0.11488 的所占比例: 0.02872
特征值 0.03508 的所占比例: 0.00877
累积贡献率:0.73888
累积贡献率:0.96251
累积贡献率:0.99123
累积贡献率:1.00000
公共因子个数:2
特征向量矩阵:
-0.74308 0.32913 -0.13071 -0.56782
0.08260 -0.81891 -0.12186 -0.55471
0.66406 0.47008 -0.13925 -0.56450
-0.00555 -0.00892 -0.97400 0.22631
特征值对应的特征向量组成的矩阵:
-0.56782 -0.13071 0.32913 -0.74308
-0.55471 -0.12186 -0.81891 0.08260
-0.56450 -0.13925 0.47008 0.66406
0.22631 -0.97400 -0.00892 -0.00555
因子载荷矩阵 A =
-0.9762 -0.1236
-0.9536 -0.1153
-0.9705 -0.1317
0.3891 -0.9212
第1次旋转:
旋转角度 E = 0.26336
0.9544 -0.2410
0.9554 -0.2427
0.9528 -0.2382
0.3671 0.7800
总方差:0.16964
第2次旋转:
旋转角度 E = -0.34344
0.8650 0.3730
0.8641 0.3734
0.8664 0.3723
-0.1049 0.8319
总方差:0.15934
第3次旋转:
旋转角度 E = 0.26620
0.8548 -0.0706
0.8544 -0.0699
0.8554 -0.0717
0.2741 0.9455
总方差:0.22881
第4次旋转:
旋转角度 E = -0.18232
0.9755 0.1867
0.9757 0.1866
0.9753 0.1869
-0.0911 0.9213
总方差:0.29104
第5次旋转:
旋转角度 E = 0.14384
0.9598 -0.1033
0.9598 -0.1033
0.9597 -0.1032
0.1515 0.9787
总方差:0.31948
因子得分:
-1.25047 0.13248
-0.80029 0.90590
-0.80045 0.79855
-0.70693 0.85854
-1.00018 -0.21637
-1.39900 -1.56003
-1.40289 -1.77527
-1.35180 -1.30202
-0.35680 0.87281
0.01184 1.16257
0.05449 0.75623
0.98757 1.51715
0.65171 0.04988
0.50812 -0.96984
0.03527 -1.34747
0.56254 -0.53229
1.52909 0.57009
1.73316 0.75769
1.57385 -0.21599
1.42119 -0.46259