In my Python script I have a Pandas DataFrame with about 5.2 million rows and 26 columns.
The current analysis I am running tests 1.5 million different subsets/filters against this DataFrame, using multiple conditions (combinations of the columns) to count the total number of occurrences. I've tried multiple methods and improved the speed a bit from my original code, but I believe this could be done faster.
Heres the code with a dummy DataFrame (5.2M rows and 26 columns A-Z) and one example of the subset method I initially used, with multiple conditions:
import pandas as pd
import numpy as np
import time
np.random.seed(101)
df = pd.DataFrame(np.random.randint(0,11,size=(5200000, 26)), columns=list('ABCDEFGHIJKLMNOPQRSTUVWXYZ'))
cond1 = (df.A > 5)
cond2 = (df.B.shift(1).between(8,10,inclusive=True))
cond3 = (df.C.shift(1) < 5)
cond4 = (df.D.shift(1) == 1)
cond5 = (df.E.shift(1).between(2,5,inclusive=True))
start = time.time()
subsetLength = len(df.loc[cond1 & cond2 & cond3 & cond4 & cond5])
stop = time.time()
print("Length:",subsetLength)
print("Time:", stop - start)
The time to retrieve the length of this subset is around 0.030s on my laptop (Macbook Pro 2020 Intel) for each subset/filter:
Length: 9781
Time: 0.029639244079589844
Note: I am using .shift(1) to check previous rows for the columns B - E and for column A this is not the case.
Now 0.030s might seem quite fast, but since I am running 1.5 million of those subsets, the script currently took about 12-13 hours. Therefor, every speed increase I can get at an individual subset/filter can be a huge time saver.
Instead of .loc[] I have also tried .shape[0] and .sum() like this:
.shape[0]
subsetLength = df[cond1 & cond2 & cond3 & cond4 & cond5].shape[0]
This seems to be a bit faster but still was in the range of 0.027s - 0.028s.
.sum()
subsetLength = (cond1 & cond2 & cond3 & cond4 & cond5).sum()
This was around 0.021s, already quite a nice improvement compared to the .loc[] which could save around 4 hours. But still the total would take a lot of hours (around 8 or 9).
np.where()
Finally I tried to use np.where(), for this I had to change the conditions a bit:
cond1 = (df['A'] > 5)
cond2 = (df['B'].shift(1).between(8,10,inclusive=True))
cond3 = (df['C'].shift(1) < 5)
cond4 = (df['D'].shift(1) == 1)
cond5 = (df['E'].shift(1).between(2,5,inclusive=True))
subsetLength = len(np.where(cond1 & cond2 & cond3 & cond4 & cond5)[0])
This took around 0.0195s and was the fastest method I got thus far.
Preferably I am looking for performance in the 0.002s or faster range so the script would take around 1 hour to finish, but I can't tell if this is possible. Any advice on how the speed of this subsetting/filtering a big DataFrame for counts can be improved? Am I perhaps using slow ways to do counts for subsets with multiple conditions? Or is this kind of analysis just heavy and is the time it takes realistic.