Nothing
test_that("categorical exposure detection works correctly", {
# Factor with 3 levels
exposure_3 <- factor(c("A", "B", "C", "A", "B", "C"))
set.seed(123)
ps_matrix <- matrix(runif(18), ncol = 3)
ps_matrix <- ps_matrix / rowSums(ps_matrix) # Normalize
colnames(ps_matrix) <- levels(exposure_3)
withr::local_options(propensity.quiet = FALSE)
expect_message(
wt_ate(ps_matrix, exposure_3),
"Treating `.exposure` as categorical"
)
# Character vector converted to factor
exposure_char <- c("low", "med", "high", "low", "med", "high")
ps_matrix <- matrix(
c(
0.5,
0.3,
0.2,
0.2,
0.5,
0.3,
0.1,
0.2,
0.7,
0.6,
0.3,
0.1,
0.3,
0.4,
0.3,
0.2,
0.2,
0.6
),
ncol = 3,
byrow = TRUE
)
colnames(ps_matrix) <- unique(sort(exposure_char))
withr::local_options(propensity.quiet = FALSE)
expect_message(
wt_ate(ps_matrix, exposure_char),
"Treating `.exposure` as categorical"
)
})
test_that("categorical exposure validation works", {
# Only 2 levels should error
exposure_2 <- factor(c("A", "B", "A", "B"))
ps_matrix_2 <- matrix(c(0.7, 0.3, 0.4, 0.6, 0.8, 0.2, 0.5, 0.5), ncol = 2)
expect_propensity_error(
wt_ate(ps_matrix_2, exposure_2, exposure_type = "categorical")
)
# Invalid focal category should error
exposure_3 <- factor(c("A", "B", "C", "A"))
ps_matrix_3 <- matrix(runif(12), ncol = 3)
expect_propensity_error(
wt_att(ps_matrix_3, exposure_3, .focal_level = "D")
)
})
test_that("propensity score matrix validation works", {
exposure <- factor(c("A", "B", "C", "A", "B"))
# Not a matrix or data.frame
expect_propensity_error(
wt_ate(c(0.3, 0.4, 0.3), exposure, exposure_type = "categorical")
)
# Wrong number of rows
ps_wrong_rows <- matrix(runif(9), ncol = 3)
expect_propensity_error(
wt_ate(ps_wrong_rows, exposure, exposure_type = "categorical")
)
# Wrong number of columns
ps_wrong_cols <- matrix(runif(10), ncol = 2)
expect_propensity_error(
wt_ate(ps_wrong_cols, exposure, exposure_type = "categorical")
)
# Rows don't sum to 1
ps_bad_sum <- matrix(
c(
0.3,
0.3,
0.3, # Sums to 0.9
0.4,
0.4,
0.2,
0.2,
0.3,
0.5,
0.3,
0.3,
0.4,
0.25,
0.25,
0.5
),
ncol = 3,
byrow = TRUE
)
expect_propensity_error(
wt_ate(ps_bad_sum, exposure, exposure_type = "categorical")
)
# Invalid probabilities
ps_invalid <- matrix(
c(
0.5,
0.6,
-0.1, # Negative value
0.3,
0.3,
0.4,
0.2,
0.3,
0.5,
0.3,
0.3,
0.4,
0.25,
0.25,
0.5
),
ncol = 3,
byrow = TRUE
)
expect_propensity_error(
wt_ate(ps_invalid, exposure, exposure_type = "categorical")
)
})
test_that("ATE weights work for categorical exposures", {
set.seed(123)
exposure <- factor(c("A", "B", "C", "A", "B", "C"))
ps_matrix <- matrix(
c(
0.5,
0.3,
0.2,
0.2,
0.5,
0.3,
0.1,
0.2,
0.7,
0.6,
0.3,
0.1,
0.3,
0.4,
0.3,
0.2,
0.2,
0.6
),
ncol = 3,
byrow = TRUE
)
colnames(ps_matrix) <- levels(exposure)
weights <- wt_ate(ps_matrix, exposure, exposure_type = "categorical")
# Check structure
expect_s3_class(weights, "psw")
expect_equal(estimand(weights), "ate")
expect_equal(length(weights), 6)
# Check attributes
expect_equal(attr(weights, "n_categories"), 3)
expect_equal(attr(weights, "category_names"), c("A", "B", "C"))
# Check weight calculations
# For ATE, h(e) = 1, so w_i = 1 / e_{i,Z_i}
expected_weights <- c(
1 / 0.5, # A: 1/0.5 = 2
1 / 0.5, # B: 1/0.5 = 2
1 / 0.7, # C: 1/0.7 = 1.43
1 / 0.6, # A: 1/0.6 = 1.67
1 / 0.4, # B: 1/0.4 = 2.5
1 / 0.6 # C: 1/0.6 = 1.67
)
expect_equal(as.numeric(weights), expected_weights, tolerance = 0.01)
})
test_that("ATE stabilization works for categorical exposures", {
exposure <- factor(c("A", "B", "C", "A", "B", "C"))
ps_matrix <- matrix(
c(
0.5,
0.3,
0.2,
0.2,
0.5,
0.3,
0.1,
0.2,
0.7,
0.6,
0.3,
0.1,
0.3,
0.4,
0.3,
0.2,
0.2,
0.6
),
ncol = 3,
byrow = TRUE
)
colnames(ps_matrix) <- levels(exposure)
weights_stab <- wt_ate(
ps_matrix,
exposure,
exposure_type = "categorical",
stabilize = TRUE
)
expect_true(is_stabilized(weights_stab))
# Check that stabilization is applied correctly
# Marginal probabilities: A=2/6, B=2/6, C=2/6 = 1/3 each
expected_weights <- c(
(1 / 3) / 0.5, # A
(1 / 3) / 0.5, # B
(1 / 3) / 0.7, # C
(1 / 3) / 0.6, # A
(1 / 3) / 0.4, # B
(1 / 3) / 0.6 # C
)
expect_equal(as.numeric(weights_stab), expected_weights, tolerance = 0.01)
})
test_that("ATT weights work for categorical exposures", {
exposure <- factor(c("A", "B", "C", "A", "B", "C"))
ps_matrix <- matrix(
c(
0.5,
0.3,
0.2,
0.2,
0.5,
0.3,
0.1,
0.2,
0.7,
0.6,
0.3,
0.1,
0.3,
0.4,
0.3,
0.2,
0.2,
0.6
),
ncol = 3,
byrow = TRUE
)
colnames(ps_matrix) <- levels(exposure)
# ATT with .focal_level = "A"
weights_att_a <- wt_att(
ps_matrix,
exposure,
.focal_level = "A",
exposure_type = "categorical"
)
expect_equal(estimand(weights_att_a), "att")
expect_equal(attr(weights_att_a, "focal_category"), "A")
# For ATT, h(e) = e_focal
# So w_i = e_{i,A} / e_{i,Z_i}
expected_weights_a <- c(
0.5 / 0.5, # A: 0.5/0.5 = 1
0.2 / 0.5, # B: 0.2/0.5 = 0.4
0.1 / 0.7, # C: 0.1/0.7 = 0.143
0.6 / 0.6, # A: 0.6/0.6 = 1
0.3 / 0.4, # B: 0.3/0.4 = 0.75
0.2 / 0.6 # C: 0.2/0.6 = 0.333
)
expect_equal(as.numeric(weights_att_a), expected_weights_a, tolerance = 0.01)
# ATT requires focal for categorical
expect_propensity_error(
wt_att(ps_matrix, exposure, exposure_type = "categorical")
)
})
test_that("ATU weights work for categorical exposures", {
exposure <- factor(c("A", "B", "C", "A", "B", "C"))
ps_matrix <- matrix(
c(
0.5,
0.3,
0.2,
0.2,
0.5,
0.3,
0.1,
0.2,
0.7,
0.6,
0.3,
0.1,
0.3,
0.4,
0.3,
0.2,
0.2,
0.6
),
ncol = 3,
byrow = TRUE
)
colnames(ps_matrix) <- levels(exposure)
# ATU with .focal_level = "A" (weights for non-A)
weights_atu_a <- wt_atu(
ps_matrix,
exposure,
.focal_level = "A",
exposure_type = "categorical"
)
expect_equal(estimand(weights_atu_a), "atu")
expect_equal(attr(weights_atu_a, "focal_category"), "A")
# For ATU, h(e) = 1 - e_focal
# So w_i = (1 - e_{i,A}) / e_{i,Z_i}
expected_weights_a <- c(
(1 - 0.5) / 0.5, # A: 0.5/0.5 = 1
(1 - 0.2) / 0.5, # B: 0.8/0.5 = 1.6
(1 - 0.1) / 0.7, # C: 0.9/0.7 = 1.286
(1 - 0.6) / 0.6, # A: 0.4/0.6 = 0.667
(1 - 0.3) / 0.4, # B: 0.7/0.4 = 1.75
(1 - 0.2) / 0.6 # C: 0.8/0.6 = 1.333
)
expect_equal(as.numeric(weights_atu_a), expected_weights_a, tolerance = 0.01)
})
test_that("ATM weights work for categorical exposures", {
exposure <- factor(c("A", "B", "C", "A", "B", "C"))
ps_matrix <- matrix(
c(
0.5,
0.3,
0.2,
0.2,
0.5,
0.3,
0.1,
0.2,
0.7,
0.6,
0.3,
0.1,
0.3,
0.4,
0.3,
0.2,
0.2,
0.6
),
ncol = 3,
byrow = TRUE
)
colnames(ps_matrix) <- levels(exposure)
weights_atm <- wt_atm(ps_matrix, exposure, exposure_type = "categorical")
expect_equal(estimand(weights_atm), "atm")
# For ATM, h(e) = min(e_1, ..., e_K)
# So w_i = min(e_i) / e_{i,Z_i}
expected_weights <- c(
0.2 / 0.5, # A: min(0.5,0.3,0.2)=0.2, 0.2/0.5 = 0.4
0.2 / 0.5, # B: min(0.2,0.5,0.3)=0.2, 0.2/0.5 = 0.4
0.1 / 0.7, # C: min(0.1,0.2,0.7)=0.1, 0.1/0.7 = 0.143
0.1 / 0.6, # A: min(0.6,0.3,0.1)=0.1, 0.1/0.6 = 0.167
0.3 / 0.4, # B: min(0.3,0.4,0.3)=0.3, 0.3/0.4 = 0.75
0.2 / 0.6 # C: min(0.2,0.2,0.6)=0.2, 0.2/0.6 = 0.333
)
expect_equal(as.numeric(weights_atm), expected_weights, tolerance = 0.01)
})
test_that("ATO weights work for categorical exposures", {
exposure <- factor(c("A", "B", "C", "A", "B", "C"))
ps_matrix <- matrix(
c(
0.5,
0.3,
0.2,
0.2,
0.5,
0.3,
0.1,
0.2,
0.7,
0.6,
0.3,
0.1,
0.3,
0.4,
0.3,
0.2,
0.2,
0.6
),
ncol = 3,
byrow = TRUE
)
colnames(ps_matrix) <- levels(exposure)
weights_ato <- wt_ato(ps_matrix, exposure, exposure_type = "categorical")
expect_equal(estimand(weights_ato), "ato")
# For ATO, h(e) = 1 / sum(1/e_k) - reciprocal of harmonic mean denominator
# So w_i = h(e_i) / e_{i,Z_i}
h_vals <- numeric(6)
h_vals[1] <- 1 / (1 / 0.5 + 1 / 0.3 + 1 / 0.2) # 0.115
h_vals[2] <- 1 / (1 / 0.2 + 1 / 0.5 + 1 / 0.3) # 0.095
h_vals[3] <- 1 / (1 / 0.1 + 1 / 0.2 + 1 / 0.7) # 0.062
h_vals[4] <- 1 / (1 / 0.6 + 1 / 0.3 + 1 / 0.1) # 0.067
h_vals[5] <- 1 / (1 / 0.3 + 1 / 0.4 + 1 / 0.3) # 0.109
h_vals[6] <- 1 / (1 / 0.2 + 1 / 0.2 + 1 / 0.6) # 0.086
expected_weights <- c(
h_vals[1] / 0.5,
h_vals[2] / 0.5,
h_vals[3] / 0.7,
h_vals[4] / 0.6,
h_vals[5] / 0.4,
h_vals[6] / 0.6
)
expect_equal(as.numeric(weights_ato), expected_weights, tolerance = 0.01)
})
test_that("Entropy weights work for categorical exposures", {
exposure <- factor(c("A", "B", "C", "A", "B", "C"))
ps_matrix <- matrix(
c(
0.5,
0.3,
0.2,
0.2,
0.5,
0.3,
0.1,
0.2,
0.7,
0.6,
0.3,
0.1,
0.3,
0.4,
0.3,
0.2,
0.2,
0.6
),
ncol = 3,
byrow = TRUE
)
colnames(ps_matrix) <- levels(exposure)
weights_entropy <- wt_entropy(
ps_matrix,
exposure,
exposure_type = "categorical"
)
expect_equal(estimand(weights_entropy), "entropy")
# For Entropy, h(e) = -sum(e_k * log(e_k))
# Calculate entropy for each observation
calc_entropy <- function(probs) {
-sum(probs * log(probs))
}
h_vals <- apply(ps_matrix, 1, calc_entropy)
expected_weights <- c(
h_vals[1] / 0.5,
h_vals[2] / 0.5,
h_vals[3] / 0.7,
h_vals[4] / 0.6,
h_vals[5] / 0.4,
h_vals[6] / 0.6
)
expect_equal(as.numeric(weights_entropy), expected_weights, tolerance = 0.01)
})
test_that("data.frame input works for categorical exposures", {
exposure <- factor(c("A", "B", "C", "A", "B", "C"))
# Test with plain column names
ps_df <- data.frame(
A = c(0.5, 0.2, 0.1, 0.6, 0.3, 0.2),
B = c(0.3, 0.5, 0.2, 0.3, 0.4, 0.2),
C = c(0.2, 0.3, 0.7, 0.1, 0.3, 0.6)
)
weights_df <- wt_ate(ps_df, exposure, exposure_type = "categorical")
# Test with parsnip-style column names
ps_df_parsnip <- data.frame(
.pred_A = c(0.5, 0.2, 0.1, 0.6, 0.3, 0.2),
.pred_B = c(0.3, 0.5, 0.2, 0.3, 0.4, 0.2),
.pred_C = c(0.2, 0.3, 0.7, 0.1, 0.3, 0.6)
)
weights_df_parsnip <- wt_ate(
ps_df_parsnip,
exposure,
exposure_type = "categorical"
)
# Both should give same results
expect_equal(as.numeric(weights_df), as.numeric(weights_df_parsnip))
# Compare to matrix input
ps_matrix <- as.matrix(ps_df)
weights_matrix <- wt_ate(ps_matrix, exposure, exposure_type = "categorical")
expect_equal(as.numeric(weights_df), as.numeric(weights_matrix))
})
test_that("stabilization works for ATE categorical exposures", {
exposure <- factor(c("A", "B", "C", "A", "B", "C"))
set.seed(123)
ps_matrix <- matrix(runif(18), ncol = 3)
ps_matrix <- ps_matrix / rowSums(ps_matrix) # Normalize
colnames(ps_matrix) <- levels(exposure)
# Test that stabilization works for ATE
expect_no_error(
wt_ate(ps_matrix, exposure, exposure_type = "categorical", stabilize = TRUE)
)
})
test_that("categorical weights handle different column orders correctly", {
set.seed(456)
n <- 100
# Create treatment with 3 categories
trt <- factor(sample(c("low", "medium", "high"), n, replace = TRUE))
# Create propensity score matrix in CORRECT order (matching factor levels)
ps_correct <- matrix(runif(n * 3), nrow = n, ncol = 3)
ps_correct <- ps_correct / rowSums(ps_correct)
colnames(ps_correct) <- levels(trt) # "high", "low", "medium" (alphabetical)
# Create same matrix with columns in WRONG order
ps_wrong <- ps_correct[, c("medium", "high", "low")]
# Calculate weights with both matrices
w_ate_correct <- wt_ate(ps_correct, trt, exposure_type = "categorical")
w_ate_wrong <- wt_ate(ps_wrong, trt, exposure_type = "categorical")
# Weights should be identical after reordering
expect_equal(as.numeric(w_ate_correct), as.numeric(w_ate_wrong))
# Test with ATT
w_att_correct <- wt_att(
ps_correct,
trt,
exposure_type = "categorical",
.focal_level = "medium"
)
w_att_wrong <- wt_att(
ps_wrong,
trt,
exposure_type = "categorical",
.focal_level = "medium"
)
expect_equal(as.numeric(w_att_correct), as.numeric(w_att_wrong))
# Verify ATT weights are correct (focal group should have weight 1)
expect_equal(unique(as.numeric(w_att_correct[trt == "medium"])), 1)
expect_equal(unique(as.numeric(w_att_wrong[trt == "medium"])), 1)
})
test_that("categorical weights work with parsnip-style column names", {
set.seed(789)
n <- 50
trt <- factor(sample(c("A", "B", "C"), n, replace = TRUE))
# Create matrix with parsnip-style names
ps_matrix <- matrix(runif(n * 3), nrow = n, ncol = 3)
ps_matrix <- ps_matrix / rowSums(ps_matrix)
colnames(ps_matrix) <- c(".pred_A", ".pred_B", ".pred_C")
# Should work without error
expect_no_error(
w_ate <- wt_ate(ps_matrix, trt, exposure_type = "categorical")
)
# Test focal matching works correctly
expect_no_error(
w_att <- wt_att(
ps_matrix,
trt,
exposure_type = "categorical",
.focal_level = "B"
)
)
# Focal group should have weight 1
expect_equal(unique(as.numeric(w_att[trt == "B"])), 1)
})
test_that("categorical weights error on mismatched column names", {
n <- 50
trt <- factor(sample(c("A", "B", "C"), n, replace = TRUE))
# Matrix with wrong column names
ps_matrix <- matrix(runif(n * 3), nrow = n, ncol = 3)
ps_matrix <- ps_matrix / rowSums(ps_matrix)
colnames(ps_matrix) <- c("X", "Y", "Z")
expect_propensity_error(
wt_ate(ps_matrix, trt, exposure_type = "categorical")
)
})
test_that("categorical weights warn on unnamed columns", {
n <- 50
trt <- factor(sample(c("A", "B", "C"), n, replace = TRUE))
# Matrix with no column names
ps_matrix <- matrix(runif(n * 3), nrow = n, ncol = 3)
ps_matrix <- ps_matrix / rowSums(ps_matrix)
# Test warning for all weight functions
expect_propensity_warning(
wt_ate(ps_matrix, trt, exposure_type = "categorical")
)
expect_propensity_warning(
wt_att(ps_matrix, trt, exposure_type = "categorical", .focal_level = "A")
)
expect_propensity_warning(
wt_atu(ps_matrix, trt, exposure_type = "categorical", .focal_level = "A")
)
expect_propensity_warning(
wt_atm(ps_matrix, trt, exposure_type = "categorical")
)
expect_propensity_warning(
wt_ato(ps_matrix, trt, exposure_type = "categorical")
)
expect_propensity_warning(
wt_entropy(ps_matrix, trt, exposure_type = "categorical")
)
})
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.