大數據技術學習,學會輕鬆的調用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格式,方便後期使用。

通過對數據「輸入」和「輸出」的簡單操作,我在後期想將數值型的特徵進行標準化時,就能很舒服去調用了。


分享到:


相關文章: