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