2015-12-29 10 views
7

私は、過去数年にわたる店舗のパネルデータと2.5年の期間にわたるパネルデータに基づいて、過去のカグルチャレンジのデータを使用します。各観測には、特定の店舗日付の顧客数が含まれます。各店舗日付について、私の目的は、過去60日間にこの店舗を訪れた平均顧客数を計算することです。パンダで過去60日間の平均をスピードアップ

以下は、まさに私が必要とするコードです。しかし、それは永遠に続く - それはc.800kの行を処理するために夜がかかるだろう。私は同じ目的をより速く達成するための巧妙な方法を探しています。

私は、店舗ID(店舗)、日付および顧客数(「得意先」)という関連変数を使用して、初期データセットの5つの観測を含めました。

注:反復の各行について

  • 、Iは、例えば、の代わり.LOC使用して結果を書き込み終わりますrow ["Lagged No customers"] "行"はセルに何も書き込まないためです。私はなぜそれが事実であるのだろうか。
  • 私は通常、 "apply、axis = 1"を使って新しい列を設定します。私は "適用"は、各行の計算が同じ行レベルの値を使用して列全体で行われるときにうまく機能することを発見しました。しかし、私は "適用"機能がどのようにこの問題に必要な行であるかを知ることができません。これまで私が見てきた唯一の例外は "diff"ですが、これはここでは有用ではありません。

ありがとうございます。


サンプルデータ:動作しますが、信じられないほど遅いです

pd.DataFrame({ 
    'Store': {0: 1, 1: 1, 2: 1, 3: 1, 4: 1}, 
    'Customers': {0: 668, 1: 578, 2: 619, 3: 635, 4: 785}, 
    'Date': { 
    0: pd.Timestamp('2013-01-02 00:00:00'), 
    1: pd.Timestamp('2013-01-03 00:00:00'), 
    2: pd.Timestamp('2013-01-04 00:00:00'), 
    3: pd.Timestamp('2013-01-05 00:00:00'), 
    4: pd.Timestamp('2013-01-07 00:00:00') 
    } 
}) 

コード:あなたの小さなサンプルデータが与えられ

import pandas as pd 
import numpy as np 
data = pd.read_csv("Rossman - no of cust/dataset.csv") 
data.Date = pd.to_datetime(data.Date) 
data.Customers = data.Customers.astype(int) 

for index, row in data.iterrows(): 
    d = row["Date"] 
    store = row["Store"] 
    time_condition = (d - data["Date"]<np.timedelta64(60, 'D')) & (d > data["Date"]) 

    sub_df = data.loc[ time_condition & (data["Store"] == store), :] 

    data.loc[ (data["Date"]==d) & (data["Store"] == store), "Lagged No customers"] = sub_df["Customers"].sum() 
    data.loc[ (data["Date"]==d) & (data["Store"] == store), "No of days"] = len(sub_df["Customers"]) 
    if len(sub_df["Customers"]) > 0: 
     data.loc[ (data["Date"]==d) & (data["Store"] == store), "Av No of customers"] = int(sub_df["Customers"].sum()/len(sub_df["Customers"])) 
+0

は、なぜあなたは 'data'の最初の100行を取るか、次のよう

あなたはローリングデータのグラフを表示することができますか? ( 'data [:100]') –

+0

@Alexander 'time_condition'は、適切な時間ウィンドウを選択するマスクに過ぎず、その後sub_dfを作成するために使用されます。 @David ZIは、出力を待って夜を過ごさないために100行を取るが、目的はデータセット全体の出力を持つことです –

+0

その場合、コードサンプルの '[:100]'をあなたの質問。結局のところ、あなたのサンプルコードは、あなたが持っている問題を説明することになっています - この場合は、遅すぎます。大したことではありませんが、何か他の理由で再度編集してしまうと、それを取り除くことも考えられます。 –

答えて

6

、私の代わりに、60日の2日間移動平均を使用。

>>> (pd.rolling_mean(data.pivot(columns='Store', index='Date', values='Customers'), window=2) 
    .stack('Store')) 
Date  Store 
2013-01-03 1  623.0 
2013-01-04 1  598.5 
2013-01-05 1  627.0 
2013-01-07 1  710.0 
dtype: float64 

日付をインデックスとして使用し、列として格納することで、ローリング平均をとることができます。その後、データを正しい形に戻すためにストアを積み重ねる必要があります。 .stack('Store')

Store   1  2  3 
Date       
2015-07-29 541.5 686.5 767.0 
2015-07-30 534.5 664.0 769.5 
2015-07-31 550.5 613.0 822.0 

、これは次のようになります:上記df命名されたと仮定すると、

Date  Store 
2015-07-29 1  541.5 
      2  686.5 
      3  767.0 
2015-07-30 1  534.5 
      2  664.0 
      3  769.5 
2015-07-31 1  550.5 
      2  613.0 
      3  822.0 
dtype: float64 

、あなたがしてマージすることができここで

は、最終的なスタックに先立って、元のデータのいくつかのサンプルが出力されます元のデータに戻します。

data.merge(df.reset_index(), 
      how='left', 
      on=['Date', 'Store']) 

EDIT: 調整したいデータには明確な季節パターンがあります。いずれにしても、ローリング平均を7の倍数にして、ほんの数週間を表すことをお勧めします。私は、以下の例(9週間)で63日間の時間枠を使用しました。

オープンした店舗(および期間の開始点)でデータが失われないようにするには、ローリング平均機能でmin_periods=1を指定します。これは、より明確にここで、何が起こっているかを確認するには、あなたの与えられた時間ウィンドウのために利用可能なすべての観測値の上にあなたの

df = data.loc[data.Customers > 0, ['Date', 'Store', 'Customers']] 
result = (pd.rolling_mean(df.pivot(columns='Store', index='Date', values='Customers'), 
      window=63, min_periods=1) 
     .stack('Store')) 
result.name = 'Customers_63d_mvg_avg' 
df = df.merge(result.reset_index(), on=['Store', 'Date'], how='left') 

>>> df.sort_values(['Store', 'Date']).head(8) 
       Date Store Customers Customers_63d_mvg_avg 
843212 2013-01-02  1  668    668.000000 
842103 2013-01-03  1  578    623.000000 
840995 2013-01-04  1  619    621.666667 
839888 2013-01-05  1  635    625.000000 
838763 2013-01-07  1  785    657.000000 
837658 2013-01-08  1  654    656.500000 
836553 2013-01-09  1  626    652.142857 
835448 2013-01-10  1  615    647.500000 

を平均値を与えるおもちゃの例である:

s = pd.Series([1,2,3,4,5] + [np.NaN] * 2 + [6]) 
>>> pd.concat([s, pd.rolling_mean(s, window=4, min_periods=1)], axis=1) 
    0 1 
0 1 1.0 
1 2 1.5 
2 3 2.0 
3 4 2.5 
4 5 3.5 
5 NaN 4.0 
6 NaN 4.5 
7 6 5.5 

ウィンドウは、4つの観測値であります5.5の最終値は(5 + 6)/ 2に等しいことに注意してください。4.0と4.5の値はそれぞれ(3 + 4 + 5)/ 3と(4 + 5)/ 2です。

この例では、左結合を行い、dfのすべての行に1つ以上のCustomersがあるため、ピボットテーブルのNaN行はdfにマージされません。

df.set_index(['Date', 'Store']).unstack('Store').plot(legend=False) 

enter image description here

+0

これまでにこれまで見たことがなかったおかげで:) rolling_sumは私が過去に持っていた他の同様の問題にも役立ちます。速度をテストするために、完全なデータセットでできるだけ早く試してみます。 –

+0

私はそれを5.66MBのトレーニングデータファイルでテストしました。半分以下です。その後、それをマージする必要があります。 – Alexander

+4

私はこのような解決策にも取り組んでいましたが、質問のリファレンス実装と全く同じことをしていないようです。そのコードは、平均をとるとき、データセットに実際にデータが存在する過去60日間の日付の数で割ったもので、通常は60未満の数値です(この数値は「No of dates "column)' rolling_mean'は、実際に空の行にデータを複製し、60で割り切れるように表示されます。いずれにせよ、私のテストは結果が全く同じではないことを示しています。 –