簡體   English   中英

根據熊貓中的日期窗口對值的累積出現進行計數

[英]Counting cumulative occurrences of values based on date window in Pandas

我有一個DataFramedf ),如下所示:

+----------+----+
| dd_mm_yy | id |
+----------+----+
| 01-03-17 | A  |
| 01-03-17 | B  |
| 01-03-17 | C  |
| 01-05-17 | B  |
| 01-05-17 | D  |
| 01-07-17 | A  |
| 01-07-17 | D  |
| 01-08-17 | C  |
| 01-09-17 | B  |
| 01-09-17 | B  |
+----------+----+

這是我想計算的最終結果:

+----------+----+-----------+
| dd_mm_yy | id | cum_count |
+----------+----+-----------+
| 01-03-17 | A  |         1 |
| 01-03-17 | B  |         1 |
| 01-03-17 | C  |         1 |
| 01-05-17 | B  |         2 |
| 01-05-17 | D  |         1 |
| 01-07-17 | A  |         2 |
| 01-07-17 | D  |         2 |
| 01-08-17 | C  |         1 |
| 01-09-17 | B  |         2 |
| 01-09-17 | B  |         3 |
+----------+----+-----------+

邏輯

要計算id但在指定時間范圍(例如4 months內值的累積出現。 即,每5個月,計數器重置為1。

要獲取累積發生次數,我們可以使用df.groupby('id').cumcount() + 1

着眼於id = B ,我們看到的第二個occurence B 2個月,因此后cum_count = 2 B的下一次出現是在01-09-17 ,回首4個月,我們只發現了另一個發生,所以cum_count = 2 ,依此cum_count = 2

我的方法是從df.groupby('id').transform調用輔助函數。 我覺得這比可能要復雜和緩慢,但似乎可行。

# test data

    date    id  cum_count_desired
2017-03-01  A   1
2017-03-01  B   1
2017-03-01  C   1
2017-05-01  B   2
2017-05-01  D   1
2017-07-01  A   2
2017-07-01  D   2
2017-08-01  C   1
2017-09-01  B   2
2017-09-01  B   3

# preprocessing

df['date'] = pd.to_datetime(df['date'])
df.set_index('date', inplace=True)
# Encode the ID strings to numbers to have a column
# to work with after grouping by ID
df['id_code'] = pd.factorize(df['id'])[0]

# solution

def cumcounter(x):
    y = [x.loc[d - pd.DateOffset(months=4):d].count() for d in x.index]
    gr = x.groupby('date')
    adjust = gr.rank(method='first') - gr.size() 
    y += adjust
    return y

df['cum_count'] = df.groupby('id')['id_code'].transform(cumcounter)

# output

df[['id', 'id_num', 'cum_count_desired', 'cum_count']]

           id  id_num  cum_count_desired  cum_count
date                                               
2017-03-01  A       0                  1          1
2017-03-01  B       1                  1          1
2017-03-01  C       2                  1          1
2017-05-01  B       1                  2          2
2017-05-01  D       3                  1          1
2017-07-01  A       0                  2          2
2017-07-01  D       3                  2          2
2017-08-01  C       2                  1          1
2017-09-01  B       1                  2          2
2017-09-01  B       1                  3          3

需要adjust

如果同一ID在同一天多次出現,則我使用的切片方法將使每個同一天的ID計數過高,因為當列表推導遇到日期時,基於日期的切片會立即獲取所有同一天的值在其中顯示多個ID。 固定:

  1. 按日期對當前DataFrame進行分組。
  2. 對每個日期組中的每一行進行排名。
  3. 從這些排名中減去每個日期組中的總行數。 這將產生一個以日期索引的負整數遞增系列,以0結尾。
  4. 將這些非正整數調整量添加到y

這只會影響給定測試數據中的一行-第二行,因為B在同一天出現兩次。

包括或排除時間間隔的左端點

要計算行一樣古老或超過 4個日歷月前更新 ,即, 包括 4個月的時間間隔的左端點,離開這條線不變:

y = [x.loc[d - pd.DateOffset(months=4):d].count() for d in x.index]

要對嚴格比 4個日歷月前新的行進行計數(即, 排除 4個月時間間隔的左端點),請改用以下方法:

y = [d.loc[d - pd.DateOffset(months=4, days=-1):d].count() for d in x.index]

您可以使用石斑魚來擴展groupby:

df['cum_count'] = df.groupby(['id', pd.Grouper(freq='4M', key='date')]).cumcount()

Out[48]: 
        date id  cum_count
0 2017-03-01  A          0
1 2017-03-01  B          0
2 2017-03-01  C          0
3 2017-05-01  B          0
4 2017-05-01  D          0
5 2017-07-01  A          0
6 2017-07-01  D          1
7 2017-08-01  C          0
8 2017-09-01  B          0
9 2017-09-01  B          1

我們也可以使用.apply行式處理切片df。 切片將基於dateutil中的relativedelta的使用。

def get_cum_sum (slice, row):
    if slice.shape[0] == 0:
        return 1
    return slice[slice['id'] == row.id].shape[0]

d={'dd_mm_yy':['01-03-17','01-03-17','01-03-17','01-05-17','01-05-17','01-07-17','01-07-17','01-08-17','01-09-17','01-09-17'],'id':['A','B','C','B','D','A','D','C','B','B']}
df=pd.DataFrame(data=d)
df['dd_mm_yy'] = pd.to_datetime(df['dd_mm_yy'], format='%d-%m-%y')

df['cum_sum'] = df.apply(lambda current_row: get_cum_sum(df[(df.index <= current_row.name) & (df.dd_mm_yy >= (current_row.dd_mm_yy - relativedelta(months=+4)))],current_row),axis=1)

>>> df
    dd_mm_yy id  cum_sum
0 2017-03-01  A        1
1 2017-03-01  B        1
2 2017-03-01  C        1
3 2017-05-01  B        2
4 2017-05-01  D        1
5 2017-07-01  A        2
6 2017-07-01  D        2
7 2017-08-01  C        1
8 2017-09-01  B        2
9 2017-09-01  B        3

考慮使用.rolling是否可行,但是幾個月不是固定期限,因此可能行不通。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM