Как эффективно заполнить временной ряд? - PullRequest
6 голосов
/ 13 мая 2019

Моя общая проблема заключается в том, что у меня есть фрейм данных, столбцы которого соответствуют значениям объектов.В кадре данных также есть столбец даты.В каждом столбце функции могут отсутствовать значения NaN.Я хочу заполнить столбец с помощью некоторой логики заполнения, такой как "fill_mean" или "fill zero".

Но я не хочу просто применять логику заполнения ко всему столбцу, потому что, если одно из более ранних значений является NaN, я не хочу, чтобы среднее значение, которое я заполняю для этого конкретного NaN, было испорчено тем, чтоСредний был позже, когда модель не должна была знать о.По сути, это общая проблема - не передавать информацию о будущем вашей модели, особенно когда я пытаюсь заполнить мой временной ряд.

В любом случае, я упростил свою задачу до нескольких строк кода.Это моя упрощенная попытка решения вышеуказанной общей проблемы:

#assume ts_values is a time series where the first value in the list is the oldest value and the last value in the list is the most recent.
ts_values = [17.0, np.NaN, 12.0, np.NaN, 18.0]
nan_inds = np.argwhere(np.isnan(ts_values))
for nan_ind in nan_inds:
    nan_ind_value = nan_ind[0]
    ts_values[nan_ind_value] = np.mean(ts_values[0:nan_ind_value])

Вывод приведенного выше сценария:

[17.0, 17.0, 12.0, 15.333333333333334, 18.0]

, что именно то, что я ожидал.

Моя единственная проблема с этим заключается в том, что это будет линейное время по отношению к числу NaN в наборе данных.Есть ли способ сделать это в постоянное или лог время, когда я не перебираю значения индекса nan.

1 Ответ

2 голосов
/ 13 мая 2019

, если вы хотите, чтобы значение nan было заменено скользящим средним (полное окно) на пандах серии s, отметив из WeNYoBen , что это не продолжит вычисление скользящего среднего во время заполнения. (т. е. ваш 15,3 становится 12,0).

s.fillna(s.expanding(1).mean())

Если вы хотите, чтобы скользящее среднее обновлялось по мере заполнения nans, это решение numba может помочь

import numpy as np
import numba
from numba import jit


@jit(nopython=True)
def rolling_fill(a): 
    for i, e in enumerate(a):
        if np.isnan(e):
            a[i] = np.mean(a[:i])

ts_values = np.array([17.0, np.NaN, 12.0, np.NaN, 18.0])
rolling_fill(ts_values)
print(ts_values)

что дает

[17.         17.         12.         15.33333333 18.        ]

Вы, вероятно, могли бы улучшить это, сохраняя сумму и не звоня .mean каждый раз.

Сложность времени

Это не log или constant время, так как вы должны интерполировать не более n-2 пропущенных элементов из массива длиной n, что составляет O(n) - но это должно быть достаточно оптимизировано (избегая повторения в native python) и вы не можете сделать теоретически лучше, но реализации вышеприведенного уровня сделают это значительно быстрее.


РЕДАКТИРОВАТЬ : Первоначально я неправильно прочитал и подумал, что вы спрашиваете об интерполяции

Вы хотели бы interpolate серии, и панды поддерживают это напрямую.

>>> s = pd.Series([0, 1, np.nan, 5])
>>> s
0    0.0
1    1.0
2    NaN
3    5.0
dtype: float64
>>> s.interpolate()
0    0.0
1    1.0
2    3.0
3    5.0
dtype: float64

Или, если вы не хотите использовать pandas, потому что ваш пример - ndarray, тогда используйте numpy.interp соответственно.

...