今日、データフレームの2つ以上の列を選択すると、1つだけを選択するよりもはるかに時間がかかる可能性があることを確認しました。
locまたはilocを使用して複数の列を選択し、listを使用して列名またはインデックスを渡すと、ilocを使用した単一の列または多数の列の選択と比較してパフォーマンスが100倍低下します(ただし、リストは渡されません)
例:
df = pd.DataFrame(np.random.randn(10**7,10), columns=list('abcdefghij'))
1つの列の選択:
%%timeit -n 100
df['b']
3.17 µs ± 147 ns per loop (mean ± std. dev. of 7 runs, 100 loops each)
%%timeit -n 100
df.iloc[:,1]
66.7 µs ± 5.95 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
%%timeit -n 100
df.loc[:,'b']
44.2 µs ± 10.5 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
2列の選択:
%%timeit -n 10
df[['b', 'c']]
96.4 ms ± 788 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
%%timeit -n 10
df.loc[:,['b', 'c']]
99.4 ms ± 4.44 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
%%timeit -n 10
df.iloc[:,[1,2]]
97.6 ms ± 1.79 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
この選択のみが期待どおりに機能します:[編集]
%%timeit -n 100
df.iloc[:,1:3]
103 µs ± 17.9 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
メカニズムの違いは何ですか、そしてなぜそれらがとても大きいのですか?
[編集]:@ run-outが指摘したように、pd.Seriesはpd.DataFrameよりもはるかに高速に処理されるようですが、なぜそうなるのか誰もが知っていますか?
一方-それは違いを説明していないdf.iloc[:,[1,2]]
としdf.iloc[:,1:3]
Pandasは、単一の行または列をpandas.Seriesとして機能します。これは、DataFrameアーキテクチャ内で機能するよりも高速です。
Pandasは、次の場合にpandas.Seriesで動作します。
%%timeit -n 10
df['b']
2.31 µs ± 1.59 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
ただし、同じ列のDataFrameをリストに入れることで呼び出すことができます。次に、次のようになります。
%%timeit -n 10
df[['b']]
90.7 ms ± 1.73 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
上記から、DataFrameを上回っているのはシリーズであることがわかります。
Pandasが列「b」をどのように処理しているかを次に示します。
type(df['b'])
pandas.core.series.Series
type(df[['b']])
pandas.core.frame.DataFrame
編集:OPがpd.seriesとpd.dataframeの方がはるかに高速である理由を深く掘り下げたいので、私は答えを拡大しています。また、これは、基盤となるテクノロジーがどのように機能するかについての私の/私たちの理解を広げるための素晴らしい質問です。専門知識のある方はチャイムを鳴らしてください。
パンダの構成要素であるnumpyから始めましょう。pandasの作者でPythonfor DataAnalysisのWesMcKinneyによると、パフォーマンスはpythonよりもnumpyで向上しています。
This is based partly on performance differences having to do with the
cache hierarchy of the CPU; operations accessing contiguous blocks of memory (e.g.,
summing the rows of a C order array) will generally be the fastest because the mem‐
ory subsystem will buffer the appropriate blocks of memory into the ultrafast L1 or
L2 CPU cache.
この例の速度差を見てみましょう。データフレームの列「b」からnumpy配列を作成しましょう。
a = np.array(df['b'])
そして今、パフォーマンステストを行います:
%%timeit -n 10
a
結果は次のとおりです。
32.5 ns ± 28.2 ns per loop (mean ± std. dev. of 7 runs, 10 loops each)
これは、2.31 µsのpd.series時間でのパフォーマンスの深刻な向上です。
パフォーマンスが向上するもう1つの主な理由は、numpyのインデックス作成がNumPy C拡張機能に直接組み込まれることですが、シリーズにインデックスを作成するときにPythonの処理が多く行われるため、処理速度が大幅に低下します。(この記事を読んでください)
なぜそうするのかという質問を見てみましょう。
df.iloc[:,1:3]
大幅に優れています:
df.iloc[:,[1,2]]
このシナリオでは、.locが.ilocと同じパフォーマンス効果を発揮することに注意してください。
何かが正しくないという最初の大きな手がかりは、次のコードにあります。
df.iloc[:,1:3] is df.iloc[:,[1,2]]
False
これらは同じ結果をもたらしますが、異なるオブジェクトです。私は違いが何であるかを見つけるために深く掘り下げてみました。私はインターネットや私の本の図書館でこれへの言及を見つけることができませんでした。
ソースコードを見ると、いくつかの違いがわかります。indexing.pyを参照します。
Class _iLocIndexerには、パンダがilocスライスのリストに対して行っている追加の作業がいくつかあります。
すぐに、入力をチェックするときに次の2つの違いに遭遇します。
if isinstance(key, slice):
return
対。
elif is_list_like_indexer(key):
# check that the key does not exceed the maximum size of the index
arr = np.array(key)
l = len(self.obj._get_axis(axis))
if len(arr) and (arr.max() >= l or arr.min() < -l):
raise IndexError("positional indexers are out-of-bounds")
これだけでパフォーマンスが低下するのに十分な原因でしょうか?知りません。
.locはわずかに異なりますが、値のリストを使用するとパフォーマンスも低下します。index.pyを見て、def _getitem_axis(self、key、axis = None)を見てください:->クラス_LocIndexer(_LocationIndexer)内:
リスト入力を処理するis_list_like_indexer(key)のコードセクションは非常に長く、多くのオーバーヘッドが含まれています。注が含まれています:
# convert various list-like indexers
# to a list of keys
# we will use the *values* of the object
# and NOT the index if its a PandasObject
確かに、値または整数のリストを処理し、スライスを指示して処理の遅延を引き起こすには、十分な追加のオーバーヘッドがあります。
コードの残りの部分は私の給与等級を超えています。誰かが見てチャイムを鳴らすことができれば、それは大歓迎です
この記事はインターネットから収集されたものであり、転載の際にはソースを示してください。
侵害の場合は、連絡してください[email protected]
コメントを追加