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 documented tibbles, so the structure of the selection remains intelligible 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() whose file was written from an executed sample: the execution receipt carries the digest, so a shipped design file supports next-wave planning without the frame or the sample. With frame, any complete sampling_design will do.

frame

Optional frame to preview the design against: one data frame for a shared hierarchy, or an ordered list of stage registers, as execute() takes them. When supplied, the report describes what the design would do on that frame rather than what it did, and 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. stages and the arguments after it follow ..., so each must be named exactly: 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.

scope

Denominator basis for population sizes and take rates. With "eligible" (default), denominators cover the units that were eligible for the recorded execution. With "universe", denominators are reported only where the digest covers the full population hierarchy. Denominators that the recorded scope cannot support are 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. The scope column always 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, n_target, n_expected, n_realized, and take_rate.

For detail = "pool", one row per selection pool and realization with stage, pool_id, replicate, parent_unit, any stratum label columns, N, n_target, n_expected, n_realized, scope, chance_status, chance (the constant per-unit chance where one applies, NA otherwise), take_rate, and capped. replicate is 1 for an ordinary execution. In a replicated execution, pool_id identifies the shared structural pool and the key is stage, pool_id, and replicate. Stratum columns of stages that do not use them are NA.

capped marks a pool that held fewer units than the stage asked for. It is always FALSE on a random-size stage, which realizes a count around its target rather than running out of units, and NA where the target fell short but the design recording which kind of stage it is was not available.

capped is a statement about selection: it marks a pool that could not supply the executable target it was given. A stratum whose target an allocation method had already reduced to the stratum population is therefore FALSE, because n_target records the post-redistribution target and the pool delivered it in full. That event is reported by execute() instead, as a message of class samplyr_message_allocation_capped or, when the stage ends up taking everything within reach, samplyr_warning_census. The digest does not retain the pre-redistribution allocation target, so the distinction is available from the conditions rather than from this table.

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. Requesting such a stage explicitly is an error rather than a silently empty result.

Details

take_rate is n_realized / N. It is a take rate, not an inclusion probability: for unequal-probability designs the two differ by design. chance_kind states what the recorded chances mean: first-order inclusion probabilities for without-replacement stages, expected hits for with-replacement stages. probabilities states how well the stage's method honors them: "exact" when the recorded chances equal the design's true first-order chances, "approximate" when the method treats them as a target achieved to a documented approximation ("pps_sps", "pps_pareto", and registered methods declared probabilities = "approximate"), 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. It can differ from n_target after probability capping or other feasibility adjustments.

  • n_realized is the number of selected units or occurrences in the realization.

These quantities often coincide for a feasible fixed-size design. For Bernoulli and Poisson sampling, n_realized varies around n_expected. n_target still records the nominal requested expectation. It is NA only when no nominal count can be recovered, for example from an older digest or a method with unspecified target semantics.

Stage detail stays compact for replicated executions: a common per-replicate realized allocation is reported, while varying n_realized and take_rate are NA. Pool detail is the drill-down: it returns one row per pool and replicate with scalar realized values, even when all replicates happen to agree. Population and design columns repeat across those rows, so select one replicate before using a replicated pool table as a next-wave planning frame. Unit detail spans the stacked replicates: is_selected marks units selected in at least one replicate and n_hits counts occurrences across all replicates, 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 shared by every replicate: later-stage pools hang off each replicate's own selected parents, so those stages are not part of the manifest and frame_summary() says so.

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

Previewing a design

With frame, the report is resolved from the design and the frame without drawing: every selection pool is enumerated and every chance resolved, but nothing is selected. This makes an allocation inspectable before it is committed to, and lets a design restored from a file be checked against next wave's frame.

Because there is no realization, 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, is the same as for a recorded digest, so the two are directly comparable.

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. This is what field planning needs: how many households to list in a selected cluster.

At detail = "stage", those conditional pools are rolled up weighted by the probability each parent is selected, so the row reports the expected size of the stage rather than the total across every candidate. A design taking 10 of 100 clusters and 5 units in each reports n_target = 50 at stage 2, not 500. For a design whose stage sizes are fixed 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 × 12
#>   stage unit_level scope    chance_kind      probabilities storage n_pools     N
#>   <int> <chr>      <chr>    <chr>            <chr>         <chr>     <int> <dbl>
#> 1     1 element    universe inclusion_proba… exact         consta…       2    20
#> # ℹ 4 more variables: n_target <dbl>, n_expected <dbl>, 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 × 12
#>   stage unit_level scope    chance_kind      probabilities storage n_pools     N
#>   <int> <chr>      <chr>    <chr>            <chr>         <chr>     <int> <dbl>
#> 1     1 element    universe inclusion_proba… exact         consta…       2    20
#> # ℹ 4 more variables: n_target <dbl>, n_expected <dbl>, 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

# 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          5       0.5
#> 2     1       1         2 A          10        3          3          3       0.3
#> 3     1       1         3 A          10        3          3          1       0.1
#> 4     1       2         1 B          10        3          3          4       0.4
#> 5     1       2         2 B          10        3          3          0       0  
#> 6     1       2         3 B          10        3          3          1       0.1

# 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>