Unión de marcos de datos de alto rendimiento en Python
Tengo dos marcos de datos, uno tiene datos de inicio y fecha de finalización, el segundo datos tiene fecha justa. Básicamente, un marco tiene datos de grupo y otro tiene datos secundarios. Por eso quiero unir todas las fechas que se encuentran entre la fecha de inicio y la fecha de finalización. A continuación se muestra el código que estoy probando, pero es comprensible que lleve bastante tiempo ya que tengo una gran cantidad de datos.
def is_between_dates(date, start, end):
return start <= date <= end
result_df = pd.DataFrame()
for idx, row in alarm_data.iterrows():
mask = event_data['Date'].apply(lambda x: is_between_dates(x, row['AlarmStart'], row['AlarmEnd']))
temp_df2 = event_data[mask].copy()
temp_df2['AlarmStart'] = row['AlarmStart']
temp_df2['AlarmEnd'] = row['AlarmEnd']
result_df = pd.concat([result_df, temp_df2])
Tengo como 2 millones de datos en el marco de datos del evento y 200000 datos en los datos de alarma que se esperan. No quiero utilizar Spark para lograr esto, ya que creará una carga en la infraestructura.
Alguna dirección será útil.
Una solución
Estás realizando operaciones bastante pesadas en cada fila, por lo que no es de extrañar que lleve tiempo.
Si ha entendido su pregunta correctamente, aquí tiene una solución que es un tercio más rápida aproximadamente.
alarm_data["events"] = alarm_data.apply(lambda row: event_data[(row['AlarmStart'] <= event_data['Date']) & (event_data['Date'] <= row['AlarmEnd'])].index.to_list(), axis=1)
result_df = alarm_data.explode("events").join(event_data, on="events")
Explicación
Usé esto como datos de muestra:
alarm_data = pd.DataFrame(
{
"AlarmStart": [1, 3, 5, 2, 5, 2, 1],
"AlarmEnd": [3, 4, 6, 4, 6, 3, 2]
}
)
event_data = pd.DataFrame(
{
"Date": [1, 2, 3, 4, 5, 6, 7],
"Event": ["A", "B", "C", "D", "E", "F", "G"],
}
)
Esta solución aplica el filtro directamente en cada fila de alarm_data sin un bucle for. Deshacerse de la segunda solicitud de event_data también ayuda.
Esta aplicación alarm_data.apply(lambda row:...
obtendrá para cada fila en los alarm_data
índices de eventos correspondientes event_data
una lista. El paso se verá así:
AlarmStart AlarmEnd events
0 1 3 [0, 1, 2]
1 3 4 [2, 3]
2 5 6 [4, 5]
...
A continuación necesitamos explotar los eventos en nuevas filas con .explode()
:
AlarmStart AlarmEnd events
0 1 3 0
0 1 3 1
0 1 3 2
1 3 4 2
...
Y finalmente une el resto de event_data al marco de datos usando la events
columna como clave de unión:
.join(event_data, on="events")
El resultado se verá así:
AlarmStart AlarmEnd events Date Event
0 1 3 0 1 A
0 1 3 1 2 B
0 1 3 2 3 C
1 3 4 2 3 C
...