Nothing
# Test Manual Weight Calculations with ALL vctrs Classes
#
# This file comprehensively tests manual calculations for ALL vctrs classes:
# - psw: propensity score weights
# - ps_trim: trimmed propensity scores
# - ps_trunc: truncated propensity scores
library(testthat)
# =============================================================================
# Manual Calculations with ps_trim Class
# =============================================================================
test_that("All arithmetic operations work with ps_trim", {
set.seed(123)
ps <- runif(100, 0.1, 0.9)
ps_trimmed <- ps_trim(ps, method = "ps", lower = 0.2, upper = 0.8)
# Basic arithmetic operations
expect_type(ps_trimmed + 0.1, "double")
expect_type(ps_trimmed - 0.1, "double")
expect_type(ps_trimmed * 2, "double")
expect_type(ps_trimmed / 2, "double")
expect_type(ps_trimmed^2, "double")
# Reciprocals
expect_type(1 / ps_trimmed, "double")
expect_type(1 / (1 - ps_trimmed), "double")
# Complex arithmetic
expect_type(ps_trimmed / (1 - ps_trimmed), "double") # Odds
expect_type(ps_trimmed * (1 - ps_trimmed), "double") # Variance function
expect_type((1 - ps_trimmed) / ps_trimmed, "double") # Inverse odds
# Mathematical functions
expect_type(sqrt(ps_trimmed), "double")
expect_type(log(ps_trimmed), "double")
expect_type(exp(ps_trimmed), "double")
expect_type(abs(ps_trimmed - 0.5), "double")
# Trigonometric functions
expect_type(sin(ps_trimmed), "double")
expect_type(cos(ps_trimmed), "double")
expect_type(tan(ps_trimmed), "double")
# Logical operations
expect_type(as.numeric(ps_trimmed) > 0.5, "logical")
expect_type(as.numeric(ps_trimmed) <= 0.3, "logical")
expect_type(as.numeric(ps_trimmed) == 0.5, "logical")
})
test_that("Manual weight calculations work with ps_trim", {
set.seed(456)
ps <- runif(50, 0.15, 0.85)
exposure <- rbinom(50, 1, ps)
ps_trimmed <- ps_trim(ps, method = "ps", lower = 0.2, upper = 0.8)
# ATE weights
ate_weights <- ifelse(exposure == 1, 1 / ps_trimmed, 1 / (1 - ps_trimmed))
expect_type(ate_weights, "double")
expect_false(inherits(ate_weights, "ps_trim"))
# ATT weights
att_weights <- ifelse(exposure == 1, 1, ps_trimmed / (1 - ps_trimmed))
expect_type(att_weights, "double")
# ATC weights
atc_weights <- ifelse(exposure == 1, (1 - ps_trimmed) / ps_trimmed, 1)
expect_type(atc_weights, "double")
# ATO weights
ato_weights <- ps_trimmed * (1 - ps_trimmed)
expect_type(ato_weights, "double")
# ATM weights
atm_weights <- pmin(as.numeric(ps_trimmed), 1 - as.numeric(ps_trimmed)) /
ifelse(exposure == 1, as.numeric(ps_trimmed), 1 - as.numeric(ps_trimmed))
expect_type(atm_weights, "double")
# Entropy weights
entropy_weights <- -ps_trimmed *
log(ps_trimmed) -
(1 - ps_trimmed) * log(1 - ps_trimmed)
expect_type(entropy_weights, "double")
})
test_that("Complex calculations work with ps_trim", {
set.seed(789)
ps <- runif(40, 0.2, 0.8)
ps_trimmed <- ps_trim(ps, method = "ps", lower = 0.3, upper = 0.7)
# Logit transformation
logit_ps <- log(ps_trimmed / (1 - ps_trimmed))
expect_type(logit_ps, "double")
# Back transformation
inv_logit <- exp(logit_ps) / (1 + exp(logit_ps))
expect_type(inv_logit, "double")
# Probit transformation
probit_ps <- qnorm(ps_trimmed)
expect_type(probit_ps, "double")
# Stabilized weights calculation
mean_ps <- mean(ps_trimmed, na.rm = TRUE)
stab_factor <- mean_ps / ps_trimmed
expect_type(stab_factor, "double")
# Effective sample size components
weights_squared <- (1 / ps_trimmed)^2
expect_type(weights_squared, "double")
})
# =============================================================================
# Manual Calculations with ps_trunc Class
# =============================================================================
test_that("All arithmetic operations work with ps_trunc", {
set.seed(234)
ps <- runif(100, 0.05, 0.95)
ps_truncated <- ps_trunc(ps, method = "ps", lower = 0.1, upper = 0.9)
# Basic arithmetic operations
expect_type(ps_truncated + 0.1, "double")
expect_type(ps_truncated - 0.1, "double")
expect_type(ps_truncated * 2, "double")
expect_type(ps_truncated / 2, "double")
expect_type(ps_truncated^2, "double")
# Reciprocals (no NAs since truncation doesn't create them)
recip <- 1 / ps_truncated
expect_type(recip, "double")
expect_true(all(is.finite(recip)))
comp_recip <- 1 / (1 - ps_truncated)
expect_type(comp_recip, "double")
expect_true(all(is.finite(comp_recip)))
# Complex arithmetic
expect_type(ps_truncated / (1 - ps_truncated), "double")
expect_type(ps_truncated * (1 - ps_truncated), "double")
expect_type((1 - ps_truncated) / ps_truncated, "double")
# Mathematical functions
expect_type(sqrt(ps_truncated), "double")
expect_type(log(ps_truncated), "double")
expect_type(exp(ps_truncated), "double")
expect_type(abs(ps_truncated - 0.5), "double")
})
test_that("Manual weight calculations work with ps_trunc", {
set.seed(567)
ps <- runif(60, 0.05, 0.95)
exposure <- rbinom(60, 1, ps)
ps_truncated <- ps_trunc(ps, method = "ps", lower = 0.1, upper = 0.9)
# ATE weights - should have no NAs
ate_weights <- ifelse(exposure == 1, 1 / ps_truncated, 1 / (1 - ps_truncated))
expect_type(ate_weights, "double")
expect_false(inherits(ate_weights, "ps_trunc"))
expect_equal(sum(is.na(ate_weights)), 0)
# ATT weights
att_weights <- ifelse(exposure == 1, 1, ps_truncated / (1 - ps_truncated))
expect_type(att_weights, "double")
expect_equal(sum(is.na(att_weights)), 0)
# ATC weights
atc_weights <- ifelse(exposure == 1, (1 - ps_truncated) / ps_truncated, 1)
expect_type(atc_weights, "double")
expect_equal(sum(is.na(atc_weights)), 0)
# ATO weights
ato_weights <- ps_truncated * (1 - ps_truncated)
expect_type(ato_weights, "double")
expect_true(all(ato_weights >= 0))
expect_true(all(ato_weights <= 0.25))
# ATM weights
atm_weights <- pmin(as.numeric(ps_truncated), 1 - as.numeric(ps_truncated)) /
ifelse(
exposure == 1,
as.numeric(ps_truncated),
1 - as.numeric(ps_truncated)
)
expect_type(atm_weights, "double")
# All weights should be finite
expect_true(all(is.finite(ate_weights)))
expect_true(all(is.finite(att_weights)))
expect_true(all(is.finite(atc_weights)))
})
test_that("Complex calculations work with ps_trunc", {
set.seed(890)
ps <- runif(45, 0.1, 0.9)
ps_truncated <- ps_trunc(ps, method = "ps", lower = 0.15, upper = 0.85)
# Bounded transformations
bounded_logit <- log((ps_truncated - 0.15) / (0.85 - ps_truncated))
expect_type(bounded_logit, "double")
# Some values may be infinite if ps_truncated is exactly at bounds
expect_true(any(is.finite(bounded_logit)))
# Variance stabilizing transformation
arcsin_sqrt <- asin(sqrt(ps_truncated))
expect_type(arcsin_sqrt, "double")
# Beta distribution parameters
alpha_param <- ps_truncated * 10
beta_param <- (1 - ps_truncated) * 10
expect_type(alpha_param, "double")
expect_type(beta_param, "double")
# Entropy calculation
entropy <- -ps_truncated *
log(ps_truncated) -
(1 - ps_truncated) * log(1 - ps_truncated)
expect_type(entropy, "double")
expect_true(all(entropy >= 0))
})
# =============================================================================
# Manual Calculations with psw Class
# =============================================================================
test_that("All arithmetic operations work with psw", {
set.seed(345)
weights <- psw(runif(80, 0.5, 2.5), estimand = "ate", stabilized = TRUE)
# Basic arithmetic operations (psw preserves class)
expect_s3_class(weights + 0.5, "psw")
expect_s3_class(weights - 0.5, "psw")
expect_s3_class(weights * 2, "psw")
expect_s3_class(weights / 2, "psw")
expect_s3_class(weights^2, "psw")
# Check attributes are preserved
doubled <- weights * 2
expect_equal(estimand(doubled), "ate")
expect_true(is_stabilized(doubled))
# Reciprocals preserve class
expect_s3_class(1 / weights, "psw")
expect_s3_class(2 / weights, "psw")
# Mathematical functions return numeric
expect_type(sqrt(weights), "double")
expect_type(log(weights), "double")
expect_type(exp(weights), "double")
expect_type(abs(weights - 1), "double")
# Trigonometric functions return numeric
expect_type(sin(weights), "double")
expect_type(cos(weights), "double")
expect_type(tan(weights), "double")
})
test_that("psw weight manipulations work correctly", {
set.seed(678)
n <- 70
ps <- runif(n, 0.2, 0.8)
exposure <- rbinom(n, 1, ps)
# Create initial weights
ate_weights <- ifelse(exposure == 1, 1 / ps, 1 / (1 - ps))
weights_obj <- psw(ate_weights, estimand = "ate")
# Trimming weights by value
trimmed_weights <- weights_obj
trimmed_weights[as.numeric(weights_obj) > 10] <- 10 # Cap at 10
expect_s3_class(trimmed_weights, "psw")
# Normalizing weights
normalized_weights <- weights_obj / sum(weights_obj)
expect_s3_class(normalized_weights, "psw")
expect_equal(sum(normalized_weights), 1, tolerance = 1e-10)
# Stabilizing weights
stab_factor <- mean(exposure) / weights_obj
expect_s3_class(stab_factor, "psw")
# Weight diagnostics
cv_weights <- sd(weights_obj) / mean(weights_obj)
expect_type(cv_weights, "double")
# Effective sample size
ess <- sum(weights_obj)^2 / sum(weights_obj^2)
expect_type(ess, "double")
})
test_that("Complex calculations work with psw", {
set.seed(901)
weights <- psw(runif(50, 0.8, 1.2), estimand = "att", stabilized = FALSE)
# Weight ratio calculations
weight_ratio <- weights / mean(weights)
expect_s3_class(weight_ratio, "psw")
# Log weight calculations (returns numeric)
log_weights <- log(weights)
expect_type(log_weights, "double")
# Exponential tilting
tilted <- exp(0.5 * log_weights) # sqrt via exp-log
expect_type(tilted, "double")
# Weight percentiles via sorting
sorted_weights <- sort(weights)
expect_s3_class(sorted_weights, "psw")
# Cumulative weights
cumsum_weights <- cumsum(weights)
expect_s3_class(cumsum_weights, "psw")
# Running products
cumprod_weights <- cumprod(weights)
expect_s3_class(cumprod_weights, "psw")
})
# =============================================================================
# Summary Functions Return Numeric for ALL Classes
# =============================================================================
test_that("Summary functions return numeric for ps_trim", {
set.seed(111)
ps_trimmed <- ps_trim(
runif(30, 0.2, 0.8),
method = "ps",
lower = 0.3,
upper = 0.7
)
# All summary functions return numeric
expect_type(min(ps_trimmed, na.rm = TRUE), "double")
expect_type(max(ps_trimmed, na.rm = TRUE), "double")
expect_type(range(ps_trimmed, na.rm = TRUE), "double")
expect_type(sum(ps_trimmed, na.rm = TRUE), "double")
expect_type(prod(ps_trimmed, na.rm = TRUE), "double")
expect_type(mean(ps_trimmed, na.rm = TRUE), "double")
expect_type(median(ps_trimmed, na.rm = TRUE), "double")
# Should work in calculations
expect_type(1 / max(ps_trimmed, na.rm = TRUE), "double")
expect_type(min(ps_trimmed, na.rm = TRUE) * 100, "double")
# Multiple arguments
ps_trimmed2 <- ps_trim(
runif(30, 0.2, 0.8),
method = "ps",
lower = 0.3,
upper = 0.7
)
expect_type(min(ps_trimmed, ps_trimmed2, na.rm = TRUE), "double")
expect_type(max(ps_trimmed, ps_trimmed2, na.rm = TRUE), "double")
})
test_that("Summary functions return numeric for ps_trunc", {
set.seed(222)
ps_truncated <- ps_trunc(
runif(35, 0.1, 0.9),
method = "ps",
lower = 0.2,
upper = 0.8
)
# All summary functions return numeric
expect_type(min(ps_truncated), "double")
expect_type(max(ps_truncated), "double")
expect_type(range(ps_truncated), "double")
expect_type(sum(ps_truncated), "double")
expect_type(prod(ps_truncated), "double")
expect_type(mean(ps_truncated), "double")
expect_type(median(ps_truncated), "double")
# No NA handling needed for truncation
expect_equal(min(ps_truncated), min(ps_truncated, na.rm = TRUE))
# Quantile functions
expect_type(quantile(ps_truncated, 0.5), "double")
expect_type(quantile(ps_truncated, c(0.25, 0.75)), "double")
})
test_that("Summary functions return numeric for psw", {
set.seed(333)
weights <- psw(runif(40, 0.5, 2.0), estimand = "ato")
# All summary functions return numeric
expect_type(min(weights), "double")
expect_type(max(weights), "double")
expect_type(range(weights), "double")
expect_type(sum(weights), "double")
expect_type(prod(weights), "double")
expect_type(mean(weights), "double")
expect_type(median(weights), "double")
# Summary statistics for weight diagnostics
expect_type(var(weights), "double")
expect_type(sd(weights), "double")
expect_type(IQR(weights), "double")
expect_type(mad(weights), "double")
})
# =============================================================================
# vec_restore Works Correctly for ALL Classes
# =============================================================================
test_that("Subsetting preserves class and attributes for all classes", {
set.seed(444)
# ps_trim subsetting
ps_trim_obj <- ps_trim(runif(50, 0.1, 0.9), method = "adaptive")
subset1 <- ps_trim_obj[1:25]
subset2 <- ps_trim_obj[seq(1, 50, by = 2)] # Every other element
subset3 <- ps_trim_obj[as.numeric(ps_trim_obj) > 0.5]
expect_s3_class(subset1, "ps_trim")
expect_s3_class(subset2, "ps_trim")
expect_s3_class(subset3, "ps_trim")
expect_equal(ps_trim_meta(subset1)$method, "adaptive")
# ps_trunc subsetting
ps_trunc_obj <- ps_trunc(
runif(50, 0.1, 0.9),
method = "pctl",
lower = 0.05,
upper = 0.95
)
subset1 <- ps_trunc_obj[1:25]
subset2 <- ps_trunc_obj[seq(1, 50, by = 2)]
subset3 <- ps_trunc_obj[as.numeric(ps_trunc_obj) > 0.5]
expect_s3_class(subset1, "ps_trunc")
expect_s3_class(subset2, "ps_trunc")
expect_s3_class(subset3, "ps_trunc")
expect_equal(ps_trunc_meta(subset1)$method, "pctl")
# psw subsetting
psw_obj <- psw(
runif(50, 0.5, 2.0),
estimand = "atm",
stabilized = TRUE,
calibrated = TRUE
)
subset1 <- psw_obj[1:25]
subset2 <- psw_obj[seq(1, 50, by = 2)]
subset3 <- psw_obj[as.numeric(psw_obj) > 1]
expect_s3_class(subset1, "psw")
expect_s3_class(subset2, "psw")
expect_s3_class(subset3, "psw")
expect_equal(estimand(subset1), "atm")
expect_true(is_stabilized(subset1))
expect_true(attr(subset1, "calibrated"))
})
# =============================================================================
# Edge Cases and Special Values
# =============================================================================
test_that("Edge cases work correctly for all classes", {
set.seed(555)
# Empty vectors
empty_trim <- ps_trim(numeric(0), method = "ps")
empty_trunc <- ps_trunc(numeric(0), method = "ps")
empty_psw <- psw(numeric(0), estimand = "ate")
expect_length(empty_trim, 0)
expect_length(empty_trunc, 0)
expect_length(empty_psw, 0)
expect_type(sum(empty_trim), "double")
expect_type(sum(empty_trunc), "double")
expect_type(sum(empty_psw), "double")
# Single element
single_trim <- ps_trim(0.5, method = "ps", lower = 0.1, upper = 0.9)
single_trunc <- ps_trunc(0.5, method = "ps", lower = 0.1, upper = 0.9)
single_psw <- psw(1.5, estimand = "ate")
expect_type(1 / single_trim, "double")
expect_type(1 / single_trunc, "double")
expect_s3_class(1 / single_psw, "psw")
# All trimmed case
all_trimmed <- ps_trim(rep(0.05, 10), method = "ps", lower = 0.1, upper = 0.9)
weights_from_trimmed <- 1 / all_trimmed
expect_true(all(is.na(weights_from_trimmed)))
# Boundary values for truncation
boundary_ps <- c(0.001, 0.999)
boundary_trunc <- ps_trunc(
boundary_ps,
method = "ps",
lower = 0.01,
upper = 0.99
)
boundary_weights <- 1 / boundary_trunc
expect_true(all(is.finite(boundary_weights)))
expect_true(all(boundary_weights > 0))
})
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.