Skip to content

Instantly share code, notes, and snippets.

@kallewesterling
Last active April 13, 2021 17:02
Show Gist options
  • Save kallewesterling/9a8d12ce073776ed52865bfb362ad073 to your computer and use it in GitHub Desktop.
Save kallewesterling/9a8d12ce073776ed52865bfb362ad073 to your computer and use it in GitHub Desktop.
Make a list of list where dates are chained together based on their delta
def periodize_dates(dates:list=[], delta:datetime.timedelta=timedelta(days=14), dateformat='%Y-%m-%d'):
"""
Takes a list of dates and returns a list of lists, where the original list has been divided into strings of dates that are within the provided delta.
Parameters:
dates (list): List of dates that you want periodized.
delta (datetime.timedelta/int): The delta that you want compared. If provided as type `int`, the value will be interpreted as `timedelta.days=<int>`.
dateformat (str): A date formatting string that all dates in the list will need to adhere to.
Returns:
list:Periodized dates
"""
try:
dates = sorted([datetime.datetime.strptime(x, dateformat) for x in dates])
except ValueError as e:
date = re.search(r'''['"](.*)['"] does not match format''', str(e))
if date:
date = date.groups()[0]
raise ValueError(f'A date found in list that did not adhere to format (`{date}`). Needs to follow format `{dateformat}`.') from None
if isinstance(delta, int):
delta = timedelta(days=delta)
periods = []
for ix, date in enumerate(dates):
min_date = date - delta
max_date = date + delta
prev_date, next_date = None, None
start_chain, end_chain, in_chain, solo_date = None, None, None, None
prev_date_in_range, next_date_in_range = None, None
try:
if ix-1 >= 0:
prev_date = dates[ix-1]
except IndexError:
prev_date = None
try:
next_date = dates[ix+1]
except IndexError:
next_date = None
if next_date:
next_date_in_range = next_date >= min_date and next_date <= max_date
if prev_date:
prev_date_in_range = prev_date >= min_date and prev_date <= max_date
if all([next_date, prev_date, prev_date_in_range, next_date_in_range]):
# In the loop and in a chain (near previous date and next)
in_chain = True
elif all([next_date, prev_date, next_date_in_range]) and not prev_date_in_range:
# In the loop and beginning of a chain (not near previous date but near next)
start_chain = True
elif all([next_date, prev_date, prev_date_in_range]) and not next_date_in_range:
# In the loop and end of a chain (near previous date but not next)
end_chain = True
elif all([next_date, prev_date]) and not all([prev_date_in_range, next_date_in_range]):
# In the loop but solo date (not not near previous date nor next)
solo_date = True
elif next_date and next_date_in_range:
# In the loop but solo date (not not near previous date nor next)
start_chain = True
elif next_date:
solo_date = True
elif prev_date and prev_date_in_range:
end_chain = True
elif prev_date:
solo_date = True
elif not next_date and not prev_date:
solo_date = True
else:
raise RuntimeError('An unexpected error occurred.')
if start_chain:
periods.append([date])
elif end_chain:
periods[len(periods)-1].append(date)
elif solo_date:
periods.append([date])
elif in_chain:
periods[len(periods)-1].append(date)
return periods
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment