永远不要增长 DataFrame!
TLDR;(只需阅读粗体字)
这里的大多数答案都会告诉你如何创建一个空的 DataFrame 并填写它,但没有人会告诉你这是一件坏事。
这是我的建议:在列表中累积数据,而不是 DataFrame。
使用列表收集数据,然后在准备好时初始化 DataFrame。list-of-lists 或 list-of-dicts 格式都可以使用,pd.DataFrame
两者都接受。
data = []
for a, b, c in some_function_that_yields_data():
data.append([a, b, c])
df = pd.DataFrame(data, columns=['A', 'B', 'C'])
这种方法的优点:
一次性追加到列表并创建一个 DataFrame 总是比创建一个空的 DataFrame(或 NaN 之一)并一遍又一遍地追加到它更便宜。
列表还占用更少的内存,并且是一种更轻的数据结构,可以使用、追加和删除(如果需要)。
dtypes
被自动推断(而不是分配object
给所有这些)。
ARangeIndex
会自动为您的数据创建,而您不必小心为每次迭代时附加的行分配正确的索引。
如果您还不相信,文档中也提到了这一点:
迭代地将行附加到 DataFrame 可能比单个连接的计算密集度更高。更好的解决方案是将这些行附加到列表中,然后将列表与原始 DataFrame 一次性连接起来。
但是,如果我的函数返回需要合并成一个大数据帧的较小数据帧怎么办?
没关系,您仍然可以通过增长或创建较小 DataFrame 的 python 列表,然后调用pd.concat
.
small_dfs = []
for small_df in some_function_that_yields_dataframes():
small_dfs.append(small_df)
large_df = pd.concat(small_dfs, ignore_index=True)
或者,更简洁地说:
large_df = pd.concat(
list(some_function_that_yields_dataframes()), ignore_index=True)
这些选项太可怕了
append
或concat
在循环内
这是我从初学者那里看到的最大错误:
df = pd.DataFrame(columns=['A', 'B', 'C'])
for a, b, c in some_function_that_yields_data():
df = df.append({'A': i, 'B': b, 'C': c}, ignore_index=True) # yuck
# or similarly,
# df = pd.concat([df, pd.Series({'A': i, 'B': b, 'C': c})], ignore_index=True)
append
为您拥有的每个或concat
操作重新分配内存。将此与循环结合起来,您就有了二次复杂度运算。
与此相关的另一个错误df.append
是用户倾向于忘记append 不是就地函数,因此必须将结果分配回。您还必须担心 dtypes:
df = pd.DataFrame(columns=['A', 'B', 'C'])
df = df.append({'A': 1, 'B': 12.3, 'C': 'xyz'}, ignore_index=True)
df.dtypes
A object # yuck!
B float64
C object
dtype: object
处理对象列从来都不是一件好事,因为 pandas 无法对这些列进行矢量化操作。你需要这样做来修复它:
df.infer_objects().dtypes
A int64
B float64
C object
dtype: object
loc
在一个循环内
我还看到loc
用于附加到创建为空的 DataFrame:
df = pd.DataFrame(columns=['A', 'B', 'C'])
for a, b, c in some_function_that_yields_data():
df.loc[len(df)] = [a, b, c]
和以前一样,您没有预先分配每次所需的内存量,因此每次创建新行时内存都会重新增长。它和 一样糟糕append
,甚至更丑陋。
NaN 的空 DataFrame
然后,创建一个 NaN 的 DataFrame,以及与之相关的所有注意事项。
df = pd.DataFrame(columns=['A', 'B', 'C'], index=range(5))
df
A B C
0 NaN NaN NaN
1 NaN NaN NaN
2 NaN NaN NaN
3 NaN NaN NaN
4 NaN NaN NaN
它创建一个对象列的 DataFrame,就像其他列一样。
df.dtypes
A object # you DON'T want this
B object
C object
dtype: object
附加仍然具有上述方法的所有问题。
for i, (a, b, c) in enumerate(some_function_that_yields_data()):
df.iloc[i] = [a, b, c]
证据就在布丁里
对这些方法进行计时是查看它们在内存和效用方面有多大差异的最快方法。
基准代码供参考。