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
---------