Слияние таблиц по условию в Pandas

Есть два датафрейма: с пульсом и давлением

df_hr = pandas.DataFrame(
    {'time': [datetime(2022,1,1,7,40), 
              datetime(2022,1,1,9,50), 
              datetime(2022,1,1,10,1)], 
     'hr': [60, 90, 100]}
)

df_bp = pandas.DataFrame(
    {'time': [datetime(2022,1,1,10),
              datetime(2022,1,1,8)], 
     'bp': [140, 120]}
)
HR:
time                    hr
0   2022-01-01 07:40:00 60
1   2022-01-01 09:50:00 90
2   2022-01-01 10:01:00 100
BP:
time                    bp
0   2022-01-01 10:00:00 140
1   2022-01-01 08:00:00 120

Нужно создать третий датафрейм так, чтобы для каждого замера давления в той же строке содержится время и значение ближайшего замера сердечного ритма, если он был сделан обязательно до замера давления и не раньше, чем за 15 минут.

Чтобы получилось типа такого:

   time_hr             hr    time_bp            bp
0   2022-01-01 09:50:00 90  2022-01-01 10:00:00 140

Ответы (2 шт):

Автор решения: CrazyElf

Как-то сложно получилось:

  • объединяем данные по ритму и давлению
  • сортируем по time
  • заполняем NA вперёд для ритма, чтобы сопоставить с давлением
  • применяем условие про 15 минут: что не подходит под него, то превращаем обратно в NA
  • оставляем только строки с давлением
import pandas as pd
from datetime import datetime

df_hr = pd.DataFrame(
    {'time': [datetime(2022,1,1,7,40), 
              datetime(2022,1,1,9,50), 
              datetime(2022,1,1,10,1)], 
     'hr': [60, 90, 100]}
)

df_bp = pd.DataFrame(
    {'time': [datetime(2022,1,1,10),
              datetime(2022,1,1,8)], 
     'bp': [140, 120]}
)

# нужна отдельная колонка, чтобы можно было её заполнять вперёд
df_hr['time_hr'] = df_hr['time']
# объединяем данные, с сортировкой по времени
df = pd.concat([df_hr, df_bp]).sort_values('time') 
# заполняем NA данные по ритму вперёд
df.loc[:, ['hr','time_hr']] = df.loc[:, ['hr','time_hr']].ffill()
# условие про 15 минут
df.loc[(df['time'] - df['time_hr']).dt.seconds//60 > 15, ['hr', 'time_hr']] = pd.NA
# оставляем только строки с давлением (и присоединённый к ним найденный по условиям ритм)
df = df.loc[~df['bp'].isna()]
df

Вывод:

                   time   hr                time_hr    bp
1   2022-01-01 08:00:00 <NA>                    NaT 120.0
0   2022-01-01 10:00:00 90.0    2022-01-01 09:50:00 140.0
→ Ссылка
Автор решения: strawdog

Воспользуйтесь методом merge_asof:

res = pd.merge_asof(df_hr.sort_values("time"),
                    df_bp.rename(columns={'time':'time_bp'}).sort_values("time_bp"),
                    left_on="time", right_on="time_bp", 
                    direction='forward', 
                    tolerance=pd.Timedelta(minutes=15)).dropna()

res:

                 time  hr             time_bp     bp
1 2022-01-01 09:50:00  90 2022-01-01 10:00:00  140.0
→ Ссылка