为什么StandardScaler为维度提供非零值,因为方差不为零?

Ram*_*ami 3 apache-spark apache-spark-ml

我有一个看起来如下的DataFrame:

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

features列是稀疏向量,大小等于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)
Run Code Online (Sandbox Code Playgroud)

输出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...|
+-----+--------------------+--------------------+
Run Code Online (Sandbox Code Playgroud)

我们可以看到user1的scaledFeatures例如只包含一个元素(其他为零),但我希望每个scaledFeatures总是包含所有维度的非零值,因为方差不为零.

让我们以第三维为例,即每个特征向量的索引2:

  • user1的值为0.0,user2的值为4.0,user3的值为7.0.
  • 这些值的平均值为:(0 + 4 + 7)/ 3 = 3.667
  • SD为:sqrt [((0-3.667)^ 2 +(4-3.667)^ 2 +(7-3.667)^ 2)/ 3] = 2.868
  • user1的单位标准偏差应为:(平均值)/ SD =(0-3.667)/2.868 = -1.279

问题是:为什么输出DataFrame中的user1对于此维度的值为零?

zer*_*323 6

这是罪魁祸首:

.setWithMean(false) 
Run Code Online (Sandbox Code Playgroud)

由于您只应用于缩放到单位标准偏差,因此结果完全如下:

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
Run Code Online (Sandbox Code Playgroud)

withMean需要密集数据.来自文档:

withMean:默认为False.在缩放之前使用均值将数据居中.它将构建一个密集的输出,因此这对稀疏输入不起作用并且会引发异常.

从评论中合并:

所以没有setWithMean它不会从值中减去均值,但它会直接将值除以sd.

为了做到.setWithMean(true)这一点,我不得不将特征转换为密集向量而不是稀疏向量(因为它抛出了稀疏向量的异常).