python - 在组对象上应用 vs 变换

标签 python pandas

考虑以下数据框:

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
为什么? The example on the documentation似乎建议调用 transform on a group 允许一个人做 row-wise 操作处理:
# 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)})

最佳答案

apply之间的两个主要区别和 transformtransform之间有两个主要区别和 apply分组方法。

  • 输入:
  • apply将每个组的所有列作为 隐式传递数据帧 到自定义函数。
  • transform将每个组的每一列分别作为 传递系列 到自定义函数。
  • 输出:
  • 自定义函数传递给 apply可以返回标量、系列或数据帧(或 numpy 数组甚至列表) .
  • 自定义函数传递给 transform必须返回一个序列 (一维系列、数组或列表)同组长度 .

  • 所以,transform一次只处理一个系列,apply一次作用于整个 DataFrame。
    检查自定义函数
    检查传递给 apply 的自定义函数的输入会很有帮助。或 transform .
    例子
    让我们创建一些示例数据并检查组,以便您了解我在说什么:
    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
    
    如您所见,一个 DataFrame 被传递到 inspect功能。您可能想知道为什么 DataFrame 类型会被打印两次。 Pandas 两次运行第一组。它这样做是为了确定是否有一种快速的方法来完成计算。这是一个小细节,您不必担心。
    现在,让我们对 transform 做同样的事情
    df.groupby('State').transform(inspect)
    <class 'pandas.core.series.Series'>
    <class 'pandas.core.series.Series'>
    RuntimeError
    
    它传递了一个系列——一个完全不同的 Pandas 对象。
    所以,transform一次只允许使用一个系列。它不可能同时作用于两列。所以,如果我们尝试减去列 a来自 b在我们的自定义函数中,我们会得到一个错误 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
    
    输出是一个系列,因为保留了原始索引,所以有点困惑,但我们可以访问所有列。

    显示传递的 Pandas 对象
    在自定义函数中显示整个 Pandas 对象更有帮助,因此您可以准确地看到您正在操作的内容。您可以使用 print我喜欢用 display 的声明来自 IPython.display 的函数模块,以便数据帧在 jupyter notebook 中以 HTML 格式很好地输出:
    from IPython.display import display
    def subtract_two(x):
        display(x)
        return x['a'] - x['b']
    
    截屏:
    enter image description here

    变换必须返回与组大小相同的单维序列
    另一个区别是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
    

    返回单个标量对象也适用于 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 - 在组对象上应用 vs 变换,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27517425/

    相关文章:

    python - Pandas:如何有条件地对两个不同数据框中的值求和

    python - pandas groupby 申请真的很慢

    python - sqlite - register_converters 不在 Python NoneType 上触发?

    Python - 对 pandas 数据框中列表中的行进行分组

    python - 如何检索 pandas Series 对象中第 n 个元素的值?

    python - 带有 pandas groupby multiindex 的箱线图,用于来自 multiindex 的指定子级别

    python - 创建对另一列最后 x 次出现进行求和的列

    python - Python 中的十进制到二进制半精度 IEEE 754

    python - 属性错误 : 'CombinedExpression' object has no attribute 'default_alias'

    python - 重新启动管理服务后,Nova 计算和网络无法联系 nova 服务