Skip to content

Instantly share code, notes, and snippets.

@MatthewCaseres
Last active September 22, 2022 19:47
Show Gist options
  • Save MatthewCaseres/d338f85d768025623dcded0dd92e33ec to your computer and use it in GitHub Desktop.
Save MatthewCaseres/d338f85d768025623dcded0dd92e33ec to your computer and use it in GitHub Desktop.
Creating exposure intervals for policy/calendar intervals
### A Pluto.jl notebook ###
# v0.19.11
using Markdown
using InteractiveUtils
# ╔═╡ fb2b4952-3a91-11ed-116b-db769de7e1cc
begin
using Dates
end
# ╔═╡ 6d618467-eed2-4851-961e-e45ed4a455d0
md"""
When simultaneously partitioning by policy and calendar `Month`/`Years` we can solve this with a two-pointers approach where our pointers are -
* `next_calendar_partition`
* `next_anniversary_partition`
In each iteration we place the next partition, `min(next_calendar_partition, next_anniversary_partition)`, increment the partition that was placed according to its increment size, and move to the next time step.
This is the basic idea of the algorithm. We also have to handle starting/ending dates of the study, which we now define some utility functions for.
"""
# ╔═╡ 2a8585fb-c4f8-4635-a999-963f442cc997
begin
"""
Returns either the first anniversary strictly after the study start date, or the first anniversary up to and including the study start date.
"""
function get_maybe_first_anniversary(timestep::Month, anniv_date::Date, study_start::Date)::Date
return Date(year(study_start), month(study_start), day(anniv_date))
end
function get_maybe_first_anniversary(timestep::Year, anniv_date::Date, study_start::Date)::Date
return Date(year(study_start), month(anniv_date), day(anniv_date))
end
"""
get_first_anniversary_partition(timestep::Union{Year, Month}, anniv_date::Date, study_start::Date)::Date
The first anniversary strictly after the study start date
"""
function get_first_anniversary_partition(timestep::Union{Year, Month}, anniv_date::Date, study_start::Date)::Date
if anniv_date < study_start
maybe_first_anniversary = get_maybe_first_anniversary(timestep, anniv_date, study_start)
return maybe_first_anniversary < study_start ? maybe_first_anniversary + timestep : maybe_first_anniversary
else
return anniv_date + timestep
end
end
end
# ╔═╡ 48378996-32d7-45a8-b11f-616903d859e1
begin
@assert get_first_anniversary_partition(Year(1), Date(2012, 1, 4), Date(2017, 2, 2)) == Date(2018, 1, 4)
@assert get_first_anniversary_partition(Year(1), Date(2012, 7, 4), Date(2017, 2, 2)) == Date(2017, 7, 4)
@assert get_first_anniversary_partition(Month(1), Date(2012, 7, 4), Date(2017, 2, 2)) == Date(2017, 2, 4)
@assert get_first_anniversary_partition(Month(1), Date(2012, 1, 1), Date(2017, 2, 2)) == Date(2017, 3, 1)
end
# ╔═╡ a42f02bd-e33a-406e-b4c1-13b51e39b99c
"""
get_first_calendar_partition(timestep::Union{Year, Month}, anniv_date::Date, study_start::Date)::Date
The first calendar partition strictly after the study start date
"""
function get_first_calendar_partition(timestep::Union{Year, Month}, anniv_date::Date, study_start::Date)::Date
return ceil(max(study_start, anniv_date) + Day(1), timestep)
end
# ╔═╡ 66f45298-6d5e-48e8-81fb-4b77deb85836
begin
@assert get_first_calendar_partition(Month(1), Date(2018, 1, 31), Date(2018, 2, 28)) == Date(2018, 3, 1)
@assert get_first_calendar_partition(Month(1), Date(2018, 2, 1), Date(2018, 1, 1)) == Date(2018, 3, 1)
@assert get_first_calendar_partition(Year(1), Date(2018, 12, 31), Date(2019, 12, 31)) == Date(2020, 1, 1)
@assert get_first_calendar_partition(Year(1), Date(2019, 1, 1), Date(2018, 1, 1)) == Date(2020, 1, 1)
end
# ╔═╡ df60e747-e48f-4572-b699-14bba7a156f3
struct AnniversaryCalendar
pol_period::Union{Month,Year}
cal_period::Union{Month,Year}
end
# ╔═╡ 76aa4c7f-a412-41d1-ae98-c10c14606b0a
"""
Create `Vector{Date}` of ordered interval endpoints
"""
function create_partition_endpoints(basis::AnniversaryCalendar, anniv_date::Date, termination_date::Union{Date, Nothing}, study_start::Date, study_end::Date)::Vector{Date}
# Get the partition start and end
partition_start = max(anniv_date, study_start)
if isnothing(termination_date)
partition_end = study_end
else
partition_end = min(termination_date, study_end)
end
# Empty if no overlap, if single day, endpoints are beggining and end of day
if partition_start > partition_end
return Vector{Date}[]
elseif partition_start == partition_end
return [partition_start, partition_end] # A single day
end
# now partition_start < partition_end
# Get the next partitions
next_anniv_partition = get_first_anniversary_partition(basis.pol_period, anniv_date, study_start)
next_cal_partition = get_first_calendar_partition(basis.cal_period, anniv_date, study_start)
# Increment pointers until we go past `partition_end`
result = [partition_start]
while result[end] < partition_end
if next_anniv_partition < next_cal_partition
push!(result, next_anniv_partition)
next_anniv_partition += basis.pol_period
elseif next_cal_partition < next_anniv_partition
push!(result, next_cal_partition)
next_cal_partition += basis.cal_period
else # next_cal_partition == next_anniv_partition
push!(result, next_anniv_partition)
next_anniv_partition += basis.pol_period
next_cal_partition += basis.cal_period
end
end
# clip to partition_end
result[end] = partition_end
return result
end
# ╔═╡ a176cd07-0a14-4916-95f4-bdc77be1a1a9
begin
# If policy not in study, return empty array
@assert create_partition_endpoints(
AnniversaryCalendar(Month(1), Year(1)), # AnniversaryCalendar
Date(2010, 5, 7), # anniv_date
Date(2012, 12, 31), # termination_date
Date(2013, 1, 1), # study_start
Date(2014, 3, 10) # study_end
) == []
# if termination_date == study_start, return [study_start, termination_date]
@assert create_partition_endpoints(
AnniversaryCalendar(Month(1), Year(1)), # AnniversaryCalendar
Date(2010, 5, 7), # anniv_date
Date(2013, 1, 1), # termination_date
Date(2013, 1, 1), # study_start
Date(2014, 3, 10) # study_end
) == [Date(2013, 1, 1), Date(2013, 1, 1)]
# termination_date == nothing
@assert create_partition_endpoints(
AnniversaryCalendar(Year(1), Year(1)), # AnniversaryCalendar
Date(2010, 5, 7), # anniv_date
nothing, # termination_date
Date(2013, 1, 1), # study_start
Date(2014, 3, 10) # study_end
) == [Date(2013, 1, 1), Date(2013, 5, 7), Date(2014, 1, 1), Date(2014, 3, 10)]
# termination_date < study_end
@assert create_partition_endpoints(
AnniversaryCalendar(Month(1), Year(1)), # AnniversaryCalendar
Date(2012, 5, 7), # anniv_date
Date(2013, 3, 1), # termination_date
Date(2013, 1, 1), # study_start
Date(2014, 3, 10) # study_end
) == [Date(2013, 1, 1), Date(2013, 1, 7), Date(2013, 2, 7), Date(2013, 3, 1)]
# termination_date > study_end
@assert create_partition_endpoints(
AnniversaryCalendar(Year(1), Month(1)), # AnniversaryCalendar
Date(2012, 2, 10), # anniv_date
Date(2016, 4, 4), # termination_date
Date(2013, 2, 4), # study_start
Date(2013, 4, 1) # study_end
) == [Date(2013, 2, 4), Date(2013, 2, 10), Date(2013, 3, 1), Date(2013, 4, 1)]
end
# ╔═╡ 00000000-0000-0000-0000-000000000001
PLUTO_PROJECT_TOML_CONTENTS = """
[deps]
Dates = "ade2ca70-3891-5945-98fb-dc099432e06a"
"""
# ╔═╡ 00000000-0000-0000-0000-000000000002
PLUTO_MANIFEST_TOML_CONTENTS = """
# This file is machine-generated - editing it directly is not advised
julia_version = "1.7.3"
manifest_format = "2.0"
[[deps.Dates]]
deps = ["Printf"]
uuid = "ade2ca70-3891-5945-98fb-dc099432e06a"
[[deps.Printf]]
deps = ["Unicode"]
uuid = "de0858da-6303-5e67-8744-51eddeeeb8d7"
[[deps.Unicode]]
uuid = "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5"
"""
# ╔═╡ Cell order:
# ╠═6d618467-eed2-4851-961e-e45ed4a455d0
# ╠═fb2b4952-3a91-11ed-116b-db769de7e1cc
# ╠═2a8585fb-c4f8-4635-a999-963f442cc997
# ╠═48378996-32d7-45a8-b11f-616903d859e1
# ╠═a42f02bd-e33a-406e-b4c1-13b51e39b99c
# ╠═66f45298-6d5e-48e8-81fb-4b77deb85836
# ╠═df60e747-e48f-4572-b699-14bba7a156f3
# ╠═76aa4c7f-a412-41d1-ae98-c10c14606b0a
# ╠═a176cd07-0a14-4916-95f4-bdc77be1a1a9
# ╟─00000000-0000-0000-0000-000000000001
# ╟─00000000-0000-0000-0000-000000000002
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment