2.3 数据分析的基本方法
数据分析是以目标为导向的,通过目标实现选择数据分析的方法,常用的分析方法是统计分析,数据挖掘则需要使用机器学习构建模型。接下来介绍一些简单的数据分析方法。
2.3.1 汇总统计
统计是指用单个数或者数的小集合捕获很大值集的特征,通过少量数值来了解大量数据中的主要信息,常见统计指标包括以下几项。
❑ 分布度量:概率分布表、频率表、直方图;
❑ 频率度量:众数;
❑ 位置度量:均值、中位数;
❑ 散度度量:极差、方差、标准差;
❑ 多元比较:相关系数;
❑ 模型评估:准确率、召回率。
汇总统计对一个弹性分布式数据集(RDD)进行概括统计,它通过调用Statistics的colStats方法实现。colStats方法可以返回RDD的最大值、最小值、均值、方差等,代码实现如下:
import org.apache.spark.MLlib.linalg.Vector import org.apache.spark.MLlib.stat.{MultivariateStatisticalSummary, Statistics} // 向量[Vector]数据集 val data: RDD[Vector] = ... // 汇总统计信息 val summary: statisticalSummary = Statistics.colStats(data) // 平均值和方差 println(summary.mean) println(summary.variance)
2.3.2 相关性分析
相关性分析是指通过分析寻找不同商品或不同行为之间的关系,发现用户的习惯,计算两个数据集的相关性是统计中的常见操作。
MLlib提供了计算多个数据集两两相关的方法。目前支持的相关性方法有皮尔逊(Pearson)相关和斯皮尔曼(Spearman)相关。一般对于符合正态分布的数据使用皮尔逊相关系数,对于不符合正态分布的数据使用斯皮尔曼相关系数。
皮尔逊相关系数是用来反映两个变量相似程度的统计量,它常用于计算两个向量的相似度,皮尔逊相关系数计算公式如下:
其中,X、Y表示两组变量,、表示两个变量的平均值,皮尔逊相关系数可以理解为对两个向量进行归一化以后,计算其余弦距离(即使用余弦函数cos计算相似度,也用向量空间中两个向量的夹角的余弦值来衡量两个文本间的相似度),皮尔逊相关系数大于0表示两个变量正相关,小于0表示两个变量负相关,皮尔逊相关系数为0时,表示两个变量没有相关性。
调用MLlib计算两个RDD皮尔逊相关性的代码如下,输入的数据可以是RDD[Double],也可以是RDD[Vector],输出是一个Double值或者相关性矩阵。
import org.apache.spark.SparkContext import org.apache.spark.MLlib.linalg._ import org.apache.spark.MLlib.stat.Statistics // 创建应用入口 val sc: SparkContext = ... // X变量 val seriesX: RDD[Double] = ... // Y变量,分区和基数同seriesX val seriesY: RDD[Double] = ... // 使用皮尔逊方法计算相关性,斯皮尔曼的方法输入"spearman" val correlation: Double = Statistics.corr(seriesX, seriesY, "pearson") // 向量数据集 val data: RDD[Vector] = ... val correlMatrix: Matrix = Statistics.corr(data, "pearson")
皮尔逊相关系数在机器学习的效果评估中经常使用,如使用皮尔逊相关系数衡量推荐系统推荐结果的效果。
2.3.3 分层抽样
分层抽样先将数据分为若干层,然后再从每一层内随机抽样组成一个样本。MLlib提供了对数据的抽样操作,分层抽样常用的函数是sampleByKey和sampleByKeyExact,这两个函数是在key-value对的RDD上操作,用key来进行分层。
其中,sampleByKey方法通过掷硬币的方式进行抽样,它需要指定所需数据大小;sampleByKeyExact抽取fkey· nkey个样本,fkey表示期望获取键为key的样本比例,nkey表示键为key的键值对的数量。sampleByKeyExact能够获取更准确的抽样结果,可以选择重复抽样和不重复抽样,当withReplacement为true时是重复抽样,为false时是不重复抽样。重复抽样使用泊松抽样器,不重复抽样使用伯努利抽样器。
分层抽样的代码如下:
import org.apache.spark.SparkContext import org.apache.spark.SparkContext._ import org.apache.spark.rdd.PairRDDFunctions val sc: SparkContext = ... // RDD[(K, V)]形式的键值对 val data = ... //指定每个键所需的份数 val fractions: Map[K, Double] = ... //从每个层次获取确切的样本 val approxSample = data.sampleByKey(withReplacement = false, fractions) val exactSample = data.sampleByKeyExact(withReplacement = false, fractions)
通过用户特征、用户行为对用户进行分类分层,形成精细化运营、精准化业务推荐,进一步提升运营效率和转化率。
2.3.4 假设检验
假设检验是统计中常用的工具,它用于判断一个结果是否在统计上是显著的、这个结果是否有机会发生。通过数据分析发现异常情况,找到解决异常问题的方法。
MLlib目前支持皮尔森卡方检验,对应的函数是Statistics类的chiSqTest。chiSqTest支持多种输入数据类型,对不同的输入数据类型进行不同的处理,对于向量(Vector)进行拟合优度检验,对于矩阵(Matrix)进行独立性检验,对于RDD进行特征选择。使用chiSqTest方法进行假设检验的代码如下:
import org.apache.spark.SparkContext import org.apache.spark.MLlib.linalg._ import org.apache.spark.MLlib.regression.LabeledPoint import org.apache.spark.MLlib.stat.Statistics._ val sc: SparkContext = ... // 定义一个由事件频率组成的向量 val vec: Vector = ... // 做皮尔森拟合优度检验 val goodnessOfFitTestResult = Statistics.chiSqTest(vec) println(goodnessOfFitTestResult) // 定义一个检验矩阵 val mat: Matrix = ... // 做皮尔森独立性检测 val independenceTestResult = Statistics.chiSqTest(mat) // 检验总结:包括假定值(p-value)、自由度(degrees of freedom) println(independenceTestResult) // pairs(feature, label). val obs: RDD[LabeledPoint] = ... // 独立性检测用于特征选择 val featureTestResults: Array[ChiSqTestResult] = Statistics.chiSqTest(obs) var i = 1 featureTestResults.foreach { result => println(s"Column $i:\n$result") i += 1 }
2.4 简单的数据分析实践
本节为了更清楚地说明简单的数据分析实现,搭建Spark开发环境,并使用gowalla数据集进行简单的数据分析,该数据集较小,可在Spark本地模式下,快速运行实践。
实践步骤如下。
1)环境准备:准备开发环境并加载项目代码;
2)数据准备:数据预处理及one-hot编码;
3)数据分析:使用均值、方差、皮尔逊相关性计算等进行数据分析。
简单数据分析实践的详细代码参考ch02\GowallaDatasetExploration.scala,本地测试参数和值如表2-3所示。
2.4.1 环境准备
Spark程序常用IntelliJ IDEA工具进行开发,下载地址为www.jetbrains.com/idea/,一般选择Community版,当前版本是ideaIC-2017.3.4,支持Windows、Mac OS X、Linux,可以根据自己的情况选择适合的操作系统进行安装。
(1)安装scala-intellij插件
启动IDEA程序,进入“Configure”界面,选择“Plugins”,点击安装界面左下角的“Install JetBrains plugin”选项,进入JetBrains插件选择页面,输入“Scala”来查找Scala插件,点击“Install plugin”按钮进行安装(如果网络不稳定,可以根据页面提示的地址下载,然后选择“Install plugin from disk”本地加载插件),插件安装完毕,重启IDEA。
(2)创建项目开发环境
启动IDEA程序,选择“Create New Project”,进入创建程序界面,选择Scala对应的sbt选项,设置Scala工程名称和本地目录(以book2-master为例),选择SDK、SBT、Scala版本(作者的开发环境是Jdk->1.8.0_162、sbt->1.1.2、scala->2.11.12),点击“Finish”按钮完成工程的创建。
导入Spark开发包具体步骤为:File->Project Structure->Libraries->+New Project Library(Java),选择spark jars(如:spark-2.3.0-bin-hadoop2.6/jars)和本地libs(如:\book2-master\libs,包括nak_2.11-1.3、scala-logging-api_2.11-2.1.2、scala-logging-slf4j_2.11-2.1.2)。
(3)拷贝项目代码
拷贝源代码中的2rd_data、libs、output、src覆盖本地开发项目目录,即可完成开发环境搭建。
除此之外,也可以通过Maven方式导入项目。
2.4.2 准备数据
我们提供的数据格式如下:
用户[user] 签到时间[check-in time] 纬度[latitude] 经度[longitude] 位置标识[location id]
数据样例如下:
0 2010-10-19T23:55:27Z 30.2359091167 -97.7951395833 22847 0 2010-10-18T22:17:43Z 30.2691029532 -97.7493953705 420315 0 2010-10-17T23:42:03Z 30.2557309927 -97.7633857727 316637 0 2010-10-17T19:26:05Z 30.2634181234 -97.7575966669 16516 0 2010-10-16T18:50:42Z 30.2742918584 -97.7405226231 5535878 0 2010-10-12T23:58:03Z 30.261599404 -97.7585805953 15372 0 2010-10-12T22:02:11Z 30.2679095833 -97.7493124167 21714 0 2010-10-12T19:44:40Z 30.2691029532 -97.7493953705 420315 0 2010-10-12T15:57:20Z 30.2811204101 -97.7452111244 153505 0 2010-10-12T15:19:03Z 30.2691029532 -97.7493953705 420315 0 2010-10-12T00:21:28Z 40.6438845363 -73.7828063965 23261
准备数据的步骤如下。
(1)数据清洗
在数据清洗阶段过滤掉不符合规范的数据,并将数据进行格式转换,保证数据的完整性、唯一性、合法性、一致性,并按照CheckIn类填充数据,具体实现方法如下:
// 定义数据类CheckIn case class CheckIn(user: String, time: String, latitude: Double, longitude: Double, location: String) // 实例化应用程序入口 val conf = new SparkConf().setAppName(this.getClass.getSimpleName). setMaster(mode) val sc = new SparkContext(conf) val gowalla = sc.textFile(input).map(_.split("\t")).mapPartitions{ case iter => val format = DateTimeFormat.forPattern("yyyy-MM-dd\'T\'HH:mm:ss\'Z\'") iter.map { // 填充数据类 case terms => CheckIn(terms(0), terms(1).substring(0, 10), terms(2).toDouble, terms(3).toDouble, terms(4)) } }
(2)数据转换
在数据转换阶段,将数据转换成向量的形式,供后面数据分析使用。
// 字段:user, checkins, checkin days, locations val data = gowalla.map{ case check: CheckIn => (check.user, (1L, Set(check.time), Set(check.location))) }.reduceByKey { // 并集 case (left, right) =>(left._1 + right._1, left._2.union(right._2), left._3. union(right._3)) }.map { case (user, (checkins, days:Set[String], locations:Set[String])) => Vectors.dense(checkins.toDouble, days.size.toDouble, locations.size.toDouble) }
2.4.3 数据分析
通过简单的数据分析流程,实现对均值、方差、非零元素的目录的统计,以及皮尔逊相关性计算,来实现对数据分析的流程和方法的理解。
简单的数据分析代码示例如下:
// 统计分析 val summary: MultivariateStatisticalSummary = Statistics.colStats(data) // 均值、方差、非零元素的目录 println("Mean"+summary.mean) println("Variance"+summary.variance) println("NumNonzeros"+summary.numNonzeros) // 皮尔逊 val correlMatrix: Matrix = Statistics.corr(data, "pearson") println("correlMatrix"+correlMatrix.toString)
简单的数据分析示例运行结果如下:
均值:[60.16221566503564,25.30645613117692,37.17676390393301] 方差:[18547.42981193066,1198.630729157736,7350.7365871949905] 非零元素:[107092.0,107092.0,107092.0] 皮尔逊相关性矩阵: 1.0 0.7329442022276709 0.9324997691135504 0.7329442022276709 1.0 0.5920355112372706 0.9324997691135504 0.5920355112372706 1.0
2.5 本章小结
本章从数据分析概念出发,总结了常用的数据分析流程,并对数据分析流程相关步骤进行了说明。本章中的流程只是作者的经验总结,可能和其他资料中的流程有所区别,一切以做好数据分析为核心,请在做好数据分析的情况下自主总结流程。
同时,本章对数据分析基本方法进行了说明,以统计分析为主,兼顾机器学习,不仅对结果进行解释,让人更加直观、清晰地认识世界,同时对模型进行评估,着眼于预测未来,并提出决策性建议。
对于一些可以从Spark官网上获取的算法示例代码,我们没有进行展示,需要的读者可以从https://github.com/datadance上下载。
本章使用的数据集是:从stanford爬取下来的gowalla数据。数据下载地址为https://snap.stanford.edu/data/loc-gowalla.html。
在下一章中,我们将重点研究使用MLlib构建分类模型。