Замените NaN или пропущенные значения на скользящее среднее или другую интерполяцию

У меня есть pandas dataframe с ежемесячными данными, для которых я хочу рассчитать скользящую среднюю за 12 месяцев. Однако данные за каждый месяц января отсутствуют (NaN), поэтому я использую

pd.rolling_mean(data["variable"]), 12, center=True)

но это просто дает мне все значения NaN.

Есть ли простой способ, которым я могу игнорировать значения NaN? Я понимаю, что на практике это станет скользящей средней за 11 месяцев.

У фрейма данных есть другие переменные, которые имеют данные за январь, поэтому я не хочу просто отбрасывать столбцы за январь и делать скользящее среднее значение за 11 месяцев.

2 ответа

Решение

Есть несколько способов приблизиться к этому, и лучший способ будет зависеть от того, систематически отличаются январские данные от других месяцев. Большинство реальных данных, вероятно, будут несколько сезонными, поэтому давайте в качестве примера возьмем среднюю высокую температуру (по Фаренгейту) случайного города в северном полушарии.

df=pd.DataFrame({ 'month' : [10,11,12,1,2,3],
                  'temp'  : [65,50,45,np.nan,40,43] }).set_index('month')

Вы можете использовать скользящее среднее, как вы предлагаете, но проблема в том, что вы получите среднюю температуру за весь год, которая игнорирует тот факт, что январь является самым холодным месяцем. Чтобы исправить это, вы можете уменьшить окно до 3, в результате чего временная температура января будет средней величиной декабрьской и февральской температуры. (Я также использую min_periods=1 как предложено в ответе @user394430.)

df['rollmean12'] = df['temp'].rolling(12,center=True,min_periods=1).mean()
df['rollmean3']  = df['temp'].rolling( 3,center=True,min_periods=1).mean()

Это улучшения, но все еще есть проблема перезаписи существующих значений с помощью скользящих средств. Чтобы избежать этого, вы можете сочетать с update() метод ( см. документацию здесь).

df['update'] = df['rollmean3']
df['update'].update( df['temp'] )  # note: this is an inplace operation

Существуют даже более простые подходы, которые оставляют существующие значения в покое, заполняя отсутствующие временные значения января либо предыдущим месяцем, следующим месяцем, либо средним значением предыдущего и следующего месяца.

df['ffill']   = df['temp'].ffill()         # previous month 
df['bfill']   = df['temp'].bfill()         # next month
df['interp']  = df['temp'].interpolate()   # mean of prev/next

В этом случае, interpolate() по умолчанию используется простая линейная интерпретация, но у вас также есть несколько других вариантов интерполяции. См. Документацию по интерполяции панд для получения дополнительной информации. Или этот вопрос переполнения статка: Интерполяция на DataFrame в пандах

Вот пример данных со всеми результатами:

       temp  rollmean12  rollmean3  update  ffill  bfill  interp
month                                                           
10     65.0        48.6  57.500000    65.0   65.0   65.0    65.0
11     50.0        48.6  53.333333    50.0   50.0   50.0    50.0
12     45.0        48.6  47.500000    45.0   45.0   45.0    45.0
1       NaN        48.6  42.500000    42.5   45.0   40.0    42.5
2      40.0        48.6  41.500000    40.0   40.0   40.0    40.0
3      43.0        48.6  41.500000    43.0   43.0   43.0    43.0

В частности, обратите внимание, что "update" и "interp" дают одинаковые результаты во все месяцы. Хотя не имеет значения, какой вы используете здесь, в других случаях так или иначе может быть лучше.

Настоящий ключ имеет min_periods=1, Также, начиная с версии 18, правильный вызов выполняется с помощью объекта Rolling. Поэтому ваш код должен быть

data["variable"].rolling(min_periods=1, center=True, window=12).mean(),

Другие вопросы по тегам