Dav*_*ard 69 python group-by aggregate-functions pandas
我在使用Pandas的groupby功能时遇到了麻烦.我已经阅读了文档,但是我无法弄清楚如何将聚合函数应用于多个列并为这些列提供自定义名称.
这非常接近,但返回的数据结构具有嵌套的列标题:
data.groupby("Country").agg(
{"column1": {"foo": sum()}, "column2": {"mean": np.mean, "std": np.std}})
Run Code Online (Sandbox Code Playgroud)
(即.我想取column2的mean和std,但将这些列作为"mean"和"std"返回)
我错过了什么?
unu*_*tbu 91
这将从分层列索引中删除最外层:
df = data.groupby(...).agg(...)
df.columns = df.columns.droplevel(0)
Run Code Online (Sandbox Code Playgroud)
如果您想保留最外层,可以使用多级列上的ravel()函数来形成新标签:
df.columns = ["_".join(x) for x in df.columns.ravel()]
Run Code Online (Sandbox Code Playgroud)
例如:
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())
Run Code Online (Sandbox Code Playgroud)
产量
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
Run Code Online (Sandbox Code Playgroud)
或者,保持索引的第一级:
df = data.groupby('Seed').agg(
{'age':['sum'],
'height':['mean', 'std']})
df.columns = ["_".join(x) for x in df.columns.ravel()]
Run Code Online (Sandbox Code Playgroud)
产量
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
Run Code Online (Sandbox Code Playgroud)
joe*_*lom 80
unutbu描述的当前接受的答案是在pandas版本<= 0.20中执行此操作的好方法.但是,从pandas 0.20开始,使用此方法会发出警告,指示在将来的pandas版本中将无法使用该语法.
系列:
FutureWarning:不推荐在系列上使用dict进行聚合,并且将在以后的版本中删除
DataFrames:
FutureWarning:不推荐使用带重命名的dict,将在以后的版本中删除
根据pandas 0.20 changelog,聚合时重命名列的推荐方法如下.
In [2]: df = pd.DataFrame({'kind': ['cat', 'dog', 'cat', 'dog'],
...: 'height': [9.1, 6.0, 9.5, 34.0],
...: 'weight': [7.9, 7.5, 9.9, 198.0]})
...:
In [3]: df
Out[3]:
kind height weight
0 cat 9.1 7.9
1 dog 6.0 7.5
2 cat 9.5 9.9
3 dog 34.0 198.0
In [4]: df.groupby('kind').agg(min_height=('height', 'min'),
max_weight=('weight', 'max'))
Out[4]:
min_height max_weight
kind
cat 9.1 9.9
dog 6.0 198.0
Run Code Online (Sandbox Code Playgroud)
有关其他详细信息,请参阅0.20更改日志.
使用旧样式字典语法,可以传递多个.agg(new_col_name=('col_name', 'agg_func')
函数.rename()
,因为这些函数将使用传递的字典中的键重命名:
In [2]: df = pd.DataFrame({"A": ['a', 'a'], 'B': [1, 2], 'C': [3, 4]})
In [3]: df.groupby("A").agg({'B': [lambda x: 0, lambda x: 1]})
Out[3]:
B
<lambda> <lambda 1>
A
a 0 1
Run Code Online (Sandbox Code Playgroud)
多个函数也可以作为列表传递给单个列:
In [4]: df.groupby("A").agg(b=('B', lambda x: 0), c=('B', lambda x: 1))
Out[4]:
b c
A
a 0 0
Run Code Online (Sandbox Code Playgroud)
但是,这不适用于lambda函数,因为它们是匿名的并且全部返回lambda
,这会导致名称冲突:
# 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'})
)
Run Code Online (Sandbox Code Playgroud)
为了避免这种情况.agg
,可以先验地定义命名函数而不是使用<lambda>
.合适的函数名称也可以避免以后调用SpecificationError
数据帧.这些函数可以使用与上面相同的列表语法传递:
>>> 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
Run Code Online (Sandbox Code Playgroud)
小智 6
如果您希望获得类似于JMP的行为,请创建列标题,以保留您可以使用的多索引中的所有信息:
newidx = []
for (n1,n2) in df.columns.ravel():
newidx.append("%s-%s" % (n1,n2))
df.columns=newidx
Run Code Online (Sandbox Code Playgroud)
它将改变您的数据框架:
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
Run Code Online (Sandbox Code Playgroud)
至
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
Run Code Online (Sandbox Code Playgroud)
我同意OP的观点,即在同一位置命名和定义输出列似乎更为自然和一致(例如,就像在R中使用tidyverse所做的summarize
那样),但目前在Pandas中的一种变通方法是使用进行聚合之前,通过以下所需名称: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()
Run Code Online (Sandbox Code Playgroud)
(使用reset_index
匝'Country'
,'f'
,'mean'
,和'std'
所有与一个单独的整数索引常规列。)