【问题标题】:Pandas - Compute z-score for all columnsPandas - 计算所有列的 z 分数
【发布时间】:2014-09-05 20:56:38
【问题描述】:

我有一个包含单列 ID 的数据框,所有其他列都是我想要计算 z 分数的数值。这是它的一个小节:

ID      Age    BMI    Risk Factor
PT 6    48     19.3    4
PT 8    43     20.9    NaN
PT 2    39     18.1    3
PT 9    41     19.5    NaN

我的某些列包含 NaN 值,我不想将其包含在 z 分数计算中,因此我打算使用针对此问题提供的解决方案:how to zscore normalize pandas column with nans?

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

我有兴趣将此解决方案应用于除 ID 列之外的所有列,以生成一个新数据框,我可以使用该数据框将其保存为 Excel 文件

df2.to_excel("Z-Scores.xlsx")

基本上是这样;如何计算每列的 z 分数(忽略 NaN 值)并将所有内容推送到新数据框中?

旁注:pandas 中有一个叫做“索引”的概念让我感到害怕,因为我不太了解它。如果索引是解决此问题的关键部分,请简化您对索引的解释。

【问题讨论】:

  • 您对索引有什么不了解?
  • 我认为它类似于 SQL 数据库中主键的概念,您可以在其中设置一个标识符,让您可以引用一行中的值;但我什至不确定。我也不明白什么时候要设置索引。
  • 索引的概念与 SQL 表没有什么不同,但与聚集索引不同的是,多索引会有不同的级别,例如按性别、年龄、体重进行分组。另一个概念是标签索引,您的索引可以是任何东西,字符串,日期,整数等。您可以使用标签索引或整数值进行索引:pandas.pydata.org/pandas-docs/stable/…
  • 有趣;一旦我掌握了它,这听起来可能真的很有用。我仍然对文档中所有不熟悉的行话感到害怕,但现在肯定感觉更容易理解了。再次感谢。
  • (可能为时已晚,无法为您提供帮助。)df.set_index(['ID']) 的一大优势是您现在无需单独处理该列的所有麻烦。

标签: python pandas indexing statistics


【解决方案1】:

从列中构建一个列表并删除您不想为其计算 Z 分数的列:

In [66]:
cols = list(df.columns)
cols.remove('ID')
df[cols]

Out[66]:
   Age  BMI  Risk  Factor
0    6   48  19.3       4
1    8   43  20.9     NaN
2    2   39  18.1       3
3    9   41  19.5     NaN
In [68]:
# now iterate over the remaining columns and create a new zscore column
for col in cols:
    col_zscore = col + '_zscore'
    df[col_zscore] = (df[col] - df[col].mean())/df[col].std(ddof=0)
df
Out[68]:
   ID  Age  BMI  Risk  Factor  Age_zscore  BMI_zscore  Risk_zscore  \
0  PT    6   48  19.3       4   -0.093250    1.569614    -0.150946   
1  PT    8   43  20.9     NaN    0.652753    0.074744     1.459148   
2  PT    2   39  18.1       3   -1.585258   -1.121153    -1.358517   
3  PT    9   41  19.5     NaN    1.025755   -0.523205     0.050315   

   Factor_zscore  
0              1  
1            NaN  
2             -1  
3            NaN  

【讨论】:

  • 有没有办法在没有 for 循环的情况下做到这一点? (假设您不需要删除其中一列...)
  • @AlexLenail 3 年后再次查看此内容,您可以定义一个 func 并使用 apply 调用此函数,因为这是 for 循环的语法糖
  • @RyszardCetnarski 查看解释 statsdirect.com/help/basics/degrees_freedom.htmstats.stackexchange.com/questions/58230/… 这取决于您的用例
  • 除非我遗漏了什么,@Manuel 下面的答案(使用 scipy 的 zscore 函数)应该更好:没有循环,使用现有函数,而且更简洁。为什么要重新发明轮子并在代码中添加更多行呢?
  • @EdChum:这很有道理——我不认为这是一个糟糕的答案;相反,SE 的目的是在顶部获得可能的最佳答案。所以,我的评论是引导人们采用更新的方法,即使 OP 没有回来更新他的选择。或者,我看到受访者将后来的答案合并到他们的答案中,将添加该回复的人归功于(再次考虑 SE 的哲学)。因此,您可以添加 scipy 元素(不删除您的元素)并归功于 Manuel。
【解决方案2】:

几乎单线解决方案:

df2 = (df.ix[:,1:] - df.ix[:,1:].mean()) / df.ix[:,1:].std()
df2['ID'] = df['ID']

【讨论】:

  • 几乎是单线也就是两线 :)
  • 一个班轮df2 = df2.assign(ID=(df.ix[:,1:] - df.ix[:,1:].mean()) / df.ix[:,1:].std())
【解决方案3】:

使用Scipy's zscore函数:

df = pd.DataFrame(np.random.randint(100, 200, size=(5, 3)), columns=['A', 'B', 'C'])
df

|    |   A |   B |   C |
|---:|----:|----:|----:|
|  0 | 163 | 163 | 159 |
|  1 | 120 | 153 | 181 |
|  2 | 130 | 199 | 108 |
|  3 | 108 | 188 | 157 |
|  4 | 109 | 171 | 119 |

from scipy.stats import zscore
df.apply(zscore)

|    |         A |         B |         C |
|---:|----------:|----------:|----------:|
|  0 |  1.83447  | -0.708023 |  0.523362 |
|  1 | -0.297482 | -1.30804  |  1.3342   |
|  2 |  0.198321 |  1.45205  | -1.35632  |
|  3 | -0.892446 |  0.792025 |  0.449649 |
|  4 | -0.842866 | -0.228007 | -0.950897 |

如果不是数据框的所有列都是数字,那么您可以使用 select_dtypes 函数将 Z 分数函数仅应用于数字列:

# Note that `select_dtypes` returns a data frame. We are selecting only the columns
numeric_cols = df.select_dtypes(include=[np.number]).columns
df[numeric_cols].apply(zscore)

|    |         A |         B |         C |
|---:|----------:|----------:|----------:|
|  0 |  1.83447  | -0.708023 |  0.523362 |
|  1 | -0.297482 | -1.30804  |  1.3342   |
|  2 |  0.198321 |  1.45205  | -1.35632  |
|  3 | -0.892446 |  0.792025 |  0.449649 |
|  4 | -0.842866 | -0.228007 | -0.950897 |

【讨论】:

  • 如何apply 原位而不是返回一个新副本?
  • @chandresh,apply没有inplace参数,所以不能用函数结果替换列数据。你应该检查这个问题:stackoverflow.com/questions/45570984/…
【解决方案4】:

如果你想计算所有列的 zscore,你可以使用以下方法:

df_zscore = (df - df.mean())/df.std()

【讨论】:

  • 奇怪的是,无论如何,对我来说,这个分​​数计算给出的结果与“from scipy.stats import zscore; df.apply(zscore)”略有不同。有人知道为什么吗?
  • @pitosalas:std 函数的默认 ddof 可能不同
  • @pitosalas:@ascripter,你是对的。传递df.std(ddof=0) 产生与df.apply(scipy.stats.zscore) 相同的结果
  • pandas 可能不会对非数字 ID 列感到满意,但无论如何它应该是一个索引。我喜欢这个对整个数据框进行操作,而不是像其他答案那样逐列操作。
【解决方案5】:

当我们处理时间序列时,计算 z 分数(或异常 - 不是一回事,但您可以轻松地调整此代码)有点复杂。例如,您每周测量 10 年的温度数据。要计算整个时间序列的 z 分数,您必须知道一年中每一天的平均值和标准差。那么,让我们开始吧:

假设您有一个 pandas DataFrame。首先,您需要一个 DateTime 索引。如果您还没有它,但幸运的是您确实有一个包含日期的列,只需将其作为您的索引。 Pandas 会尝试猜测日期格式。这里的目标是拥有 DateTimeIndex。你可以试试看:

type(df.index)

如果你没有,让我们来吧。

df.index = pd.DatetimeIndex(df[datecolumn])
df = df.drop(datecolumn,axis=1)

下一步是计算每组天数的平均值和标准差。为此,我们使用 groupby 方法。

mean = pd.groupby(df,by=[df.index.dayofyear]).aggregate(np.nanmean)
std = pd.groupby(df,by=[df.index.dayofyear]).aggregate(np.nanstd)

最后,我们循环遍历所有的日期,执行计算 (value - mean)/stddev;但是,如前所述,对于时间序列,这并不是那么简单。

df2 = df.copy() #keep a copy for future comparisons 
for y in np.unique(df.index.year):
    for d in np.unique(df.index.dayofyear):
        df2[(df.index.year==y) & (df.index.dayofyear==d)] = (df[(df.index.year==y) & (df.index.dayofyear==d)]- mean.ix[d])/std.ix[d]
        df2.index.name = 'date' #this is just to look nicer

df2 #this is your z-score dataset.

for 循环内的逻辑是:对于给定的年份,我们必须将每一年的每一天与其平均值和标准差相匹配。我们在您的时间序列中运行了所有年份。

【讨论】:

    【解决方案6】:

    这是使用自定义函数获取 Zscore 的另一种方法:

    In [6]: import pandas as pd; import numpy as np
    
    In [7]: np.random.seed(0) # Fixes the random seed
    
    In [8]: df = pd.DataFrame(np.random.randn(5,3), columns=["randomA", "randomB","randomC"])
    
    In [9]: df # watch output of dataframe
    Out[9]:
        randomA   randomB   randomC
    0  1.764052  0.400157  0.978738
    1  2.240893  1.867558 -0.977278
    2  0.950088 -0.151357 -0.103219
    3  0.410599  0.144044  1.454274
    4  0.761038  0.121675  0.443863
    
    ## Create custom function to compute Zscore 
    In [10]: def z_score(df):
       ....:         df.columns = [x + "_zscore" for x in df.columns.tolist()]
       ....:         return ((df - df.mean())/df.std(ddof=0))
       ....:
    
    ## make sure you filter or select columns of interest before passing dataframe to function
    In [11]: z_score(df) # compute Zscore
    Out[11]:
       randomA_zscore  randomB_zscore  randomC_zscore
    0        0.798350       -0.106335        0.731041
    1        1.505002        1.939828       -1.577295
    2       -0.407899       -0.875374       -0.545799
    3       -1.207392       -0.463464        1.292230
    4       -0.688061       -0.494655        0.099824
    

    使用 scipy.stats zscore 复制结果

    In [12]: from scipy.stats import zscore
    
    In [13]: df.apply(zscore) # (Credit: Manuel)
    Out[13]:
        randomA   randomB   randomC
    0  0.798350 -0.106335  0.731041
    1  1.505002  1.939828 -1.577295
    2 -0.407899 -0.875374 -0.545799
    3 -1.207392 -0.463464  1.292230
    4 -0.688061 -0.494655  0.099824
    

    【讨论】:

      【解决方案7】:

      对于 Z 分数,我们可以坚持使用文档而不是使用“应用”功能

      from scipy.stats import zscore
      df_zscore = zscore(cols as array, axis=1)
      

      【讨论】:

      • zscore 在哪个包中?
      • 我刚刚修好了。它是 scipy 库
      【解决方案8】:

      要快速计算整列的 z 分数,请执行以下操作:

      from scipy.stats import zscore
      import pandas as pd
      
      df = pd.DataFrame({'num_1': [1,2,3,4,5,6,7,8,9,3,4,6,5,7,3,2,9]})
      df['num_1_zscore'] = zscore(df['num_1'])
      
      display(df)
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2022-11-15
        • 1970-01-01
        • 2014-07-29
        • 2021-09-10
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多