科多大數據帶你學會輕鬆的調用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格式,方便後期使用。
通過對數據「輸入」和「輸出」的簡單操作,我在後期想將數值型的特徵進行標準化時,就能很舒服去調用了。
閱讀更多 浩瀚科技數碼 的文章