考虑下面的数据文件:

columns = ['A', 'B', 'C', 'D']
records = [
    ['foo', 'one', 0.162003, 0.087469],
    ['bar', 'one', -1.156319, -1.5262719999999999],
    ['foo', 'two', 0.833892, -1.666304],     
    ['bar', 'three', -2.026673, -0.32205700000000004],
    ['foo', 'two', 0.41145200000000004, -0.9543709999999999],
    ['bar', 'two', 0.765878, -0.095968],
    ['foo', 'one', -0.65489, 0.678091],
    ['foo', 'three', -1.789842, -1.130922]
]
df = pd.DataFrame.from_records(records, columns=columns)

"""
     A      B         C         D
0  foo    one  0.162003  0.087469
1  bar    one -1.156319 -1.526272
2  foo    two  0.833892 -1.666304
3  bar  three -2.026673 -0.322057
4  foo    two  0.411452 -0.954371
5  bar    two  0.765878 -0.095968
6  foo    one -0.654890  0.678091
7  foo  three -1.789842 -1.130922
"""

以下命令起作用:

df.groupby('A').apply(lambda x: (x['C'] - x['D']))
df.groupby('A').apply(lambda x: (x['C'] - x['D']).mean())

但以下工作都没有:

df.groupby('A').transform(lambda x: (x['C'] - x['D']))
# KeyError or ValueError: could not broadcast input array from shape (5) into shape (5,3)

df.groupby('A').transform(lambda x: (x['C'] - x['D']).mean())
# KeyError or TypeError: cannot concatenate a non-NDFrame object

Why? The example on the documentation似乎表明,在组中调用transform可以进行行操作处理:

# Note that the following suggests row-wise operation (x.mean is the column mean)
zscore = lambda x: (x - x.mean()) / x.std()
transformed = ts.groupby(key).transform(zscore)

换句话说,我认为转换本质上是一种特定类型的应用(不聚合).我错在哪里?

作为参考,下面是上面原始数据框的构造:

df = pd.DataFrame({'A' : ['foo', 'bar', 'foo', 'bar',
                          'foo', 'bar', 'foo', 'foo'],
                   'B' : ['one', 'one', 'two', 'three',
                         'two', 'two', 'one', 'three'],
                   'C' : randn(8), 'D' : randn(8)})

推荐答案

Two major differences between apply and transform

transformapply groupby方法之间有两个主要区别.

  • Input:
    • apply将每个组的所有列作为DataFrame隐式传递给自定义函数.
    • transform将每个组的每个列作为Series分别传递给自定义函数.
  • Output:

因此,transform一次只能处理一个系列,apply一次处理整个数据帧.

判断自定义函数

判断传递到applytransform的自定义函数的输入会有很大帮助.

例子

让我们创建一些示例数据并判断这些组,这样您就可以看到我所说的内容:

import pandas as pd
import numpy as np
df = pd.DataFrame({'State':['Texas', 'Texas', 'Florida', 'Florida'], 
                   'a':[4,5,1,3], 'b':[6,10,3,11]})

     State  a   b
0    Texas  4   6
1    Texas  5  10
2  Florida  1   3
3  Florida  3  11

让我们创建一个简单的自定义函数,该函数打印出隐式传递的对象的类型,然后引发异常以便停止执行.

def inspect(x):
    print(type(x))
    raise

现在,让我们将此函数传递给groupby applytransform方法,以查看传递给它的对象:

df.groupby('State').apply(inspect)

<class 'pandas.core.frame.DataFrame'>
<class 'pandas.core.frame.DataFrame'>
RuntimeError

如您所见,一个数据帧被传递到inspect函数中.你可能想知道为什么数据框被打印了两次.Pandas 在第一组跑了两次.它这样做是为了确定是否有一种快速的方法来完成计算.这是一个你不应该担心的小细节.

现在,让我们对transform做同样的事情

df.groupby('State').transform(inspect)
<class 'pandas.core.series.Series'>
<class 'pandas.core.series.Series'>
RuntimeError

它被传递了一个系列——一个完全不同的对象.

因此,transform一次只能处理一个系列.它不可能同时作用于两列.所以,如果我们try 从自定义函数中的b中减go a列,我们会得到一个错误transform.见下文:

def subtract_two(x):
    return x['a'] - x['b']

df.groupby('State').transform(subtract_two)
KeyError: ('a', 'occurred at index a')

我们得到一个KeyError,因为Pandas 试图查找不存在的系列索引a.您可以使用apply完成此操作,因为它包含整个DataFrame:

df.groupby('State').apply(subtract_two)

State     
Florida  2   -2
         3   -8
Texas    0   -2
         1   -5
dtype: int64

输出是一个Series,由于保留了原始索引,有点令人困惑,但我们可以访问所有列.


显示传递的对象

在自定义函数中显示整个Pandas 对象甚至更有帮助,这样您就可以准确地看到您正在操作的是什么.您可以使用print条语句,我喜欢使用IPython.display模块中的display函数,这样DataFrame就可以在jupyter笔记本中以HTML格式很好地输出:

from IPython.display import display
def subtract_two(x):
    display(x)
    return x['a'] - x['b']

Screenshot: enter image description here


Transform必须返回与组大小相同的一维序列

另一个区别是,transform必须返回与组大小相同的一维序列.在这个特定的实例中,每个组有两行,因此transform必须返回两行的序列.如果没有,则会引发错误:

def return_three(x):
    return np.array([1, 2, 3])

df.groupby('State').transform(return_three)
ValueError: transform must return a scalar value for each group

错误消息并不能真正描述问题.必须返回与组长度相同的序列.所以,这样的函数会起作用:

def rand_group_len(x):
    return np.random.rand(len(x))

df.groupby('State').transform(rand_group_len)

          a         b
0  0.962070  0.151440
1  0.440956  0.782176
2  0.642218  0.483257
3  0.056047  0.238208

Returning a single scalar object also works for transform

如果只从自定义函数返回一个标量,则transform将对组中的每一行使用它:

def group_sum(x):
    return x.sum()

df.groupby('State').transform(group_sum)

   a   b
0  9  16
1  9  16
2  4  14
3  4  14

Python相关问答推荐

Gekko解算器错误results.json未找到,无法找出原因

两极按组颠倒顺序

请从Python访问kivy子部件的功能需要帮助

遵循轮廓中对象方向的计算线

Python中的函数中是否有充分的理由接受float而不接受int?

从包含数字和单词的文件中读取和获取数据集

pandas DataFrame GroupBy.diff函数的意外输出

如何使用symy打印方程?

Pystata:从Python并行运行stata实例

max_of_three使用First_select、second_select、

删除任何仅包含字符(或不包含其他数字值的邮政编码)的观察

ModuleNotFound错误:没有名为flags.State的模块; flags不是包

在Polars(Python库)中将二进制转换为具有非UTF-8字符的字符串变量

' osmnx.shortest_track '返回有效源 node 和目标 node 的'无'

如何保持服务器发送的事件连接活动?

CommandeError:模块numba没有属性generated_jit''''

网格基于1.Y轴与2.x轴显示在matplotlib中

将标签移动到matplotlib饼图中楔形块的开始处

在pandas/python中计数嵌套类别

在matplotlib中使用不同大小的标记顶部添加批注