はてだBlog(仮称)

私的なブログど真ん中のつもりでしたが、気づけばWebサイト系のアプリケーション開発周りで感じたこと寄りの自分メモなどをつれづれ述べています。2020年6月現在、Elasticsearch、pandas、CMSなどに関する話題が多めです。...ですが、だんだんとより私的なプログラムのスニペット置き場になりつつあります。ブログで述べている内容は所属組織で販売している製品などに関するものではなく、また所属する組織の見解を代表するものではありません。

Pandasのapply関連の書きっぷりバリエーションと処理時間の雑な傾向確認

PandasでDataFrameのカラムAの値とBの値を結合して、新たにC列を作りたい...てなことがよくあると思います。

普段は自分の中で可読性が高いと思っているapply系の手グセで記述しているのですが、まれに、他の言語やフレームワークに置き換えるまではいかないものの、またガチのチューニングは不要なものの、2、3ステップの気の利いたタイピングで高速化されるならそうしたいという時があります。

... というときに、何が良いんじゃろうかということで雑に確認してみた...という記事です。

確認用コード

↓ Gistに貼りました。また、雑な確認の雑な結論としては、明らかに遅いなと感じたら、DataFrameの中にある(と思われる)リストデータの存在をイメージした記法を意識した演算にするとかなり高速化される場合もあるな、というところです。

Pandasのapply関連の書きっぷりバリエーションと処理時間の雑な傾向確認

実行結果 : iMac Late 2014 ( 4GHz Intel Core i7 )

ex1〜 は全て同じ要件ではないので単純比較はできないのですが、特に ex5、ex6、ex12の見比べが注目でしょうか。

ex1
    df[colname] = df['a'].apply(lambda x: foo.get(x))
CPU times: user 438 ms, sys: 1.31 ms, total: 439 ms
Wall time: 440 ms
                    a                   b                          ex1
0  0.3790985254337701  0.3790985254337701  {'b': '0.3790985254337701'}
1   0.567098167179614   0.567098167179614   {'b': '0.567098167179614'}
2  0.5955925126571219  0.5955925126571219  {'b': '0.5955925126571219'}

---------

ex2
    df[colname] = df[['a']].apply(lambda s: foo.get(s['a']), axis=1)
CPU times: user 10.6 s, sys: 15.4 ms, total: 10.6 s
Wall time: 10.6 s
                    a                   b                          ex2
0  0.3790985254337701  0.3790985254337701  {'b': '0.3790985254337701'}
1   0.567098167179614   0.567098167179614   {'b': '0.567098167179614'}
2  0.5955925126571219  0.5955925126571219  {'b': '0.5955925126571219'}

---------

ex3
        df[colname + i] = df[i].apply(lambda x: foo.get(x))
CPU times: user 895 ms, sys: 1.08 ms, total: 896 ms
Wall time: 897 ms
                    a                   b                         ex3a                         ex3b
0  0.3790985254337701  0.3790985254337701  {'b': '0.3790985254337701'}  {'b': '0.3790985254337701'}
1   0.567098167179614   0.567098167179614   {'b': '0.567098167179614'}   {'b': '0.567098167179614'}
2  0.5955925126571219  0.5955925126571219  {'b': '0.5955925126571219'}  {'b': '0.5955925126571219'}

---------

ex4
        df.loc[:, colname + i] = df[i].apply(lambda x: foo.get(x))
CPU times: user 1.07 s, sys: 15.2 ms, total: 1.09 s
Wall time: 1.09 s
                    a                   b                         ex4a                         ex4b
0  0.3790985254337701  0.3790985254337701  {'b': '0.3790985254337701'}  {'b': '0.3790985254337701'}
1   0.567098167179614   0.567098167179614   {'b': '0.567098167179614'}   {'b': '0.567098167179614'}
2  0.5955925126571219  0.5955925126571219  {'b': '0.5955925126571219'}  {'b': '0.5955925126571219'}

---------

ex5
    df[colname] = df.apply(lambda s: s['a'] + s['b'], axis=1)
CPU times: user 19 s, sys: 25.8 ms, total: 19 s
Wall time: 19 s
                    a                   b                                   ex5
0  0.3790985254337701  0.3790985254337701  0.37909852543377010.3790985254337701
1   0.567098167179614   0.567098167179614    0.5670981671796140.567098167179614
2  0.5955925126571219  0.5955925126571219  0.59559251265712190.5955925126571219

---------

ex6
    df[colname] = [s['a'][i] + s['b'][i] for i in range(l)]
CPU times: user 236 ms, sys: 20.3 ms, total: 257 ms
Wall time: 260 ms
                    a                   b                                   ex6
0  0.3790985254337701  0.3790985254337701  0.37909852543377010.3790985254337701
1   0.567098167179614   0.567098167179614    0.5670981671796140.567098167179614
2  0.5955925126571219  0.5955925126571219  0.59559251265712190.5955925126571219

---------

ex7
    df[colname] = [i['a'] + i['b'] for i in s]
CPU times: user 2.4 s, sys: 51.7 ms, total: 2.45 s
Wall time: 2.46 s
                    a                   b                                   ex7
0  0.3790985254337701  0.3790985254337701  0.37909852543377010.3790985254337701
1   0.567098167179614   0.567098167179614    0.5670981671796140.567098167179614
2  0.5955925126571219  0.5955925126571219  0.59559251265712190.5955925126571219

---------

ex8
    df[colname] = df.apply(abfunc1, axis=1)
CPU times: user 19 s, sys: 28.8 ms, total: 19 s
Wall time: 19 s
                    a                   b                                   ex8
0  0.3790985254337701  0.3790985254337701  0.37909852543377010.3790985254337701
1   0.567098167179614   0.567098167179614    0.5670981671796140.567098167179614
2  0.5955925126571219  0.5955925126571219  0.59559251265712190.5955925126571219

---------

ex9
    df[colname] = v_abfunc2(s['a'], s['b'])
CPU times: user 840 ms, sys: 274 ms, total: 1.11 s
Wall time: 1.17 s
                    a                   b                                   ex9
0  0.3790985254337701  0.3790985254337701  0.37909852543377010.3790985254337701
1   0.567098167179614   0.567098167179614    0.5670981671796140.567098167179614
2  0.5955925126571219  0.5955925126571219  0.59559251265712190.5955925126571219

---------

ex10
    df[colname] = v_abfunc2(**s)
CPU times: user 1.53 s, sys: 84.1 ms, total: 1.62 s
Wall time: 1.63 s
                    a                   b                                  ex10
0  0.3790985254337701  0.3790985254337701  0.37909852543377010.3790985254337701
1   0.567098167179614   0.567098167179614    0.5670981671796140.567098167179614
2  0.5955925126571219  0.5955925126571219  0.59559251265712190.5955925126571219

---------

ex11
    df[colname] = df['a'] + df['b']
CPU times: user 80.8 ms, sys: 1.06 ms, total: 81.9 ms
Wall time: 82.2 ms
                    a                   b                                  ex11
0  0.3790985254337701  0.3790985254337701  0.37909852543377010.3790985254337701
1   0.567098167179614   0.567098167179614    0.5670981671796140.567098167179614
2  0.5955925126571219  0.5955925126571219  0.59559251265712190.5955925126571219

---------

ex12
    df[colname] = df['a'] + ',' + df['b']
CPU times: user 152 ms, sys: 9.18 ms, total: 162 ms
Wall time: 171 ms
                    a                   b                                   ex12
0  0.3790985254337701  0.3790985254337701  0.3790985254337701,0.3790985254337701
1   0.567098167179614   0.567098167179614    0.567098167179614,0.567098167179614
2  0.5955925126571219  0.5955925126571219  0.5955925126571219,0.5955925126571219

---------

ex13
    df[colname] = df['a'].str.cat([df['b'], df['b']], sep=',')
CPU times: user 475 ms, sys: 13.9 ms, total: 489 ms
Wall time: 491 ms
                    a                   b                                               ex13
0  0.3790985254337701  0.3790985254337701  0.3790985254337701,0.3790985254337701,0.379098...
1   0.567098167179614   0.567098167179614  0.567098167179614,0.567098167179614,0.56709816...
2  0.5955925126571219  0.5955925126571219  0.5955925126571219,0.5955925126571219,0.595592...


余談(M1 macにて)

なんとなくですが、母艦のiMac(5年ものでここのところファンがうるさいが、奮発したのでそれなりのスペックと自負)と M1 mac book Air(iMacの故障に備えたつなぎのつもり)で処理時間を比較してみました。

厳密なベンチマークでもなく、条件も同一ではありませんし、複数回の計測もしていませんが、M1 mac book Air の5倍の圧勝で、うれしいようなカナシイような... (この例であれば、五分五分かと思っていましたが...)

1) iMac Late 2014 (4GHz Intel Core i7 メモリ32GB )



---------

ex5
    df[colname] = df.apply(lambda s: s['a'] + s['b'], axis=1)
CPU times: user 19 s, sys: 25.8 ms, total: 19 s
Wall time: 19 s
                    a                   b                                   ex5
0  0.3790985254337701  0.3790985254337701  0.37909852543377010.3790985254337701
1   0.567098167179614   0.567098167179614    0.5670981671796140.567098167179614
2  0.5955925126571219  0.5955925126571219  0.59559251265712190.5955925126571219

---------

2) mac book Air (M1, 2020) メモリ16GB


※  MiniForge

--------

ex5
    df[colname] = df.apply(lambda s: s['a'] + s['b'], axis=1)
CPU times: user 4.12 s, sys: 42.9 ms, total: 4.17 s
Wall time: 4.17 s
                    a                   b                                   ex5
0  0.3790985254337701  0.3790985254337701  0.37909852543377010.3790985254337701
1   0.567098167179614   0.567098167179614    0.5670981671796140.567098167179614
2  0.5955925126571219  0.5955925126571219  0.59559251265712190.5955925126571219

---------