Construct an operational schedule from a rotating panel plan
Source:R/design_schedule.R
design_schedule.RdTranslate the launch and recruitment quantities in a rotating svyplan_panel into named cohort components, a dense panel-by-occasion schedule, and any interview commitments beyond the planning horizon. The result is a planning object. It does not inspect a frame, execute a sample, or construct analysis weights.
Usage
design_schedule(
panel_plan,
horizon,
horizon_policy,
refreshment,
frame_vintage = NULL,
rounding,
take = NULL,
tenures = 1,
subperiods = 1
)Arguments
- panel_plan
A rotating
svyplan_panelfromn_panel()orprec_panel()withstart = "immediate"orstart = "gradual". Itsrotationis the life the schedule lays out, and the overlap the manifest records is computed from it, so neither is supplied here.- horizon
A positive whole number of program occasions in the finite planning window.
- horizon_policy
One of
"continuing","truncate_lives", or"close_intake". Under"continuing", interviews owed afterhorizonare returned separately intail_commitments."truncate_lives"cuts those interviews at the horizon, and a life it cuts ends at its last interview inside the window."close_intake"stops recruitment soon enough for every recruited cohort to finish by the horizon.- refreshment
Whether later cohorts are drawn from an
"entrant_register"or a"whole_vintage". This records a frame role and does not establish disjointness or combine weights.- frame_vintage
NULL, or a named character vector supplying frame identifiers for any subset of the generated cohort names. Unnamed or unknown entries are refused rather than matched by position.- rounding
The explicit operational rounding rule. The schema supports
"ceiling", applied to each startup panel and intake cohort before totals are formed. Operational counts are stored as whole-valued numerics so plans above R's 32-bit integer range remain representable.- take
NULL(default), or a positive whole number of units a PSU takes per tenure. With it the assignment unit is the PSU. Each panel issuesceiling(n_entrants / take)PSUs, rounded at the PSU, and the units it stands for are that number timestake. The unit quantities stay with the panel plan, whose target carries the clustering through itsdeff.- tenures
1(default) or"open". With1a PSU leaves with its units at the end of their life. With"open"the PSUs stay through the horizon and only the units rotate. Each panel is then a PSU group whose life repeats the rotation back to back, andvisitrestarts at 1 at each tenure, where the group's new units are drawn. A finite number of tenures above 1 is not available.- subperiods
A positive whole number,
1by default. The rotation, the panel plan andhorizonare stated per reporting period, and the schedule is written inhorizon * subperiodsfieldwork waves. See the section on sub-periods.
Value
An svyplan_schedule list with scalar planning metadata and four
data frames. schema_version is 2. The metadata carries n_entrants,
n_in_sample and n_held from the panel plan, and n_cohorts, the
cohorts in sample at an occasion, which is the number of visits.
life_length is the life in occasions, gaps included. take,
tenures (1, or Inf for "open") and subperiods record the
arguments.
componentsOne row per execution: startup followed by the intake cohorts permitted by the horizon policy. Continuous and operational issue remain separate.
scheduleThe dense component-panel grid over occasions 1 through
horizon.periodnames each wave's reporting period,(wave - 1) %/% subperiods + 1, which equalswavewhensubperiods = 1. A row is in one of three states. Before a panel's entry or after its life it hasactive = FALSEandlife_stageandvisitNA. Held out of sample, in a gap of its life, it hasactive = FALSE, alife_stageandvisit = NA. Interviewed, it hasactive = TRUE, alife_stageand avisit.tail_commitmentsA sparse table of interviews after
horizonowed to components entering within a"continuing"window, with the sameperiod,life_stageandvisit. It lists interviews only and has a fixed empty schema under the other policies.issueThe continuous and operational recruitment issued at each in-horizon occasion, in assignment units, and
tenure_issue, the units first interviewed there, which is where a tenure's units are drawn.as.data.frame()returns this view.
The generated activity is checked against panel_plan$launch_waves over
every shared period that the horizon policy has not changed. Its realized
issued overlap is also checked against the overlap the rotation
produces, at every mature membership origin for which the destination
occasion exists.
Details
life_stage counts the occasions of a panel's own life and visit its
interviews, both from 1. They are equal for a life without a break. Under
"2-2-2" an intake cohort has life_stage 1 to 6 and visit 1, 2,
NA, NA, 3, 4.
An immediate start covers every stage of the life at the first occasion,
one startup panel per stage, as n_panel() plans it. A panel covering a
stage out of sample is selected at the first occasion and held until the
rest of its life returns to sample. Its first interview, where visit is
1, comes later. Under "2-2-2" the panels covering stages 3 and 4 are
first interviewed at occasions 3 and 2. A panel numbers its own stages
and interviews, so every panel has a visit of 1 at its first interview.
PSUs and tenures
With take, the schedule counts PSUs. Each panel is a group of
ceiling(n_entrants / take) PSUs, and a group takes take units in each
PSU at every tenure. The PSU allocation then reads its total from the
schedule, as in n_alloc(frame, n = schedule$components$operational_issue[1], n_step = G) with G the number of startup panels, so that the master has
the PSU count the schedule promises.
Under tenures = "open" no PSU is replaced inside the horizon. An
immediate start selects every group at the first occasion, each group's
first tenure being the rest of the life from the stage it covers, and a
gradual start brings in one group an occasion until the life is covered.
A new tenure is a new draw of units, so the overlap of the units is still
the one design_overlap() gives for the rotation. The horizon policies
apply to tenures. "continuing" lists the rest of every tenure opened in
the window, "truncate_lives" cuts tenures at the horizon, and
"close_intake" opens no tenure that cannot finish by it.
Sub-periods
Monthly fieldwork for quarterly estimates is a schedule over months. With
subperiods = s, each reporting panel g becomes s panels, numbered
(g - 1) * s + j for j in 1 to s, so the group of a panel is
ceiling(panel / s). Panel j is interviewed at wave
(period - 1) * s + j of every period its group is interviewed in, and is
held in the other waves of the group's life. A unit keeps its sub-period at
every visit and across tenures. period names the reporting period of
each wave, and life_stage, life_length and visit count periods and
interviews, not waves. horizon, wave, entry_wave and
steady_state_from count waves, and a cohort entering in period t
enters at wave (t - 1) * s + 1, every sub-period panel being selected
then. Each sub-period panel takes ceiling(n_entrants / s) units, or that
over take in PSUs, so a period issues up to s - 1 more per group than
the continuous plan. The launch and overlap checks run on the reporting
grid, and $overlap is the reporting-period profile.
See also
design_rotation() for the pattern this schedules, n_panel()
and prec_panel() for the rotating plan, plot.svyplan_overlap() for
the rotation chart, and print.svyplan_schedule() for the printed form
and its summary().
Other repeated survey functions:
design_overlap(),
design_rotation(),
n_change(),
n_panel(),
n_pooled(),
prec_change(),
prec_panel(),
prec_pooled()
Examples
panel <- n_panel(
n_prop(p = 0.5, moe = 0.03),
retention = c(0.9, 0.9, 0.9),
resp_rate = 0.75,
design = "rotating",
start = "immediate"
)
schedule <- design_schedule(
panel,
horizon = 6,
horizon_policy = "continuing",
refreshment = "entrant_register",
rounding = "ceiling"
)
schedule
#> Longitudinal design schedule (immediate launch, continuing)
#> life: 4 stages over 6 occasions, steady from occasion 4
#> rounding: ceiling at panel and cohort level
#> issue: 1656 at startup, 414 per occasion (occasions 2-6)
#> tail commitments: 6 panel-interviews after occasion 6
#> # summary() for the occasion-by-occasion profile and the overlap
schedule$components
#> cohort entry_wave frame_role planned_issue operational_issue panels
#> 1 startup 1 startup 1654.856 1656 4
#> 2 intake_2 2 entrant_register 413.714 414 1
#> 3 intake_3 3 entrant_register 413.714 414 1
#> 4 intake_4 4 entrant_register 413.714 414 1
#> 5 intake_5 5 entrant_register 413.714 414 1
#> 6 intake_6 6 entrant_register 413.714 414 1
#> panel_issue frame_vintage status
#> 1 414 <NA> planned
#> 2 414 <NA> planned
#> 3 414 <NA> planned
#> 4 414 <NA> planned
#> 5 414 <NA> planned
#> 6 414 <NA> planned
# A 2-2-2 rotation: six startup panels, four interviewed at each occasion
quarterly <- n_panel(
n_prop(p = 0.5, moe = 0.03),
retention = c(0.9, 0.85, 0.9),
resp_rate = 0.75,
design = "rotating",
start = "immediate",
rotation = "2-2-2"
)
gapped <- design_schedule(
quarterly,
horizon = 8,
horizon_policy = "continuing",
refreshment = "entrant_register",
rounding = "ceiling"
)
gapped
#> Longitudinal design schedule (immediate launch, continuing)
#> life: 6 stages over 8 occasions, steady from occasion 6
#> rotation: 2 in, 2 out, 2 in
#> rounding: ceiling at panel and cohort level
#> issue: 2550 at startup, 425 per occasion (occasions 2-8)
#> tail commitments: 10 panel-interviews after occasion 8
#> # summary() for the occasion-by-occasion profile and the overlap
startup <- gapped$schedule[gapped$schedule$cohort == "startup", ]
xtabs(visit ~ panel + wave, startup, addNA = TRUE)
#> wave
#> panel 1 2 3 4 5 6 7 8
#> 1 1 2 3 4
#> 2 1 2 3
#> 3 1 2
#> 4 1 2
#> 5 1 2
#> 6 1
# Quarterly reporting on a life of five quarters, 14 units per PSU, PSUs
# kept and monthly fieldwork: 15 panels, each in its own month
monthly <- design_schedule(
n_panel(n_prop(p = 0.5, moe = 0.02, deff = 1.8),
retention = rep(0.9, 4), resp_rate = 0.8,
design = "rotating", start = "immediate"),
horizon = 4,
horizon_policy = "continuing",
refreshment = "entrant_register",
rounding = "ceiling",
take = 14,
tenures = "open",
subperiods = 3
)
monthly
#> Longitudinal design schedule (immediate launch, continuing)
#> life: 5 periods over 4 periods of 3 waves, steady state not reached in the window
#> take: 14 units per PSU per tenure
#> tenures: open, PSUs kept through the horizon
#> rounding: ceiling at psu and subperiod level
#> issue: 480 PSUs at startup, no PSU intake from period 2
#> tail commitments: 30 panel-interviews after wave 12
#> # summary() for the occasion-by-occasion profile and the overlap
xtabs(visit ~ panel + wave, monthly$schedule, addNA = TRUE)
#> wave
#> panel 1 2 3 4 5 6 7 8 9 10 11 12
#> 1 1 2 3 4
#> 2 1 2 3 4
#> 3 1 2 3 4
#> 4 1 2 3 4
#> 5 1 2 3 4
#> 6 1 2 3 4
#> 7 1 2 3 1
#> 8 1 2 3 1
#> 9 1 2 3 1
#> 10 1 2 1 2
#> 11 1 2 1 2
#> 12 1 2 1 2
#> 13 1 1 2 3
#> 14 1 1 2 3
#> 15 1 1 2 3