在 Pandas 聚合函数中命名返回的列?

新手上路,请多包涵

我在使用 Pandas 的 groupby 功能时遇到了问题。我已阅读 文档,但看不出如何将聚合函数应用于多列 以及 如何为这些列设置自定义名称。

这非常接近,但返回的数据结构具有嵌套的列标题:

 data.groupby("Country").agg(
        {"column1": {"foo": sum()}, "column2": {"mean": np.mean, "std": np.std}})

(即我想采用 column2 的平均值和标准差,但将这些列返回为“平均值”和“标准差”)

我错过了什么?

原文由 David Chouinard 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 828
2 个回答

对于熊猫 >= 0.25

命名返回聚合列的功能已 在 master 分支中重新引入, 并针对 pandas 0.25。新语法是 .agg(new_col_name=('col_name', 'agg_func') 。上面链接的 PR 中的详细示例:

 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

也可以将多个 lambda 表达式与此语法和我之前(下面)根据 此 PR 建议的两步重命名语法一起使用。同样,从 PR 中的示例复制:

 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

然后 .rename() ,或者一次性:

 In [4]: df.groupby("A").agg(b=('B', lambda x: 0), c=('B', lambda x: 1))
Out[4]:
   b  c
A
a  0  0


对于熊猫 < 0.25

unutbu 描述的当前接受的答案是在 <= 0.20 的熊猫版本中执行此操作的好方法。但是,从 pandas 0.20 开始,使用此方法会引发警告,表明该语法在未来的 pandas 版本中将不可用。

系列:

FutureWarning:在系列上使用字典进行聚合已被弃用,并将在未来版本中删除

数据框:

FutureWarning:不推荐使用带重命名的字典,并将在未来版本中删除

根据 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 更新日志

更新 2017-01-03 以回应@JunkMechanic 的评论。

使用旧式字典语法,可以将多个 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

原文由 joelostblom 发布,翻译遵循 CC BY-SA 4.0 许可协议

这将从分层列索引中删除最外层:

 df = data.groupby(...).agg(...)
df.columns = df.columns.droplevel(0)

如果您想保留最外层,可以在多级列上使用 ravel() 函数来形成新标签:

 df.columns = ["_".join(x) for x in df.columns.ravel()]

_更新_:在较新的熊猫中而 use df.columns = ["_".join(x) for x in df.columns.tolist()] .ravel() .tolist() .to_numpy()


例如:

 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

原文由 unutbu 发布,翻译遵循 CC BY-SA 4.0 许可协议

推荐问题