Skip to contents

An executed sample can carry a frame digest, a compact record of the frames, selection pools, resolved selection chances, and allocation that each stage encountered at execution time. frame_summary() turns that record into tibbles, so the structure of the selection stays readable after the original frame has gone away.

Usage

frame_summary(
  x,
  frame = NULL,
  ...,
  stages = NULL,
  scope = c("eligible", "universe"),
  detail = c("stage", "pool", "unit")
)

Arguments

x

A tbl_sample produced by execute() that carries a frame digest, or a sampling_design restored by read_design() from a file written from an executed sample. That file's execution receipt carries the digest, so it supports next-wave planning without the frame or the sample. With frame, any complete sampling_design will do.

frame

Optional frame on which to preview what the design would do, in a form execute() accepts: one data frame for a shared hierarchy, or an ordered list of stage registers. No sampling is performed, no random numbers are drawn, and .Random.seed is left as it was. See "Previewing a design" below.

...

These dots are for future extensions and must be empty. The arguments after them must be named exactly, so the singular stage is reported rather than prefix-matched.

stages

An integer vector of stage numbers to report, or NULL (default) for all recorded stages. With frame, only the stages up to the last one named are resolved, so the frame needs the variables of those stages only, as execute() does with stages.

scope

Denominator basis for population sizes and take rates. "eligible" (default) counts the units eligible for the recorded execution. "universe" reports denominators only where the digest covers the full population hierarchy. A denominator the recorded scope cannot support is NA, never invented.

detail

Resolution of the report: "stage" (one row per stage), "pool" (one row per selection pool and realization), or "unit" (one row per population unit, only for stages that retained a unit-level representation).

Value

A tibble. Its scope column states how complete the underlying representation is ("eligible", "universe", "conditional", "partial", or "unknown").

For detail = "stage", one row per stage with stage, unit_level, scope, chance_kind, probabilities, storage, n_pools, n_capped, N, n_target, n_expected, n_certainty, n_moved, n_tied, n_realized, and take_rate. n_capped counts the stage's pools whose capped is TRUE at detail = "pool", and is NA when any of them is NA. Like n_pools, it counts pools without weighting, so in a preview it counts candidate pools. n_moved is the number of units an allocation method moved away from strata it saturated, which n_target no longer shows. A preview reports it, rolled up like n_target. It is NA for a stage without an allocation method and on a recorded digest, which does not keep it. n_tied counts the pools marked tied at detail = "pool", without weighting, and is NA in the same cases. n_certainty adds up the pools' n_certainty, rolled up like n_target. A design that sets reserve in draw() adds n_reserve, the reserve units its pools list after their take, rolled up like n_target. n_expected and n_realized then count the whole list, take and reserve.

For detail = "pool", one row per selection pool and realization with stage, pool_id, replicate, parent_unit, pool_key, any stratum label columns, N, n_target, n_expected, n_certainty, n_realized, scope, chance_status, chance (the constant per-unit chance where one applies, NA otherwise), take_rate, capped, and tied, and n_reserve when the design sets reserve. replicate is 1 for an ordinary execution. pool_key names the pool in the frame's values, its parent path then its stratum, as the pool_keys field of a condition from execute() does. Only a preview can name pools, so it is NA on a recorded digest, and on a first-stage pool without strata. In a replicated execution, pool_id identifies the shared structural pool and the key is stage, pool_id, and replicate. Stratum columns are NA for stages that do not use them.

capped marks a pool that could not supply the executable target it was given. It is always FALSE on a random-size stage, and NA for a shortfall when no design is available to record which kind of stage it is. A stratum that an allocation method had already reduced to its population is FALSE, because n_target records the post-redistribution target. execute() reports that event as samplyr_message_allocation_capped or, when the stage takes everything within reach, samplyr_warning_census (see execution_conditions).

n_certainty counts the units of a pool whose chance is 1, so a pool taken whole counts all of its units. Before the draw it shows which pools a fixed-size PPS stage will take units from with certainty (see the certainty section of draw()). After the draw it comes from the stored chances or from the sample's .certainty_k column, and it is NA when neither is available, as for a with-replacement stage, whose chances are expected hits.

tied marks a pool whose allocated take was decided by a tie in the rounding: its remainder equals that of a stratum on the other side of the cut, and the rule in stratify_by() chose which got the extra unit. A preview reports it under an allocation method, and it is NA otherwise and on a recorded digest.

For detail = "unit", one row per population unit of each stage that stored units, with stage, pool_id, unit_id, unit_order, chance, is_certainty, n_descendants, is_selected, and n_hits. Stages that stored only a constant or a chance distribution contribute no rows, and requesting such a stage explicitly is an error.

Details

take_rate is n_realized / N, not an inclusion probability, and the two differ for unequal-probability designs. chance_kind states what the recorded chances mean: first-order inclusion probabilities for without-replacement stages, expected hits for with-replacement stages. probabilities is the stage's probability tier (see sample_columns): "exact" when the recorded chances equal the design's true first-order chances, "approximate" when the method achieves them only to a documented approximation, and NA for digests recorded before the field existed.

The allocation columns distinguish three quantities:

  • n_target is the nominal count requested by the design, before execution-time population limits, probability capping, or certainty overflow. For a random-size design specified with frac, it is N * frac and may be fractional.

  • n_expected is the sum of the final resolved inclusion probabilities or expected hits. Probability capping or other feasibility adjustments can make it differ from n_target.

  • n_realized is the number of selected units or occurrences.

The three often coincide for a feasible fixed-size design. Under Bernoulli and Poisson sampling, n_realized varies around n_expected and n_target keeps the nominal expectation. n_target is NA only when no nominal count can be recovered, as from an older digest or a method with unspecified target semantics.

In a replicated execution, stage detail reports the realized allocation when every replicate shares it, and NA for n_realized and take_rate otherwise. Pool detail has one row per pool and replicate even when the replicates agree, with population and design columns repeated, so select one replicate before using it as a next-wave planning frame. Unit detail spans the stacked replicates: is_selected marks units selected in at least one and n_hits counts occurrences across all, so a without-replacement unit can show n_hits > 1. The per-replicate trace is the replicate column of the digest's selected units. A replicated multi-stage execution records only the stage prefix every replicate shares, because later-stage pools hang off each replicate's own selected parents, and frame_summary() says so.

A materialized wave carries the digest of the realization it was activated from: its master's, or, while the master's later stages are drawn one wave at a time, the master's stages and the pools of the tenures or periods it reads. A master whose panels hold several tenures records every tenure drawn so far, its pools keyed by the wave the tenure opened at. Stage detail then has a tenure column and one row per tenure of a stage drawn by tenure, and pool detail a tenure column. Both also have n_excluded, the units of the listing that the tenure left out because earlier tenures drew them (see panel_assignment). These columns are absent otherwise.

A digest whose sample was modified after execution (rows, weights, or design columns changed) is reported as invalidated and refused.

Previewing a design

With frame, every selection pool is enumerated and every chance resolved, but nothing is selected. An allocation can be inspected before it is committed to, and a design restored from a file can be checked against next wave's frame. n_realized and take_rate are NA, as are is_selected and n_hits at detail = "unit". Every other column, and the shape of the table, matches a recorded digest, so the two are directly comparable.

A frame of primary units can be previewed before the frames below it exist. frame_summary(design, frame, stages = 1) checks the chances of the first stage, certainty included, on a frame that lacks the cluster or size variables of later stages.

At detail = "pool", a stage below a clustered stage reports one pool per candidate parent, and its n_target is what that stage would take given that parent is selected, such as how many households to list in a selected cluster. At detail = "stage", those pools are rolled up weighted by the probability each parent is selected, so the row reports the expected stage size rather than the total over every candidate. A design taking 10 of 100 clusters and 5 units in each reports n_target = 50 at stage 2, not 500. With fixed stage sizes this is also the exact size. Where per-parent takes vary (frac over unequal clusters, for instance) the realized size varies around it.

Not every design can be previewed. A stage below a with-replacement stage is refused, because the number of times each parent is hit is random, and so is a design whose non-final stage selects elements, which cannot be executed either (samplyr_error_exante_unsupported, samplyr_error_stage_parent_id).

Examples

frame <- data.frame(
  id = 1:20,
  stratum = rep(c("A", "B"), each = 10)
)

# Fixed-size allocation: target, expectation, and realization agree.
fixed <- sampling_design() |>
  stratify_by(stratum) |>
  draw(n = 3) |>
  execute(frame, seed = 42)
frame_summary(fixed)
#> # A tibble: 1 × 16
#>   stage unit_level scope    chance_kind   probabilities storage n_pools n_capped
#>   <int> <chr>      <chr>    <chr>         <chr>         <chr>     <int>    <int>
#> 1     1 element    universe inclusion_pr… exact         consta…       2        0
#> # ℹ 8 more variables: N <dbl>, n_target <dbl>, n_expected <dbl>,
#> #   n_certainty <dbl>, n_moved <dbl>, n_tied <int>, n_realized <dbl>,
#> #   take_rate <dbl>
allocation_columns <- c(
  "stage", "pool_id", "replicate", "stratum", "N",
  "n_target", "n_expected", "n_realized", "take_rate"
)
fixed_pools <- frame_summary(fixed, detail = "pool")
print(fixed_pools[allocation_columns], width = Inf)
#> # A tibble: 2 × 9
#>   stage pool_id replicate stratum     N n_target n_expected n_realized take_rate
#>   <int>   <int>     <int> <chr>   <dbl>    <dbl>      <dbl>      <dbl>     <dbl>
#> 1     1       1         1 A          10        3          3          3       0.3
#> 2     1       2         1 B          10        3          3          3       0.3

# The same report before anything is drawn: pass the frame instead.
# Nothing is selected and no random numbers are used, so an allocation
# can be checked before it is committed to.
planned <- sampling_design() |>
  stratify_by(stratum) |>
  draw(n = 3)
frame_summary(planned, frame)
#> # A tibble: 1 × 16
#>   stage unit_level scope    chance_kind   probabilities storage n_pools n_capped
#>   <int> <chr>      <chr>    <chr>         <chr>         <chr>     <int>    <int>
#> 1     1 element    universe inclusion_pr… exact         consta…       2        0
#> # ℹ 8 more variables: N <dbl>, n_target <dbl>, n_expected <dbl>,
#> #   n_certainty <dbl>, n_moved <dbl>, n_tied <int>, n_realized <dbl>,
#> #   take_rate <dbl>
print(
  frame_summary(planned, frame, detail = "pool")[allocation_columns],
  width = Inf
)
#> # A tibble: 2 × 9
#>   stage pool_id replicate stratum     N n_target n_expected n_realized take_rate
#>   <int>   <int>     <int> <chr>   <dbl>    <dbl>      <dbl>      <dbl>     <dbl>
#> 1     1       1         1 A          10        3          3         NA        NA
#> 2     1       2         1 B          10        3          3         NA        NA

# A short pool before drawing: one of three villages holds fewer
# households than the take of 4, and the preview names it.
villages <- data.frame(
  village = rep(c("v1", "v2", "v3"), c(6, 3, 8)),
  household = 1:17
)
two_stage <- sampling_design() |>
  add_stage() |>
  cluster_by(village) |>
  draw(n = 2) |>
  add_stage() |>
  draw(n = 4)
frame_summary(two_stage, villages)[c("stage", "n_pools", "n_capped")]
#> # A tibble: 2 × 3
#>   stage n_pools n_capped
#>   <int>   <int>    <int>
#> 1     1       1        0
#> 2     2       3        1
short <- frame_summary(two_stage, villages, detail = "pool")
short[short$capped, c("stage", "pool_key", "N", "n_target")]
#> # A tibble: 1 × 4
#>   stage pool_key     N n_target
#>   <int> <chr>    <dbl>    <dbl>
#> 1     2 v2           3        4

# Random-size allocation: one scalar row per pool and replicate.
random_sample <- sampling_design() |>
  stratify_by(stratum) |>
  draw(frac = 0.3, method = "bernoulli", on_empty = "silent") |>
  execute(frame, seed = 42, reps = 3)
random_pools <- frame_summary(random_sample, detail = "pool")
print(random_pools[allocation_columns], width = Inf)
#> # A tibble: 6 × 9
#>   stage pool_id replicate stratum     N n_target n_expected n_realized take_rate
#>   <int>   <int>     <int> <chr>   <dbl>    <dbl>      <dbl>      <dbl>     <dbl>
#> 1     1       1         1 A          10        3          3          3       0.3
#> 2     1       1         2 A          10        3          3          2       0.2
#> 3     1       1         3 A          10        3          3          2       0.2
#> 4     1       2         1 B          10        3          3          3       0.3
#> 5     1       2         2 B          10        3          3          4       0.4
#> 6     1       2         3 B          10        3          3          4       0.4

# Probability capping can make the resolved expectation smaller
# than the nominal target.
pps_frame <- data.frame(id = 1:20, mos = c(100, rep(1, 19)))
capped <- sampling_design() |>
  draw(n = 5, method = "pps_poisson", mos = mos, on_empty = "silent") |>
  execute(pps_frame, seed = 42)
#> Warning: Stage 1: PPS Poisson expected sample size fell short of the reachable target in
#> 1 pool.
#> ✖ Reachable 5 units, expected 1.8.
#> ✖ 1 unit has an inclusion probability clipped at 1.
#> ℹ Handle dominant units explicitly with `certainty_size` or `certainty_prop`.
#> ℹ See `?selection_methods` for the "pps_poisson" contract.
capped_pool <- frame_summary(capped, detail = "pool")
print(
  capped_pool[c("N", "n_target", "n_expected", "n_realized")],
  width = Inf
)
#> # A tibble: 1 × 4
#>       N n_target n_expected n_realized
#>   <dbl>    <dbl>      <dbl>      <dbl>
#> 1    20        5       1.80          1

# Full digests support anonymous unit-level inspection.
full <- sampling_design() |>
  draw(n = 5, method = "pps_brewer", mos = id) |>
  execute(frame, seed = 42, frame_digest = "full")
head(frame_summary(full, detail = "unit"))
#> # A tibble: 6 × 9
#>   stage pool_id unit_id unit_order chance is_certainty n_descendants is_selected
#>   <int>   <int>   <int>      <int>  <dbl> <lgl>                <int> <lgl>      
#> 1     1       1       1          1 0.0238 FALSE                   NA FALSE      
#> 2     1       1       2          2 0.0476 FALSE                   NA FALSE      
#> 3     1       1       3          3 0.0714 FALSE                   NA FALSE      
#> 4     1       1       4          4 0.0952 FALSE                   NA FALSE      
#> 5     1       1       5          5 0.119  FALSE                   NA FALSE      
#> 6     1       1       6          6 0.143  FALSE                   NA FALSE      
#> # ℹ 1 more variable: n_hits <int>

# Before drawing, the pools a PPS stage will take units from with
# certainty. Poura's largest urban EA holds more than 1/12 of its
# households, so 12 EAs drawn by size include it for sure.
poura <- subset(bfa_eas, commune == "Poura" & urban_rural == "Urban")
by_size <- sampling_design() |>
  draw(n = 12, method = "pps_systematic", mos = households)
frame_summary(by_size, poura, detail = "pool")[
  c("N", "n_target", "n_certainty")
]
#> # A tibble: 1 × 3
#>       N n_target n_certainty
#>   <dbl>    <dbl>       <int>
#> 1    29       12           1