【问题标题】:Get average number of days per year greater than a daily mean value with Pandas/Python使用 Pandas/Python 获取每年大于每日平均值的平均天数
【发布时间】:2021-09-24 17:40:55
【问题描述】:

假设我创建了以下 Pandas 系列,其中包含 10 年来在三个不同站点的一些日常测量

import numpy as np
import pandas as pd

stations = ['a', 'b', 'c']
dates    = pd.date_range(start = '2000-01-01', end = '2009-12-31')
index    = [(stations[i], dates[j]) for i in range(len(stations)) for j in range(len(dates))]
index    = pd.MultiIndex.from_tuples(index, names=["station", "date"])

x        = np.random.random(len(index))

df       = pd.Series(index = index, data = x)

产生一个看起来像这样的系列:

>>> df
station  date      
a        2000-01-01    0.736381
         2000-01-02    0.203178
         2000-01-03    0.640063
         2000-01-04    0.942664
         2000-01-05    0.953994
                         ...   
c        2009-12-27    0.713189
         2009-12-28    0.800085
         2009-12-29    0.033923
         2009-12-30    0.972547
         2009-12-31    0.387804
Length: 10959, dtype: float64

现在,对于每个站点,我想计算每年的平均天数,其测量值大于给定日期的每日平均值。

我知道我可以像这样计算每个站点的每日平均值:

daily_mean = df.groupby(['station',index.get_level_values('date').dayofyear]).mean()

>>> daily_mean
station  date
a        1       0.529211
         2       0.432048
         3       0.438350
         4       0.629226
         5       0.523919
                   ...   
c        362     0.524537
         363     0.346734
         364     0.423349
         365     0.433348
         366     0.316085
Length: 1098, dtype: float64

但是在这一步之后,我不知道该怎么做。 基本上我想做这样的事情:

df['a','2000-01-01'] > daily_mean['a', 1]
df['a','2000-01-02'] > daily_mean['a', 2]
...
df['a','2000-12-31'] > daily_mean['a', 365]

...然后计算那一年有多少天高于平均水平,并为每一年做这个,然后取所有年份高于平均水平的平均天数。然后对每个站点执行此操作。

我可能可以通过一些痛苦的循环来做我想做的事,但我认为可能有更 Pandas 的方式来做到这一点?

【问题讨论】:

    标签: python pandas


    【解决方案1】:

    您可以使用以下模式将列的值与组内列的平均值进行比较。

    此技术在分组数据帧上使用transform 方法,这将产生与原始分组数据帧长度相同的结果,而不是压缩行。举个例子:

    test = pd.DataFrame({'A': np.random.choice(['a', 'b', 'c'], 10), 'B': np.random.beta(2, 9, 10)})
    test
    

    出来

       A         B
    0  b  0.099245
    1  c  0.081244
    2  b  0.239556
    3  b  0.211645
    4  c  0.256624
    5  c  0.091649
    6  b  0.213261
    7  a  0.327473
    8  a  0.240529
    9  c  0.235569
    
    test.groupby('A').B.mean()
    

    出来

    A
    a    0.284001
    b    0.190927
    c    0.166271
    Name: B, dtype: float64
    

    使用变换:

    test['within_A_mean'] = test.groupby('A').B.transform('mean')
    test.sort_values('A')
    

    出来

       A         B  within_A_mean
    7  a  0.327473       0.284001
    8  a  0.240529       0.284001
    0  b  0.099245       0.190927
    2  b  0.239556       0.190927
    3  b  0.211645       0.190927
    6  b  0.213261       0.190927
    1  c  0.081244       0.166271
    4  c  0.256624       0.166271
    5  c  0.091649       0.166271
    9  c  0.235569       0.166271
    

    所以,回到你的例子:

    # setting up the data as a dataframe instead of a series, with 'measurement' column
    import numpy as np
    import pandas as pd
    
    stations = ['a', 'b', 'c']
    dates    = pd.date_range(start = '2000-01-01', end = '2009-12-31')
    index    = [(stations[i], dates[j]) for i in range(len(stations)) for j in range(len(dates))]
    index    = pd.MultiIndex.from_tuples(index, names=["station", "date"])
    
    x        = np.random.random(len(index))
    
    df       = pd.DataFrame(index = index, data = x, columns=['measurement'])
    
    # create a new boolean column which will indicate if a particular measurement
    # is above the average measurement for the same day of year across the dataset
    df['above_average'] = df\
            .groupby(df.index.get_level_values('date').dayofyear)\
            .measurement\
            .transform(lambda x: x > x.mean())
    

    df['above_average'] 的表达式为:对于每个分组的数据帧(即对于每一年的每个数据帧),对于每一行,行值是否大于组 df 中列的平均值?
    计算完此布尔列后,您可以轻松获得每年高于平均水平的天数:

    df.groupby(df.index.get_level_values('date').year).above_average.mean()
    

    出来

    date
    2000    0.478142
    2001    0.515068
    2002    0.508676
    2003    0.466667
    2004    0.534608
    2005    0.518721
    2006    0.478539
    2007    0.484932
    2008    0.467213
    2009    0.509589
    Name: above_average, dtype: float64
    

    您还可以获得高于全年平均天数的总体平均天数:

    df.above_average.mean()
    

    出来

    0.49621315813486633
    

    编辑
    要获取数字而不是平均值,请使用 sum() 而不是 mean() 作为聚合函数。按站点/年份获取此计数是按这些值分组的问题。

    df = df.reset_index()
    df.groupby(['station', df['date'].dt.year]).above_average.sum()
    

    出来

    station  date
    a        2000    193
             2001    175
             2002    181
             2003    177
             2004    163
             2005    183
             2006    200
             2007    178
             2008    180
             2009    176
    b        2000    159
             2001    185
             2002    186
             2003    170
             2004    188
             2005    176
             2006    190
             2007    175
             2008    185
             2009    171
    c        2000    183
             2001    186
             2002    194
             2003    178
             2004    181
             2005    187
             2006    185
             2007    169
             2008    195
             2009    175
    Name: above_average, dtype: int64
    

    【讨论】:

    • 感谢详细的解释!尽管如此,它仍然不是我想要的。首先,这给了我高于平均天数的平均值,但我需要高于平均天数的 number 个。此外,我需要每个站点单独每年的天数高于平均水平。即a站高于平均天数、b站高于平均天数等
    • 编辑了更多解释
    • 我相信还有另一个错误。我正在寻找将每个站点与该站点的平均值进行比较,而不是所有站点的平均值。我相信 lambda 函数按原样将一个值与所有站点的平均值进行比较?
    • 好的,通过将您的行 df['above_average'] = df.groupby(df.index.get_level_values('date').dayofyear)... 更改为 df.groupby([df.index.get_level_values('date').dayofyear, df.index.get_level_values('station')]),我能够得到我想要的。不过,这需要很长时间才能在我的数据上运行约 5000 个站点! ://
    • 按高基数字段分组会很慢。您可以尝试将您的站转换为分类数据类型,以便 pandas 可以用整数代码表示它,而不是按字符串字段分组。
    猜你喜欢
    • 2015-04-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-07-01
    • 2021-01-30
    • 1970-01-01
    相关资源
    最近更新 更多