Collects two or more samples, each selected from a frame that covers part of one target population, into a single object that records which frames every sampled unit belongs to. Frame A and frame B are two registers of the same people, and a unit listed in both had two chances of being selected.
The samples are selected independently: two designs, two frames, two seeds,
two calls to execute(). Nothing about selection is shared, so nothing is
combined until here.
Arguments
- ...
Two or more named samples, one per frame, each an executed
tbl_sample. The names are the frame names and they appear in the.domainlabels, so none may contain+.- membership
A named character vector mapping every frame name to the column holding that frame's membership indicator, in the same direction as
c(frame_name = "column_name"). Every component must contain every one of the columns.- key
A bare column identifying the target-population unit, present in every component. The same unit may be selected from several frames, so the key repeats across components on purpose; within a component it must be unique, or unique per selection occurrence where the component replicates rows with replacement.
- overlaps
Optional. The probability, or the weight, each sampled unit would have had in every frame, including the frames it was not selected from. Declare the scale with
overlap_probabilities()oroverlap_weights(); it is never inferred from the values. Required byestimator = "expected"at export and unused by the default estimator, which reads membership alone.
Value
An object of class frame_stack: a named list of the component
samples, unchanged, carrying the membership mapping, the key, and any
overlap record. It is not a tbl_sample and not a data frame. [[
returns an intact component; as.data.frame() gives the row-bound
inspection view.
Details
What it does not do
It does not composite the weights. Which compositing factor to use, and whether it should follow Hartley, the multiplicity estimator or pseudo-maximum-likelihood, depends on the estimand and on the design effects, so it is an estimation-time choice rather than a property of the stack. The components keep their own design weights, unchanged.
It does not assert that the frames together cover the target population. That is an assumption about the registers, not something this call can establish, which is why the verb is not named after the union.
Membership is declared, and it is logical
membership maps each frame's name to the column saying whether a unit
belongs to that frame. Every component must carry all of the columns, not
only its own: a unit selected from frame A has to say whether it was also
listed in frame B, and that is the information compositing needs. An
unresolved membership is an error rather than NA.
Membership columns must be logical. Integer 0 and 1 are refused
rather than read, because numeric overlap information is a different input
on a different scale.
Independence is assumed, and one violation of it is detectable
Two components carrying the same recorded seed warn with
samplyr_warning_frame_seed_reused. Distinct seeds are not evidence of
independence; the warning detects one recorded common-random-number
mistake and nothing more. A component executed with seed = NULL records
no seed and never warns, so a deterministic take-all component is exempt
unless it was given a seed it did not use.
References
Lohr, S. L. (2021). Multiple-frame surveys for a multiple-data-source world. Survey Methodology, 47(2), 229-263.
Mecatti, F. (2007). A single frame multiplicity estimator for multiple frame surveys. Survey Methodology, 33(2), 151-157.
See also
as.data.frame.frame_stack() for the row-bound view,
share_weights() for reaching a linked population through one frame
Other multiple frames:
as.data.frame.frame_stack(),
as_svrepdesign.frame_stack(),
as_svydesign.frame_stack(),
exante_overlaps(),
exante_probabilities(),
overlap_probabilities(),
overlap_weights(),
summary.frame_stack()
Examples
population <- data.frame(
person_id = 1:60,
in_landline = rep(c(TRUE, FALSE), times = c(40, 20)),
in_cell = rep(c(FALSE, TRUE), times = c(10, 50))
)
s_landline <- sampling_design() |>
draw(n = 10) |>
execute(population[population$in_landline, ], seed = 1)
s_cell <- sampling_design() |>
draw(n = 12) |>
execute(population[population$in_cell, ], seed = 2)
frames <- stack_frames(
landline = s_landline,
cell = s_cell,
membership = c(landline = "in_landline", cell = "in_cell"),
key = person_id
)
frames
#> ── Frame Stack ─────────────────────────────────────────────────────────────────
#>
#> ℹ 2 frames over key person_id
#> • landline: 10 rows, in_landline, seed 1
#> • cell: 12 rows, in_cell, seed 2
#> ℹ Weights are each frame's own and are not composited here.
#>
summary(frames)
#> ── Frame Stack Summary ─────────────────────────────────────────────────────────
#>
#> ℹ 2 frames | 22 rows | key person_id
#>
#> Frames
#> • landline: 10 rows | in_landline | seed 1
#> • cell: 12 rows | in_cell | seed 2
#>
#> Domains
#> • cell: 3 rows
#> • cell+landline: 17 rows
#> • landline: 2 rows
#>
#> ℹ A unit listed in two frames is counted once per frame that selected it.
#>
head(as.data.frame(frames))
#> .frame .domain person_id in_landline in_cell .weight .sample_id
#> 1 landline landline 4 TRUE FALSE 4 1
#> 2 landline cell+landline 39 TRUE TRUE 4 2
#> 3 landline landline 1 TRUE FALSE 4 3
#> 4 landline cell+landline 34 TRUE TRUE 4 4
#> 5 landline cell+landline 23 TRUE TRUE 4 5
#> 6 landline cell+landline 14 TRUE TRUE 4 6
#> .stage .weight_1 .fpc_1
#> 1 1 4 40
#> 2 1 4 40
#> 3 1 4 40
#> 4 1 4 40
#> 5 1 4 40
#> 6 1 4 40