我遇到了Pandas的groupby功能问题。我已阅读the documentation,但我无法弄清楚如何将聚合函数应用于多个列和具有这些列的自定义名称。
这非常接近,但返回的数据结构具有嵌套的列标题:
data.groupby("Country").agg(
{"column1": {"foo": sum()}, "column2": {"mean": np.mean, "std": np.std}})
(即我想取column2的mean和std,但将这些列作为“mean”和“std”返回)
我错过了什么?
答案 0 :(得分:81)
这将从分层列索引中删除最外层:
df = data.groupby(...).agg(...)
df.columns = df.columns.droplevel(0)
如果您想保留最外层,可以使用多级列上的ravel()函数来形成新标签:
df.columns = ["_".join(x) for x in df.columns.ravel()]
例如:
import pandas as pd
import pandas.rpy.common as com
import numpy as np
data = com.load_data('Loblolly')
print(data.head())
# height age Seed
# 1 4.51 3 301
# 15 10.89 5 301
# 29 28.72 10 301
# 43 41.74 15 301
# 57 52.70 20 301
df = data.groupby('Seed').agg(
{'age':['sum'],
'height':['mean', 'std']})
print(df.head())
# age height
# sum std mean
# Seed
# 301 78 22.638417 33.246667
# 303 78 23.499706 34.106667
# 305 78 23.927090 35.115000
# 307 78 22.222266 31.328333
# 309 78 23.132574 33.781667
df.columns = df.columns.droplevel(0)
print(df.head())
产量
sum std mean
Seed
301 78 22.638417 33.246667
303 78 23.499706 34.106667
305 78 23.927090 35.115000
307 78 22.222266 31.328333
309 78 23.132574 33.781667
或者,保持索引的第一级:
df = data.groupby('Seed').agg(
{'age':['sum'],
'height':['mean', 'std']})
df.columns = ["_".join(x) for x in df.columns.ravel()]
产量
age_sum height_std height_mean
Seed
301 78 22.638417 33.246667
303 78 23.499706 34.106667
305 78 23.927090 35.115000
307 78 22.222266 31.328333
309 78 23.132574 33.781667
答案 1 :(得分:50)
unutbu当前接受的答案是在pandas版本< = 0.20中执行此操作的好方法。但是,从pandas 0.20开始,使用此方法会发出警告,指示语法在将来的pandas版本中不可用。
系列:
FutureWarning:不推荐在系列上使用dict进行聚合,并将在以后的版本中删除
DataFrames:
FutureWarning:不推荐使用带重命名的dict,并将在以后的版本中删除
根据pandas 0.20 changelog,聚合时重命名列的推荐方法如下:
# Create a sample data frame
df = pd.DataFrame({'A': [1, 1, 1, 2, 2],
'B': range(5),
'C': range(5)})
# ==== SINGLE COLUMN (SERIES) ====
# Syntax soon to be deprecated
df.groupby('A').B.agg({'foo': 'count'})
# Recommended replacement syntax
df.groupby('A').B.agg(['count']).rename(columns={'count': 'foo'})
# ==== MULTI COLUMN ====
# Syntax soon to be deprecated
df.groupby('A').agg({'B': {'foo': 'sum'}, 'C': {'bar': 'min'}})
# Recommended replacement syntax
df.groupby('A').agg({'B': 'sum', 'C': 'min'}).rename(columns={'B': 'foo', 'C': 'bar'})
# As the recommended syntax is more verbose, parentheses can
# be used to introduce line breaks and increase readability
(df.groupby('A')
.agg({'B': 'sum', 'C': 'min'})
.rename(columns={'B': 'foo', 'C': 'bar'})
)
有关其他详细信息,请参阅0.20 changelog。
使用旧样式字典语法,可以将多个lambda
函数传递给.agg
,因为这些函数将使用传递的字典中的键重命名:
>>> df.groupby('A').agg({'B': {'min': lambda x: x.min(), 'max': lambda x: x.max()}})
B
max min
A
1 2 0
2 4 3
多个函数也可以作为列表传递给单个列:
>>> df.groupby('A').agg({'B': [np.min, np.max]})
B
amin amax
A
1 0 2
2 3 4
但是,这不适用于lambda函数,因为它们是匿名的并且都返回<lambda>
,这会导致名称冲突:
>>> df.groupby('A').agg({'B': [lambda x: x.min(), lambda x: x.max]})
SpecificationError: Function names must be unique, found multiple named <lambda>
为避免SpecificationError
,可以先验地定义命名函数,而不是使用lambda
。合适的函数名称也可以避免以后在数据框上调用.rename
。这些函数可以使用与上面相同的列表语法传递:
>>> def my_min(x):
>>> return x.min()
>>> def my_max(x):
>>> return x.max()
>>> df.groupby('A').agg({'B': [my_min, my_max]})
B
my_min my_max
A
1 0 2
2 3 4
答案 2 :(得分:6)
如果您希望获得类似于JMP的行为,请创建列标题,以保留您可以使用的多索引中的所有信息:
newidx = []
for (n1,n2) in df.columns.ravel():
newidx.append("%s-%s" % (n1,n2))
df.columns=newidx
它会改变您的数据框架:
I V
mean std first
V
4200.0 25.499536 31.557133 4200.0
4300.0 25.605662 31.678046 4300.0
4400.0 26.679005 32.919996 4400.0
4500.0 26.786458 32.811633 4500.0
到
I-mean I-std V-first
V
4200.0 25.499536 31.557133 4200.0
4300.0 25.605662 31.678046 4300.0
4400.0 26.679005 32.919996 4400.0
4500.0 26.786458 32.811633 4500.0
答案 3 :(得分:4)
我同意OP认为在同一个地方命名和定义输出列似乎更自然和一致(例如用tidyverse's summarize
in R完成),但现在大熊猫的解决办法是在进行聚合之前,通过assign
创建具有所需名称的新列:
data.assign(
f=data['column1'],
mean=data['column2'],
std=data['column2']
).groupby('Country').agg(dict(f=sum, mean=np.mean, std=np.std)).reset_index()
(使用reset_index
将'Country'
,'f'
,'mean'
和'std'
全部转换为带有单独整数索引的常规列。)
答案 4 :(得分:0)
借助@Joel Ostblom的灵感
对于那些已经只有聚合的可用字典的人,可以使用/修改以下代码用于较新的版本聚合,分离聚合和重命名部分。如果有多个项目,请注意嵌套字典。
def agg_translate_agg_rename(input_agg_dict):
agg_dict = {}
rename_dict = {}
for k, v in input_agg_dict.items():
if len(v) == 1:
agg_dict[k] = list(v.values())[0]
rename_dict[k] = list(v.keys())[0]
else:
updated_index = 1
for nested_dict_k, nested_dict_v in v.items():
modified_key = k + "_" + str(updated_index)
agg_dict[modified_key] = nested_dict_v
rename_dict[modified_key] = nested_dict_k
updated_index += 1
return agg_dict, rename_dict
one_dict = {"column1": {"foo": 'sum'}, "column2": {"mean": 'mean', "std": 'std'}}
agg, rename = agg_translator_aa(one_dict)
我们得到了
agg = {'column1': 'sum', 'column2_1': 'mean', 'column2_2': 'std'}
rename = {'column1': 'foo', 'column2_1': 'mean', 'column2_2': 'std'}
如果有更聪明的方法,请告诉我。感谢。
答案 5 :(得分:0)
例如这种数据框,列名称有两个级别:
shop_id item_id date_block_num item_cnt_day
target
0 0 30 1 31
我们可以使用此代码:
df.columns = [col[0] if col[-1]=='' else col[-1] for col in df.columns.values]
结果是:
shop_id item_id date_block_num target
0 0 30 1 31