使用groupby后更新apply中的数据帧

时间:2017-05-11 22:19:26

标签: python pandas dataframe

我有一个我要分组的pandas数据框,然后使用iterrowsset_value更新原始数据框。这似乎不起作用。

这是一个例子。

In [1]: def func(df, n):
   ...:     for i, row in df.iterrows():
   ...:         print("Updating {0} with value {1}".format(i, n))
   ...:         df.set_value(i, 'B', n)

In [2]: df = pd.DataFrame({"A": [1, 2], "B": [0, 0]})

In [3]: df
Out[4]:
   A  B
0  1  0
1  2  0

In [125]: func(df, 1)
Updating 0 with value 1
Updating 1 with value 1

In [126]: df
Out[126]:
   A  B
0  1  1
1  2  1

In [127]: df.groupby('A').apply(lambda df: func(df, 2))
Updating 0 with value 2
Updating 0 with value 2
Updating 1 with value 2
In [126]: df
Out[126]:
   A  B
0  1  1
1  2  1

我希望B更新为2

为什么这不起作用,实现这一结果的最佳方法是什么?

3 个答案:

答案 0 :(得分:1)

编写内容的方式,您似乎希望函数func(df, n)能够修改df。但是df.groupby('A')(在某种意义上)创建了另一组数据帧(每组一个),因此使用func()作为df.groupby('A').apply()的参数只修改这些新创建的数据帧而不是原始数据帧df。此外,返回的数据帧是以每个组作为参数调用的func()输出的串联,这就是返回的数据帧为空的原因。

问题的最短时间解决方法是在return df结束时func

def func(df, n):
    for i, row in df.iterrows():
        print("Updating {0} with value {1}".format(i, n))
        df.set_value(i, 'B', n)
    return df
df = df.groupby('A').apply(lambda df: func(df, 2))

我认为这并不是你想到的,因为你可能期望修改一切。如果您想要修改所有内容,则需要使用for循环和.loc的组合,但如果您打算使用.loc修改数据框,则计算成本会很高多次致电.loc

我还猜测你设置值的功能取决于更复杂的标准,但通常你可以对事物进行矢量化,避免必须完全使用.iterrows()

为避免出现XY问题,我建议您更详细地描述您的功能,因为您可以通过使用.loc并避免迭代的几行来完成所有工作。通过Python中的每一行。例证:df['B'] = 2(没有print声明)是解决问题的单线解决方案。

答案 1 :(得分:0)

这不起作用,因为您正在更改由df对象的groupby方法传递的get_group复制的子集。你正在改变一些事情,而不是你所期待的。

如果这还不够理由,你会注意到你有3个打印声明。那是因为pandas运行第一组一次来测试和推断输出。然后再次实际做的事情。如果您更改了范围之外的内容,最终可能会产生意想不到的后果。

其他人可以提供更好的例子来说明如何做到这一点。我只想解释它为什么不起作用。

答案 2 :(得分:0)

在某些情况下,如果func()根据索引执行操作,则可以直接修改原始数据框。

代替此:

def func(group, n):
    for i, row in group.iterrows():
        print("Updating {0} with value {1}".format(i, n))
        group.set_value(i, 'B', n)
    return group

df.groupby('A').apply(lambda group: func(group, 2))

您可以这样做:

for key, group in df.groupby('A'):
    n = 2
    for i, row in group.iterrows():
        print("Updating {0} with value {1}".format(i, n))
        df.set_value(i, 'B', n)