DataFrameのスピード~行の追加

概要

pandas.DataFrameで数千行のデータの組み換えをやろうとしたときにかなり時間がかかったので、簡単な例で実行時間を確認してみた。

結論から言うと、他の様々なサイトで言及されているように、「行単位の追加はかなり時間がかかるが、列単位の追加は圧倒的に早い」ということになる。また、先にリストなどでデータを構成しておいてからDataFrameを生成する方法も高速なことが分かった。

問題設定

次のように、3つの列を持つ行データを1万個、DataFrameに追加していく例を考える。

appendメソッド

appendメソッドは2つのDataFrameを結合するメソッドで、行の追加方法としてもよく紹介されている。実行結果は以下の通りで約7秒(3回繰り返して同程度)。

appendでリストをDataFrameにする際、リストをそのまま渡すと列と解釈されるので、2次元化して行であることを明示している。また列名を指定しないと新たな列として4~6列目に行が加えられていくので、加えるDataFrameでも列名を指定している。

リストをそのまま渡して列として生成し、行インデックスに列名を渡してDataFrameを生成してから'.T'で転置している例なども見られた。

なお、この場合のDataFrameの各要素は整数型となる。

locプロパティーはインデックス指定に注意

DataFramelocプロパティーは、スライスによって複数行・列の要素の参照・代入ができる。これを利用して、空のDataFrameに1行ずつ追加していく。実行時間は7秒台。

この場合のDataFrameの各要素も整数になる。

興味深いのことに、loc[i:, ]ではなくてloc[i]で指定すると実行時間が倍以上、20秒近くになる。

なお、locの代わりにilocを使うと"IndexError: iloc cannot enlarge its target object"とエラーになる。

DataFrameの領域を確保した場合

リストで確保した場合

予めデータのサイズがわかっている場合に、ダミーデータで埋めたリストで領域を確保してみる。領域を一気に確保して値を入れていくだけなので実行速度は速い。実行時間は0.7秒程度で、appendやlocで1行ずつ追加していくのに比べて1/10。

ここでloc[i, :]loc[i]とすると、実行時間は0.5秒程度と少し早くなる。これは1行ずつ追加する場合と逆の傾向だが、この場合はその差は追加の場合に比べて小さい。

なお、この方法では領域が既に確保されているのでilocに変更しても同じ結果となる。

ndarrayで確保した場合

リストではなくndarrayで領域を確保してみると、実行速度はリストの場合と同程度。

ただし、この場合各要素は実数となる。整数が必要ならndarrayのコンストラクターでdtype='int'を指定する。

ここでndarrayのdtypeを整数で指定すると実行時間が以下のような傾向となった。

  • int8, int16→4秒台
  • int32, int64→0.6秒台

ワード境界の中に値を埋め込んでいくのに時間がかかっていると考えられる。

列ごとのリストを加える方法はかなり速い

列ごとの辞書でDataFrameを生成する方法

列ごとのリストを作っておいて、それらから全体のデータを辞書として準備し、DataFrameを生成する方法。

これは更に速く、実行時間は0.015秒前後。loc[i, :]で行ごとに加えていく方法の1/1000の時間で済むことになる。

ただし辞書のキーで列名を指定するところがやや煩雑か。

列単位でリストを加えていく方法

列ごとのリストを、順次DataFrameに加えていく方法。

この場合もかなり速いが、上の方法では実行時間が一定しているのに対して、こちらは0.015~0.03秒と少しばらついて、ほんの僅かだが遅め。

列ごとのndarrayを加える方法

空のndarrayを準備して要素を加えていき、これを列単位でDataFrameに加える方法。

実行時間は0.25秒程度でリストの時の10倍の時間がかかっている。別途ndarrayの要素追加時部分だけの時間を計測すると、この部分だけで0.2秒台で、配列の要素追加のところで時間がかかっている。

2次元リストから生成する方法がベスト

リストなどを列ごとに加えるのではなく、2次元のリストを構成しておいて、それを使ってDataFrameを生成する方法。

実行時間は0.01~0.02秒程度で、最も早い部類に入る。順次行を追加するという発想にコードも近く、速度・可読性ともに最適のようである。

既存のDataFrameに追加する場合

既にデータがあるDataFrameに新たな行を追加する場合を考える。これまでの例で、どうやらリストの形で操作するのが速そうで、DataFrameからリストへの変換がそれなりに速いのなら、その方法が最もよさそうだと予想できる。

以下のコードはこのことを確認したもの。3つのパートに分かれていて、最初がこれまでと同じDataFrameの生成、次がDataFrameからリストへの変換、最後がリストへの追加と追加後のDataFrameの生成となっている。

DataFrameからリストへの変換は、to_numpy()メソッドでndarrayが得られるので(DataFramevaluesでもndarrayは非推奨)、それをtolist()メソッドでリストに変換している。

結果はかなり高速で、DataFrameのままでlocで追加するよりもはるかに速い。

まとめ

今回のケースの場合、1万行の追加でlocを使うと7秒で1行当たり0.0007秒。100行まとめて追加すると0.07秒で、この時点でリストに変換して追加した方が速くなる。

数少ない行を低頻度で追加するのでなければ、DataFrameにまとまった行を追加したり、既にあるDataFrameの構造を変換するには、一旦リストに変換してからデータを追加し、DataFrameに変換し直した方が速いと言える。

 

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です