Last active
April 13, 2021 17:02
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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