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(またはNaNの1つ)を作成して何度も追加するよりも、リストに追加してDataFrameを一度に作成する方が常に安価です。
リストはまた、より少ないメモリを使用し、操作、追加、および削除(必要な場合)するためのはるかに軽いデータ構造です。
dtypes
object
(それらすべてに割り当てるのではなく)自動的に推測されます。
RangeIndex
各反復で追加する行に正しいインデックスを割り当てるように注意する必要はなく、データに対してAが自動的に作成されます。
まだ確信が持てない場合は、ドキュメントにも記載されています。
DataFrameに行を繰り返し追加すると、単一の連結よりも計算量が多くなる可能性があります。より良い解決策は、それらの行をリストに追加してから、リストを元のDataFrameと一度に連結することです。
しかし、関数が1つの大きな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
操作ごとに再割り当てされます。これをループと組み合わせると、2次の複雑さの操作ができます。
関連するもう1つの間違いは、ユーザーが追加df.append
を忘れがちなことです。そのため、結果を元に戻す必要があります。また、dtypeについても心配する必要があります。
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
パンダはそれらの列に対する操作をベクトル化できないため、オブジェクト列を処理することは決して良いことではありません。あなたはそれを修正するためにこれをする必要があるでしょう:
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]
証拠はプリンにあります
これらのメソッドのタイミングは、メモリとユーティリティの点でどれだけ異なるかを確認するための最速の方法です。

参照用のベンチマークコード。