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
- 使用列表理解创建 22 周的专栏
- 将文件读入pandas数据框
- 按指标、ID 和名称对 df 进行分组,对指标的所有周列求和='A'
- 按指标、ID 和名称对 df 进行分组,查找指标='B' 和 'C'
的周列的最大值
- 按指标、ID 和名称对 df 进行分组以找到最大大小
- 合并两个 dfs 不保留重复项
- 使用主 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
我的数据框如下所示:
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
- 使用列表理解创建 22 周的专栏
- 将文件读入pandas数据框
- 按指标、ID 和名称对 df 进行分组,对指标的所有周列求和='A'
- 按指标、ID 和名称对 df 进行分组,查找指标='B' 和 'C' 的周列的最大值
- 按指标、ID 和名称对 df 进行分组以找到最大大小
- 合并两个 dfs 不保留重复项
- 使用主 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