2015-12-02 84 views
2

我想計算pandas列的rolling_max,其中窗口大小變化,並且是當前行索引和滿足某些條件的行之間的差異。在df列中指定可變窗口大小的Pandas rolling_max

所以,作爲一個例子,我有:

df = pd.DataFrame({'a': [0,1,0,0,0,1,0,0,0,0,1,0], 
        'b': [5,4,3,6,1,2,3,4,2,1,7,8]}) 

我想df.b的rolling_max因爲df.a == 1之前的時間。即我希望得到這樣的:

 a b rm 
0 0 5 NaN <- no previous a==1 
1 1 4 4 <- a==1 
2 0 3 4 
3 0 6 6 
4 0 1 6 
5 1 2 2 <- a==1 
6 0 3 3 
7 0 4 4 
8 0 2 4 
9 0 1 4 
10 1 7 7 <- a==1 
11 0 8 8 

我DF具有無間隙的整數索引,所以我試圖做到這一點:

df['last_a'] = np.where(df.a == 1, df.index, np.nan) 
df['last_a'].fillna(method='ffill', inplace=True) 
df['rm'] = pd.rolling_max(df['b'], window = df.index - df['last_a'] + 1) 

,但我得到一個類型錯誤:需要一個整數。

這是在相當大的數據框架上運行的長腳本的一部分,所以我需要儘可能快的解決方案。我已經成功嘗試用循環代替rolling_max來完成此操作,但速度很慢。能否請你幫忙?

僅供參考。醜陋的和長循環,我現在有,並且,不論其美醜,似乎是相當快的我的數據幀(50,000×25的測試)上,如下:

df['rm2'] = df.b 
df['rm1'] = np.where((df['a'] == 1) | (df['rm2'].diff() > 0), df['rm2'], np.nan) 
df['rm1'].fillna(method = 'ffill', inplace = True) 
df['Dif'] = (df['rm1'] - df['rm2']).abs() 
while df['Dif'].sum() != 0: 
    df['rm2'] = df['rm1'] 
    df['rm1'] = np.where((df['a'] == 1) | (df['rm2'].diff() > 0), df['rm2'], np.nan) 
    df['rm1'].fillna(method = 'ffill', inplace = True) 
    df['Dif'] = (df['rm1'] - df['rm2']).abs() 
+0

大數據幀?最快的解決方案?有條件的聚合? SQL引擎解決方案的所有經典需求,讓您的RAM休息一下。數據是否來自RDMS? – Parfait

+0

感謝您的建議Parfait!可能我會遵循它,因爲我目前的過程非常緩慢。我並沒有期待它那麼慢,我也希望在這個過程中學習一些Python。 –

回答

0

我將創建一個指數和groupby該指數使用cummax

import numpy as np 

df['index'] = df['a'].cumsum() 
df['rm'] = df.groupby('index')['b'].cummax() 

df.loc[df['index']==0, 'rm'] = np.nan 

In [104]: df 
Out[104]: 
    a b index rm 
0 0 5  0 NaN 
1 1 4  1 4 
2 0 3  1 4 
3 0 6  1 6 
4 0 1  1 6 
5 1 2  2 2 
6 0 3  2 3 
7 0 4  2 4 
8 0 2  2 4 
9 0 1  2 4 
10 1 7  3 7 
11 0 8  3 8 
+0

非常感謝Beauvel上校!優雅和完美的作品!同時,令人驚訝的是,我的漫長而醜陋的循環解決方案在大數據框架上運行速度要快得多(我已經在50,000 x 25上測試過)。我的醜陋循環爲17.5ms,可愛的爲2.68s。可能,正如Parfait所建議的那樣,我真的寧願轉向使用這些任務的SQL。 僅供大家參考,我會用我得到的一個循環更新我的初始文章 - 也許還有一些東西要從中擠出來。 不幸的是,我還沒有給你+1,如果我獲得更高的聲望,我會這樣做。 –

+0

感謝您的反饋!有趣的是看到groupby是相當緩慢的。我正在考慮使用itertools更快的解決方案,將嘗試將它發佈到這一週。對於+1無後顧之憂,您可以稍後再做! –

0

事實上,任何時候你需要包括列和表之間的關係調整數據,可以考慮使用關係數據庫管理系統(關係數據庫管理系統)的SQL解決方案。特別是如果您的數據來自數據庫,請這樣做。離開熊貓進行數據分析。當然,如果你將大數據存儲在數據庫中,那麼這就是另一個問題!

Python配備了內置庫SQLite,它是流行的免費開源文件級數據庫。另外,還可以安裝用於MySQL,SQL Server,PostgreSQL,Oracle和其他RDMS的Python庫。您可以將每個連接無縫地集成到pandas中。以下是三個等效版本的查詢來實現您的條件組最大值。每個假設您在源表中維護自動編號主鍵索引ID,在這裏命名爲RollingMax

import sqlite3 as lite 
import pandas as pd 

con = lite.connect('C:\\Path\\SQLite\\DB.db') 

# SQL WITH DERIVED TABLES 
sql = """SELECT a, b, 
       (SELECT Max(dtbl2.B) 
       FROM 
        (SELECT t1.ID, t1.a, t1.b, 
          (SELECT Count(*) FROM RollingMax t2 
          WHERE t1.ID >= t2.ID AND t2.A > 0) As GrpA 
        FROM RollingMax t1) dtbl2 
       WHERE dtbl1.ID >= dtbl2.ID 
       AND dtbl1.GrpA = dtbl2.GrpA) As rm 

     FROM 
     (
       SELECT t1.ID, t1.a, t1.b, 
        (SELECT Count(*) FROM RollingMax t2 
       WHERE t1.ID >= t2.ID AND t2.A > 0) As GrpA 
       FROM RollingMax t1 
     ) As dtbl1;""" 

# SQL USING CTE WINDOW FUNCTION (AVAILABLE AS OF VERSION 3.8.3) 
sql = """WITH grp (ID, a, b, GrpA) 
     AS (
       SELECT t1.ID, t1.a, t1.b, 
        (SELECT Count(*) FROM RollingMax t2 
        WHERE t1.ID >= t2.ID AND t2.A > 0) As GrpA 
       FROM RollingMax t1 
      ) 
     SELECT a, b, 
       (SELECT Max(dtbl2.B) 
       FROM grp AS dtbl2 
       WHERE dtbl1.ID >= dtbl2.ID 
       AND dtbl1.GrpA = dtbl2.GrpA) As rm 
     FROM grp AS dtbl1;""" 

# SQL USING SAVED VIEW 
'''To be saved inside database''' 
saved_view = """SELECT t1.ID, t1.a, t1.b, 
        (SELECT Count(*) FROM RollingMax t2 
        WHERE t1.ID >= t2.ID AND t2.A > 0) As GrpA 
       FROM RollingMax t1;""" 

sql = """SELECT a, b, 
      (SELECT Max(dtbl2.B) 
       FROM saved_view AS dtbl2 
       WHERE dtbl1.ID >= dtbl2.ID 
       AND dtbl1.GrpA = dtbl2.GrpA) As rm 
     FROM saved_view As dtbl1;""" 

df = pd.read_sql(sql, conn) 

OUTPUT(唯一的挑戰這裏是一個沒有== 1之前的第一分組)

a b rm 
0 5 5 
1 4 4 
0 3 4 
0 6 6 
0 1 6 
1 2 2 
0 3 3 
0 4 4 
0 2 4 
0 1 4 
1 7 7 
0 8 8 
+0

非常感謝Parfait。目前我的數據是通過API下載的CSV表格。不幸的是,服務器不允許直接的SQL連接。因此,我必須考慮設置本地MySQL服務器,並在嘗試提出建議之前將整個流程從Python轉換爲SQL。 –

+0

考慮[SQLite](http://www.sqlite.org/index.html),一個輕量級的文件級RDMS,但仍然強大(每個文件大小限制 - [140 terabytes](http://www.sqlite.org/) limits.html))。 MySQL是企業級服務器級RDMS,可能需要一些基礎設施來設置和運行。但後來我把它從筆記本電腦上運行下來!祝你好運! – Parfait