Convolving a timeseries
Introduction
This tutorial shows what convolve_series does by plotting it. It convolves a numeric series with a delay PMF on the unit lag grid, the renewal-style observation layer that turns an infection curve into an expected count curve.
What are we going to do in this exercise
Build a delay PMF and a synthetic infection curve.
Convolve the infection curve into an expected downstream count curve.
What might I need to know before starting
This tutorial builds on the Getting started overview and uses AlgebraOfGraphics.jl and CairoMakie.jl for plotting. No fitting or MCMC is involved; every quantity is a direct evaluation.
Packages used
using ConvolvedDistributions, Distributions
using CairoMakie, AlgebraOfGraphics, DataFramesMeta
CairoMakie.activate!(type = "png", px_per_unit = 2)Timeseries convolution
The timeseries form convolve_series convolves a numeric series with a delay PMF on the unit lag grid. The delay here is discrete, so it is passed straight in and its own PMF is read off the lag grid. With the series an expected infection curve, the result is the expected downstream count curve.
t = 0:40
infections = 100 .* exp.(-((t .- 12.0) .^ 2) ./ 30.0)
expected = convolve_series(NegativeBinomial(5, 0.5), infections)
timeseries_df = vcat(
DataFrame(t = t, count = infections, Series = "Infections"),
DataFrame(t = t, count = expected, Series = "Expected reports")
)
draw(
data(timeseries_df) *
mapping(:t, :count, color = :Series) *
visual(Lines, linewidth = 2);
axis = (xlabel = "Day", ylabel = "Expected count")
)The report curve is shifted right by the mean total delay and is flatter than the infection curve, because convolution smears each day's infections across the delay distribution. Mass delayed beyond the series window is truncated rather than renormalised, so the report curve carries slightly less total mass.
A delay that changes over the window
A single PMF assumes the delay never changes. Passing one delay per time point — here a Poisson delay whose mean falls from six days to two across the window — convolves each time point through its own PMF. See convolve_series for the forms the delays can take.
mean_delay = range(6.0, 2.0; length = length(t))
delays = [Poisson(m) for m in mean_delay]
timevarying = convolve_series(delays, infections)41-element Vector{Float64}:
0.002039950341117194
0.017092678078832992
0.07615216151417342
0.24303387388741157
0.6299107840703946
1.4188006842797898
2.8863669367023856
5.417935296010671
9.487625641998088
15.58205582834721
24.059274212078897
34.962428271485074
47.841916072303526
61.66418839968367
74.88030244148642
85.68227901785319
92.40095593035608
93.92786440490991
90.01474460604962
81.33944047699794
69.31424883532519
55.71106724781502
42.239627070027446
30.214713129462332
20.393648217766778
12.989897328958138
7.809176121665721
4.431450367709474
2.3739874347255077
1.2007479595560748
0.5734742104419669
0.25864920227382465
0.11017666108191518
0.044329741693661065
0.016849012478262464
0.006050278215095767
0.0020528081253985437
0.0006581845745687515
0.00019944942850141142
5.7130763883041196e-5
1.547158394750587e-5indexed_by names which time the delay belongs to: :primary (the default) the events, :secondary the reporting date.
timevarying_secondary = convolve_series(delays, infections; indexed_by = :secondary)
timevarying_df = vcat(
DataFrame(t = t, count = infections, Series = "Infections"),
DataFrame(t = t, count = timevarying, Series = "Time-varying (primary)"),
DataFrame(
t = t, count = timevarying_secondary,
Series = "Time-varying (secondary)"
)
)
draw(
data(timevarying_df) *
mapping(:t, :count, color = :Series) *
visual(Lines, linewidth = 2);
axis = (xlabel = "Day", ylabel = "Expected count")
)Early infections carry the long delay and late ones the short delay, so the report curve is pulled forward and compressed relative to the constant-delay run. The two conventions separate wherever the delay is changing.
Summary
The timeseries form turns an infection curve into an expected count curve through the delay's PMF.
A time-varying delay is one PMF per time point, with
indexed_bynaming which time it belongs to.
See also: Convolving distributions, The difference of two delays, The product of two delays.