Search code examples
pythonpandasoptimizationquantilequartile

Optimising quartiling of columns of panda dataframe?


I have multiple columns in a data frame that have numerical data. I want to quartile each column, changing each value to either q1, q2, q3 or q4.

I currently loop through each column and change them using the pandas qcut function:

for column_name in df.columns:
    df[column_name] = pd.qcut(df[column_name].astype('float'), 4, ['q1','q2','q3','q4'])

This is very slow! Is there a faster way to do this?


Solution

  • Played around with the the following example a little. Looks like converting to float from a string is increasing the time. Though a working example was not provided, so the original type can't be known. df[column].astype(copy=) appears to be performant if copying or not. Not much else to go after.

    import pandas as pd
    import numpy as np
    import random
    import time
    
    random.seed(2)
    
    indexes = [i for i in range(1,10000) for _ in range(10)]
    df = pd.DataFrame({'A': indexes, 'B': [str(random.randint(1,99)) for e in indexes], 'C':[str(random.randint(1,99)) for e in indexes], 'D':[str(random.randint(1,99)) for e in indexes]})
    #df = pd.DataFrame({'A': indexes, 'B': [random.randint(1,99) for e in indexes], 'C':[random.randint(1,99) for e in indexes], 'D':[random.randint(1,99) for e in indexes]})
    
    df_result = pd.DataFrame({'A': indexes, 'B': [random.randint(1,99) for e in indexes], 'C':[random.randint(1,99) for e in indexes], 'D':[random.randint(1,99) for e in indexes]})
    
    def qcut(copy, x):
        for i, column_name in enumerate(df.columns):
            s = pd.qcut(df[column_name].astype('float', copy=copy), 4, ['q1','q2','q3','q4'])
            df_result["col %d %d"%(x, i)] = s.values
    
    times = []
    for x in range(0,10):
        a = time.clock()
        qcut(True, x)
        b = time.clock()
        times.append(b-a)
    
    print np.mean(times)
    
    for x in range(10, 20):
        a = time.clock()
        qcut(False, x)
        b = time.clock()
        times.append(b-a)
    print np.mean(times)