0

"Pipeline" API from Quantopian/Ziplineには非常に奇妙なパターンが見つかりました。CustomFactorクラスがあり、独自のFactorモデルを実装するときにはcompute()メソッドをオーバーライドします。Quantopian/Zipline:パイプラインパッケージの奇妙なパターン

compute()の署名がある:​​、パラメータ「アウト」の次のコメントで:assetsと同じ形状の

出力アレイ。 computeは、希望の戻り値をoutに書き込む必要があります。

機能は、単に代わりに入力パラメータへの書き込みの出力配列を返すことができなかった理由を私は尋ねられたとき、私は次のような答え受け取っ:APIは、出力配列が返されることを必要とした場合、」

をcompute()によって、実際の出力バッファに配列のコピーを作成することになり、余分なコピーが不必要に作成されることになります。私は、彼らがそうすることに終わる理由を理解することはできない

...明らかにPythonで値渡しについて何ら問題はありませんし、不必要なデータをコピーするの危険性はありません。これは、彼らがコードに人を推薦している実装の一種であるので、これは本当に痛いです:

def compute(self, today, assets, out, data): 
     out[:] = data[-1] 

だから私の質問は、なぜそれが単純になることができませんでしたされています

def compute(self, today, assets, data): 
     return data[-1] 

答えて

1

(私が設計し、実装しました

Pythonオブジェクトは関数に渡したり渡したりするとコピーされません。 CustomFactorから行を返し、指定された配列に値を書き込むのに違いがあるのは、というコードで行われるコピーと、CustomFactor計算メソッドを呼び出すことの違いです。 CustomFactor APIはもともと、あなたの計算方法は、おおよそこのように見えた呼び出すコードを設計した

def _compute(self, windows, dates, assets): 
    # `windows` here is list of iterators yielding 2D slices of 
    # the user's requested inputs 

    # `dates` and `assets` are row/column labels for the final output. 

    # Allocate a (dates x assets) output array. 
    # Each invocation of the user's `compute` function 
    # corresponds to one row of output. 
    output = allocate_output() 

    for i in range(len(dates)): 

     # Grab the next set of input arrays. 
     inputs = [next(w) for w in windows] 

     # Call the user's compute, which is responsible for writing 
     # values into `out`. 
     self.compute(
      dates[i], 
      assets, 
      # This index is a non-copying operation. 
      # It creates a view into row `i` of `output`. 
      output[i], 
      *inputs # Unpack all the inputs. 
     ) 

    return output 

ここで基本的な考え方は、我々はデータのかなりの量を事前にフェッチしたことで、そのデータにウィンドウをループし、そのデータに対してユーザーの計算関数を呼び出し、結果を事前に割り当てられた出力配列に書き込んで、それをさらに変換に渡します。

私たちが何をしても、ユーザのcompute関数の結果を出力配列に取得するには、少なくとも1つのコピーのコストを支払わなければなりません。そして、それがAPIた場合

# Get the result row from the user. 
result_row = self.compute(dates[i], assets, *inputs) 
# Copy the user's result into our output buffer. 
output[i] = result_row 

、我々は:

呼び出し元のコードは次のようになり、その場合に最も明白なAPI、ご指摘のように、ユーザーは単に出力行を返すことです、 compute

  1. ユーザーが返す〜64000バイトの配列を割り当てます。
  2. ユーザーの計算されたデータのコピーをユーザーの出力配列にコピーします。
  3. ユーザーの出力配列から独自の大きな配列へのコピー。

既存のAPIでは、コスト(1)と(3)を避けます。

これまでのところ、私たちはCustomFactorsの動作を変更して、上記の最適化の一部をあまり役に立たないようにしました。特に、その日にマスクされていないアセットのデータをcomputeに渡すだけです。これは、computeの呼び出し前後で出力配列の部分的なコピーを必要とします。

既存のAPIを好む理由はまだまだあります。特に、エンジンを出力配分の制御に任せておくと、複数出力要因に対してrecarraysを渡すようなことを簡単に行うことができます。