Pandas условное внешнее объединение на основе timedelta (merge_asof) - PullRequest
1 голос
/ 12 февраля 2020

У меня есть несколько фреймов данных, которые мне нужно объединить в один набор данных на основе уникального идентификатора (uid) и временной шкалы между датами в каждом фрейме данных.

Вот упрощенный пример фреймов данных:

df1

   uid    tx_date last_name first_name  meas_1
0   60 2004-01-11      John      Smith     1.3
1   60 2016-12-24      John      Smith     2.4
2   61 1994-05-05     Betty      Jones     1.2
3   63 2006-07-19     James       Wood     NaN
4   63 2008-01-03     James       Wood     2.9
5   65 1998-10-08       Tom      Plant     4.2
6   66 2000-02-01     Helen       Kerr     1.1

df2

   uid    rx_date last_name first_name meas_2
0   60 2004-01-14      John      Smith      A
1   60 2017-01-05      John      Smith     AB
2   60 2017-03-31      John      Smith    NaN
3   63 2006-07-21     James       Wood      A
4   64 2002-04-18      Bill    Jackson      B
5   65 1998-10-08       Tom      Plant     AA
6   65 2005-12-01       Tom      Plant      B
7   66 2013-12-14     Helen       Kerr      C

В основном я пытаюсь объединить записи для одного и того же лица из двух отдельных источников, где существует связь между записями для уникальных лиц: «uid» и связь между строками (где она существует) для каждого индивида является нечеткой взаимосвязью между «tx_date» и «rx_date», которая (обычно) может быть согласована с заданной c дельтой времени. Не всегда будет точное или нечеткое соответствие между датами, данные могут отсутствовать в любом столбце, кроме 'uid', и каждый фрейм данных будет содержать различное, но пересекающееся подмножество 'uid's.

Мне нужно быть возможность объединять строки, в которых совпадают столбцы «uid», и где абсолютная дельта времени между «tx_date» и «rx_date» находится в заданном диапазоне (например, максимальная дельта 14 дней). В тех случаях, когда дельта времени находится за пределами этого диапазона или отсутствует одно из значений tx_date или rx_date или если uid существует только в одном из фреймов данных, мне все равно необходимо сохранить данные в этой строке. Конечный результат должен выглядеть примерно так:

    uid    tx_date    rx_date first_name last_name  meas_1 meas_2
0    60 2004-01-11 2004-01-14       John     Smith     1.3      A
1    60 2016-12-24 2017-01-05       John     Smith     2.4     AB
2    60        NaT 2017-03-31       John     Smith     NaN    NaN
3    61 1994-05-05        NaT      Betty     Jones     1.2    NaN
4    63 2006-07-19 2006-07-21      James      Wood     NaN      A
5    63 2008-01-03        NaT      James      Wood     NaN    NaN
6    64 2002-04-18        NaT       Bill   Jackson     NaN      B
7    65 1998-10-08 1998-10-08        Tom     Plant     4.2     AA
8    65        NaT 2005-12-01        Tom     Plant     NaN      B
9    66 2000-02-01        NaT      Helen      Kerr     1.1    NaN
10   66        NaT 2013-12-14      Helen      Kerr     NaN      C

Похоже, pandas .merge_asof должно быть полезно здесь, но я не смог заставить его делать то, что я нужно.

Попробовав merge_asof на двух реальных фреймах данных, я выдал ошибку ValueError: left keys must be sorted

Согласно этому вопросу проблема была на самом деле из-за наличия NaT значения в столбце «дата» для некоторых строк. Я удалил строки со значениями NaT и отсортировал столбцы 'date' в каждом кадре данных, но результат все еще не совсем тот, который мне нужен.

В приведенном ниже коде показаны предпринятые шаги.

import pandas as pd


df1['date'] = df1['tx_date']
df1['date'] = pd.to_datetime(df1['date'])
df1['date'] = df1['date'].dropna()
df1 = df1.sort_values('date')

df2['date'] = df2['rx_date']
df2['date'] = pd.to_datetime(df2['date'])
df2['date'] = df2['date'].dropna()
df2 = df2.sort_values('date')

df_merged = (pd.merge_asof(df1, df2, on='date', by='uid', tolerance=pd.Timedelta('14 days'))).sort_values('uid')

Результат:

   uid    tx_date    rx_date last_name_x first_name_x  meas_1 meas_2
3   60 2004-01-11 2004-01-14        John        Smith     1.3      A
6   60 2016-12-24 2017-01-05        John        Smith     2.4     AB
0   61 1994-05-05        NaT       Betty        Jones     1.2    NaN
4   63 2006-07-19 2006-07-21       James         Wood     NaN      A
5   63 2008-01-03        NaT       James         Wood     2.9    NaN
1   65 1998-10-08 1998-10-08         Tom        Plant     4.2     AA
2   66 2000-02-01        NaT       Helen         Kerr     1.1    NaN   

Это выглядит как левое соединение, а не как полное внешнее соединение, поэтому везде, где есть строка в df2 без совпадения по 'uid' и 'date' в df1, теряется (и это не очень ясно из этого упрощенного примера, но мне также нужно добавить строки туда, где дата была NaT).

Есть ли какой-нибудь способ добиться слияния без потерь, либо путем какого-либо внешнего соединения с merge_asof, либо с помощью какого-то другого подхода?

...