2016-11-21 5 views
2

パフォーマンスを向上させるために、パンダのforループをベクトル化しようとしています。私は、ユーザー、製品、各サービスの日付、および提供された日数で構成されるデータセットを持っています。データの次のサブセットを考える:パンダ:条件付き累積和のベクトル化

testdf = pd.DataFrame(data={"USERID": ["A"] * 6, 
          "PRODUCTID": [1] * 6, 
          "SERVICEDATE": [datetime(2016, 1, 1), datetime(
           2016, 2, 5), 
          datetime(2016, 2, 28), datetime(2016, 3, 25), 
          datetime(2016, 4, 30), datetime(2016, 5, 30)], 
          "DAYSSUPPLY": [30] * 6}) 

testdf=testdf.set_index(["USERID", "PRODUCTID"]) 
testdf["datediff"] = testdf["SERVICEDATE"].diff() 
testdf.loc[testdf["datediff"].notnull(), "datediff"] = testdf.loc[ 
    testdf["datediff"].notnull(), "datediff"].apply(lambda x: x.days) 
testdf["datediff"] = testdf["datediff"].fillna(0) 
testdf["datediff"] = pd.to_numeric(testdf["datediff"]) 
testdf["over_under"] = testdf["DAYSSUPPLY"].shift() - testdf["datediff"] 

私は次のような結果を取得したいと思います:基本的に

    DAYSSUPPLY SERVICEDATE datediff over_under desired 
USERID PRODUCTID              
A  1     30 2016-01-01   0   NaN  0 
     1     30 2016-02-05  35  -5.0  0 
     1     30 2016-02-28  23   7.0  7 
     1     30 2016-03-25  26   4.0  11 
     1     30 2016-04-30  36  -6.0  5 
     1     30 2016-05-30  30   0.0  5 

を、私は私の目的の列がover_underの実行中の合計になりたいが、唯一の合計に私は

running_total = 0 
desired_loop = [] 
for row in testdf.itertuples(): 
    over_under=row[4] 
    # skip first row 
    if pd.isnull(over_under): 
     desired_loop.append(0) 
     continue 
    running_total += over_under 
    running_total = max(running_total, 0) 
    desired_loop.append(running_total) 
testdf["desired_loop"] = desired_loop 

        desired_loop 
USERID PRODUCTID    
A  1     0.0 
     1     0.0 
     1     7.0 
     1     11.0 
     1     5.0 
     1     5.0 

:前のライン上の所望の値が0を下回る[ユーザー、製品]グループの上に迅速かつ汚いループを取得することはありません希望> 0の場合、負の値が次のようになりますセントベクタライゼーションやパンダ、そして一般的に新しいものではありません。私はこのdfで他のすべての計算をベクトル化することができましたが、この特殊な累積合計のケースでは、どうやってそれを調べるのか分かりません。

ありがとうございます!

答えて

0

私は同様の問題を抱えていましたが、やや非凡な反復を使用して解決しました。

testdf["desired"] = testdf["over_under"].cumsum() 
current = np.argmax(testdf["desired"] < 0) 
while current != 0: 
    testdf.loc[current:,"desired"] += testdf["desired"][current] # adjust the cumsum going forward 
    # the previous statement also implicitly sets 
    # testdf.loc[current, "desired"] = 0 
    current = np.argmax(testdf["desired"][current:] < 0) 

本質的に、すべての「イベント」を見つけ、実行中の累計を時間の経過とともに再調整しています。すべての操作とテスト操作はベクトル化されているため、desired列があまりにも頻繁に負の値を超えない場合は、かなり高速になるはずです。

これは間違いなくハックですが、それは私の仕事を完了しました。