如何使用 Pandas 对含有 NaN 值的列进行 Z-Score 标准化?

27

我有一个带有实数列的pandas数据框,我想对它进行z-score标准化:

>> a
array([    nan,  0.0767,  0.4383,  0.7866,  0.8091,  0.1954,  0.6307,
        0.6599,  0.1065,  0.0508])
>> df = pandas.DataFrame({"a": a})
问题在于一个单独的nan值会使整个数组变成nan:

问题在于一个单独的nan值会使整个数组变成nan:

>> from scipy.stats import zscore
>> zscore(df["a"])
array([ nan,  nan,  nan,  nan,  nan,  nan,  nan,  nan,  nan,  nan])

如何正确地将 zscore(或不来自scipy的等效函数)应用于pandas dataframe的一列,并忽略nan值?我希望它与原始列具有相同的维度,并使用np.nan表示无法归一化的值。

编辑:也许最好的解决方案是使用scipy.stats.nanmeanscipy.stats.nanstd?我不明白为什么需要改变std的自由度:

zscore = lambda x: (x - scipy.stats.nanmean(x)) / scipy.stats.nanstd(x)
4个回答

28

好的,pandasmeanstd 的版本会处理 Nan 值,所以您可以使用这种方式计算(要得到与 scipy zscore 相同的结果,我认为您需要在 std 上使用 ddof=0):

df['zscore'] = (df.a - df.a.mean())/df.a.std(ddof=0)
print df

        a    zscore
0     NaN       NaN
1  0.0767 -1.148329
2  0.4383  0.071478
3  0.7866  1.246419
4  0.8091  1.322320
5  0.1954 -0.747912
6  0.6307  0.720512
7  0.6599  0.819014
8  0.1065 -1.047803
9  0.0508 -1.235699

2
如果您想在整个DataFrame上获取z_score,可以尝试使用以下代码: return (df-df.mean())/df.std(ddof=0)``` 以及 ```df.apply(z_score)``` - Pengju Zhao
虽然对于小样本的统计数据集,我会考虑使用ddof=1。 - Ryszard Cetnarski

7

我不确定这个参数从什么时候开始存在,因为我并没有长时间使用Python。但你可以简单地使用参数nan_policy = 'omit',从而在计算中忽略NaN值:

a = np.array([np.nan,  0.0767,  0.4383,  0.7866,  0.8091,  0.1954,  0.6307, 0.6599, 0.1065,  0.0508])
ZScore_a = stats.zscore(a,nan_policy='omit')

print(ZScore_a)
[nan -1.14832945  0.07147776  1.24641928  1.3223199  -0.74791154
0.72051236  0.81901449 -1.0478033  -1.23569949]

6
您可以使用isnan来忽略NaN值。
z = a                    # initialise array for zscores
z[~np.isnan(a)] = zscore(a[~np.isnan(a)])
pandas.DataFrame({'a':a,'Zscore':z})

     Zscore       a
0       NaN     NaN
1 -1.148329  0.0767
2  0.071478  0.4383
3  1.246419  0.7866
4  1.322320  0.8091
5 -0.747912  0.1954
6  0.720512  0.6307
7  0.819014  0.6599
8 -1.047803  0.1065
9 -1.235699  0.0508

1
我认为你需要使用“z = a.copy()”而不是“z = a”,否则z指向同一数组,你将会覆盖a中的元素及其z分数。 - Pont

0

解决此问题的另一种替代方案是在计算z-score时使用列均值填充DataFrame中的NaN。这将导致NaN被计算为具有0的z-score,然后可以使用原始df上的notna进行掩码处理。

您可以使用以下一行代码创建与原始df相同维度的DataFrame,其中包含原始df值和NaN的z-score:

zscore_df = pd.DataFrame(scipy.stats.zscore(df.fillna(df.mean())), index=df.index, columns=df.columns).where(df.notna())

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接