决不能按行增长DataFrame!
太长,读不下去了(只需阅读粗体文本)
这里的大多数答案都会告诉你如何创建一个空的DataFrame并填充它,但没有人会告诉你这样做是坏事。
我的建议是:在列表中累积数据,而不是在DataFrame中。
使用列表收集数据,然后在准备就绪时初始化DataFrame。列表列表或字典列表格式都可以,pd.DataFrame同时接受这两种格式。
data = []
for row in some_function_that_yields_data():
data.append(row)
df = pd.DataFrame(data)
pd.DataFrame将行列表(其中每一行都是标量值)转换为DataFrame。如果函数生成DataFrames,请调用pd.concat。
这种方法的优点:
与创建一个空的DataFrame(或一个NaN)并反复追加到列表中相比,一次性追加到列表并创建DataFrame总是更便宜。列表也占用更少的内存,并且是一种更轻的数据结构,可以处理、附加和删除(如果需要)。数据类型是自动推断的(而不是将对象分配给所有数据类型)。RangeIndex会自动为数据创建,而不必在每次迭代时为要附加的行指定正确的索引。
如果您还不确信,文档中也会提到这一点:
迭代地将行附加到DataFrame可能会更具计算能力比单个连接更密集。更好的解决方案是附加将这些行添加到列表中,然后将列表与原始行连接起来一次完成DataFrame。
***熊猫更新>=1.4:追加现在已弃用***
从panda 1.4开始,append已被弃用!请改用pd.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)
内存会重新分配给您的每个追加或合并操作。再加上一个循环,你就得到了一个二次复杂度运算。
与df.append相关的另一个错误是,用户倾向于忘记append不是一个就地函数,因此必须将结果赋值回。您还必须担心数据类型:
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
处理对象列从来都不是一件好事,因为panda无法对这些列进行矢量化操作。您需要执行以下操作来修复它:
df.infer_objects().dtypes
A int64
B float64
C object
dtype: object
在循环中定位
我还看到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]
与之前一样,您没有预先分配每次所需的内存量,因此每次创建新行时,内存都会重新增长。它和追加一样糟糕,甚至更难看。
NaN的空数据帧
然后,创建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]
证据在布丁里
计时这些方法是查看它们在内存和实用性方面有多大差异的最快方式。
基准测试代码供参考。