Reputation: 1
I often find myself with a list of filters that I need to apply to a pandas dataframe. I apply each filter and do some calculations, but this often results in slow code. I would like to optimize the performance. I have created an example of my slow solution which filters a dataframe on a list of date ranges and calculate a sum of a column for the rows that match my date range, and then assign this value to the date matching the start of the date range:
import numpy as np
import pandas as pd
import datetime
def generateTestDataFrame(N=50, windowSizeInDays=5):
dd = {"AsOfDate" : [],
"WindowEndDate" : [],
"X" : []}
d = datetime.date.today()
for i in range(N):
dd["AsOfDate"].append(d)
dd["WindowEndDate"].append(d + datetime.timedelta(days=windowSizeInDays))
dd["X"].append(float(i))
d = d + datetime.timedelta(days=1)
newDf = pd.DataFrame(dd)
return newDf
def run():
numRows = 50
windowSizeInDays = 5
print "NumRows: %s" % (numRows)
print "WindowSizeInDays: %s" % (windowSizeInDays)
df = generateTestDataFrame(numRows, windowSizeInDays)
newAggColumnName = "SumOverNdays"
df[newAggColumnName] = np.nan # Initialize the column to nan
for i in range(df.shape[0]):
row_i = df.iloc[i]
startDate = row_i["AsOfDate"]
endDate = row_i["WindowEndDate"]
sumAggOverNdays = df.loc[ (df["AsOfDate"] >= startDate) & (df["AsOfDate"] < endDate) ]["X"].sum()
df.loc[df["AsOfDate"] == startDate, newAggColumnName] = sumAggOverNdays
print df.head(10)
if __name__ == "__main__":
run()
This produces the following output:
NumRows: 50
WindowSizeInDays: 5
AsOfDate WindowEndDate X SumOverNdays
0 2019-01-15 2019-01-20 0.0 10.0
1 2019-01-16 2019-01-21 1.0 15.0
2 2019-01-17 2019-01-22 2.0 20.0
3 2019-01-18 2019-01-23 3.0 25.0
4 2019-01-19 2019-01-24 4.0 30.0
5 2019-01-20 2019-01-25 5.0 35.0
6 2019-01-21 2019-01-26 6.0 40.0
7 2019-01-22 2019-01-27 7.0 45.0
8 2019-01-23 2019-01-28 8.0 50.0
9 2019-01-24 2019-01-29 9.0 55.0
Upvotes: 0
Views: 1919
Reputation: 854
Try using pandas.DataFrame.apply() for calculations.
doc: https://pandas.pydata.org/pandas-docs/stable/generated/pandas.DataFrame.apply.html
Using your code:
%%timeit
run()
205 ms ± 33.7 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
Adapting:
%%timeit
windowSizeInDays = 5
rows = 50
df_ = pd.DataFrame(index=range(rows),columns=['AsOfDate','WindowEndDate','X','SumOverNdays'])
asofdate = [datetime.date.today() + datetime.timedelta(days=i) for i in range(rows)]
windowenddate = [i + datetime.timedelta(days=windowSizeInDays) for i in asofdate]
df_['AsOfDate'] = asofdate
df_['WindowEndDate'] = windowenddate
df_['X'] = np.arange(float(df_.shape[0]))
df_['SumOverNdays'] = df_.apply(lambda x: df_.loc[ (df_["AsOfDate"] >= x['AsOfDate']) & (df_["AsOfDate"] < x['WindowEndDate']) ]["X"].sum(), axis=1)
df_
112 ms ± 3.69 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
Not a BIG difference but in this particular example we can't do better than that...
Upvotes: 1