Skip to contents

Extension of bounds_sharp() to fuzzy designs where treatment take‑up jumps (up or down) at the cutoff but is not deterministic. The estimator solves a linear‑fractional programme as described in Rosenman et al. (2025) using CVXR. Manipulation to the right of the cutoff is bounded by true_counts obtained from .density_estimation().

Usage

bounds_fuzzy(
  x,
  y,
  z,
  cutoff,
  true_counts,
  weights = NULL,
  poly_order = 1L,
  bounds = c("both", "lower", "upper"),
  treat_direction = c("increase", "decrease"),
  solver = getOption("rdpartial.solver", "ECOS"),
  runVarPlots = FALSE,
  ylab = NULL,
  xlab = NULL,
  ...
)

Arguments

x

Numeric running variable.

y

Numeric outcome variable.

z

Numeric vector (0/1) – treatment take‑up.

cutoff

Numeric threshold separating control (< cutoff) from treatment (>= cutoff).

true_counts

Data.frame with columns x (support points >= cutoff) and n_true (estimated number of non-manipulated observations at each support point).

weights

Numeric vector of observation weights (optional).

poly_order

Integer polynomial order for local regression (default 1L).

bounds

Character – which bound(s) to return; one of "both" (default), "lower", "upper".

treat_direction

Either "increase" (treatment prob. jumps up at the cutoff – standard case) or "decrease" (jumps down, e.g. donor deferral example). Determines the sign of the optimisation objective.

solver

Character – CVXR solver (default taken from getOption("rdpartial.solver"); package default is "ECOS").

runVarPlots

Logical. If TRUE, generate running variable plots showing outcomes and treatment probabilities.

ylab

Character. Label for the y-axis in plots.

xlab

Character. Label for the x-axis in plots.

...

Further arguments passed on to CVXR::solve(). Use this to override CVXR tolerances or provide a different solver control list.

Value

Numeric vector of length 2 (lower, upper) when bounds = "both"; otherwise a single numeric. CVXR solutions attached as attributes opt_lwr/opt_upr.

Required Inputs

  • x – running variable.

  • y – outcome.

  • z – realised treatment indicator (0/1).

  • cutoff – threshold.

  • true_countsdata.frame(x, n_true) of non‑manipulated counts.

Examples

set.seed(321)
n      <- 6000
x      <- rpois(n, 15)
cutoff <- 16
z      <- rbinom(n, 1, prob = ifelse(x >= cutoff, 0.8, 0.2))
y0     <- 0.2 * x + rnorm(n)
y1     <- y0 + 2
y      <- ifelse(z == 1, y1, y0)

freq <- table(factor(x[x >= cutoff], levels = cutoff:max(x)))
tc   <- data.frame(
  x      = as.integer(names(freq)),
  n_true = round(as.vector(freq) * 0.85)
)

bounds_fuzzy(x, y, z, cutoff, true_counts = tc)
#>    lower    upper 
#> 1.480718 2.672821 
#> attr(,"opt_lwr")
#>  Status: optimal
#>  Objective value: 1.480718
#>  Solver: ECOS
#> → <me>$getValue(x) returns value of variable/constraint x
#> attr(,"opt_upr")
#>  Status: optimal
#>  Objective value: 2.672821
#>  Solver: ECOS
#> → <me>$getValue(x) returns value of variable/constraint x