2016-02-19 127 views
2

我有一個數據幀,看起來像如下:爲什麼StandardScaler會給出非零的維值,因爲方差不是零?

+-----+--------------------+ 
| uid|   features| 
+-----+--------------------+ 
|user1|  (7,[1],[5.0])| 
|user2|(7,[0,2],[13.0,4.0])| 
|user3|(7,[2,3],[7.0,45.0])| 
+-----+--------------------+ 

的特點列是稀疏向量,大小等於4

我申請一個StandardScaler如下:

import org.apache.spark.ml.feature.StandardScaler 

val scaler = new StandardScaler() 
    .setInputCol("features") 
    .setOutputCol("scaledFeatures") 
    .setWithStd(true) 
    .setWithMean(false) 

val scalerModel = scaler.fit(df) 

// Normalize each feature to have unit standard deviation. 
val scaledData = scalerModel.transform(transformed) 

輸出DataFrame如下所示:

+-----+--------------------+--------------------+ 
| uid|   features|  scaledFeatures| 
+-----+--------------------+--------------------+ 
|user1|  (7,[1],[5.0])|(7,[1],[1.7320508...| 
|user2|(7,[0,2],[13.0,4.0])|(7,[0,2],[1.73205...| 
|user3|(7,[2,3],[7.0,45.0])|(7,[2,3],[1.99323...| 
+-----+--------------------+--------------------+ 

正如我們所看到的,user1的scaledFeatures僅包含一個元素(其他元素爲零),但我期望每個scaledFeatures對於所有維度始終包含非零值,因爲方差不爲零。

讓我們例如第三維,即,每個特徵向量的索引2:

  • 此維度具有0.0用戶1,4.0和用戶2 7.0用戶3的值。 (0 + 4 + 7)/ 3 = 3.667
  • SD爲:sqrt [ 3.667)^ 2)/ 3] = 2.868
  • 爲User1的單位標準偏差應爲:(值均)/ SD =(0-3.667)/2.868 = -1.279

的問題是:爲什麼輸出DataFrame中的user1對此維度具有零值?

回答

5

這裏是罪魁禍首:

.setWithMean(false) 

因爲只有你申請的是擴展到單位標準偏差的結果是完全應該的:

xs1 <- c(5, 0, 0) 
xs1/sd(xs1) 
## [1] 1.732051 0.000000 0.000000 
sd(xs1/sd(xs1)) 
## [1] 1 

xs2 <- c(0.0, 4.0, 7.0) 
xs2/sd(xs2) 
## [1] 0.000000 1.138990 1.993232 
sd(xs2/sd(xs2)) 
## [1] 1 

而且withMean需要密集的數據。從文檔:

withMean:默認爲False。在縮放之前將數據與平均值集中。它將構建一個密集的輸出,所以這對於稀疏輸入不起作用,並會引發異常。

從註釋合併:

因此,沒有setWithMean也不會值減去平均值,但它會直接sd劃分值。

爲了做.setWithMean(true)我不得不將特徵轉換爲密集矢量而不是稀疏矢量(因爲它會引發稀疏矢量的異常)。