Pandas groupby 使用两次应用时会复制组

Posted

技术标签:

【中文标题】Pandas groupby 使用两次应用时会复制组【英文标题】:Pandas groupby is duplicating groups when using apply twice 【发布时间】:2020-05-10 09:15:02 【问题描述】:

pandas groupby 可以使用groupby.apply(func) 并在func 内部使用.apply() 的另一个实例而不复制和覆盖数据吗?

在某种程度上,.apply() 的使用是嵌套的。

Python 3.7.3 pandas==0.25.1

import pandas as pd


def dummy_func_nested(row):
    row['new_col_2'] = row['value'] * -1
    return row


def dummy_func(df_group):
    df_group['new_col_1'] = None

    # apply dummy_func_nested
    df_group = df_group.apply(dummy_func_nested, axis=1)

    return df_group


def pandas_groupby():
    # initialize data
    df = pd.DataFrame([
        'country': 'US', 'value': 100.00, 'id': 'a',
        'country': 'US', 'value': 95.00, 'id': 'b',
        'country': 'CA', 'value': 56.00, 'id': 'y',
        'country': 'CA', 'value': 40.00, 'id': 'z',
    ])

    # group by country and apply first dummy_func
    new_df = df.groupby('country').apply(dummy_func)

    # new_df and df should have the same list of countries
    assert new_df['country'].tolist() == df['country'].tolist()
    print(df)


if __name__ == '__main__':
    pandas_groupby()

上面的代码应该返回

  country  value id new_col_1  new_col_2
0      US  100.0  a      None     -100.0
1      US   95.0  b      None      -95.0
2      CA   56.0  y      None      -56.0
3      CA   40.0  z      None      -40.0

但是,代码返回

  country  value id new_col_1  new_col_2
0      US  100.0  a      None     -100.0
1      US   95.0  a      None      -95.0
2      US   56.0  a      None      -56.0
3      US   40.0  a      None      -40.0

仅当两个组的行数相等时才会出现此行为。如果一组有更多行,则输出如预期。

【问题讨论】:

【参考方案1】:

在使用 groupby 时,我们应该避免在使用 apply() 的函数中使用 apply() 方法

产生所需结果的正确代码如下。

免责声明:可以更有效地编写代码。目的是为了证明我们应该避免在groupby.apply() 内部调用apply() 方法。如果我们应用它的组在每个组中具有相同数量的行,则会产生不利影响。如果每组中的行数不相等,则一切顺利。同样,这只发生在组具有相同数量的行时。

致用户:u10-forward

import pandas as pd


def dummy_func_nested(df):
    df['new_col_2'] = df['value'] * -1
    return df


def dummy_func(df_group):
    df_group['new_col_1'] = None

    # apply dummy_func_nested
    df_group = dummy_func_nested(df_group)

    return df_group


def pandas_groupby():
    # initialize data
    df = pd.DataFrame([
        'country': 'US', 'value': 100.00, 'id': 'a',
        'country': 'US', 'value': 95.00, 'id': 'b',
        'country': 'CA', 'value': 56.00, 'id': 'y',
        'country': 'CA', 'value': 40.00, 'id': 'z',
    ])

    # group by country and apply first dummy_func
    new_df = df.groupby('country').apply(dummy_func)

    # new_df and df should have the same list of countries
    assert new_df['country'].tolist() == df['country'].tolist()
    print(df)


if __name__ == '__main__':
    pandas_groupby()

也就是说,我仍然认为这是一个错误,无法在 groupby.apply() 中调用 apply() 方法。

【讨论】:

【参考方案2】:

引用documentation:

在当前实现中,apply 在第一列/行上调用 func 两次,以决定它是否可以采用快速或慢速代码路径。如果 func 有副作用,这可能会导致意外行为,因为它们会对第一列/行生效两次。

尝试在您的代码中更改以下代码:

def dummy_func(df_group):
    df_group['new_col_1'] = None

    # apply dummy_func_nested
    df_group = df_group.apply(dummy_func_nested, axis=1)

    return df_group

收件人:

def dummy_func(df_group):
    df_group['new_col_1'] = None

    # apply dummy_func_nested
    df_group = dummy_func_nested(df_group)

    return df_group

您不需要apply

当然,更有效的方法是:

df['new_col_1'] = None
df['new_col_2'] = -df['value']
print(df)

或者:

print(df.assign(new_col_1=None, new_col_2=-df['value']))

【讨论】:

我记得在文档中读到过,然后马上就忘记了,很好! 使用dummy_func_nested(df_group) 而不是df_group.apply(dummy_func_nested, axis=1) 会导致此输出gist.github.com/olehdubno/0bc25b11c1efe3dd83b955a39f2422f7 输出显示原始组,但行仍被另一个组的副本覆盖。 我运行了代码,似乎得到了您想要的输出,但似乎无法重现您的输出。你在做什么来得到那个输出? 使用独立于apply()dummy_func_nested(),即df_group = dummy_func_nested(df_group),会产生所需的结果。不知道我跑了什么导致框架以一种奇怪的方式显示。我的理解是,我们不应该在使用 groupby 的其他 apply() 方法中调用 apply() 方法。我们应该直接应用函数而不是使用apply。 @OlehDubno 请接受并支持它的工作原理:-)

以上是关于Pandas groupby 使用两次应用时会复制组的主要内容,如果未能解决你的问题,请参考以下文章

Pandas GroupBy.apply 方法复制第一组

为啥我在 pandas 中的 groupby 后会出现“关键错误”? [复制]

带有 pandas、groupby、子图、计算/描述性统计、聚合的箱线图

去掉MacOS(MacBook)复制的时候的咔嚓声为什么复制时会出现两次咔嚓声

在 pandas groupby 之后并行化应用

使用'groupby'方法获取Dataframe的所有列