Панды заполняют пропущенную дату в каждой группе информацией в предыдущем ряду - PullRequest
0 голосов
/ 22 мая 2018

Подобный вопрос к этот , но с некоторыми изменениями:

Вместо заполнения пропущенных дат для каждой группы между минимальной и максимальной датой всего столбца, мы должны толькозаполнение дат между минимальным и максимальным значениями для этой группы и вывод данных с последней строкой в ​​каждой группе

Воспроизводимый пример:

x = pd.DataFrame({'dt': ['2016-01-01','2016-01-03', '2016-01-04','2016-01-01','2016-01-01','2016-01-04']
                    ,'amount': [10.0,30.0,40.0,78.0,80.0,82.0]
                    , 'sub_id': [1,1,1,2,2,2]
                    })

Визуально:

            dt   sub_id   amount
0   2016-01-01        1     10.0
1   2016-01-03        1     30.0
2   2016-01-04        1     40.0
3   2017-01-01        2     78.0
4   2017-01-01        2     80.0
5   2017-01-04        2     82.0

Вывод мне нужен:

            dt   sub_id   amount
0   2016-01-01        1     10.0
1   2016-01-02        1     10.0
2   2016-01-03        1     30.0
3   2016-01-04        1     40.0
4   2017-01-01        2     80.0
5   2017-01-02        2     80.0
6   2017-01-03        2     80.0
7   2017-01-04        2     82.0

Мы группируем по dt и sub_id.Как вы можете видеть, в sub_id = 1 была добавлена ​​строка для 2016-01-02, и сумма была вменена в 10,0, так как предыдущая строка была 10,0 (Предположим, данные предварительно отсортированы, чтобы включить это).Для sub_id = 2 была добавлена ​​строка для 2017-01-02 и 2017-01-03, а сумма равна 80,0, поскольку это была последняя строка до этой даты.Первая строка для 2017-01-01 также была удалена, потому что мы просто хотим сохранить последнюю строку для каждой даты и sub_id.

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

Спасибо!

Ответы [ 4 ]

0 голосов
/ 22 мая 2018

используйте asfreq & groupby

сначала конвертируйте dt в datetime и избавьтесь от дубликатов

, затем для каждой группы sub_id используйте asfreq('D', method='ffill') длягенерировать пропущенные даты и вмененные суммы

наконец reset_index в столбце amount, поскольку есть дубликат столбца sub_id и индекс.

x.dt = pd.to_datetime(x.dt)
x.drop_duplicates(
  ['dt', 'sub_id'], 'last'
).groupby('sub_id').apply(
  lambda x: x.set_index('dt').asfreq('D', method='ffill')
).amount.reset_index()

# output:

   sub_id         dt  amount
0       1 2016-01-01    10.0
1       1 2016-01-02    10.0
2       1 2016-01-03    30.0
3       1 2016-01-04    40.0
4       2 2016-01-01    80.0
5       2 2016-01-02    80.0
6       2 2016-01-03    80.0
7       2 2016-01-04    82.0
0 голосов
/ 22 мая 2018

Нижеследующее работает для меня и кажется довольно эффективным, но я не могу сказать, достаточно ли это эффективно.Это позволяет избежать лямбда-тос.

Я назвал ваши данные df.

Создать base_df со всей сеткой даты / sub_id:

import pandas as pd
from itertools import product

base_grid = product(pd.date_range(df['dt'].min(), df['dt'].max(), freq='D'), list(range(df['sub_id'].min(), df['sub_id'].max() + 1, 1)))

base_df = pd.DataFrame(list(base_grid), columns=['dt', 'sub_id'])

Получить максимальное значение для dt / sub_id из df:

max_value_df = df.loc[df.groupby(['dt', 'sub_id'])['amount'].idxmax()]
max_value_df['dt']  = max_value_df['dt'].apply(pd.Timestamp)

Объединить base_df по максимальным значениям:

merged_df = base_df.merge(max_value_df, how='left', on=['dt', 'sub_id'])

Сортировать и заполнить максимальное значение:

merged_df = merged_df.sort_values(by=['sub_id', 'dt', 'amount'], ascending=True)
merged_df['amount'] = merged_df.groupby(['sub_id'])['amount'].fillna(method='ffill')

Результат:

    dt  sub_id  amount
0   2016-01-01  1   10.0
2   2016-01-02  1   10.0
4   2016-01-03  1   30.0
6   2016-01-04  1   40.0
1   2016-01-01  2   80.0
3   2016-01-02  2   80.0
5   2016-01-03  2   80.0
7   2016-01-04  2   82.0
0 голосов
/ 22 мая 2018

Получение правильной даты курса:

x.dt = pd.to_datetime(x.dt)

Тогда это:

cols = ['dt', 'sub_id']

pd.concat([
    d.asfreq('D').ffill(downcast='infer')
    for _, d in x.drop_duplicates(cols, keep='last')
                 .set_index('dt').groupby('sub_id')
]).reset_index()

          dt  amount  sub_id
0 2016-01-01      10       1
1 2016-01-02      10       1
2 2016-01-03      30       1
3 2016-01-04      40       1
4 2016-01-01      80       2
5 2016-01-02      80       2
6 2016-01-03      80       2
7 2016-01-04      82       2
0 голосов
/ 22 мая 2018

Используя resample с groupby

x.dt=pd.to_datetime(x.dt)
x.set_index('dt').groupby('sub_id').apply(lambda x : x.resample('D').max().ffill()).reset_index(level=1)
Out[265]: 
               dt  amount  sub_id
sub_id                           
1      2016-01-01    10.0     1.0
1      2016-01-02    10.0     1.0
1      2016-01-03    30.0     1.0
1      2016-01-04    40.0     1.0
2      2016-01-01    80.0     2.0
2      2016-01-02    80.0     2.0
2      2016-01-03    80.0     2.0
2      2016-01-04    82.0     2.0
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...