Spark 中的 StandardScaler 未按预期工作

2024-05-16

知道为什么 Spark 会这样做吗StandardScaler?根据定义StandardScaler:

StandardScaler 将一组特征标准化为均值为零 标准差为 1。 withStd 标志将数据缩放为 单位标准差,而标志 withMean (默认为 false) 在缩放之前将数据居中。

>>> tmpdf.show(4)
+----+----+----+------------+
|int1|int2|int3|temp_feature|
+----+----+----+------------+
|   1|   2|   3|       [2.0]|
|   7|   8|   9|       [8.0]|
|   4|   5|   6|       [5.0]|
+----+----+----+------------+

>>> sScaler = StandardScaler(withMean=True, withStd=True).setInputCol("temp_feature")
>>> sScaler.fit(tmpdf).transform(tmpdf).show()
+----+----+----+------------+-------------------------------------------+
|int1|int2|int3|temp_feature|StandardScaler_4fe08ca180ab163e4120__output|
+----+----+----+------------+-------------------------------------------+
|   1|   2|   3|       [2.0]|                                     [-1.0]|
|   7|   8|   9|       [8.0]|                                      [1.0]|
|   4|   5|   6|       [5.0]|                                      [0.0]|
+----+----+----+------------+-------------------------------------------+

在 numpy 世界中

>>> x
array([2., 8., 5.])
>>> (x - x.mean())/x.std()
array([-1.22474487,  1.22474487,  0.        ])

在sklearn世界里

>>> scaler = StandardScaler(with_mean=True, with_std=True)
>>> data
[[2.0], [8.0], [5.0]]
>>> print(scaler.fit(data).transform(data))
[[-1.22474487]
 [ 1.22474487]
 [ 0.        ]]

您的结果不符合预期的原因是pyspark.ml.feature.StandardScaler https://spark.apache.org/docs/latest/api/python/pyspark.ml.html#pyspark.ml.feature.StandardScaler使用无偏样本标准差而不是总体标准差。

来自文档:

“单位标准差”是使用以下公式计算的修正样本标准差 https://en.wikipedia.org/wiki/Standard_deviation#Corrected_sample_standard_deviation,计算为无偏样本方差的平方根。

如果你想尝试你的numpy使用样本标准差编写代码,您会看到相同的结果:

import numpy as np

x = np.array([2., 8., 5.])
print((x - x.mean())/x.std(ddof=1))
#array([-1.,  1.,  0.])

从建模的角度来看,这几乎肯定不是问题(除非您的数据是整个群体,但事实并非如此)。另请记住,对于大样本量,样本标准差接近总体标准差。因此,如果您的 DataFrame 中有很多行,则此处的差异可以忽略不计。


但是,如果您坚持让缩放器使用总体标准差,一种“hacky”方法是向 DataFrame 添加一行,该行是列的平均值。

回想一下,标准差定义为均值差平方和的平方根。或者作为一个函数:

# using the same x as above
def popstd(x): 
    return np.sqrt(sum((xi - x.mean())**2/len(x) for xi in x))

print(popstd(x))
#2.4494897427831779

print(x.std())
#2.4494897427831779

使用无偏标准差时的区别只是除以len(x)-1代替len(x)。因此,如果您添加等于平均值​​的样本,则可以增加分母,而不会影响总体平均值。

假设您有以下 DataFrame:

df = spark.createDataFrame(
    np.array(range(1,10,1)).reshape(3,3).tolist(),
    ["int1", "int2", "int3"]
)
df.show()
#+----+----+----+
#|int1|int2|int3|
#+----+----+----+
#|   1|   2|   3|
#|   4|   5|   6|
#|   7|   8|   9|
#+----+----+----+

将此 DataFrame 与每列的平均值合并:

import pyspark.sql.functions as f
# This is equivalent to UNION ALL in SQL
df2 = df.union(df.select(*[f.avg(c).alias(c) for c in df.columns]))

现在衡量你的价值观:

from pyspark.ml.feature import VectorAssembler, StandardScaler
va = VectorAssembler(inputCols=["int2"], outputCol="temp_feature")

tmpdf = va.transform(df2)
sScaler = StandardScaler(
    withMean=True, withStd=True, inputCol="temp_feature", outputCol="scaled"
)
sScaler.fit(tmpdf).transform(tmpdf).show()
#+----+----+----+------------+---------------------+
#|int1|int2|int3|temp_feature|scaled               |
#+----+----+----+------------+---------------------+
#|1.0 |2.0 |3.0 |[2.0]       |[-1.2247448713915892]|
#|4.0 |5.0 |6.0 |[5.0]       |[0.0]                |
#|7.0 |8.0 |9.0 |[8.0]       |[1.2247448713915892] |
#|4.0 |5.0 |6.0 |[5.0]       |[0.0]                |
#+----+----+----+------------+---------------------+
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Spark 中的 StandardScaler 未按预期工作 的相关文章

随机推荐