【问题标题】:Pandas Groupby Conditional AggregationPandas Groupby 条件聚合
【发布时间】:2019-08-10 06:08:55
【问题描述】:

假设你有一个如下的数据框:

data = pd.DataFrame({'Year': [2019]*5+[2020]*5,
          'Month': [1,1,2,2,3]*2,
          'Hour': [0,1,2,3,4]*2,
          'Value': [0.2,0.3,0.2,0.1,0.4,0.3,0.2,0.5,0.1,0.2]})

然后,将“低”时间设置为 1 到 3 小时(含)之间的时间,将“高”时间设置为所有其他时间(在本例中为 0 和 4 小时)。我想做的是获得每个YearMonth 的“低”和“高”时间的平均Value。理想情况下,这些将作为新列附加到 groupby() 数据帧(即,最终数据帧将具有 YearMonthLowHigh 列)。

For 循环可以工作,但并不理想。我还可以创建一个虚拟变量(例如,0 和 1)来表示数据帧中的“低”和“高”时间到 groupby。但是,在我看来,应该有某种方法可以使用 Pandas groupby(['Year', 'Month']).agg(...) 以有效/最佳的方式实现结果。到目前为止,我没有任何运气使用 groupby+agg,主要是因为 agg() 只使用一个系列(而不是剩余的数据帧),所以不能使用基于 Hour 的 agg 中的条件来计算平均值Value.

样本数据的预期结果:

Year Month High Low 0 2019 1 0.2 0.30 1 2019 2 NaN 0.15 2 2019 3 0.4 NaN 3 2020 1 0.3 0.20 4 2020 2 NaN 0.30 5 2020 3 0.2 NaN

任何帮助表示赞赏:)

【问题讨论】:

  • 第一次读错你的问题,如果我理解你到底想要4行?每年与每月相结合。那是对的吗?请参阅我的输出答案。

标签: python pandas pandas-groupby


【解决方案1】:

在创建低/高类型指标字段后考虑pivot_table

data['Type'] = np.where(data['Hour'].between(1,3), 'Low', 'High')

pvt_df = (pd.pivot_table(data, index=['Year', 'Month'], 
                         columns='Type', values='Value', aggfunc=np.mean)
            .reset_index()
            .rename_axis(None, axis='columns')
         )    

print(pvt_df)
#    Year  Month  High   Low
# 0  2019      1   0.2  0.30
# 1  2019      2   NaN  0.15
# 2  2019      3   0.4   NaN
# 3  2020      1   0.3  0.20
# 4  2020      2   NaN  0.30
# 5  2020      3   0.2   NaN

【讨论】:

    【解决方案2】:

    可能不会赢得最漂亮的代码的价格,但如果我理解正确,这就是你想要的。

    (如果我错了,请纠正我,因为没有包含预期的输出)

    Groupby 4 次并将年份和月份连接在一起。 之后进行最终合并以将所有列放在一起

    low_hours = [1, 2, 3]
    
    groupby1 = data[data.Hour.isin(low_hours)].groupby('Year').Value.mean().reset_index().rename({'Value':'Value_year_low'},axis=1)
    groupby2 = data[~data.Hour.isin(low_hours)].groupby('Year').Value.mean().reset_index().rename({'Value':'Value_year_high'},axis=1).drop('Year', axis=1)
    groupby3 = data[data.Hour.isin(low_hours)].groupby(['Year','Month']).Value.mean().reset_index().rename({'Value':'Value_month_low'},axis=1)
    groupby4 = data[~data.Hour.isin(low_hours)].groupby(['Year','Month']).Value.mean().reset_index().rename({'Value':'Value_month_high'},axis=1).drop(['Year','Month'], axis=1)
    
    df_final1 = pd.concat([groupby1, groupby2], axis=1)
    df_final2 = pd.concat([groupby3, groupby4], axis=1)
    
    df_final = pd.merge(df_final1, df_final2, on='Year')
    print(df_final)
       Year  Value_year_low  Value_year_high  Month  Value_month_low  \
    0  2019        0.200000             0.30      1             0.30   
    1  2019        0.200000             0.30      2             0.15   
    2  2020        0.266667             0.25      1             0.20   
    3  2020        0.266667             0.25      2             0.30   
    
       Value_month_high  
    0               0.2  
    1               0.4  
    2               0.3  
    3               0.2  
    

    【讨论】:

    • 是的。生成的数据框应该有四行:2019/1、2019/2、2020/1、2020/2。但是,结果中应该只有四列:YearMonthLowHigh。因此,LowHigh 值应该是该月和该年各自小时的平均值。
    • 在我的输出 Value_month_lowValue_month_high 中,只有名称是错误的。正确的?如果是,我将使用正确的输出编辑答案。 @rhozzy
    • 是的,看起来这些值是正确的。但是,第 3 个月的结果在哪里?此外,据我所知,这个答案并没有真正找到一种有效/最佳的方式来做到这一点......
    【解决方案3】:
    data = pd.DataFrame({'Year': [2019]*5+[2020]*5,
              'Month': [1,1,2,2,3]*2,
              'Hour': [0,1,2,3,4]*2,
              'Value': [0.2,0.3,0.2,0.1,0.4,0.3,0.2,0.5,0.1,0.2]})
    
    data['low'] = (data['Hour'] > 0) & (data['Hour'] < 4)
    
    data[data['low']][['Month', 'Year']].mean()
    data[~data['low']][['Month', 'Year']].mean()
    

    【讨论】:

    • 谢谢,但这与为数据框创建新标志并没有什么不同,正如我在问题中所建议的那样。有没有更好的办法?
    • 另外,您提出的解决方案似乎实际上并没有通过YearMonth 返回平均Value
    猜你喜欢
    • 2018-09-29
    • 1970-01-01
    • 1970-01-01
    • 2021-12-17
    • 2014-11-23
    • 2018-02-03
    • 2022-01-21
    • 2017-11-03
    • 2021-09-19
    相关资源
    最近更新 更多