I have rewritten my python code in cython in order to speed up it quite a bit. However after checking the code in jupyter it seems that part of it is still compiled as python code, therefore its not being sped up enough.
My function is pretty basic, it gets start date, end date and creates array of additional dates based on that and some other conditions.
The thing is I’m a bit clueless how could I change the highlighted yellow part of code of the date_range_np
function as seen on the attached imaged. Because I’m already using numpy arrays so I thought it would be compiled as cython code. There is also one loop that I think is slowing down the function but so far I wasn’t able to come up with any replacement that sginificatly sped up the function. Actually speed is very important as those functions are executed thousands of times.
Any ideas how could I refactor it to speed it up?
Here is my code:
%%cython -a
import numpy as np
cimport numpy as np
from datetime import datetime
cpdef np.int64_t get_days(np.int64_t year, np.int64_t month):
cdef np.ndarray months=np.array([31,28,31,30,31,30,31,31,30,31,30,31])
if month==2:
if (year%4==0 and year%100!=0) or (year%400==0):
return 29
return months[month-1]
cpdef np.ndarray[np.int64_t] date_range_np(np.int64_t start, np.int64_t end, char* freq):
cdef np.ndarray res
cdef np.int64_t m_start
cdef np.int64_t m_end
if freq.decode("utf-8")[len(freq)-1] == "M":
m_start = np.int64(start).astype('M8[D]').astype('M8[M]').view("int64")
m_end = np.int64(end).astype('M8[D]').astype('M8[M]').view("int64")
res = np.arange(m_start, m_end-2, np.int64(freq[:(len(freq)-1)])).view("M8[M]").astype("M8[D]").view("int64")
return np.array([np.min([x+datetime.fromtimestamp(start*24*60*60).day-1, x+get_days(datetime.fromtimestamp(start*24*60*60).year,datetime.fromtimestamp(start*24*60*60).month)]) for x in res])
elif freq.decode("utf-8")[len(freq)-1] == "D":
return np.arange(start, end-2, np.int64(freq[:(len(freq)-1)]))
cpdef np.ndarray[np.int64_t] loanDates(np.int64_t startDate,np.int64_t endDate,np.int64_t freq):
# if frequency indicates repayment every n months
if int(12 / freq) == 12 / freq:
# Generate date range and add offset depending on starting day
#print(date_range_np(start=startDate, end=endDate, freq=(str(-int(12 / freq)) + "M").encode("utf8")))
ts = date_range_np(start=startDate, end=endDate, freq=(str(-int(12 / freq)) + "M").encode("utf8"))
else:
ts = date_range_np(start=startDate, end=endDate, freq=(str(-int(365 / freq)) + "D").enocode("utf8"))
#print(ts)
if ts.shape[0] == 0:
return np.array([])
elif ts.shape[0] >= 1 and ts[0] > startDate:
ts = np.delete(arr=ts, obj=0)
if ts[ts.shape[0]-1] < endDate:
ts = np.delete(arr=ts, obj=-1)
if ts[0] != startDate:
ts = np.insert(ts,0,startDate)
# If no dates generated (start date>end date)
ts = ts
# If last date generated is not end date add it
return ts.astype('int64')
And the function highlighted:
If you want to test the functions you can try:
%timeit date_range_np(20809,17986, b"-1M")
I get about 1ms for that function.
pandas.date_range
? \$\endgroup\$