Skip to contents

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.

Usage

stack_frames(..., membership, key, overlaps = NULL)

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 .domain labels, 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() or overlap_weights(); it is never inferred from the values. Required by estimator = "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.

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