[Python] Improve pandas merge performance with one large dataframe

Discussão em 'Python' iniciado por Stack, Janeiro 22, 2025.

  1. Stack

    Stack Membro Participativo

    I have two dataframe df_X and df_Y, and I want to merge these two (df_X has 1000 rows) with merge (how='inner'), everything is fine when df_Y has 1000 rows but it is very slow with big dataframe (expected: 40_000_000 rows).

    I would like to speed up the calculations. I tried to use df1.join(df2) and sort them before the merge but with poor results. Any ideas to improve the code ?

    NB : I am working with Linux or MacOS computers / python 3.12

    Here is reproductible example of my attempts :

    import pandas as pd
    import numpy as np
    import time

    match_columns = ['a', 'b', 'c', 'd', 'e']
    extra_columns_X = ['X' + str(i) for i in range(9)]
    extra_columns_Y = ['Y' + str(i) for i in range(9)]

    # Define function to generate synthetic dataframes
    def generate_synthetic_data():
    # Generate df1 with coarse resolution
    n_rows = 1000 # Desired number of rows
    df_X = pd.DataFrame({
    match_columns[0]: np.linspace(0, 10000, n_rows), # Finer steps
    match_columns[1]: np.linspace(0, 5000, n_rows),
    match_columns[2]: np.linspace(10, 11000, n_rows),
    match_columns[3]: np.linspace(20, 12000, n_rows),
    match_columns[4]: np.linspace(30, 13000, n_rows),
    })
    # Add some extra columns to df1
    for col in extra_columns_X:
    df_X[col] = np.random.uniform(0, 1, len(df_X)) # Random values

    df_X = df_X.astype('float32')
    df_X = np.around(df_X, 2)

    # Generate df2 with finer resolution for match columns
    n_rows = 40000000 # Desired number of rows
    df_Y = pd.DataFrame({
    match_columns[0]: np.linspace(0, 10000, n_rows), # Finer steps
    match_columns[1]: np.linspace(0, 5000, n_rows),
    match_columns[2]: np.linspace(10, 11000, n_rows),
    match_columns[3]: np.linspace(20, 12000, n_rows),
    match_columns[4]: np.linspace(30, 13000, n_rows),
    })

    # This enforce common values in match_columns
    interval = n_rows // len(df_X)
    for i, col in enumerate(match_columns):
    df_Y.iloc[::interval, i] = df_X[col].values # Assign df_X values at regular intervals

    # Add some extra columns to df2
    for col in extra_columns_Y:
    df_Y[col] = np.random.uniform(0, 1, len(df_Y)) # Random values

    df_Y = df_Y.astype('float32')
    df_Y = np.around(df_Y, 2)

    return df_X, df_Y

    # Generate the dataframes
    df_X, df_Y = generate_synthetic_data()

    # Timing for pd.merge
    start_time = time.time()
    merged_with_merge = pd.merge(df_X, df_Y, on=match_columns, how='inner')
    print("Merge Time:", time.time() - start_time)

    # # Timing for pd.merge
    # start_time = time.time()
    # df_X.sort_values(match_columns, inplace=True)
    # df_Y.sort_values(match_columns, inplace=True)
    # merged_with_merge = pd.merge(df_X, df_Y, on=match_columns, how='inner')
    # print("Merge Time if sorted:", time.time() - start_time)

    # Timing with dask
    import dask.dataframe as dd
    start_time = time.time()
    ddf_X = dd.from_pandas(df_X, npartitions=10)
    ddf_Y = dd.from_pandas(df_Y, npartitions=10)
    merged_with_dask = dd.merge(ddf_X, ddf_Y, on=match_columns, how='inner').compute()
    print("Merge Time with dask:", time.time() - start_time)

    # Timing for set_index + join
    start_time = time.time()
    df_X.sort_values(match_columns, inplace=True)
    df_Y.sort_values(match_columns, inplace=True)
    df_X_indexed = df_X.set_index(match_columns)
    df_Y_indexed = df_Y.set_index(match_columns)
    merged_with_join = df_X_indexed.join(df_Y_indexed, how='inner').reset_index()
    print("Join Time if sorted:", time.time() - start_time)

    # Results
    print("Rows Merged with Merge:", len(merged_with_merge))
    print("Rows Merged with Join:", len(merged_with_join))

    Continue reading...

Compartilhe esta Página