замените конкретное значение в кадрах данных pandas, используя среднее значение между 10 предыдущими и следующими значениями

python #pandas #dataframe #imputation

#python #pandas #фрейм данных #вменение

Вопрос:

Допустим, у меня есть следующий фрейм данных

 df.Consumption

0        16.208
1        11.193
2         9.845
3         9.348
4         9.091
          ...  
19611     0.000
19612     0.000
19613     0.000
19614     0.000
19615     0.000
Name: Consumption, Length: 19616, dtype: float64
 

Я хочу заменить значения 0 на среднее значение 10 предыдущих и следующих значений, которые не равны 0,00

Каков хороший способ сделать это? Я думал об использовании методов replace и interpolate, но я не вижу, как это эффективно написать

Комментарии:

1. Как распределяются числа предыдущих и следующих значений? Должны ли мы иметь по 5 для ненулевых предыдущих и следующих чисел или у нас должны быть максимально близкие предыдущие и следующие ненулевые значения с предыдущим приоритетом?

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

Ответ №1:

Вы можете использовать Series.rolling() with center=True вместе с Rolling.mean() , чтобы получить среднее значение предыдущих и следующих значений.

Замените 0 на NaN , если вы хотите исключить 0 из вычисления среднего.

Установите center=True так, чтобы скользящие окна просматривали как предыдущие, так и следующие записи.

Наконец, установите для этих записей значение 0 со средними значениями, используя .loc , следующим образом:

 n = 10     # check previous and next 10 entries

# rolling window size is (2n   1)
Consumption_mean = (df['Consumption'].replace(0, np.nan)
                                     .rolling(n * 2   1, min_periods=1, center=True)
                                     .mean())

df.loc[df['Consumption'] == 0, 'Consumption'] = Consumption_mean
 

ДЕМОНСТРАЦИЯ

Использование меньшего размера окна n = 3 для демонстрации:

 df


    Consumption
0        16.208
1        11.193
2         9.845
3         9.348
4         9.091
5         8.010
6         0.000              <====   target entry
7         7.100
8         0.000              <====   target entry
9         6.800
10        6.500
11        6.300
12        5.900
13        5.800
14        5.600

#n = 10     # check previous and next 10 entries
n = 3     # smaller window size for demo

# rolling window size is (2n   1)
Consumption_mean = (df['Consumption'].replace(0, np.nan)
                                     .rolling(n * 2   1, min_periods=1, center=True)
                                     .mean())

# Update into a new column `Consumption_New` for demo purpose
df['Consumption_New'] = df['Consumption']    
df.loc[df['Consumption'] == 0, 'Consumption_New'] = Consumption_mean

 

Демонстрационный результат:

 print(df)

    Consumption  Consumption_New
0        16.208          16.2080
1        11.193          11.1930
2         9.845           9.8450
3         9.348           9.3480
4         9.091           9.0910
5         8.010           8.0100
6         0.000           8.0698   # 8.0698 = (9.348   9.091   8.01   7.1   6.8) / 5 with skipping 0.000 between 7.100 and 6.800
7         7.100           7.1000
8         0.000           6.9420   # 6.942 = (8.01   7.1   6.8   6.5   6.3) / 5 with skipping 0.000 between 8.010 and 7.100
9         6.800           6.8000
10        6.500           6.5000
11        6.300           6.3000
12        5.900           5.9000
13        5.800           5.8000
14        5.600           5.6000
 

Комментарии:

1. Спасибо за ваш ответ. Я проверю это как правильное решение. Однако у меня есть вопрос. Допустим, у меня есть полоса из 10 nan, если я выберу окно из 7, те, что в центре, будут иметь только среднее значение nan. есть ли способ подсчитать строки только со значениями, отличными от Nans?

2. @lalaland На самом деле, если вы хотите подсчитывать строки только со значениями, отличными от Nans, наиболее четким способом является удаление строк с NaN помощью before processing . например, используя dropna() для удаления NaN строк перед обработкой.

3. но результирующий фрейм данных не будет иметь такого же размера, и я не смогу заменить его вычисленным средним значением, нет?

4. @lalaland Вы можете удалить Nan строки прямо перед всеми шагами здесь, чтобы вычисление средних и шаг замены основывались на одном и том же удаленном фрейме данных, чтобы данные были согласованы и правильно выровнены.

5. Да, я имел в виду второй вариант. Значит, нет способа динамически изменять размер окна? Спасибо за ответ. Я ценю это

Ответ №2:

Это должно подвести вас довольно близко. Он использует преимущества нулевых значений, которые не учитываются в среднем, поэтому вы можете заменить ноль на nan, а затем просто выполнить цикл.

Я не уверен в лучшем способе обойтись без применения по строкам.

Что-то подсказывает мне, что выполнение фактического цикла, в котором вы обновляете df на каждой итерации, даст вам несколько иные результаты, поскольку вы будете по ходу дела присваивать нули, что приведет к тому, что предыдущие 10 результатов всегда будут иметь значение.

 import pandas as pd
df = pd.DataFrame({'Consumption':[1,1,1,1,1,1,1,1,1,0,2,2,2,2,2,2,2,2,2,2]})
df.replace(0,np.nan, inplace=True)
df.update(df.apply(lambda x:np.mean(df.Consumption.iloc[max(x.name-10,0):]), axis=1).to_frame('Consumption'),overwrite=False)
 

Вывод

 Consumption
0   1.000000
1   1.000000
2   1.000000
3   1.000000
4   1.000000
5   1.000000
6   1.000000
7   1.000000
8   1.000000
9   1.526316
10  2.000000
11  2.000000
12  2.000000
13  2.000000
14  2.000000
15  2.000000
16  2.000000
17  2.000000
18  2.000000
19  2.000000
 

Комментарии:

1. Спасибо @Chris, это очень полезно 🙂