Pandas 按特定条件分组

Pandas Group by certain condition

我的数据框如下所示:

Id name size metric week1 week2 ... until week 20
54 foo 1 A 20 20 ....
54 foo 1 B 0 0 ....
54 foo 1 C 0.39 0.39 ....
54 foo 6 A 40 40 ....
54 foo 6 B 0.50 0.50 ....
54 foo 6 C 0.39 0.39 ....

所以我每个 id 有 6 行,但我只想要 3 行。我想将第 1 行与第 4 行合并,第 2 行与第 5 行合并,第 3 行与第 6 行合并。

所以我的目标是得到这样的东西:

Id name size metric week1 week2 ... until week 20
54 foo 6 A 60 60 ....
54 foo 6 B 0.50 0.50 ....
54 foo 6 C 0.39 0.39 ....

-> 对于“度量”为 A 的行,对每个“周”列求和。
-> 对于带有“metric”B 和 C 的行,检查值是否相等(如果不相等:获取较高的值)

我已经尝试过:

df.groupby(["id","metric"])[df.columns[4:].sum()

但是所有带有“公制”B 和 C 的行也是总和。


尺寸:
保持(指标 A 的)周值较大的大小。 因此,大小为 1 的行的值为 20,大小为 6 的行的值为 40 -> 因此保持 6 为大小。

我知道有一个嵌套循环的解决方案,但我很确定这可以用 pandas 来完成。

您可以使用下面的代码生成预期的输出,假设 bcz 6 中的 6 号是最大值。

data = {"Id": [54, 54, 54, 54, 54, 54],"name": ["foo", "foo","foo","foo","foo","foo"],"size":[1,1,1,6,6,6], 
    "metrics":["A", "B", "C", "A", "B", "C"], "week1": [20, 0, 0.39, 40, 0.50, 0.39],
    "week2": [20, 0, 0.39, 40, 0.50, 0.39]
   }


grup = df.groupby(["metrics"]).agg({"Id":max, "name":max, "size":max, "week1":sum, "week2":sum})

grup["week1"] = df.groupby(["metrics"]).apply(lambda x: x["week1"].max() if x["metrics"].max() in ["C","B"] else x["week1"].sum())
grup["week2"] = df.groupby(["metrics"]).apply(lambda x: x["week2"].max() if x["metrics"].max() in ["C","B"] else x["week2"].sum()) # Use loop for repeating week column
grup.reset_index()

输出

metrics Id name size week1 week2
A 54 foo 6 60.00 60.00
B 54 foo 6 0.50 0.50
C 54 foo 6 0.39 0.39
week = ['week{}'.format(i) for i in range(1,23)]
df = pd.read_excel('a.xlsx')
df2 = pd.concat([df.query("metric == 'A'") 
                .groupby(['metric','id','name'],as_index=False)[week].agg('sum'),
           df.query("metric != 'A'")
                .groupby(['metric','id','name'],as_index=False)[week].agg('max')
          ]).reset_index(drop=True)
df3 = pd.concat([df.groupby(['metric','id','name'],as_index=False)['size'].agg('max')]).reset_index(drop=True)
df4 = pd.merge(df2, df3, how='inner', on=['metric'], suffixes=('', '_DROP')).filter(regex='^(?!.*_DROP)')
df = df4.reindex(columns=df.columns)
df
  1. 使用列表理解创建 22 周的专栏
  2. 将文件读入pandas数据框
  3. 按指标、ID 和名称对 df 进行分组,对指标的所有周列求和='A'
  4. 按指标、ID 和名称对 df 进行分组,查找指标='B' 和 'C'
  5. 的周列的最大值
  6. 按指标、ID 和名称对 df 进行分组以找到最大大小
  7. 合并两个 dfs 不保留重复项
  8. 使用主 df 的引用重新索引最终 df 的列

输出:

    id  name    size    metric  week1   week2   
0   54  foo       6      A       60.0    60.0   
1   54  foo       6      B       0.50    0.50    
2   54  foo       6      C       0.39    0.39    

另一种方法:

df_pivot = df.pivot_table(index=['Id','name','metric'], 
               values = ['size', 'week1','week2'], 
               aggfunc={'size':'max', 'week1':('sum','max'),'week2':('sum','max')}).reset_index()

n_weeks = 20 #number of weeks

for n in range(1,n_weeks+1):
    week_sum = 'week'+str(n),'sum'
    week_max = 'week'+str(n),'max'
    def week_final(row):
        metric = row['metric']
   
        if metric[0] == 'A':
            return row[week_sum]
        else:
            return row[week_max]

    df_pivot['res'+str(n)] = df_pivot.apply(week_final, axis=1)
    df_pivot = df_pivot.drop(columns=[week_sum, week_max])

2周的产出:

    Id  name    metric  size    res1    res2
                        max     
0   54  foo     A       6      60.00    60.00
1   54  foo     B       6      0.50     0.50
2   54  foo     C       6      0.39     0.39