Skip to contents

Native optimal full matching (Rosenbaum 1991; Hansen 2004): every unit is placed in a matched set of one treated unit and one or more controls, or one control and one or more treated units, so that the total within-set propensity-score distance is the smallest possible. It is computed exactly: an unrestricted full match is a minimum-weight edge cover of the treated-control distance graph, found here through a maximum-weight matching (a rectangular assignment problem, solved by shortest augmenting paths). The distance is the absolute difference of propensity scores from a logistic regression on covariates (or ps), and the weights are those of MatchIt::matchit(method = "full", estimand = "ATT"): 1 for treated units, treated/control counts of the set for controls, each group rescaled to its size. Cross-validated against MatchIt + optmatch in tests/cross (optmatch solves on distances rounded to a tolerance, so its total is never below this one). No MatchIt/optmatch at runtime.

Usage

morie_matching_full(data, treatment, covariates, ps = NULL, n_subclasses = 10L)

Arguments

data

Data frame.

treatment

Binary treatment column name.

covariates

Character vector of covariates.

ps

Optional propensity scores, one per row of data (or named by its row names); estimated by logistic regression when NULL.

n_subclasses

Ignored (the number of matched sets is part of the optimum); kept for back-compatibility.

Value

A list of class morie_match_result: matched_data (every matched row, with distance, weights and subclass columns), match_pairs (the treated-control links of the sets, with their distances) and details$total_distance.

References

Rosenbaum, P. R. (1991). A characterization of optimal designs for observational studies. JRSS B, 53(3), 597–610.

Hansen, B. B. (2004). Full matching in an observational study of coaching for the SAT. JASA, 99(467), 609–618.

Examples

set.seed(1)
df <- data.frame(y = rnorm(200), d = rbinom(200, 1, 0.4),
                 x1 = rnorm(200), x2 = rnorm(200))
fm <- morie_matching_full(df, "d", c("x1", "x2"))
fm$details$total_distance
#> [1] 0.3162015
head(fm$matched_data[, c("d", "subclass", "weights")])
#>   d subclass   weights
#> 1 1        1 1.0000000
#> 2 0        2 0.3247126
#> 3 1        3 1.0000000
#> 4 1        4 1.0000000
#> 5 1        5 1.0000000
#> 6 1        6 1.0000000