2017-07-01 63 views
0

我一直在爲此工作一段時間,似乎無法解決這個問題。我有一個多索引的數據幀正與2級水平,這看起來如下:pandas在多索引數據幀內對連續事件進行計數

def data(): 
    data = {'date': pd.Series(['2016-1-1', '2016-1-1', '2016-1-1', 
           '2016-1-2', '2016-1-2', '2016-1-2', 
           '2016-1-3', '2016-1-3', '2016-1-3', 
           '2016-1-4', '2016-1-4', '2016-1-4', 
           '2016-1-5', '2016-1-5', '2016-1-5']), 
      'group': pd.Series(['groupA', 'groupB', 'groupC', 
           'groupA', 'groupB', 'groupC', 
           'groupA', 'groupB', 'groupC', 
           'groupA', 'groupB', 'groupC', 
           'groupA', 'groupB', 'groupC']), 
      'series1': pd.Series([1, 2, 3, 
            1, 2, 3, 
            1, 2, 3, 
            1, 3, 4, 
            2, 3, 4]), 
      'series2': pd.Series([1, 3, 4, 
           2, 3, 3, 
           2, 4, 2, 
           1, 2, 3, 
           1, 2, 3])} 
    df = pd.DataFrame(data) 
    df['date'] = pd.to_datetime(df['date']) 
    df.set_index(['date', 'group'], inplace=True) 
return df 

我有指定1 3的條件的欄。編寫這部分代碼可能有更簡潔的方式,但這不是我的問題。

def add_cond(df): 
    df['1minus2'] = df['series1'] - df['series2'] 
    # first condition 
    mask1 = df['series1'] < df['series2'] 
    df.loc[mask1, 'result'] = 'less' 
    # second condition 
    mask2 = df['series1'] > df['series2'] 
    df.loc[mask2, 'result'] = 'greater' 
    # third condition 
    mask3 = df['series1'] == df['series2'] 
    df.loc[mask3, 'result'] = 'equal' 
return df 

我的問題是,我想添加一個列來計算每日連續條件的數量。我已經嘗試了groupbycumcount的幾種不同實現,我可以獲得所有條件的累積計數,但我希望在日期索引不連續時重置它們。

下面我列出了一些我已經嘗試過的相關文章。我認爲Pandas: conditional rolling count中的第二個答案可以工作,但它使用了一個transform方法,因爲我有多個列,所以在這裏似乎不起作用。

按照Finding consecutive segments in a pandas data frame後面概述的策略,我創建了以下代碼,該代碼創建一個Series,numpy arrays,其中包含來自「結果」列的日期和組索引值以及數據。我想我可以用這樣一種方式來分割這個df,這樣我就可以計算每個連續的組,並將這些結果合併回原來的df。

df1 = df.reset_index(level=['date','group']).groupby(['result']).apply(np.array) 

輸出將如下所示。

1 
1 
1 
1 
2 
1 
2 
3 
1 
1 
1 
2 
1 
2 
3 

這是一個有點很難看到連續累計條件是否得到滿足與分層結構DF,但如果我拆散了DF它更容易看什麼我想要實現的。也許有一種方法可以使用unstack來定位數據,這樣可以給我我想要的結果嗎?

df['result'].groupby(['date','group']).head().unstack() 

Out[9]: 
group  groupA groupB groupC 
date         
2016-01-01 equal  less  less 
2016-01-02  less  less equal 
2016-01-03  less  less greater 
2016-01-04 equal greater greater 
2016-01-05 greater greater greater 
+0

我想你可以有很多關於你正在尋找的確切機制更加清晰。我不確定'date'列與它有什麼關係(是否相關)?另外,請注意''np.sign(series1 - series2)'是執行'add_cond()'的更好方法(如果這是您真實的情況)。 –

+0

謝謝@約翰Zwinck我會看看我可以澄清。我在我的原始代碼中使用了np.sign,但不喜歡零作爲符號更改評估的結果,並且無法按照我希望的方式在lambda表達式中使用它。 – Greg

回答

2

讓我們用這種方法與groupbycumcount

df = data() 
df1 = add_con(df) 
df1['Consec'] = df1[['result']].groupby(['group','result']).cumcount() + 1 

輸出:

    series1 series2 1minus2 result Consec 
date  group            
2016-01-01 groupA  1  1  0 equal  1 
      groupB  2  3  -1  less  1 
      groupC  3  4  -1  less  1 
2016-01-02 groupA  1  2  -1  less  1 
      groupB  2  3  -1  less  2 
      groupC  3  3  0 equal  1 
2016-01-03 groupA  1  2  -1  less  2 
      groupB  2  4  -2  less  3 
      groupC  3  2  1 greater  1 
2016-01-04 groupA  1  1  0 equal  2 
      groupB  3  2  1 greater  1 
      groupC  4  3  1 greater  2 
2016-01-05 groupA  2  1  1 greater  1 
      groupB  3  2  1 greater  2 
      groupC  4  3  1 greater  3 
+0

爲什麼不只是'df1 ['Consec'] = df1 [['result']] .groupby ...'?不需要「分配」 - 它只是使代碼更加冗長而且效率更低。 –

+0

謝謝@ScottBoston,但是當我按照書面形式運行它時,出現'KeyError:group'錯誤。它運行,如果我重新排列一下,並把...... groupby(level ='group')['result']'...而是然後結果是錯誤的。任何想法? – Greg

+0

好吧,我會升級我的版本。剛剛檢查,我在0.18.1。謝謝你的幫助! – Greg