Skip to contents

What coordination does

When several samples use the same register, we can deliberately control how much they overlap. Positive coordination keeps more of the same units. This can help with estimates of change, but it concentrates repeated response burden on those units. Negative coordination does the opposite. It spreads response burden and increases the number of distinct units covered.

Permanent random numbers, or PRNs, provide a practical way to coordinate samples. Each frame unit receives one random number between zero and one. The number stays with the unit and is reused or updated for later selections. This approach has a long history in business surveys (Brewer, Early, and Joyce 1972; Esbjörn Ohlsson 1995).

This article starts with Bernoulli sampling because its coordination rule is exact and easy to see. It then shows the fixed-size PPS workflow commonly used for business surveys. samplyr accepts PRNs with bernoulli, pps_poisson, pps_sps, and pps_pareto.

Coordination is not the same as a longitudinal design. A PRN controls overlap between draws. A longitudinal plan also decides how long units remain in the sample, how many units enter at each wave, and how change will be estimated. See vignette("rotating-panels") for that workflow.

Prepare the register

We use the synthetic ken_enterprises register and target about 200 enterprises per wave.

data(ken_enterprises)

set.seed(1)
frame <- ken_enterprises |>
  mutate(prn = runif(n()))

n_wave <- 200

In production, store prn in the register rather than recreating it in every sampling script. A stable identifier and a stable PRN are both needed for coordination to survive register updates.

The simplest positive coordination

Under Bernoulli sampling, a unit is selected when its PRN is below the sampling fraction. Reusing both the PRN and the fraction therefore selects the same units. The realized sample size can vary around the target.

fraction <- n_wave / nrow(frame)

design_bernoulli <- sampling_design() |>
  draw(frac = fraction, method = "bernoulli", prn = prn)

wave_1 <- execute(design_bernoulli, frame, seed = 10)
wave_2 <- execute(design_bernoulli, frame, seed = 20)

tibble(
  size_wave_1 = nrow(wave_1),
  size_wave_2 = nrow(wave_2),
  overlap = length(intersect(wave_1$enterprise_id, wave_2$enterprise_id))
)
#> # A tibble: 1 × 3
#>   size_wave_1 size_wave_2 overlap
#>         <int>       <int>   <int>
#> 1         208         208     208

The seeds differ, but the samples do not. Once a supported method receives a PRN, selection is determined by the PRN values and the design.

The simplest negative coordination

For a second Bernoulli wave, subtract the inclusion probability from each PRN and wrap the result back into the unit interval (Esbjörn Ohlsson 1995).

frame_wave_2 <- frame |>
  mutate(prn = (prn - fraction) %% 1)

wave_2_negative <- execute(design_bernoulli, frame_wave_2, seed = 20)

tibble(
  size_wave_1 = nrow(wave_1),
  size_wave_2 = nrow(wave_2_negative),
  overlap = length(intersect(
    wave_1$enterprise_id,
    wave_2_negative$enterprise_id
  ))
)
#> # A tibble: 1 × 3
#>   size_wave_1 size_wave_2 overlap
#>         <int>       <int>   <int>
#> 1         208         212       0

Here the two samples are disjoint. That conclusion is exact for this equal- probability Bernoulli example because twice the sampling fraction is below one. More generally, high-probability units may have to appear in both samples.

The important operational rule is simple: update PRNs on the full frame, not only on the selected rows. Otherwise the next draw no longer follows the intended sampling design.

A fixed-size PPS sample

Business surveys often need a fixed sample size and want large enterprises to be selected more often. Sequential Poisson sampling (pps_sps) provides that combination and supports PRNs (Esbjorn Ohlsson 1998).

First compute each unit’s target inclusion probability on the full frame.

frame_pps <- frame |>
  mutate(pik = inclusion_prob(revenue_millions, n_wave))

summary(frame_pps$pik)
#>      Min.   1st Qu.    Median      Mean   3rd Qu.      Max. 
#> 0.0001479 0.0012640 0.0024473 0.0117619 0.0062391 1.0000000

Then declare the design once and execute it.

design_pps <- sampling_design() |>
  draw(
    n = n_wave,
    method = "pps_sps",
    mos = revenue_millions,
    prn = prn
  )

pps_wave_1 <- execute(design_pps, frame_pps, seed = 1)

frame_pps_2 <- frame_pps |>
  mutate(prn = (prn - pik) %% 1)

pps_wave_2 <- execute(design_pps, frame_pps_2, seed = 1)

tibble(
  size_wave_1 = nrow(pps_wave_1),
  size_wave_2 = nrow(pps_wave_2),
  overlap = length(intersect(
    pps_wave_1$enterprise_id,
    pps_wave_2$enterprise_id
  ))
)
#> # A tibble: 1 × 3
#>   size_wave_1 size_wave_2 overlap
#>         <int>       <int>   <int>
#> 1         200         200      15

The update encourages negative coordination, but the exact Bernoulli argument does not carry over to pps_sps. Sequential Poisson sampling orders all units and fixes the sample size. Its supplied pik values are targets rather than exact first-order inclusion probabilities. The approximation improves for large populations and samples (Rosén 1997; Esbjorn Ohlsson 1998). The same distinction applies to pps_pareto.

If exact first-order probabilities matter more than a fixed realized size, use pps_poisson. Its sample size is random. Very large measures of size can also produce certainty units or reduce the expected sample size after probabilities are capped at one. execute() reports that case. See ?draw for the certainty options.

Repeat the update across waves

For a stable frame, a short loop is enough. Each sample uses the current PRN, then every frame unit receives its next PRN.

set.seed(2026)

frame_many <- ken_enterprises |>
  mutate(
    prn = runif(n()),
    pik = inclusion_prob(revenue_millions, n_wave)
  )

n_waves <- 4
waves <- vector("list", n_waves)

for (wave in seq_len(n_waves)) {
  waves[[wave]] <- execute(design_pps, frame_many, seed = 1)
  frame_many <- frame_many |>
    mutate(prn = (prn - pik) %% 1)
}

ids <- lapply(waves, `[[`, "enterprise_id")
overlap <- outer(
  ids,
  ids,
  Vectorize(\(x, y) length(intersect(x, y)))
)
dimnames(overlap) <- rep(list(paste("Wave", seq_len(n_waves))), 2)
overlap
#>        Wave 1 Wave 2 Wave 3 Wave 4
#> Wave 1    200     22     35     39
#> Wave 2     22    200     11     41
#> Wave 3     35     11    200     15
#> Wave 4     39     41     15    200

The matrix is descriptive. There is no general promise that adjacent fixed- size PPS samples will be disjoint or that a later pair will have a particular overlap.

Stratified samples

Compute pik within the same strata used by the design. The PRN update itself still applies to every row.

set.seed(1980)

frame_stratified <- ken_enterprises |>
  mutate(prn = runif(n())) |>
  mutate(
    pik = inclusion_prob(revenue_millions, 50),
    .by = size_class
  )

design_stratified <- sampling_design() |>
  stratify_by(size_class) |>
  draw(
    n = 50,
    method = "pps_sps",
    mos = revenue_millions,
    prn = prn
  )

stratified_wave_1 <- execute(design_stratified, frame_stratified, seed = 1)

frame_stratified_2 <- frame_stratified |>
  mutate(prn = (prn - pik) %% 1)

stratified_wave_2 <- execute(
  design_stratified,
  frame_stratified_2,
  seed = 1
)

length(intersect(
  stratified_wave_1$enterprise_id,
  stratified_wave_2$enterprise_id
))
#> [1] 9

If allocations, strata, or measures of size change, recompute the probabilities before updating the PRNs. Reusing a PRN still coordinates the draws, but it does not by itself maximize retention under a redesigned sample (Kish and Scott 1971).

Births, deaths, and register maintenance

For a cross-sectional survey, surviving units keep their PRNs, births receive fresh independent PRNs, and units no longer in the target population leave the frame. Recompute inclusion probabilities after the register changes.

These steps do not solve longitudinal attrition. If the target follows a fixed cohort, a closed enterprise or a nonrespondent needs an analysis rule as well as a frame update. PRNs also cannot detect identifier reuse, duplicate units, or incomplete coverage. Those are register-quality checks.

Coordination across surveys

Different surveys using the same register can share a PRN column. Reusing the same PRN tends to increase overlap. A half-turn shift is a simple way to seek less overlap.

frame_for_second_survey <- frame |>
  mutate(prn_second = (prn + 0.5) %% 1)

For equal-probability Bernoulli samples, separated intervals give an exact result when their sampling fractions fit without overlap. For fixed-size PPS samples with different sizes, strata, or size measures, the half-turn is only a heuristic. Large survey programmes generally need a dedicated coordination system rather than repeated ad hoc shifts (Esbjörn Ohlsson 1995; Rivière 2001).

A practical checklist

  1. Add one stable PRN column to the full register.
  2. Compute inclusion probabilities on the full selection pool, within strata when the design is stratified.
  3. Pass the PRN column to a supported method.
  4. Keep the PRN for positive coordination, or update it for negative coordination.
  5. Save the updated full frame before the next wave.
  6. Recompute probabilities whenever the frame or design changes.

Start with the Bernoulli example if you are learning the mechanism. Move to pps_sps only when you need both unequal selection probabilities and an exact realized sample size. Use vignette("rotating-panels") when the requirement is a controlled entry-and-exit schedule rather than simply more or less overlap.

References

Brewer, K. R. W., L. J. Early, and S. F. Joyce. 1972. “Selecting Several Samples from a Single Population.” Australian Journal of Statistics 14 (3): 231–39.
Kish, Leslie, and Alastair Scott. 1971. “Retaining Units After Changing Strata and Probabilities.” Journal of the American Statistical Association 66: 461–70.
Ohlsson, Esbjorn. 1998. “Sequential Poisson Sampling.” Journal of Official Statistics 14 (2): 149–62.
Ohlsson, Esbjörn. 1995. “Coordination of Samples Using Permanent Random Numbers.” In Business Survey Methods, edited by Brenda G. Cox, David A. Binder, B. Nanjamma Chinnappa, Anders Christianson, Michael J. Colledge, and Phillip S. Kott, 153–69. New York: John Wiley & Sons. https://doi.org/10.1002/9781118150504.ch9.
Rivière, Pascal. 2001. “Coordination d’échantillons Par La méthode Des Microstrates.” In Recueil Du Symposium 2001 de Statistique Canada. Statistics Canada.
Rosén, Bengt. 1997. “Asymptotic Theory for Order Sampling.” Journal of Statistical Planning and Inference 62 (2): 135–58.