大数据技术学习,学会轻松的调用Spark中的数据标准化库

科多大数据带你学会轻松的调用Spark中的数据标准化库。

这篇文章的核心,不是在于介绍「数据标准化」,也不是在于实现「Spark调用」,毕竟这些概念大家应该耳濡目染了,至于调用方法一搜一大堆。

省略的内容,参考下面链接:

《数据标准化/归一化》 链接:数据标准化/归一化normalization - CSDN博客

《Spark ML包中的几种归一化方法总结》 链接:Spark ML包中的几种归一化方法总结 | Ron's Blog

Ok,我们在进入文章的重点前,允许我先吐槽一下。

其实我一直以来都不太愿意去直接调用Spark的一些工具包,因为很多数据的「输入」和「输出」都是向量和数组类型,直接使用起来很不灵活。

而且我也觉得DataFrame的功能太多此一举了,很多操作用Hive就可以替代,而且很多分析最好还是用Excel和SPSS去做。

但是没有办法,毕竟有些功能实现是现成的,可以节省很多开发成本,就比如数据预处理中的「标准化」,你就没有必要再单独去开发了。

然而,我们先看一下Spark要做「标准化」的输入数据样式。

// 原原始数据

+---+-----------------+

| id| features |

+---+-----------------+

| 0 |[1.0,0.5,-1.0]|

| 1 | [2.0,1.0,1.0]|

| 2 |[4.0,10.0,2.0]|

+---+-----------------+

看到这,我就不想去用了,除了简单的DataFrame赋值,正常情况下的业务特征都是一张宽表,或者是其他特征工程的组合形式。

那有人会无聊去把数据的存储形式保存为向量型的呢?虽然也可以这样做,但是我觉得不太方便去回顾数据。

无奈之下,我在使用DataFrame和「标准化库」时,做了一个简单的优化,具体如下所示:

// 原始数据

Userid,Feature1,Feature2,Feature3

import sqlContext.implicits._

//需要进行数据标准化的特征(除Userid外)有:

val value = behavData.map(_.split(",")).map(record =>

{

var featureArray:Array[Double] = new Array[Double](3)

val userid = record(0)

val feature = ( for(i

val featureVector = Vectors.dense(feature)

(userid,featureVector)

}

).toDF("userid","featureSet")

这样的话,我就可以直接将「原始数据」转化为Spark标准化库所要求的样式了。

// 转化数据

Userid,[Feature1,Feature2,Feature3]

提醒一下,其他向量类型还不行,必须是import org.apache.spark.mllib.linalg.Vectors;

令人反感的「数据输入」解决了一半,我们再着手「数据输出」,尽量让后期的建模工作顺畅起来。

// 这是其中一种标准化方法的数据输出。

+------+----------------------+-------------------------------------------------------------------+

| id | features | scaledFeatures |

+------+-----------------------+------------------------------------------------------------------+

| 0 | [1.0,0.5,-1.0] | [0.654653670707,0.09352195,-0.654653670] |

| 1 | [2.0,1.0,1.0] | [1.3093073414159544,0.18704390,0.65465] |

| 2 | [4.0,10.0,2.0] | [2.618614682831909,1.87043905,1.309307] |

+-----+-------------------------+-----------------------------------------------------------------+

可能是我真的看不习惯,要说这结果输出的灵活性太差也不为过,所以我又做了一个简单的优化。

//将DataFrame转换成RDD再存储于HDFS上

val resultRDD = inputValue.rdd.map(record =>

{

val ouputResult = new StringBuilder()

ouputResult.append(record(0).toString()).append(",")

//调用字符串StrDealOne函数

StrDealOne(record(1).toString()).split(",").map(records =>

{

ouputResult.append(round(records.toDouble,4)).append(",")

}

)

//调用字符串StrDealTwo函数

StrDealTwo(ouputResult.toString())

}

)

其中

/**

* 字符串处理(替换特殊字符、去掉字符串末尾一位)

*/

def StrDealOne(InputValue:String):String = {

InputValue.replaceAll("\\(","").replaceAll("\\)","").replaceAll("\\[","").replaceAll("\\]","")

}

def StrDealTwo(InputValue:String):String = {

InputValue.substring(0, InputValue.toString().length()-1)

}

简单来说,就是让标准化后的数据恢复最初的Userid,Feature1,Feature2,Feature3格式,方便后期使用。

通过对数据「输入」和「输出」的简单操作,我在后期想将数值型的特征进行标准化时,就能很舒服去调用了。


分享到:


相關文章: