高性能のPandasコードを書く

6270 ワード

高性能のPandasコードを書く
pythonは科学計算の最もよく使われる言語の一つとして、大量のデータ計算に対応し、遅すぎると、試行錯誤を繰り返す科学計算方法に時間がかかりすぎると思います.だから私はよく考えています.pythonはいったいどれだけ遅くて、みんなに最初から遅く感じさせますか.またどのくらい速くて、みんなにそれを使ってGBのデータの計算をさせますか?
pandasは科学計算データを処理するのに最もよく使われるフレームワークですが、pandasの性能はどうですか.一歩一歩試してみると、コードの書き方にかかっていることに気づきました.次に,データセットを巡るこのシナリオの下で,いくつかの書き方の性能消費がどのようなものかを比較する.
データはネット上で勝手に探しているデータセットを採用しています.
import pandas as pd
import numpy as np

data = pd.read_csv("https://vincentarelbundock.github.io/Rdatasets/csv/datasets/EuStockMarkets.csv")
data.head()

Unnamed: 0
DAX
SMI
CAC
FTSE
0
1
1628.75
1678.1
1772.8
2443.6
1
2
1613.63
1688.5
1750.5
2460.2
2
3
1606.51
1678.6
1718.0
2448.2
3
4
1621.04
1684.1
1708.1
2470.4
4
5
1618.16
1686.6
1723.1
2484.7
data.describe()

Unnamed: 0
DAX
SMI
CAC
FTSE
count
1860.000000
1860.000000
1860.000000
1860.000000
1860.000000
mean
930.500000
2530.656882
3376.223710
2227.828495
3565.643172
std
537.080069
1084.792740
1663.026465
580.314198
976.715540
min
1.000000
1402.340000
1587.400000
1611.000000
2281.000000
25%
465.750000
1744.102500
2165.625000
1875.150000
2843.150000
50%
930.500000
2140.565000
2796.350000
1992.300000
3246.600000
75%
1395.250000
2722.367500
3812.425000
2274.350000
3993.575000
max
1860.000000
6186.090000
8412.000000
4388.500000
6179.000000
DAXという列を使って、テストを行います.テストに使う関数は大体sin(DAX) * 1.1で、特別な意味はありません.純粋に時間を消費します.
次のいくつかの書き方をテストします.
  • 地味forサイクル
  • iterrowsメソッドループ
  • applyメソッド
  • ベクトル法
  • 消費時間を一つ一つテストし、統計時間方法をtimeitに統一します.
    まず最初の方法を見てみましょう
    地味forサイクル
    %%timeit
    
    result = [np.sin(data.iloc[i]['DAX']) * 1.1 for i in range(len(data))]
    
    data['target'] = result
    422 ms ± 29.3 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
    

    普通のforサイクルを使って、1860サイクル、400+msかかります.この数字はパソコンごとに異なるはずですが、次のいくつかの書き方の実行時間とは対照的です.
    この書き方は時間がかかり、pythonです.この書き方もおすすめできないので、改善してみましょう
    iterrowsメソッドループ
    pandasはiterrows法を提供し,indexと現在のループのオブジェクトを得ることができるenumerateのような動作を提供した.
    iterrowsの性能が地味なforサイクルにとって、性能はかなり向上し、使い勝手もいいです.同時にpandasはitertuples法を提供し,高い性能を提供できるが,利便性を犠牲にしてループのindexがなくなるだけでなく,ループのオブジェクトがその名の通りtupleになるため,その列のindex名で値を取ることはできず,tupleのindexで値を取るしかない.
    %%timeit
    
    result = [np.sin(row['DAX']) * 1.1 for index,row in data.iterrows()]
    
    data['target'] = result
    103 ms ± 1.15 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
    
    %%timeit
    
    result = [np.sin(row[2]) * 1.1 for row in data.itertuples()]
    
    data['target'] = result
    9.67 ms ± 649 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
    

    iterrowsという方法のほかに、もう一つの方法があります.
    applyメソッド
    このメソッドはコールバックメソッドとしてDataFrame全体を巡回し、axisパラメータを使用して巡回する軸(行で巡回するか列で巡回するか)を指定できます.
    apply法の性能はiterrowsより優れているがitertuplesより弱く,利便性はiterrowsに匹敵する.
    %%timeit
    
    data['target'] = data.apply(lambda row: np.sin(row['DAX']) * 1.1, axis=1)
    60.7 ms ± 3.14 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
    

    itertuplesを使用した場合、最高性能は9 ms程度に最適化されており、約10倍に最適化されていますが、強化は続けられますか?
    ベクトルメソッド
    科学的な計算をすれば、ベクトルを知るべきだ.DataFrameのデータは、基本的にフィルタ条件を含むベクトルです.
    データフレームのベクトル単位で計算すると、もっと速くなりますか?
    %%timeit
    
    data['target'] = np.sin(data['DAX']) * 1.1
    427 µs ± 36.4 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
    

    このように書くと、より簡潔で、効率も高く、400+usしか必要とせず、最初の400 msより1000倍最適化され、ミリ秒未満であることがわかります.
    考える
    深く考えれば、なぜ現在のパフォーマンスを一歩一歩最適化できるのかがわかるはずです.
    素朴なforループは情報が多すぎて使用する必要がないことが多く、pythonの遅いループを使用しています.では、この2つの点について最適化することができます.まずpythonサイクルを最適化し、apply関数を使用し、Cコードを使用してサイクルすることで、性能を2倍に高めることができます.その後、携帯情報を減らし、可変のtupleのみを使用することで、かなりのパフォーマンスを向上させることができます.
    しかし、実はtupleはDataFrameの元の構造ではなく、tupleに変換するのに時間がかかるので、直接DataFrameの構造を使って計算することができ、多くの性能を向上させることができるはずです.
    したがって,ベクトルを直接用いて計算すると,性能が20倍に向上した.
    では、この考え方に沿って考えることができますが、実はDataFrameのベクトルにもいくつかの付加情報が付いています.例えば、DataFrameのindexでは、これらの情報を捨てて、最下層の計算単位で演算を行うことで、一部の性能を向上させることができるはずです.
    PandasはNumpyを使用した基本データ単位なので、ベクトルから元のNumpy配列を抽出して計算することができ、一部の性能を向上させることができます.
    %%timeit
    
    data['target'] = np.sin(data['DAX'].values) * 1.1
    218 µs ± 6.34 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
    

    性能はまた2倍近く向上し、最初より2000倍向上した.
    まとめ
    多くのシステム設計の初めから、性能の問題を心配していたので、全く必要ないと思います.
    システム設計の難点は、この機能を最良またはより良い方法で実現することであり、システムの性能はシステムのボトルネックではない.Pythonは試行錯誤しやすく、迅速に反復でき、性能最適化の手段も多く、これらの手段が需要を満たすことができなければ、Cythonを使用したり、直接Cを使用してコードの一部を記述したりすることができます.
    もちろん、性能はこれまでPythonがみんなに突っ込まれたところで、最初から遅いラベルをつけていました.だからあまり性能を気にする必要もなく、数十行のコードで機能を作って実践し、何千行のCコードに書き換えるのも望ましくないことではないでしょうか.