五 SparkMLlib,R实战 SVD分析

     上一篇SVD 原理文章详细分析了SVD模型理论,本片主要看看SVD的对应分析,这种分析方法适用于矩阵型数据,用途广泛。

     相比较对应分析,SVD能同时实现R型和Q型分析,而且在解析经济结构,分析隐藏因子方面表现比较好。在文本词频方面也表现较好,能够分离出代表含义词汇和文章。
     当然,单独使用SVD难以形成完整的分析,我们还可以结合其他方法,比如Kmeans,cluster形成完整的分析。
这里使用的原始数据来自于中国统计年鉴,各省经济类农产品产出数据。不哆嗦了,先上源代码.
这里是该博客用到的数据

一 R语言分析

library(data.table)
library(ggplot2)
data =read.csv2(file = "C:/Drawsky/Book1.csv",head=T,sep=",",dec = ".")# 读取源
data[is.na(data)]=0 # 缺失数据表示产出为0。
rowname=data[,1]
data[,1]=NULL
#data=as.data.frame(sapply(X = data,FUN = function(x)x=(x-mean(x))))#如果需要的话,可以中心化
name=stringr::str_replace_all(string = names(data),pattern = "\\.|X",replacement = "")#名称有点乱,整理一下。
names(data)<-name
row.names(data)=rowname
res=svd(x = data)# SVD分解
k=sum(cumsum(res$d)/sum(res$d)<0.85)+1# 计算隐藏因子的数量

#对应分析,将样本和指标合并,SVD分解后的数据处理,将原数据的样本和指标视为新的样本,隐藏因子转化成新的指标。
names1=c(names(data),stringr::str_replace_all(string =rowname,pattern = " ",replacement = ""))
newdata=rbind(res$v,res$u)[,1:k]
row.names(newdata)=names1

lambda=rep(x = res$d[1:k],each=n)
lambda = matrix(data = lambda,nrow = n)
hivefactot=newdata*lambda

hivefactot<- as.data.frame(newdata)
hivefactot$V8=names1
## SVD分解后的数据处理完毕

## 隐藏因子如果三个以内的话,可以直接作图显示,如果超过四个,怎么作图呢?
## 化解方法是kmeans ,我们看不见,但是可以分类,看看那些地区和产品在隐藏因子上表现的比较像。
km=kmeans(x = hivefactot[,1:k],centers = k,iter.max = 10,nstart = 40)
class=km$cluster
split(names(class),f = class)

看看分析结果

> split(names(class),f = class)
$`1`
 [1] "芝麻"   "麻类"   "烟叶"   "蚕茧"   "茶叶"   "葡萄"   "北京"   "天津"   "山西"   "内蒙古" "辽宁"   "吉林"   "上海"  "江苏"   "浙江"   "安徽"   "福建"   "西藏"   "甘肃"   "青海"   "宁夏"  

$`2`
[1] "香蕉" "广东" "广西" "海南" "云南"

$`3`
[1] "薯类" "梨"   "河北" "重庆" "四川" "贵州"

$`4`
[1] "苹果" "山东" "陕西"

$`5`
[1] "油菜籽" "柑桔"   "江西"   "湖北"   "湖南"  

$`6`
[1] "豆类"   "花生"   "黑龙江" "河南"  

$`7`
[1] "棉花" "甜菜" "新疆"

看看农产品和地区的对应关系,是不是跟没分析一样?要的就是这个效果,符合常识;
第一组,基本上是没什么特色的组,产品那哪儿都产,对应地区也一样,什么都有特点不突出。
第二到,第七组,特色相当鲜明。

注: 数据分析的结果如果令人费解的话,要么分析方法用错了,要么出现了以前不知道的东西,应当仔细甄别;当然还有可能数据是假的。

R 中的kmeans分类,初始挑选中心带有一定的随机性,多次执行会发现有些省份和农产品分类一直在摇摆。可以通过设置迭代次数和扩大初始化中心的所需的样品数,这样可以形成稳定的分类。

二 Spark做SVD分析

     先吐槽一下spark 的数据类型,转换实在麻烦,MLlib和ML的类型同名还不能相互转换,很搞,很搞,很搞。。。
     不说废话了了直接上源码。

Java源码

public static void svd(SparkSession spark) {
//读取CSV文件
Dataset data = spark.read().option("header", true).option("encoding", "gb2312").option("nullValue", 0)
        .csv("C:/drawsky/Book1.csv");

//收集指标名称,行名称(这里就是省份)
List  colname =Arrays.asList( data.columns());
List rowName = data.select(colname.get(0)).collectAsList().stream().map(e -> e.getString(0).replaceAll("\b", ""))
        .collect(Collectors.toList());
colname=colname.stream().map(e->e.replaceAll("[#\b]+", "")).collect(Collectors.toList());
List allname = new ArrayList();
allname.addAll(colname);
allname.addAll(rowName);
allname.remove(0);

//转换成Rdd
JavaRDD dataRdd = data.toJavaRDD();
JavaRDD set = dataRdd.map(e -> {
    int n = e.length();
    double[] s = new double[n - 1];
    for (int i = 1; i < n; i++) {
        if (null == e.getString(i)) {
            s[i - 1] = 0;
        } else {
            s[i - 1] = Double.parseDouble("" + e.getString(i));
        }
    }
    return Vectors.dense(s);
});

//转换成矩阵形式
RowMatrix mat = new RowMatrix(set.rdd());

//SVD分解
SingularValueDecomposition svd = mat.computeSVD(16, true, 1E-9d);

RowMatrix U = svd.U(); // The U factor is a RowMatrix.
Vector s = svd.s(); // The singular values are stored in a local dense
                    // vector.
Matrix V = svd.V();

//      检验一下SVD分解,看看分解后的矩阵相乘能不能回到原始数据
//      List  hehe=U.multiply(Matrices.diag(s)).multiply(V.transpose()).rows().toJavaRDD().collect();

//为下一步kmeans准备数据
DenseMatrix s1 = (DenseMatrix) Matrices.diag(s);
V = V.multiply(s1);
U = U.multiply(s1);

//V 是本地矩阵,需要转换成Rdd先转换成List
List v = new ArrayList<>();
Iterator it = V.rowIter();
while (it.hasNext()) {
    v.add(new Bean(it.next()));
}
//V 转换成RDD
JavaRDD vv = spark.createDataFrame(v, Bean.class).toJavaRDD().map(f -> (Vector) f.get(0));
//合并UV
JavaRDD res = vv.union(U.rows().toJavaRDD());

//Kmeans 聚类参数
int numClusters = 7;
int numIterations = 100;

//Kmeans 计算分类样本中心
KMeansModel clusters = KMeans.train(res.rdd(), numClusters, numIterations,0,"k-means||",40);
//分类
List cla = clusters.predict(res).collect();
//将分类转换成人看得懂的形式
Map> o = new HashMap>();

for (int i = 0; i < cla.size(); i++) {
    if (!o.containsKey(cla.get(i))) {
        o.put(cla.get(i), new ArrayList<>());
    }
    o.get(cla.get(i)).add(allname.get(i));
}

System.out.println("分类标签:"+cla);
System.out.println("样本名称:"+allname);
System.out.println("分析结果:\n"+o);
//      data.show();
//      hehe.stream().map(e->e.toJson()).forEach(e->System.out.println(e));
    }

bean.java 的源码

package com.SparkTest;
import java.io.Serializable;
import org.apache.spark.mllib.linalg.Vector;
public class Bean implements Serializable {
    Vector v =null;
    public Bean(Vector s){
        this.v=s;
    }
    private static final long serialVersionUID = 126198628520278041L;
    public Vector getV() {
        return v;
    }
    public void setV(Vector v) {
        this.v = v;
    }
}

运行结果

分类标签:[3, 4, 2, 0, 4, 3, 3, 2, 3, 3, 3, 1, 5, 0, 3, 5, 3, 3, 0, 0, 3, 0, 3, 3, 3, 3, 3, 3, 4, 4, 1, 0, 4, 4, 5, 5, 3, 4, 4, 3, 3, 3, 6, 3, 3, 3, 2]

样本名称:[豆  类, 薯  类, 棉  花, 花  生, 油菜籽, 芝  麻, 麻  类, 甜  菜, 烟  叶, 蚕  茧, 茶  叶, 苹  果, 柑  桔, 梨, 葡  萄, 香  蕉,  北  京,  天  津,  河  北,  山  西,  内蒙古,  辽  宁,  吉  林,  黑龙江,  上  海,  江  苏,  浙  江,  安  徽,  福  建,  江  西,  山  东,  河  南,  湖  北,  湖  南,  广  东,  广  西,  海  南,  重  庆,  四  川,  贵  州,  云  南,  西  藏,  陕  西,  甘  肃,  青  海,  宁  夏,  新  疆]

分析结果:
{0=[花  生, 梨,  河  北,  山  西,  辽  宁,  河  南], 
1=[苹  果,  山  东], 
2=[棉  花, 甜  菜,  新  疆],
3=[豆  类, 芝  麻, 麻  类, 烟  叶, 蚕  茧, 茶  叶, 葡  萄,  北  京,  天  津,  内蒙古,  吉  林,  黑龙江,  上  海,  江  苏,  浙  江,  安  徽,  海  南,  贵  州,  云  南,  西  藏,  甘  肃,  青  海,  宁  夏], 
5=[柑  桔, 香  蕉,  广  东,  广  西], 
6=[ 陕  西]
}

结果和R运行的有点差别,貌似没有R分析的效果好,调了很久的参数,百思不得七姐呀。。。要知道,Spark里面哥用的是Kmeans++算法呀,这么烂??

你可能感兴趣的:(spark快速大数据分析,机器学习)