¿Cómo unir dos marcos de datos para los cuales los valores de columna están dentro de un cierto rango?

Dados dos marcos de datos df_1 y df_2 , cómo unirlos de manera que la columna de fecha y hora df_1 esté entre el start y el end en el dataframe df_2 :

 print df_1 timestamp AB 0 2016-05-14 10:54:33 0.020228 0.026572 1 2016-05-14 10:54:34 0.057780 0.175499 2 2016-05-14 10:54:35 0.098808 0.620986 3 2016-05-14 10:54:36 0.158789 1.014819 4 2016-05-14 10:54:39 0.038129 2.384590 print df_2 start end event 0 2016-05-14 10:54:31 2016-05-14 10:54:33 E1 1 2016-05-14 10:54:34 2016-05-14 10:54:37 E2 2 2016-05-14 10:54:38 2016-05-14 10:54:42 E3 

Obtenga el event correspondiente donde df1.timestamp está entre df_2.start y df2.end

  timestamp AB event 0 2016-05-14 10:54:33 0.020228 0.026572 E1 1 2016-05-14 10:54:34 0.057780 0.175499 E2 2 2016-05-14 10:54:35 0.098808 0.620986 E2 3 2016-05-14 10:54:36 0.158789 1.014819 E2 4 2016-05-14 10:54:39 0.038129 2.384590 E3 

Una solución simple es crear un interval index desde el start and end . closed = both luego usar get_loc para obtener el evento, es decir (Espere que todas las fechas estén en el tipo de fecha y hora)

 df_2.index = pd.IntervalIndex.from_arrays(df_2['start'],df_2['end'],closed='both') df_1['event'] = df_1['timestamp'].apply(lambda x : df_2.iloc[df_2.index.get_loc(x)]['event']) 

Salida:

             evento de fecha y hora AB
 0 2016-05-14 10:54:33 0.020228 0.026572 E1
 1 2016-05-14 10:54:34 0.057780 0.175499 E2
 2 2016-05-14 10:54:35 0.098808 0.620986 E2
 3 2016-05-14 10:54:36 0.158789 1.014819 E2
 4 2016-05-14 10:54:39 ​​0.038129 2.384590 E3
 idx = pd.IntervalIndex.from_arrays(df_2['start'], df_2['end'], closed='both') event = df_2.loc[idx.get_indexer(df_1.timestamp), 'event'] event 0 E1 1 E2 1 E2 1 E2 2 E3 Name: event, dtype: object df_1['event'] = event.values df_1 timestamp AB event 0 2016-05-14 10:54:33 0.020228 0.026572 E1 1 2016-05-14 10:54:34 0.057780 0.175499 E2 2 2016-05-14 10:54:35 0.098808 0.620986 E2 3 2016-05-14 10:54:36 0.158789 1.014819 E2 4 2016-05-14 10:54:39 0.038129 2.384590 E3 

Referencia: Una pregunta en IntervalIndex.get_indexer.

Opción 1

 idx = pd.IntervalIndex.from_arrays(df_2['start'], df_2['end'], closed='both') df_2.index=idx df_1['event']=df_2.loc[df_1.timestamp,'event'].values 

opcion 2

 df_2['timestamp']=df_2['end'] pd.merge_asof(df_1,df_2[['timestamp','event']],on='timestamp',direction ='forward',allow_exact_matches =True) Out[405]: timestamp AB event 0 2016-05-14 10:54:33 0.020228 0.026572 E1 1 2016-05-14 10:54:34 0.057780 0.175499 E2 2 2016-05-14 10:54:35 0.098808 0.620986 E2 3 2016-05-14 10:54:36 0.158789 1.014819 E2 4 2016-05-14 10:54:39 0.038129 2.384590 E3 

Puedes usar el módulo pandasql.

 import pandasql as ps sqlcode = ''' select df_1.timestamp ,df_1.A ,df_1.B ,df_2.event from df_1 inner join df_2 on d1.timestamp between df_2.start and df2.end ''' newdf = ps.sqldf(sqlcode,locals()) 

En este método, asumimos que los objetos TimeStamp se utilizan.

 df2 start end event 0 2016-05-14 10:54:31 2016-05-14 10:54:33 E1 1 2016-05-14 10:54:34 2016-05-14 10:54:37 E2 2 2016-05-14 10:54:38 2016-05-14 10:54:42 E3 event_num = len(df2.event) def get_event(t): event_idx = ((t >= df2.start) & (t <= df2.end)).dot(np.arange(event_num)) return df2.event[event_idx] df1["event"] = df1.timestamp.transform(get_event) 

Explicación de get_event

Para cada marca de tiempo en df1 , diga t0 = 2016-05-14 10:54:33 ,

(t0 >= df2.start) & (t0 <= df2.end) contendrá 1 verdadero. (Ver ejemplo 1). Luego, tome un producto de puntos con np.arange(event_num) para obtener el índice del evento al que pertenece t0 .

Ejemplos:

Ejemplo 1

  t0 >= df2.start t0 <= df2.end After & np.arange(3) 0 True True -> T 0 event_idx 1 False True -> F 1 -> 0 2 False True -> F 2 

Tome t2 = 2016-05-14 10:54:35 para otro ejemplo

  t2 >= df2.start t2 <= df2.end After & np.arange(3) 0 True False -> F 0 event_idx 1 True True -> T 1 -> 1 2 False True -> F 2 

Finalmente usamos la transform para transformar cada marca de tiempo en un evento.