为什么 StandardScaler 在方差不为零时为维度提供非零值?
Why does StandardScaler give non-zero values for dimensions as the variance is not zero?
我有一个如下所示的 DataFrame:
+-----+--------------------+
| 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:
- 此维度的值对于用户 1 为 0.0,对于用户 2 为 4.0,对于用户 3 为 7.0。
- 这些值的平均值是:(0+4+7)/3 = 3.667
- 标准差是:sqrt[ ( (0-3.667)^2 + (4-3.667)^2 + (7-3.667)^2 ) /3] = 2.868
- 用户 1 的单位标准偏差应为:(平均值)/SD = (0-3.667)/2.868 = -1.279
问题是:为什么输出 DataFrame 中的 user1 这个维度的值为零?
这是罪魁祸首:
.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 by default. Centers the data with mean before scaling. It will build a dense output, so this does not work on sparse input and will raise an exception.
从评论中合并:
所以没有setWithMean
它不会从值中减去平均值,而是直接将值除以sd
。
为了做到这一点 .setWithMean(true)
我不得不将特征转换为密集向量而不是稀疏向量(因为它会为稀疏向量抛出异常)。
我有一个如下所示的 DataFrame:
+-----+--------------------+
| 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:
- 此维度的值对于用户 1 为 0.0,对于用户 2 为 4.0,对于用户 3 为 7.0。
- 这些值的平均值是:(0+4+7)/3 = 3.667
- 标准差是:sqrt[ ( (0-3.667)^2 + (4-3.667)^2 + (7-3.667)^2 ) /3] = 2.868
- 用户 1 的单位标准偏差应为:(平均值)/SD = (0-3.667)/2.868 = -1.279
问题是:为什么输出 DataFrame 中的 user1 这个维度的值为零?
这是罪魁祸首:
.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 by default. Centers the data with mean before scaling. It will build a dense output, so this does not work on sparse input and will raise an exception.
从评论中合并:
所以没有setWithMean
它不会从值中减去平均值,而是直接将值除以sd
。
为了做到这一点 .setWithMean(true)
我不得不将特征转换为密集向量而不是稀疏向量(因为它会为稀疏向量抛出异常)。