resample перед pct_change () и пропущенными значениями - PullRequest
0 голосов
/ 18 января 2019

У меня есть датафрейм:

import pandas as pd
df = pd.DataFrame([['A', 'G1', '2019-01-01', 11],
             ['A', 'G1', '2019-01-02', 12], 
             ['A', 'G1', '2019-01-04', 14], 
             ['B', 'G2', '2019-01-01', 11], 
             ['B', 'G2', '2019-01-03', 13], 
             ['B', 'G2', '2019-01-06', 16]], 
            columns=['cust', 'group', 'date', 'val'])
df

enter image description here

df = df.groupby(['cust', 'group', 'date']).sum()
df

enter image description here

Фрейм данных сгруппирован, и теперь я хотел бы вычислить pct_change, но только если есть предыдущая дата. Если я сделаю это так:

df['pct'] = df.groupby(['cust', 'group']).val.pct_change()
df

enter image description here

Я получу pct_change, но без учета пропущенных дат. Например, в группе ('A', 'G1'), pct для даты 2019-01-04 должно быть np.nan, поскольку нет (предыдущей) даты 2019-01-03.

Может быть, решением было бы сделать повторную выборку по дням, где каждая новая строка будет иметь np.nan как val, а затем сделать pct_change.

Я пытался использовать df.resample('1D', level=2), но я получаю сообщение об ошибке:

TypeError: Действителен только с DatetimeIndex, TimedeltaIndex или PeriodIndex, но получил экземпляр MultiIndex

Для группы ('B', 'G2') все pct_change должны быть np.nan, поскольку ни одна из строк не имеет предыдущей даты.

Ожидаемый результат:

enter image description here

Как рассчитать pct_change с учетом пропущенных дат?

Решение:

new_df = pd.DataFrame()

for x, y  in df.groupby(['cust', 'group']):
    resampled=y.set_index('date').resample('D').val.mean().to_frame().rename({'val': 'resamp_val'}, axis=1) 
    resampled = resampled.join(y.set_index('date')).fillna({'cust':x[0],'group':x[1]})
    resampled['resamp_val_pct'] = resampled.resamp_val.pct_change(fill_method=None)

    new_df = pd.concat([new_df, resampled])

new_df = new_df[['cust', 'group', 'val', 'resamp_val', 'resamp_val_pct']]
new_df

enter image description here

Ответы [ 2 ]

0 голосов
/ 18 января 2019

Может быть, вы могли бы попытаться сравнить разницу между последовательными строками, не равной 1 дню, а затем изменить pct_change.

df= df.groupby(['cust', 'group', 'date'])\
      .agg({'val':'sum','date':[min,max]}).reset_index()
df.columns = ['%s%s' % (a, '_%s' % b if b else '') for a, b in df.columns]

df['date_diff']=df['date'].diff()
df['pct_change_val']=df.val_sum.pct_change()
df['pct_change_final'] = df.apply(lambda row: np.NaN if pd.isnull(row.date_diff) \
                                  else np.NaN if row.date_diff != np.timedelta64(1, 'D') else row.pct_change_val ,axis=1)


#output:

    cust    group   date    date_min    date_max    val_sum date_diff   pct_change_val  pct_change_final
0   A   G1  2019-01-01  2019-01-01  2019-01-01  11          
1   A   G1  2019-01-02  2019-01-02  2019-01-02  12  1 days 00:00:00.000000000   0.09090909090909083 0.09090909090909083
2   A   G1  2019-01-04  2019-01-04  2019-01-04  14  2 days 00:00:00.000000000   0.16666666666666674 
3   B   G2  2019-01-01  2019-01-01  2019-01-01  11  -3 days +00:00:00.000000000 -0.2142857142857143 
4   B   G2  2019-01-03  2019-01-03  2019-01-03  13  2 days 00:00:00.000000000   0.18181818181818188 
5   B   G2  2019-01-06  2019-01-06  2019-01-06  16  3 days 00:00:00.000000000   0.23076923076923084 
0 голосов
/ 18 января 2019

Проверьте с помощью groupby, затем вам нужно сначала resample и получите изменение pct с помощью логической маски, поскольку pct_change будет игнорировать NaN

d={}
for x, y  in df.groupby(['cust', 'group']):
    s=y.set_index('date').resample('D').val.mean()
    d[x]=pd.concat([s,s.pct_change().mask(s.shift().isnull()|s.isnull())],1)
newdf=pd.concat(d)
newdf.columns=['val','pct']
newdf
Out[651]: 
                  val       pct
     date                      
A G1 2019-01-01  11.0       NaN
     2019-01-02  12.0  0.090909
     2019-01-03   NaN       NaN
     2019-01-04  14.0       NaN
B G2 2019-01-01  11.0       NaN
     2019-01-02   NaN       NaN
     2019-01-03  13.0       NaN
     2019-01-04   NaN       NaN
     2019-01-05   NaN       NaN
     2019-01-06  16.0       NaN

Вы можете добавить reset_index (inplace = True) в конце, чтобы вернуть весь индекс обратно к столбцам

...