Tengo dos marcos de datos:
df_A = pd.DataFrame({'start_date':['2021-04-01 00:00:00','2021-05-01 00:00:00', '2021-05-02 00:00:00'],'end_date':['2021-04-01 00:11:00','2021-05-01 00:06:00', '2021-05-03 00:00:00'], 'eventname':['birthday', 'wedding', 'birthday'] }) df_B = pd.DataFrame({'event_date':['2021-04-01 00:06:00','2021-05-01 00:03:00', '2021-05-04 00:00:00'],'price':[100,200,500]}) df_A['end_date'] = pd.to_datetime(df_A.end_date) df_A['start_date'] = pd.to_datetime(df_A.start_date) df_B['event_date'] = pd.to_datetime(df_B.event_date) df_A start_date end_date eventname 0 2021-04-01 2021-04-01 00:11:00 birthday 1 2021-05-01 2021-05-01 00:06:00 wedding 2 2021-05-02 2021-05-03 00:00:00 birthday df_B event_date price 0 2021-04-01 00:06:00 100 1 2021-05-01 00:03:00 200 2 2021-05-04 00:00:00 500 Quiero unirme a ellos de manera que la columna event_date se encuentre entre la columna start_date y end_date de df_A. Este es mi código:
df_A.index = pd.IntervalIndex.from_arrays(df_A['start_date'],df_A['end_date'],closed='both') df_B['start_date'] = df_B['event_date'].apply(lambda x : df_A.iloc[df_A.index.get_loc(x)]['start_date']) df_B Sin embargo, recibo un error de clave ya que algunos de los valores en la columna event_date de df_B no se encuentran entre ninguna de las columnas start_date y end_date de df_A.
Este es mi resultado esperado:
event_date price start_date 0 2021-04-01 00:06:00 100 2021-04-01 1 2021-05-01 00:03:00 200 2021-05-01 2 2021-05-04 00:00:00 500 NaNIntenté usar la intersección, pero eso solo funciona si los valores son exactamente iguales. ¿Cómo hago esto?
Si su start_date y end_date no se superponen, cree un índice de intervalo y combine sus dos marcos de datos:
bins = pd.IntervalIndex.from_arrays(df_A['start_date'], df_A['end_date'], closed='both') out = df_B.assign(interval=pd.cut(df_B['event_date'], bins)) \ .merge(df_A.assign(interval=bins), on='interval', how='left') print(out[['event_date', 'price', 'start_date']]) # Output: event_date price start_date 0 2021-04-01 00:06:00 100 2021-04-01 1 2021-05-01 00:03:00 200 2021-05-01 2 2021-05-04 00:00:00 500 NaTSi puede evitar una combinación no equitativa, hágalo; use una técnica de agrupamiento, similar a la excelente respuesta de @Corralien, donde los contenedores no se superponen (más rápido/más eficiente). Otra opción, si es posible, es encontrar una manera de introducir uniones de fusión, y tal vez unir su resultado final -> los pasos pueden ser más largos, sin embargo, creo que si se hace bien, sería más eficiente que una unión no equi. Sin embargo, si solo le interesa la primera coincidencia, pd.merge_asof debería ser suficiente.
Volviendo a la pregunta:
Una opción es a través de conditional_join de pyjanitor :
# pip install pyjanitor import pandas as pd import janitor (df_A.conditional_join( df_B, # column from left frame, column from right frame, comparision operator ('start_date', 'event_date', '<='), ('end_date', 'event_date', '>='), how = 'right') .filter(['event_date', 'price', 'start_date']) ) event_date price start_date 0 2021-04-01 00:06:00 100 2021-04-01 1 2021-05-01 00:03:00 200 2021-05-01 2 2021-05-04 00:00:00 500 NaT Lo que hace es comparar columnas de df_A con df_B :
('start_date', 'event_date', '<=') significa verificar si start_date de df_A es <= event_date de df_B . mismo concepto se aplica a las otras condiciones. El parámetro aquí es argumentos variables, lo que significa que puede pasar tantas condiciones como sea posible. Las columnas deben ser del mismo tipo (flotante frente a flotante, entero frente a entero, fecha frente a fecha, ...).
Debajo del capó, utiliza la búsqueda binaria para uniones no equitativas (principalmente para evitar una unión cartesiana, que puede ser ineficiente en términos de memoria a medida que crece el tamaño del marco de datos); para uniones de rango como en este caso, utiliza una búsqueda simple para reducir el espacio de búsqueda para obtener las filas coincidentes.
La función puede manejar índices superpuestos, así como escenarios en los que la combinación de rango involucra dos columnas diferentes -> a < b and c > d .
Puedes echar un vistazo a más ejemplos aquí