#python #pandas #dataframe #missing-data #linear-interpolation
Вопрос:
Я работаю в Python 3 с фреймом данных Pandas. В нем есть столбцы для категории, даты и значения. Для каждой категории я хочу добавить строки с недостающими днями, чтобы значение было линейно интерполировано.
Чтобы создать минимальный пример, я использую следующий код
df = pd.DataFrame({
'cat':['A', 'A', 'A', 'A', 'B', 'B', 'B'],
'date': ['2021-1-1', '2021-1-4', '2021-1-5', '2021-1-7', '2021-11-1', '2021-11-2', '2021-11-5'],
'value': [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 9.0]})
df['cat'] = df['cat'].astype('category')
df['date'] = df['date'].astype('datetime64')
Что дает следующий фрейм данных
cat date value
A 2021-01-01 1.0
A 2021-01-04 2.0
A 2021-01-05 3.0
A 2021-01-07 4.0
B 2021-11-01 5.0
B 2021-11-02 6.0
B 2021-11-05 9.0
Я бы хотел, чтобы результат был похож на этот пример, где я использовал ‘<‘ для обозначения вновь вставленных строк
cat date value
A 2021-01-01 1.0
A 2021-01-02 1.333 <
A 2021-01-03 1.667 <
A 2021-01-04 2.0
A 2021-01-05 3.0
A 2021-01-06 3.5 <
A 2021-01-07 4.0
B 2021-11-01 5.0
B 2021-11-02 6.0
B 2021-11-03 7.0 <
B 2021-11-04 8.0 <
B 2021-11-05 9.0
В реальной проблеме мне не нужны выходные дни (суббота и воскресенье), но я изложил проблему, как указано выше, чтобы предотвратить добавление дополнительных слоев (я могу легко отфильтровать выходные дни позже, если это необходимо). Однако, если не включать их в первую очередь, это может привести к более эффективному коду, поэтому я подумал, что также упомяну об этой проблеме. Спасибо за любую помощь!
Ответ №1:
Используйте DataFrame.groupby
с повторной выборкой или Series.asfreq
для пропущенных значений для дней, а затем интерполируйте по группам в лямбда-функции:
df = (df.set_index('date')
.groupby('cat')['value']
.apply(lambda x: x.asfreq('d').interpolate())
.reset_index())
print (df)
cat date value
0 A 2021-01-01 1.000000
1 A 2021-01-02 1.333333
2 A 2021-01-03 1.666667
3 A 2021-01-04 2.000000
4 A 2021-01-05 3.000000
5 A 2021-01-06 3.500000
6 A 2021-01-07 4.000000
7 B 2021-11-01 5.000000
8 B 2021-11-02 6.000000
9 B 2021-11-03 7.000000
10 B 2021-11-04 8.000000
11 B 2021-11-05 9.000000
df = (df.set_index('date')
.groupby('cat')['value']
.apply(lambda x: x.resample('d').first().interpolate())
.reset_index())
print (df)
cat date value
0 A 2021-01-01 1.000000
1 A 2021-01-02 1.333333
2 A 2021-01-03 1.666667
3 A 2021-01-04 2.000000
4 A 2021-01-05 3.000000
5 A 2021-01-06 3.500000
6 A 2021-01-07 4.000000
7 B 2021-11-01 5.000000
8 B 2021-11-02 6.000000
9 B 2021-11-03 7.000000
10 B 2021-11-04 8.000000
11 B 2021-11-05 9.000000
Или:
f = lambda x: x.interpolate()
s = df.set_index('date').groupby('cat')['value'].resample('d').first().groupby(level=0).apply(f)
print (s)
cat date
A 2021-01-01 1.000000
2021-01-02 1.333333
2021-01-03 1.666667
2021-01-04 2.000000
2021-01-05 3.000000
2021-01-06 3.500000
2021-01-07 4.000000
B 2021-11-01 5.000000
2021-11-02 6.000000
2021-11-03 7.000000
2021-11-04 8.000000
2021-11-05 9.000000
Name: value, dtype: float64
Комментарии:
1. Это сработало отлично. Также помогло мне узнать о том, как думать о проблемах такого типа. Спасибо!
Ответ №2:
Вы могли бы использовать вспомогательную функцию:
def interpolate(d, on='date', vals=['value']):
return (d.set_index(on).reindex(pd.date_range(d[on].min(), d[on].max()))
[vals].interpolate()
.rename_axis(on)
)
df.groupby('cat').apply(interpolate).reset_index()
вывод:
cat date value
0 A 2021-01-01 1.000000
1 A 2021-01-02 1.333333
2 A 2021-01-03 1.666667
3 A 2021-01-04 2.000000
4 A 2021-01-05 3.000000
5 A 2021-01-06 3.500000
6 A 2021-01-07 4.000000
7 B 2021-11-01 5.000000
8 B 2021-11-02 6.000000
9 B 2021-11-03 7.000000
10 B 2021-11-04 8.000000
11 B 2021-11-05 9.000000
Ответ №3:
Опция представляет собой комбинацию интерполяции с полным:
# pip install git https://github.com/pyjanitor-devs/pyjanitor.git
import pandas as pd
import janitor
dates = dict(date = lambda df: pd.date_range(df.min(), df.max(), freq='1D'))
(df.complete(dates, by='cat', sort = True)
.assign(value = lambda df: df.value.interpolate())
)
cat date value
0 A 2021-01-01 1.000000
1 A 2021-01-02 1.333333
2 A 2021-01-03 1.666667
3 A 2021-01-04 2.000000
4 A 2021-01-05 3.000000
5 A 2021-01-06 3.500000
6 A 2021-01-07 4.000000
7 B 2021-11-01 5.000000
8 B 2021-11-02 6.000000
9 B 2021-11-03 7.000000
10 B 2021-11-04 8.000000
11 B 2021-11-05 9.000000
complete
предоставляет недостающие значения, после чего мы затем выполняем интерполяцию по linear
методу.
Комментарии:
1. : D Я действительно подумал: » Теперь не хватает только решения pyjanitor, где sammywemmy? «. Ну, вот и все 😉