Unión de marcos de datos de alto rendimiento en Python

Resuelto Pijush asked hace 8 meses • 0 respuestas

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.

Pijush avatar Feb 16 '24 14:02 Pijush
Aceptado

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_datauna 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 eventscolumna 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
...
Teemu Risikko avatar Feb 16 '2024 10:02 Teemu Risikko