2017-10-13 10 views
4

私はサンプルデータセットを与えて、元のサンプルデータセットから複数のサンプルを選択したいとします。私はこの小さな関数をPythonで書いています。pandas range_dateの機能が極端に遅くなる

import timeit 
import pandas as pd 
import numpy as np 
sample_data = np.random.randn(10000, 15) 
index = pd.date_range("20000101", periods=10000, freq='B') 
sample_data_df = pd.DataFrame(sample_data, index=index) 
def f(n, sample_data_df, f): 
    s = (1+sample_data_df).resample(f, axis=0) 
    r = s.prod()-1 
    out = r.sample(n, replace=True) 
    # out_index = pd.date_range(start=sample_data_df.index[0], 
    #        periods=len(out.index), 
    #        freq=f) 
    # out.index = output_index 
    return out 


start_time = timeit.default_timer() 
N = 1000 
a = [f(500, sample_data_df, 'BM') for i in range(N)] 
elapsed = timeit.default_timer() - start_time 
print(elapsed) 

このコードを実行すると、35.8964748383秒かかります。しかし、私はすなわち

def f(n, sample_data_df, f): 
     s = (1+sample_data_df).resample(f, axis=0) 
     r = s.prod()-1 
     out = r.sample(n, replace=True) 
     out_index = pd.date_range(start=sample_data_df.index[0], 
            periods=len(out.index), 
            freq=f) 
     out.index = output_index 
     return out 

、私は機能の行のコメントを解除することになるブロックのそれぞれに取り付けられたインデックスを持っていると思い今機能は72.2418179512をとります。これは気違いです。このようなインデックスを各出力に含める必要がある場合は、どのように高速化できますか?私は一度インデックスを生成し、後でそれを各出力に添付することに気付いています。しかし、他のケースでも関数を使用したいので、インデックス付けの割り当てが関数内で行われると非常に感謝します。

また、インデックス作成のほかに、速度を向上させるための他の情報源がありますか? 35.8964748383の索引付けをしなくても長い時間がかかります。

+0

あなたが関数内でリサンプリングする必要がありますか? – DJK

+0

@ djk47463はい、この関数は実際に再サンプリングする目的のクラスのメソッドです。私が考えていたのは、インデックス作成を追加するデコレータを書くことです。それはピジョンソニックですか?あなたはなぜパンダのインデックス作成が遅いのか知っていますか?私のような初心者のために:インデックス作成はかなり安いもののように聞こえる。パンダでは日付の種類が効率的に処理されないのですか? – math

+1

頻度が1日より大きいリサンプリング/日付範囲は、パンダの既知のperf問題です。リンクされた問題を参照してください。 https://github.com/pandas-dev/pandas/issues/16463 – chrisb

答えて

3

編集:問題は、そんなにリサンプリングの速度やインデックスではありません新しい日付インデックスに 新しいインデックスを作成するために、キャッシュされた機能を追加しました

  • を作成するための

    • を追加しましタイミングタイミングを見れば、

      %timeit (1+sample_data_df).resample('BM', axis=0).prod()-1 
      21.7 ms ± 170 µs per loop (mean ± std. dev. of 7 runs, 10 loops each) 
      %timeit pd.date_range(start="20000101", periods=500, freq='BM') 
      21.4 ms ± 272 µs per loop (mean ± std. dev. of 7 runs, 10 loops each) 
      

      22 ms私にとっては悪くないようです。c私たちは150,000要素のリサンプリングとリダクションを行っています。

      あなたの問題は、あなたのケースでは必要ではありません(まったく同じことをしているため)。 できることは、関数でリサンプリングを保持したい場合、リサンプリングの結果をキャッシュすることです。残念ながら、関数の結果をキャッシュする標準的な方法(lru_cache)では、変更可能なオブジェクト(dfs、リストなど)を処理することはできません。のハッシュ限り、キャッシュされている今、

      from functools import lru_cache 
      class Sampler(): 
          def __init__(self, df): 
          self.df = df 
      
          def get_resampled_sample(self, n, freq): 
          resampled = self._wraper_resample_prod(freq) 
          return resampled.sample(n, replace=True) 
      
          def _wraper_resample_prod(self, freq): 
          hash_df = hash(self.df.values.tobytes()) 
          return self._resample_prod(hash_df, freq) 
      
          @lru_cache(maxsize=1) 
          def _resample_prod(self, hash_df, freq): 
          return (self.df+1).resample(freq, axis=0).prod()-1 
      

      リサンプリングの結果:これは私の解決策は、ハッシュを作成する機能でリサンプリングをラップし、パラメータとしてハッシュと実際の関数を呼び出すことですdfの値は変更されません。そして、これは我々がはるかに高速にサンプリングできることを意味します。

      %timeit [sampler.get_resampled_sample(500, 'BM') for i in range(1000)] 
      881 ms ± 10.6 ms per loop (mean ± std. dev. of 7 runs, 1 loop each) 
      

      あなたは、インデックスと同じことを行うことができますが、pd.date_rangeのすべてのパラメータは不変オブジェクトであるため、ここでは、カスタムハッシュを作成する必要はありません。

      class Sampler(): 
          def __init__(self, df): 
          self.df = df 
      
          def update_df(self, df): 
          self.df = df 
      
          def get_resampled_sample(self, n, freq): 
          resampled = self._wraper_resample_prod(freq) 
          df = resampled.sample(n, replace=True) 
          df.index = self._create_date_range(self.df.index[0], n, freq) 
          return df 
      
          def _wraper_resample_prod(self, freq): 
          hash_df = hash(self.df.values.tobytes()) 
          return self._resample_prod(hash_df, freq) 
      
          @lru_cache(maxsize=1) 
          def _resample_prod(self, hash_df, freq): 
          return (self.df+1).resample(freq, axis=0).prod()-1 
      
          @lru_cache(maxsize=1) 
          def _create_date_range(self, start, periods, freq): 
          return pd.date_range(start=start, periods=periods, freq=freq) 
      

      タイミング:

      %timeit [sampler.get_resampled_sample(500, 'BM') for i in range(1000)] 
      1.11 s ± 43.8 ms per loop (mean ± std. dev. of 7 runs, 1 loop each) 
      
    +0

    あなたの答えに多くの感謝。私はこれが効率的に物事をスピードアップすることに同意する。正しい方向へのヒントのために多くの感謝の前にキャッシュを使用したことはありません。全体的な最適化を提供していますが、インデックス作成とインデックス作成の違いが(時間的に)大きな違いを生む理由を理解するためにはまだ苦労しています。私は賞金を開いて、他の誰かが別の答えを出すかどうかを確認します。 – math

    +0

    @math新しい日付インデックスを作成するためにキャッシュされた関数を追加しました。ここでも同じ議論が有効です。インデックスの作成は実際には遅くはありません(自分のPCで約20ミリ秒かかる)が、リストの理解度を1000倍にするだけで足りる。 –

    +0

    追加していただきありがとうございます。インデックス作成のためのデコレータ(キャッシュなし)を使用するときれいになります。プレーン関数はインデックスを作成せず、装飾された関数はそれを行いますか? – math

    関連する問題