Pandas groupby在使用两次apply时会重复分组

3
可以使用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

只有当两组行数相等时,才会出现这种情况。如果其中一组的行数较多,则输出结果符合预期。

2个回答

2

来自文档的一句话:

在当前实现中,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

To:

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']))

我记得在文档中读到过这个,但立刻就忘了,你发现了不错! - Kaj
使用dummy_func_nested(df_group)代替df_group.apply(dummy_func_nested, axis=1)会导致此输出 https://gist.github.com/olehdubno/0bc25b11c1efe3dd83b955a39f2422f7该输出显示了原始分组,但是行仍然被其他组的重复项覆盖。 - Oleh Dubno
1
我运行了代码,似乎得到了你想要的输出,但是无法复现你的输出。你是如何得到那个输出的? - Kaj
独立于apply()使用dummy_func_nested(),即df_group = dummy_func_nested(df_group),可以产生所需的结果。不确定我运行了什么导致框架以奇怪的方式显示。我的理解是,在使用groupby的其他apply()方法内部不应调用apply()方法。我们应该直接应用函数而不使用apply。 - Oleh Dubno
在使用 groupby 时,应避免在使用 apply() 的函数内部再次使用 apply() 方法。 - Oleh Dubno
1
@OlehDubno 请接受并点赞,如果它有效的话 :-) - U13-Forward

0

在使用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()方法。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接