pandas で df[df[列番号]==数値] と df[df[列番号].isin([数値])] のどちらが速いか(数値編) - ablog の文字列版。
サマリー
14MBの CSV ファイルを読んで 1,000 回 df[df[列番号]=='文字列'] または df[df[列番号].isin(['文字列'])] の実行時間を計測すると、isin のほうが 4.599s(=8.057s-3.458s)速い結果になった。cProfile でプロファイリングすると df[df[列番号]=='文字列'] の場合、pandas._libs.ops.scalar_compare で 79%(6.378s) の時間を消費している。一方、df[df[列番号].isin(['文字列'])] は algorithms.py:457(isin) が最も時間を消費している( 1.934s)。
テストコードでは 100,000行(14MB)の CSV ファイル(|区切り)を pandas で読み込んで DataFrame を生成して df[df[3]==10] または df[df[3].isin([10])] を行っている。これを 1,000 回繰り返している。
| コード | 実行時間 |
|---|---|
df[df[1]=='Supplier#000100000']]] |
8.057s |
df[df[1].isin(['Supplier#000100000'])] |
3.458s |
計測結果
- df[df[列番号]=='文字列']
$ time python -m cProfile -o pandas_equal_operator_string.prof pandas_equal_operator_string.py real 0m8.057s user 0m7.920s sys 0m0.173s
- df[df[列番号].isin(['文字列'])]
$ time python -m cProfile -o pandas_isin_string.prof pandas_isin_string.py real 0m3.458s user 0m3.431s sys 0m0.130s
ボトルネック分析
- tottime: 関数で消費した時間(sub-function の呼び出しで消費した時間を含まない)
- cumttime: 関数で消費した時間(sub-function の呼び出しで消費した時間を含む)
tottime でソート
- cProfile でプロファイリングした結果(バイナリ)をテキストに変換
$ python cprofile2txt.py pandas_equal_operator_string.prof tottime > cprof_pandas_equal_operator_string.txt $ python cprofile2txt.py pandas_isin_string.prof tottime > cprof_pandas_isin_string.txt
- cprof_pandas_equal_operator_string.txt
Thu Oct 12 04:27:05 2023 pandas_equal_operator_string.prof
741298 function calls (716789 primitive calls) in 7.870 seconds
Ordered by: internal time
ncalls tottime percall cumtime percall filename:lineno(function)
1000 6.378 0.006 6.378 0.006 {pandas._libs.ops.scalar_compare} ★ここに時間がかかっている
1 0.324 0.324 0.325 0.325 {method 'read_low_memory' of 'pandas._libs.parsers.TextReader' objects}
408 0.061 0.000 0.061 0.000 {built-in method marshal.loads}
1000 0.046 0.000 0.046 0.000 {method 'nonzero' of 'numpy.ndarray' objects}
- cprof_pandas_isin_string.txt
Thu Oct 12 04:27:32 2023 pandas_isin_string.prof
723298 function calls (698789 primitive calls) in 3.280 seconds
Ordered by: internal time
ncalls tottime percall cumtime percall filename:lineno(function)
1000 1.934 0.002 1.997 0.002 algorithms.py:457(isin)
1 0.268 0.268 0.269 0.269 {method 'read_low_memory' of 'pandas._libs.parsers.TextReader' objects}
408 0.059 0.000 0.059 0.000 {built-in method marshal.loads}
cumtime でソート
- cProfile でプロファイリングした結果(バイナリ)をテキストに変換
$ python cprofile2txt.py pandas_equal_operator_string.prof cumtime > cprof_pandas_equal_operator_string_cumtime.txt $ python cprofile2txt.py pandas_isin_string.prof cumtime > cprof_pandas_isin_string_cumtime.txt
- cprof_pandas_equal_operator_string_cumtime.txt
Thu Oct 12 04:27:05 2023 pandas_equal_operator_string.prof
741298 function calls (716789 primitive calls) in 7.870 seconds
Ordered by: cumulative time
ncalls tottime percall cumtime percall filename:lineno(function)
427/1 0.005 0.000 7.870 7.870 {built-in method builtins.exec}
1 0.011 0.011 7.870 7.870 pandas_equal_operator_string.py:1(<module>)
1000 0.002 0.000 6.586 0.007 common.py:62(new_method)
1000 0.002 0.000 6.581 0.007 arraylike.py:38(__eq__)
1000 0.007 0.000 6.579 0.007 series.py:5790(_cmp_method)
1000 0.008 0.000 6.414 0.006 array_ops.py:290(comparison_op)
1000 0.006 0.000 6.390 0.006 array_ops.py:115(comp_method_OBJECT_ARRAY)
1000 6.378 0.006 6.378 0.006 {pandas._libs.ops.scalar_compare}
66 0.002 0.000 1.286 0.019 __init__.py:1(<module>)
524/2 0.004 0.000 0.456 0.228 <frozen importlib._bootstrap>:1165(_find_and_load)
- cprof_pandas_isin_string_cumtime.txt
Thu Oct 12 04:27:32 2023 pandas_isin_string.prof
723298 function calls (698789 primitive calls) in 3.280 seconds
Ordered by: cumulative time
ncalls tottime percall cumtime percall filename:lineno(function)
427/1 0.005 0.000 3.280 3.280 {built-in method builtins.exec}
1 0.009 0.009 3.280 3.280 pandas_isin_string.py:1(<module>)
1000 0.009 0.000 2.121 0.002 series.py:5273(isin)
1000 1.934 0.002 1.997 0.002 algorithms.py:457(isin)
66 0.002 0.000 1.272 0.019 __init__.py:1(<module>)
524/2 0.004 0.000 0.449 0.224 <frozen importlib._bootstrap>:1165(_find_and_load)
計測プログラム
- pandas_equal_operator_string.py
#!/usr/bin/env python3 import pandas as pd df = pd.read_csv('csv/supplier.tbl', delimiter='|', header=None) for i in range(1000): df[df[1]=='Supplier#000100000']
- pandas_isin_string.py
#!/usr/bin/env python3 import pandas as pd df = pd.read_csv('csv/supplier.tbl', delimiter='|', header=None) for i in range(1000): df[df[1].isin(['Supplier#000100000'])]
cProfile のプロファイリング結果をテキストに変換するスクリプト
- cprofile2txt.py
#!/usr/bin/env python3 import sys import pstats cprof_binary_file = sys.argv[1] cprof_sort_key = sys.argv[2] sts = pstats.Stats(cprof_binary_file) sts.strip_dirs().sort_stats(cprof_sort_key).print_stats()