Podría usar algo de ayuda para acelerar este bloque de código. Supongo que las operaciones concat son lo que está ralentizando el código, pero no estoy seguro. Este bucle tarda aproximadamente 20 veces el tiempo en ejecutar el resto del código en mi función.

df = pd.DataFrame()
for index, row in p_data_df.iterrows():
    test_df = log_df.loc[row['Mid-C']].to_frame().transpose()
    if 'S' not in test_df.columns:
        test_df.insert(0, 'S', row.loc['S'])
        test_df.insert(1, 'C #', row.loc['C #'])
        test_df.insert(2, 'Num', row.loc['Num'])

    df = pd.concat([df, test_df], axis=0)
-1
John 2 oct. 2019 a las 19:20

3 respuestas

La mejor respuesta

Nunca llame a pd.concat dentro de un ciclo for. Conduce a una copia cuadrática: concat devuelve un nuevo DataFrame. Se debe asignar espacio para el nuevo DataFrame, y los datos de los antiguos DataFrames se deben copiar en el nuevo DataFrame.

Entonces, con su marco de datos teniendo N filas, tendría O (N ^ 2) copias necesarias para completar el ciclo.

Use una lista de diccionarios o una lista de listas en lugar del marco de datos para acumular los resultados, y fuera del ciclo for, cree su marco de datos con la lista de resultados. De esta forma ahorrará mucho tiempo de ejecución, pandas no se hace para esto.

Así es como puedes hacerlo:

list_res = []
for index, row in p_data_df.iterrows():
    test_df = log_df.loc[row['Mid-C']].to_frame().transpose()
    if 'S' not in test_df.columns:
        test_df.insert(0, 'S', row.loc['S'])
        test_df.insert(1, 'C #', row.loc['C #'])
        test_df.insert(2, 'Num', row.loc['Num'])
    list_res.append(test_df)

df = pd.concat(list_res, axis=0)

Más consejos para acelerar tu código

iterrows es el método más lento posible para iterar el marco de datos, ya que cada línea debe transformarse en una serie. Si usa itertuples esto funciona no sucede. Puede usar itertuples sin cambiar demasiado el código pero ganando rendimiento.

Existen otros métodos (vectorización, función de aplicación, Cython ...), que requerirían una modificación un poco más amplia de su código pero que le permitirían tener un código más eficiente. Os dejo este enlace para un poco más de información.

2
Massifox 2 oct. 2019 a las 17:07

El concat como se usa está dando como resultado una copia cuadrática. Cada vez que agrega una fila, esta operación devuelve una copia del marco de datos anterior más una nueva fila. Un mejor enfoque es mantener una lista de todas las filas y luego hacer una concatenación final al final.

df = []
for index, row in p_data_df.iterrows():
    test_df = log_df.loc[row['Mid-C']].to_frame().transpose()
    if 'S' not in test_df.columns:
        test_df.insert(0, 'S', row.loc['S'])
        test_df.insert(1, 'C #', row.loc['C #'])
        test_df.insert(2, 'Num', row.loc['Num'])
    df.append(test_df)
df = pd.concat(df, axis=0)
1
Alexander 2 oct. 2019 a las 16:30

Pude llegar a esto, que parece ser al menos un orden de magnitud más rápido:

df = pd.concat([p_data_df.loc[:, ['S', 'C #', 'Num']], log_df[log_df['MD'].isin(p_data_df['Mid-C'].values)]], sort=False, axis=1)
1
John 2 oct. 2019 a las 18:10
58205497