Cumsum reset при NaN
Если у меня есть pandas.core.series.Series
с именем ts
из 1 или NaN, как это:
3382 NaN
3381 NaN
...
3369 NaN
3368 NaN
...
15 1
10 NaN
11 1
12 1
13 1
9 NaN
8 NaN
7 NaN
6 NaN
3 NaN
4 1
5 1
2 NaN
1 NaN
0 NaN
Я хотел бы рассчитать cumsum этой серии, но он должен быть reset (установлен на ноль) в местоположении NaN, как показано ниже:
3382 0
3381 0
...
3369 0
3368 0
...
15 1
10 0
11 1
12 2
13 3
9 0
8 0
7 0
6 0
3 0
4 1
5 2
2 0
1 0
0 0
В идеале я хотел бы иметь векторизованное решение!
Я когда-либо видел аналогичный вопрос с Matlab:
Matlab cumsum reset в NaN?
но я не знаю, как перевести эту строку d = diff([0 c(n)]);
Ответы
Ответ 1
Простой перевод Numy вашего кода Matlab выглядит следующим образом:
import numpy as np
v = np.array([1., 1., 1., np.nan, 1., 1., 1., 1., np.nan, 1.])
n = np.isnan(v)
a = ~n
c = np.cumsum(a)
d = np.diff(np.concatenate(([0.], c[n])))
v[n] = -d
np.cumsum(v)
Выполнение этого кода возвращает результат array([ 1., 2., 3., 0., 1., 2., 3., 4., 0., 1.])
. Это решение будет только таким же правильным, как и оригинальное, но, возможно, оно поможет вам придумать что-то лучшее, если этого недостаточно для ваших целей.
Ответ 2
Здесь немного более pandas -нихий способ сделать это:
v = Series([1, 1, 1, nan, 1, 1, 1, 1, nan, 1], dtype=float)
n = v.isnull()
a = ~n
c = a.cumsum()
index = c[n].index # need the index for reconstruction after the np.diff
d = Series(np.diff(np.hstack(([0.], c[n]))), index=index)
v[n] = -d
result = v.cumsum()
Обратите внимание, что для любого из них требуется, чтобы вы использовали pandas
по крайней мере на 9da899b
или новее. Если вы этого не сделаете, вы можете направить bool
dtype
на int64
или float64
dtype
:
v = Series([1, 1, 1, nan, 1, 1, 1, 1, nan, 1], dtype=float)
n = v.isnull()
a = ~n
c = a.astype(float).cumsum()
index = c[n].index # need the index for reconstruction after the np.diff
d = Series(np.diff(np.hstack(([0.], c[n]))), index=index)
v[n] = -d
result = v.cumsum()
Ответ 3
Еще более pandas -нический способ сделать это:
v = pd.Series([1., 3., 1., np.nan, 1., 1., 1., 1., np.nan, 1.])
cumsum = v.cumsum().fillna(method='pad')
reset = -cumsum[v.isnull()].diff().fillna(cumsum)
result = v.where(v.notnull(), reset).cumsum()
В отличие от кода matlab, это также работает для значений, отличных от 1.
Ответ 4
Если вы можете принять аналогичную логическую серию b
, попробуйте
(b.cumsum() - b.cumsum().where(~b).fillna(method='pad').fillna(0)).astype(int)
Начиная с вашей серии ts
, либо b = (ts == 1)
, либо b = ~ts.isnull()
.