Ускорение повторной выборки конечной и начальной даты для каждой группы, но сохраняйте промежутки между датами - PullRequest
0 голосов
/ 28 апреля 2020

У меня есть следующий фрейм данных:

df = pd.DataFrame({'startdate': ['2013-03-04', '2012-01-02', '2012-08-06', '2013-02-04', '2013-05-07'],
                   'enddate': ['2013-03-07', '2012-01-06', '2012-08-10', '2013-02-11', '2013-05-09'],
                   'regnr': [111, 111, 111, 222, 222],
                   'contracttype': ['ABU', 'ABU', 'ULDB', 'ULDB', 'ABU']})

df['startdate'], df['enddate'] = pd.to_datetime(df['startdate']), pd.to_datetime(df['enddate'])

print(df)

   startdate    enddate  regnr contracttype
0 2013-03-04 2013-03-07    111          ABU
1 2012-01-02 2012-01-06    111          ABU
2 2012-08-06 2012-08-10    111         ULDB
3 2013-02-04 2013-02-11    222         ULDB
4 2013-05-07 2013-05-09    222          ABU

Я хочу расширить (изменить) начальную дату и конечную дату до дневного временного ряда. Но что важно, так это то, что мы сохраняем разрыв между датами. Итак, если вы посмотрите на regnr = 111, мы увидим в индексе 2 enddate = 2012-11-04, а следующие startdate = 2013-03-04 в индексе 0. Так что между этими датами есть разрыв.


Так что ожидается вывод равен:

         date  regnr contracttype
0  2013-03-04    111          ABU
1  2013-03-05    111          ABU
2  2013-03-06    111          ABU
3  2013-03-07    111          ABU
4  2012-01-02    111          ABU
5  2012-01-03    111          ABU
6  2012-01-04    111          ABU
7  2012-01-05    111          ABU
8  2012-01-06    111          ABU
9  2012-08-06    111         ULDB
10 2012-08-07    111         ULDB
11 2012-08-08    111         ULDB
12 2012-08-09    111         ULDB
13 2012-08-10    111         ULDB
14 2013-02-04    222         ULDB
15 2013-02-05    222         ULDB
16 2013-02-06    222         ULDB
17 2013-02-07    222         ULDB
18 2013-02-08    222         ULDB
19 2013-02-09    222         ULDB
20 2013-02-10    222         ULDB
21 2013-02-11    222         ULDB
22 2013-05-07    222          ABU
23 2013-05-08    222          ABU
24 2013-05-09    222          ABU

Так в чем же проблема?

У меня есть рабочее решение, но оно довольно медленное. У меня уходит ~ 90 - 110 секунд для кадра данных из 66 тыс. Строк, который в итоге будет преобразован в 10,5 млн. Строк.

Вот рабочее решение:

def resample_data(df):
    """
    :param df: dataframe where datetime has to be resampled to daily
    :return: resampled dataframe
    """
    tables = []

    for _, d in df.groupby('regnr'):
        for __, data in d.iterrows():
            dates = pd.date_range(data['startdate'], data['enddate'])
            dfn = pd.DataFrame({'regnr': data['regnr'],
                                'contracttype': data['contracttype']}, index=dates)
            tables.append(dfn)

    df = pd.concat(tables).rename_axis('date').reset_index()

    return df

Векторизация решение, но не работает

Поэтому я попробовал векторизованный метод (ожидаем применения в групповом режиме), но он не работает, поскольку он заполнит промежутки между датами.

dfn = df.melt(id_vars=['regnr', 'contracttype'], 
              value_name='date', 
              var_name='start_end').drop('start_end', axis=1)

dfn = (
    dfn.groupby('regnr').apply(lambda x: x.set_index('date').resample('D').first().ffill())
    .reset_index(level=0, drop=True)
    .reset_index()
)

print(dfn)

          date  regnr contracttype
0   2012-01-02  111.0          ABU
1   2012-01-03  111.0          ABU
2   2012-01-04  111.0          ABU
3   2012-01-05  111.0          ABU
4   2012-01-06  111.0          ABU
..         ...    ...          ...
521 2013-05-05  222.0         ULDB
522 2013-05-06  222.0         ULDB
523 2013-05-07  222.0          ABU
524 2013-05-08  222.0          ABU
525 2013-05-09  222.0          ABU

[526 rows x 3 columns]

1 Ответ

0 голосов
/ 28 апреля 2020

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

tables = pd.DataFrame(index=[], columns=['Date','regnr','contracttype'])
for i in range(len(df)):
    days = pd.DataFrame({'Date':list(pd.date_range(df.startdate[i], df.enddate[i])),
            'regnr':[df.regnr[i]]*len(pd.date_range(df.startdate[i],df.enddate[i])),
            'contracttype':[df.contracttype[i]]*len(pd.date_range(df.startdate[i], df.enddate[i]))})
tables = pd.concat([tables, days], ignore_index=True)

    Date    regnr   contracttype
0   2013-03-04  111 ABU
1   2013-03-05  111 ABU
2   2013-03-06  111 ABU
3   2013-03-07  111 ABU
4   2012-01-02  111 ABU
5   2012-01-03  111 ABU
6   2012-01-04  111 ABU
7   2012-01-05  111 ABU
8   2012-01-06  111 ABU
9   2012-08-06  111 ULDB
10  2012-08-07  111 ULDB
11  2012-08-08  111 ULDB
12  2012-08-09  111 ULDB
13  2012-08-10  111 ULDB
14  2013-02-04  222 ULDB
15  2013-02-05  222 ULDB
16  2013-02-06  222 ULDB
17  2013-02-07  222 ULDB
18  2013-02-08  222 ULDB
19  2013-02-09  222 ULDB
20  2013-02-10  222 ULDB
21  2013-02-11  222 ULDB
22  2013-05-07  222 ABU
23  2013-05-08  222 ABU
24  2013-05-09  222 A
...