The interval helpers allow a
pipeline to stop after counts, or add intervals later. Both accept the
same arguments: .data, percent_ci,
se, and critical_value. They share their
calculations with freq_table().
.data is normally a complete or filtered result from
freq_tbl(). Its metadata retains the original sample size,
so filtering a result does not change a denominator. Grouped tables also
carry explicit n_group.
counts <- freq_tbl(mtcars, am)
counts |> wald_ci()
#> # A tibble: 2 × 5
#> am n prop lcl ucl
#> <dbl> <int> <dbl> <dbl> <dbl>
#> 1 0 19 0.594 0.414 0.774
#> 2 1 13 0.406 0.226 0.586
counts |> logit_ci()
#> # A tibble: 2 × 5
#> am n prop lcl ucl
#> <dbl> <int> <dbl> <dbl> <dbl>
#> 1 0 19 0.594 0.409 0.755
#> 2 1 13 0.406 0.245 0.591
counts |> filter(am == 0) |> logit_ci()
#> # A tibble: 1 × 5
#> am n prop lcl ucl
#> <dbl> <int> <dbl> <dbl> <dbl>
#> 1 0 19 0.594 0.409 0.755Default output appends lcl/ucl for
ungrouped results and lcl_group/ucl_group for
grouped results. If overall proportions are present, the helpers also
compute lcl_total/ucl_total.
A plain data frame is supported with explicit denominators. The
required columns are n, n_total, and
prop (or percent), or n,
n_group, and prop_group (or
percent_group). Counts and denominators must be finite
nonnegative integers, with n <= denominator. Displayed
proportions must agree with them.
plain <- data.frame(n = c(19L, 13L), n_total = 32L, prop = c(19, 13) / 32)
wald_ci(plain)
#> n n_total prop lcl ucl
#> 1 19 32 0.59375 0.4138446 0.7736554
#> 2 13 32 0.40625 0.2263446 0.5861554
logit_ci(plain)
#> n n_total prop lcl ucl
#> 1 19 32 0.59375 0.4094225 0.7549765
#> 2 13 32 0.40625 0.2450235 0.5905775
plain_group <- data.frame(n = c(3L, 7L), n_group = 10L, prop_group = c(.3, .7))
wald_ci(plain_group)
#> n n_group prop_group lcl_group ucl_group
#> 1 3 10 0.3 -0.04555021 0.6455502
#> 2 7 10 0.7 0.35444979 1.0455502
logit_ci(plain_group)
#> n n_group prop_group lcl_group ucl_group
#> 1 3 10 0.3 0.07636623 0.6895849
#> 2 7 10 0.7 0.31041511 0.9236338The helpers never infer an absent original denominator by summing a
potentially filtered count table. If metadata has been removed, provide
the original n_total explicitly.
For continuity, both methods use the existing package convention:
p = n / N
SE = sqrt(p * (1 - p) / (N - 1))
t = qt((1 + percent_ci / 100) / 2, df = N - 1)
For a grouped interval, N is the group size in both formulas. For an overall interval, it is the total number of input observations. The former implementation used total-sample degrees of freedom for within-group limits; those limits change with this correction.
wald_ci() returns p - t * SE and
p + t * SE. logit_ci() applies
t * SE / (p * (1 - p)) on the log-odds scale, then
transforms back with plogis(). This is a t-based
compatibility convention; it is not the usual normal-z binomial Wald
formula.
percent_ci = 95 is the default; use a number strictly
between 0 and 100 to change it. Larger levels produce wider intervals
for interior proportions.
counts |> wald_ci(percent_ci = 90)
#> # A tibble: 2 × 5
#> am n prop lcl ucl
#> <dbl> <int> <dbl> <dbl> <dbl>
#> 1 0 19 0.594 0.444 0.743
#> 2 1 13 0.406 0.257 0.556
counts |> wald_ci(percent_ci = 99)
#> # A tibble: 2 × 5
#> am n prop lcl ucl
#> <dbl> <int> <dbl> <dbl> <dbl>
#> 1 0 19 0.594 0.352 0.836
#> 2 1 13 0.406 0.164 0.648
counts |> logit_ci(percent_ci = 90)
#> # A tibble: 2 × 5
#> am n prop lcl ucl
#> <dbl> <int> <dbl> <dbl> <dbl>
#> 1 0 19 0.594 0.440 0.731
#> 2 1 13 0.406 0.269 0.560
counts |> logit_ci(percent_ci = 99)
#> # A tibble: 2 × 5
#> am n prop lcl ucl
#> <dbl> <int> <dbl> <dbl> <dbl>
#> 1 0 19 0.594 0.349 0.799
#> 2 1 13 0.406 0.201 0.651
# Independently verify the first logit lower bound.
p <- 19 / 32
expected <- plogis(qlogis(p) - qt(.975, 31) *
sqrt(p * (1 - p) / 31) / (p * (1 - p)))
stopifnot(isTRUE(all.equal(logit_ci(counts)$lcl[1], expected)))se = TRUE appends se,
se_group, and/or se_total.
critical_value = TRUE appends t_crit,
t_crit_group, and/or t_crit_total. Each option
defaults to FALSE; either can be enabled without the
other.
counts |> wald_ci(se = TRUE)
#> # A tibble: 2 × 6
#> am n prop se lcl ucl
#> <dbl> <int> <dbl> <dbl> <dbl> <dbl>
#> 1 0 19 0.594 0.0882 0.414 0.774
#> 2 1 13 0.406 0.0882 0.226 0.586
counts |> wald_ci(critical_value = TRUE)
#> # A tibble: 2 × 6
#> am n prop t_crit lcl ucl
#> <dbl> <int> <dbl> <dbl> <dbl> <dbl>
#> 1 0 19 0.594 2.04 0.414 0.774
#> 2 1 13 0.406 2.04 0.226 0.586
counts |> logit_ci(se = TRUE)
#> # A tibble: 2 × 6
#> am n prop se lcl ucl
#> <dbl> <int> <dbl> <dbl> <dbl> <dbl>
#> 1 0 19 0.594 0.0882 0.409 0.755
#> 2 1 13 0.406 0.0882 0.245 0.591
counts |> logit_ci(critical_value = TRUE)
#> # A tibble: 2 × 6
#> am n prop t_crit lcl ucl
#> <dbl> <int> <dbl> <dbl> <dbl> <dbl>
#> 1 0 19 0.594 2.04 0.409 0.755
#> 2 1 13 0.406 2.04 0.245 0.591
grouped <- mtcars |> group_by(cyl) |> freq_tbl(am, overall = TRUE)
grouped |> wald_ci(se = TRUE, critical_value = TRUE)
#> # A tibble: 6 × 15
#> cyl am n n_group prop_group n_total prop_total se_group t_crit_group
#> <dbl> <dbl> <int> <int> <dbl> <int> <dbl> <dbl> <dbl>
#> 1 4 0 3 11 0.273 32 0.0938 0.141 2.23
#> 2 4 1 8 11 0.727 32 0.25 0.141 2.23
#> 3 6 0 4 7 0.571 32 0.125 0.202 2.45
#> 4 6 1 3 7 0.429 32 0.0938 0.202 2.45
#> 5 8 0 12 14 0.857 32 0.375 0.0971 2.16
#> 6 8 1 2 14 0.143 32 0.0625 0.0971 2.16
#> # ℹ 6 more variables: lcl_group <dbl>, ucl_group <dbl>, se_total <dbl>,
#> # t_crit_total <dbl>, lcl_total <dbl>, ucl_total <dbl>
grouped |> logit_ci(se = TRUE, critical_value = TRUE)
#> # A tibble: 6 × 15
#> cyl am n n_group prop_group n_total prop_total se_group t_crit_group
#> <dbl> <dbl> <int> <int> <dbl> <int> <dbl> <dbl> <dbl>
#> 1 4 0 3 11 0.273 32 0.0938 0.141 2.23
#> 2 4 1 8 11 0.727 32 0.25 0.141 2.23
#> 3 6 0 4 7 0.571 32 0.125 0.202 2.45
#> 4 6 1 3 7 0.429 32 0.0938 0.202 2.45
#> 5 8 0 12 14 0.857 32 0.375 0.0971 2.16
#> 6 8 1 2 14 0.143 32 0.0625 0.0971 2.16
#> # ℹ 6 more variables: lcl_group <dbl>, ucl_group <dbl>, se_total <dbl>,
#> # t_crit_total <dbl>, lcl_total <dbl>, ucl_total <dbl>When counts use percent = TRUE, bounds and standard
errors also use the 0–100 scale. Critical values are unchanged. The
helpers obtain the scale from the prop* or
percent* columns; percent_ci always specifies
the confidence level, not the display scale.
pct <- mtcars |> group_by(cyl) |> freq_tbl(am, percent = TRUE, overall = TRUE)
pct |> wald_ci(se = TRUE, critical_value = TRUE)
#> # A tibble: 6 × 15
#> cyl am n n_group percent_group n_total percent_total se_group
#> <dbl> <dbl> <int> <int> <dbl> <int> <dbl> <dbl>
#> 1 4 0 3 11 27.3 32 9.38 14.1
#> 2 4 1 8 11 72.7 32 25 14.1
#> 3 6 0 4 7 57.1 32 12.5 20.2
#> 4 6 1 3 7 42.9 32 9.38 20.2
#> 5 8 0 12 14 85.7 32 37.5 9.71
#> 6 8 1 2 14 14.3 32 6.25 9.71
#> # ℹ 7 more variables: t_crit_group <dbl>, lcl_group <dbl>, ucl_group <dbl>,
#> # se_total <dbl>, t_crit_total <dbl>, lcl_total <dbl>, ucl_total <dbl>
pct |> logit_ci(se = TRUE, critical_value = TRUE)
#> # A tibble: 6 × 15
#> cyl am n n_group percent_group n_total percent_total se_group
#> <dbl> <dbl> <int> <int> <dbl> <int> <dbl> <dbl>
#> 1 4 0 3 11 27.3 32 9.38 14.1
#> 2 4 1 8 11 72.7 32 25 14.1
#> 3 6 0 4 7 57.1 32 12.5 20.2
#> 4 6 1 3 7 42.9 32 9.38 20.2
#> 5 8 0 12 14 85.7 32 37.5 9.71
#> 6 8 1 2 14 14.3 32 6.25 9.71
#> # ℹ 7 more variables: t_crit_group <dbl>, lcl_group <dbl>, ucl_group <dbl>,
#> # se_total <dbl>, t_crit_total <dbl>, lcl_total <dbl>, ucl_total <dbl>
# Replacing Wald intervals with logit intervals does not multiply by 100 again.
pct |> wald_ci(se = TRUE) |> logit_ci()
#> # A tibble: 6 × 11
#> cyl am n n_group percent_group n_total percent_total lcl_group
#> <dbl> <dbl> <int> <int> <dbl> <int> <dbl> <dbl>
#> 1 4 0 3 11 27.3 32 9.38 7.16
#> 2 4 1 8 11 72.7 32 25 35.4
#> 3 6 0 4 7 57.1 32 12.5 15.0
#> 4 6 1 3 7 42.9 32 9.38 9.06
#> 5 8 0 12 14 85.7 32 37.5 52.0
#> 6 8 1 2 14 14.3 32 6.25 2.92
#> # ℹ 3 more variables: ucl_group <dbl>, lcl_total <dbl>, ucl_total <dbl>Repeated calls replace interval columns, and unrequested
se* and t_crit* columns are removed. Renaming
proportions or removing denominator columns/metadata can make the table
unsuitable for subsequent calculations. Use formatting only after all
computations.
At p = 0 or 1, logit limits are missing because the logit is infinite; no continuity correction is applied. Wald limits equal p when N > 1. For interior values, Wald intervals can extend beyond [0, 1] and are not clipped. N = 0 gives an undefined proportion. N <= 1 gives missing standard errors, critical values, and limits in either method.
boundary <- data.frame(y = factor(rep("yes", 4), levels = c("no", "yes")))
freq_tbl(boundary, y) |> logit_ci()
#> # A tibble: 2 × 5
#> y n prop lcl ucl
#> <fct> <int> <dbl> <dbl> <dbl>
#> 1 no 0 0 NA NA
#> 2 yes 4 1 NA NA
freq_tbl(boundary, y) |> wald_ci()
#> # A tibble: 2 × 5
#> y n prop lcl ucl
#> <fct> <int> <dbl> <dbl> <dbl>
#> 1 no 0 0 0 0
#> 2 yes 4 1 1 1
freq_tbl(boundary[1, , drop = FALSE], y) |> wald_ci(se = TRUE)
#> # A tibble: 2 × 6
#> y n prop se lcl ucl
#> <fct> <int> <dbl> <dbl> <dbl> <dbl>
#> 1 no 0 0 NA NA NA
#> 2 yes 1 1 NA NA NA
freq_tbl(boundary[FALSE, , drop = FALSE], y) |> logit_ci()
#> # A tibble: 2 × 5
#> y n prop lcl ucl
#> <fct> <int> <dbl> <dbl> <dbl>
#> 1 no 0 NA NA NA
#> 2 yes 0 NA NA NA
wald_ci(data.frame(n = 1, n_total = 3, prop = 1/3))
#> n n_total prop lcl ucl
#> 1 1 3 0.3333333 -1.100884 1.767551Missing logit bounds do not mean the population proportion is known to be exactly zero or one. These approximations need care with small or extreme counts; the package does not automatically select another interval method.
counts |> logit_ci(percent_ci = 100)
#> Error:
#> ! percent_ci must be a number strictly between 0 and 100.wald_ci(data.frame(n = 6, n_total = 5, prop = 1.2))
#> Error:
#> ! n_total must contain finite integer denominators at least as large as n.logit_ci(data.frame(n = 19, prop = 19/32))
#> Error:
#> ! Missing n_total; retain freqtables metadata or supply the original denominator.See R’s Student t distribution documentation for the critical-value function used here.