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, это очень полезно 🙂