Кадр данных Pandas вперед заполняется распадом

Я использую Python 3.5 и Pandas v 0.19.2. У меня есть кадр данных, как показано ниже. Прямое заполнение пропущенных значений выполняется прямолинейно.

import pandas as pd
import numpy as np

d = {'A': np.array([10, np.nan, np.nan, -3, np.nan, 4, np.nan, 0]),
     'B': np.array([np.nan, np.nan, 5, -3, np.nan, np.nan, 0, np.nan ])}
df = pd.DataFrame(d)
df_filled = df.fillna(axis='index', method='ffill')
print(df_filled)
Out[8]: 
      A    B
0  10.0  NaN
1  10.0  NaN
2  10.0  5.0
3  -3.0 -3.0
4  -3.0 -3.0
5   4.0 -3.0
6   4.0  0.0
7   0.0  0.0

Мой вопрос: как лучше всего реализовать форвардную заливку с затуханием? Я понимаю, что pd.ffill() и pd.fillna() не поддерживают это. Например, результат, который я получаю, приведен ниже (в отличие от обычного заполнения выше), где значение переносится вдвое в каждый период:

Out[5]: 
      A    B
0  10.0  NaN
1   5.0  NaN
2   2.5  5.0
3  -3.0 -3.0
4  -1.5 -1.5
5   4.0 -0.75
6   2.0  0.0
7   0.0  0.0

person Zhubarb    schedule 22.06.2018    source источник
comment
Привет, как здесь должен работать распад? Только половина предыдущих значений?   -  person rpanai    schedule 22.06.2018
comment
Да, например, только половина предыдущего значения. Я добавил обычный вывод ffill() и нужный для сравнения.   -  person Zhubarb    schedule 22.06.2018
comment
В любом случае вы можете попросить добавить это как функцию на github.   -  person rpanai    schedule 22.06.2018


Ответы (2)


Да, нет простого способа сделать это. Я бы рекомендовал делать это по одному столбцу за раз, используя groupby и apply.

for c in df:
    df[c] = df[c].groupby(df[c].notnull().cumsum()).apply(
        lambda y: y.ffill() / 2 ** np.arange(len(y))
    )

df
      A     B
0  10.0   NaN
1   5.0   NaN
2   2.5  5.00
3  -3.0 -3.00
4  -1.5 -1.50
5   4.0 -0.75
6   2.0  0.00
7   0.0  0.00
person cs95    schedule 22.06.2018
comment
В конце есть лишняя скобка, и она возвращает эту ошибку NameError: name 'x' is not defined - person rpanai; 22.06.2018

Есть векторное решение. Он частично использует этот ответ.

import pandas as pd
import numpy as np

d = {'A': np.array([10, np.nan, np.nan, -3, np.nan, 4, np.nan, 0]),
     'B': np.array([np.nan, np.nan, 5, -3, np.nan, np.nan, 0, np.nan ])}
df = pd.DataFrame(d)

decay_rate = 2

ddf = df.isnull().cumsum().diff().fillna(0)
ddf = ddf!=0
ddf = ddf.cumsum() - ddf.cumsum()\
                        .where(~ddf)\
                        .ffill()\
                        .fillna(0)
df_filled = df.ffill()/(ddf * decay_rate).replace(0, 1)

Изменить: в моих экспериментах это решение в 1,8 раза быстрее, чем другое. Должно быть интересно сравнить результаты с полным df.

person rpanai    schedule 22.06.2018
comment
Спасибо, завтра утром попробую оба (с большим ДФ) - person Zhubarb; 22.06.2018