I have a datetime instance with dates ( dfDates
):
2017-03-01 00:00
2017-03-02 00:00
2017-03-04 00:00
...
For the last day (here: 2017-03-04
) I calculate the previous day/month/year/etc. as follows:
def previous_day(dtToday):
return dtToday - pd.DateOffset(days=1)
This returns 2017-03-03
. However, this business day is not available in my range of dates ( dfDates
).
I am therefore looking for a robust way to find the date that is the closest to the previous day/month/year/etc.. In this case for the previous day it should return 2017-03-02
.
Note, I understand that you can do something like index -1
to get the previous day. It becomes however complicated when taking the previous month (there are not always 30
days in a month) and even the previous year (there are not always 252
working days in a year). Is there therefore a robust method to get the closest available date?
Update
I understand also that you can use timedelta as follows:
from datetime import datetime, timedelta
d = datetime.today() - timedelta(days=days_to_subtract)
However, how does that relate to dtToday
and how can I link it with dfDates
? dtToday
in my case is not always datetime.today()
. Sometimes its a random date.
Not the most efficient, but you can try:
# From your function
day_minus_one = previous_day(dtToday)
# Return LAST element in INDEX of DF FROM START TO DAY_MINUS_ONE
actual = df.loc[:day_minus_one].index[-1]
This essentially returns you the last index of a copy of your df, up to and including day_minus_one, if any of the dates exist . This should give you the closest date, or the date itself.
You can try:
# Returns LAST element of INDEX of df from DAY_MINUS_ONE_HUNDRED to DAY_MINUS_ONE
actual_better = df.loc[day_minus_one_hundred:day_minus_one].index[-1]
To only look back one_hundred days from minus_one if your dataset is huge, so you don't have to return a huge array just to find one date.
If I understand correctly, you don't want to actually subtract 1 day, you want to get the previous available day from the list of available dates. If that's the case, then consider this :
available_dates = [
2017-03-01 00:00,
2017-03-02 00:00,
2017-03-04 00:00,
...
]
def previous_day(dtToday):
today_index = available_dates.index(dtToday)
return available_dates[today_index-1]
This assumes, of course, that your available_dates
is sorted
EDIT:
If you want to be able to subtract month and years, then something a little bit more complex is needed :
# Sorted
available_dates = [
2017-03-01 00:00,
2017-03-02 00:00,
2017-03-04 00:00,
...
]
subtract_from_date(date, day=None, month=None, year=None):
# check if it's day/month/yeah
...
# do the actual subtraction and store it in substracted_date
...
# get the closest date
for index, date in enumerate(available_dates):
if date > substracted_date:
return available_dates[index-1]
I solved it like this:
dtToday
= the reference date dtDates
= a datetime sequence of the available dates nbOffset
= the number of days/months/years we want to go back Code:
def previous_day(dtToday, dtDates, nbOffset):
prevx = dtToday - pd.DateOffset(days=nbOffset)
return test_day_in(prevx, dtDates)
def previous_month(dtToday, dtDates, nbOffset):
prevx = dtToday - pd.DateOffset(months=nbOffset)
return test_day_in(prevx, dtDates)
def previous_year(dtToday, dtDates, nbOffset):
prevx = dtToday - pd.DateOffset(years=nbOffset)
return test_day_in(prevx, dtDates)
def test_day_in(dtTest, dtDates):
if dtTest in dtDates:
return dtTest
else:
return tryNextDay(dtTest, dtDates)
def tryNextDay(dtTest, dtDates):
# if not outside the bound
if (dtTest < dtDates.min()):
return dtDates.min()
# check if next day exist
if (dtTest + pd.DateOffset(days=1) <= dtDates.max()):
return previous_day(dtTest + pd.DateOffset(days=2), dtDates, 1) # 2-1
else:
print('warning, tryNextDay triggered')
# should not be triggered, it should take by default the dtDates.min() if far outside range
return dtTest
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.