【问题标题】:Issue with appending to DataFrame if empty如果为空,则附加到 DataFrame 的问题
【发布时间】:2016-05-31 07:55:45
【问题描述】:

我有一个超出本地方法范围的数据框。我想做如下:

def outer_method():
    ... do outer scope stuff here
    df = pd.DataFrame(columns=['A','B','C','D'])
    def recursive_method(arg):
        ... do local stuff here
        # func returns a data frame to be appended to empty data frame
        results_df = func(args)
        df.append(results_df, ignore_index=True)
        return results
recursive_method(arg)
return df

但是,这不起作用。如果我以这种方式附加到 df ,则它始终为空。

我在这里找到了我的问题的答案:appending-to-an-empty-data-frame-in-pandas... 如果空的 DataFrame 对象在方法的范围内,这可行,但不适用于我的情况。根据@DSM 的评论“但附加不会就地发生,因此如果需要,您必须存储输出:”

IOW,我需要类似的东西:

df = df.append(results_df, ignore_index=True)

在我的本地方法中,但这并不能帮助我访问我的外部范围变量 df 以附加到它。

有没有办法做到这一点?这适用于扩展列表对象内容的 python extend 方法(我意识到 DataFrames 不是列表,但是......)。是否有类似的方法可以使用 DataFrame 对象来执行此操作,而无需处理我的 df 范围问题?

顺便说一句,Pandas concat 方法也可以,但我遇到了变量范围的问题。

【问题讨论】:

  • 抱歉,您是说df = df.append(df_join_out, ignore_index=True) 不起作用?
  • 然后将右侧的 df 作为未解析的引用给出。

标签: python pandas scope append dataframe


【解决方案1】:

在 Python3 中,您可以使用 nonlocal 关键字

def outer_method():
    ... do outer scope stuff here
    df = pd.DataFrame(columns=['A','B','C','D'])
    def recursive_method(arg):
        nonlocal df
        ... do local stuff here
        # func returns a data frame to be appended to empty data frame
        results_df = func(args)
        df = df.append(results_df, ignore_index=True)
        return results

return df

但请注意,调用df.append 每次都会返回一个新的DataFrame,因此需要将所有旧数据复制到新的DataFrame 中。如果你在一个循环中这样做 N 次,你最终会制作 1+2+3+...+N = O(N^2) 份副本——这对性能非常不利。


如果您不需要 dfrecursive_method 中用于除 追加,最好追加到一个列表中,然后构造 recursive_method 完成后的 DataFrame(通过调用 pd.concat 一次):

df = pd.DataFrame(columns=['A','B','C','D'])
data = [df]
def recursive_method(arg, data):
    ... do stuff here
     # func returns a data frame to be appended to empty data frame
     results_df = func(args)
     data.append(df_join_out)
     return results
recursive_method(arg, data)
df = pd.concat(data, ignore_index=True)

如果您需要做的只是收集内部数据,这是最佳解决方案 recursive_method 并且可以等待构建新的 df 之后 recursive_method 完成。


在Python2中,如果你必须在recursive_method里面使用df,那么你可以通过 df 作为recursive_method 的参数,也返回df

df = pd.DataFrame(columns=['A','B','C','D'])
def recursive_method(arg, df):
    ... do stuff here
     results, df = recursive_method(arg, df)
     # func returns a data frame to be appended to empty data frame
     results_df = func(args)
     df = df.append(results_df, ignore_index=True)
     return results, df
results, df = recursive_method(arg, df)

但请注意,您将付出沉重的代价进行 O(N^2) 复制 上面提到过。


为什么 DataFrames 不能不应该被追加到原地

DataFrame 中的底层数据存储在 NumPy 数组中。数据在一个 NumPy 数组来自一个连续的内存块。有时没有 有足够的空间将 NumPy 数组调整为更大的连续内存块 即使内存可用——想象一下夹在中间的数组 其他数据结构。在这种情况下,为了调整数组的大小,一个新的更大的 内存块必须分配到其他地方,并且所有数据都来自 必须将原始数组复制到新块中。一般是做不到的 就地。

DataFrames 确实有一个私有方法,_update_inplace,它可以是 用于将 DataFrame 的底层数据重定向到新数据。这只是一个 伪就地操作,因为新数据(想想 NumPy 数组)必须是 首先分配(连同所有随之而来的复制)。所以使用_update_inplace对它的两次打击:它使用了一种私有方法(理论上)可能不是 在 Pandas 的未来版本中,它会导致 O(N^2) 复制惩罚。

In [231]: df = pd.DataFrame([[0,1,2]])

In [232]: df
Out[232]: 
   0  1  2
0  0  1  2

In [233]: df._update_inplace(df.append([[3,4,5]]))

In [234]: df
Out[234]: 
   0  1  2
0  0  1  2
0  3  4  5

【讨论】:

  • 感谢您的解释。这说得通。出于这个确切原因,我绝对不希望将 df 作为参数传递给我的递归方法(或使用 nonlocal 关键字)。另外,我HAD一直在使用列表,但是我在列表和数据帧之间来回切换,这在性能上很昂贵,所以由于我使用数据帧进行设置操作,所以我想我会避免在这两种对象类型之间来回切换,直到最后,当我通过 JSON 返回最终结果时。但是,我认为您对concat 使用列表的建议是一个很好的折衷方案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-10-17
  • 1970-01-01
  • 2015-05-12
  • 1970-01-01
  • 2014-09-20
  • 1970-01-01
相关资源
最近更新 更多