Apply Restricted Temporal Regression to mitigate temporal confounding. RTR orthogonalizes the temporal effect to the covariate space, preventing the temporal random effect from absorbing covariate information.
This is important when covariates have temporal trends (e.g., increasing over time) because the temporal random effect can "steal" variance from these covariates, leading to biased coefficient estimates.
Usage
temporal_rtr(temporal, restrict_to)
# S3 method for class 'ratiod_rtr'
print(x, ...)Arguments
- temporal
A temporal specification (
temporal_rw1,temporal_gp, etc.)- restrict_to
Formula specifying which covariates to orthogonalize against (e.g.,
~ year_effect + trend). The temporal effect will be constrained to be orthogonal to the column space of these covariates.- x
A ratiod_rtr object
- ...
Passed to underlying print method
Details
The RTR approach (analogous to Restricted Spatial Regression) modifies the temporal random effect to be orthogonal to the fixed effect design matrix:
$$f_{RTR}(t) = (I - P_X) f(t)$$
where \(P_X = X(X'X)^{-1}X'\) is the projection matrix onto the column space of X.
When to use RTR:
Covariates have temporal trends (e.g., increasing/decreasing over time)
Interested in causal interpretation of covariate effects
Coefficients appear attenuated toward zero
Time and covariates are correlated
When NOT to use RTR:
Covariates are temporally uncorrelated (random sampling over time)
Temporal effect is the primary quantity of interest
Prediction is the main goal (not causal inference)
References
Hanks, E. M., Schliep, E. M., Hooten, M. B., & Hoeting, J. A. (2015). Restricted spatial regression in practice: geostatistical models, confounding, and robustness under model misspecification. Environmetrics, 26(4), 243-254.
See also
temporal_rw1(), temporal_rw2(), temporal_gp(),
spatial_rsr() for the spatial analog
Examples
# Create RTR temporal structure
rtr <- temporal_rtr(
temporal_rw2("year"),
restrict_to = ~ temperature + precipitation
)
print(rtr)
#> tulpaRatio temporal specification
#> ============================
#>
#> Type: RW2 (Second-order Random Walk)
#> Time variable: year
#> Shared: Yes (enters both processes)
#>
#> Restricted Temporal Regression (RTR):
#> Orthogonal to: ~temperature + precipitation
#> (Temporal effect constrained to be orthogonal to covariate space)
# \donttest{
# Generate data with temporal confounding
set.seed(170)
years <- 2000:2023
n_per_year <- 4
df <- data.frame(
year = rep(years, each = n_per_year),
# Temperature increasing over time (confounded with year)
temperature = rep(seq(15, 18, length.out = length(years)), each = n_per_year) +
rnorm(length(years) * n_per_year, 0, 0.5),
count = rpois(length(years) * n_per_year, lambda = 20),
effort = rgamma(length(years) * n_per_year, shape = 4, rate = 1)
)
# Standard RW2 (may have temporal confounding)
fit1 <- tratio(
count | effort ~ temperature,
data = df,
family = ratiod_poisson_gamma(),
temporal = temporal_rw2("year"),
mode = "hmc",
control = list(iter = 400, chains = 1, seed = 1)
)
# RTR to protect temperature coefficient
fit2 <- tratio(
count | effort ~ temperature,
data = df,
family = ratiod_poisson_gamma(),
temporal = temporal_rtr(
temporal_rw2("year"),
restrict_to = ~ temperature
),
mode = "hmc",
control = list(iter = 400, chains = 1, seed = 1)
)
# Compare coefficient estimates
summary(fit1) # May be attenuated
#> ratio model summary
#> ===================
#>
#> Inference: Exact (Tier 1) via hmc
#> Family: poisson_gamma
#>
#> Fixed effects (numerator):
#> parameter mean sd 2.5% 97.5% rhat ess_bulk
#> num:(Intercept) 1.720 0.809 -0.152 3.046 1.098 14
#> num:temperature 0.079 0.049 -0.003 0.193 1.086 14
#>
#> Fixed effects (denominator):
#> parameter mean sd 2.5% 97.5% rhat ess_bulk
#> denom:(Intercept) 0.450 1.354 -1.909 2.558 1.026 29
#> denom:temperature 0.054 0.082 -0.071 0.196 1.023 30
#>
#> Variance components:
#> parameter mean sd 2.5% 97.5% rhat ess_bulk
#> shape 3.229 0.521 2.366 4.297 1.000 69
#> tau_temporal 315.842 166.225 77.682 715.092 1.029 42
#>
#> Other parameters:
#> parameter mean sd 2.5% 97.5% rhat ess_bulk
#> temporal[10] 0.002 0.056 -0.110 0.119 0.999 33
#> temporal[11] -0.013 0.050 -0.104 0.083 1.013 29
#> temporal[12] -0.014 0.053 -0.113 0.083 1.038 38
#> temporal[13] -0.006 0.053 -0.096 0.100 1.049 40
#> temporal[14] 0.007 0.061 -0.116 0.130 1.006 52
#> temporal[15] 0.006 0.064 -0.118 0.136 1.010 63
#> temporal[16] -0.014 0.062 -0.124 0.095 1.022 56
#> temporal[17] -0.038 0.055 -0.149 0.064 1.097 12
#> temporal[18] -0.052 0.052 -0.143 0.037 1.074 16
#> temporal[19] -0.055 0.059 -0.177 0.052 1.031 21
#> temporal[1] 0.161 0.111 -0.020 0.345 1.015 27
#> temporal[20] -0.047 0.077 -0.188 0.127 1.028 26
#> temporal[21] -0.044 0.083 -0.201 0.106 1.017 21
#> temporal[22] -0.101 0.084 -0.283 0.048 1.045 16
#> temporal[23] -0.172 0.093 -0.381 -0.004 1.099 12
#> temporal[24] -0.231 0.123 -0.492 -0.005 1.065 13
#> temporal[2] 0.168 0.077 0.050 0.338 0.995 20
#> temporal[3] 0.156 0.069 0.055 0.295 1.002 24
#> temporal[4] 0.141 0.070 0.023 0.287 1.019 22
#> temporal[5] 0.091 0.068 -0.035 0.221 1.009 30
#> temporal[6] 0.037 0.060 -0.081 0.148 1.003 36
#> temporal[7] 0.004 0.056 -0.106 0.116 1.042 30
#> temporal[8] 0.008 0.059 -0.082 0.139 1.043 21
#> temporal[9] 0.005 0.055 -0.093 0.105 1.017 33
#>
#> Diagnostics:
#> Divergences: 0
#> Avg. acceptance: 0.926
#> Chains: 1
#> Iterations: 400
#> Warmup: 200
#> R-hat > 1.01 for 23 parameter(s), largest 1.099 (temporal[23]).
#> Bulk ESS < 100 for 30 parameter(s), smallest 12 (temporal[17]).
#> Run longer chains or reparameterise before using these estimates.
summary(fit2) # Protected from temporal confounding
#> ratio model summary
#> ===================
#>
#> Inference: Exact (Tier 1) via hmc
#> Family: poisson_gamma
#>
#> Fixed effects (numerator):
#> parameter mean sd 2.5% 97.5% rhat ess_bulk
#> num:(Intercept) 1.720 0.809 -0.152 3.046 1.098 14
#> num:temperature 0.079 0.049 -0.003 0.193 1.086 14
#>
#> Fixed effects (denominator):
#> parameter mean sd 2.5% 97.5% rhat ess_bulk
#> denom:(Intercept) 0.450 1.354 -1.909 2.558 1.026 29
#> denom:temperature 0.054 0.082 -0.071 0.196 1.023 30
#>
#> Variance components:
#> parameter mean sd 2.5% 97.5% rhat ess_bulk
#> shape 3.229 0.521 2.366 4.297 1.000 69
#> tau_temporal 315.842 166.225 77.682 715.092 1.029 42
#>
#> Other parameters:
#> parameter mean sd 2.5% 97.5% rhat ess_bulk
#> temporal[10] 0.002 0.056 -0.110 0.119 0.999 33
#> temporal[11] -0.013 0.050 -0.104 0.083 1.013 29
#> temporal[12] -0.014 0.053 -0.113 0.083 1.038 38
#> temporal[13] -0.006 0.053 -0.096 0.100 1.049 40
#> temporal[14] 0.007 0.061 -0.116 0.130 1.006 52
#> temporal[15] 0.006 0.064 -0.118 0.136 1.010 63
#> temporal[16] -0.014 0.062 -0.124 0.095 1.022 56
#> temporal[17] -0.038 0.055 -0.149 0.064 1.097 12
#> temporal[18] -0.052 0.052 -0.143 0.037 1.074 16
#> temporal[19] -0.055 0.059 -0.177 0.052 1.031 21
#> temporal[1] 0.161 0.111 -0.020 0.345 1.015 27
#> temporal[20] -0.047 0.077 -0.188 0.127 1.028 26
#> temporal[21] -0.044 0.083 -0.201 0.106 1.017 21
#> temporal[22] -0.101 0.084 -0.283 0.048 1.045 16
#> temporal[23] -0.172 0.093 -0.381 -0.004 1.099 12
#> temporal[24] -0.231 0.123 -0.492 -0.005 1.065 13
#> temporal[2] 0.168 0.077 0.050 0.338 0.995 20
#> temporal[3] 0.156 0.069 0.055 0.295 1.002 24
#> temporal[4] 0.141 0.070 0.023 0.287 1.019 22
#> temporal[5] 0.091 0.068 -0.035 0.221 1.009 30
#> temporal[6] 0.037 0.060 -0.081 0.148 1.003 36
#> temporal[7] 0.004 0.056 -0.106 0.116 1.042 30
#> temporal[8] 0.008 0.059 -0.082 0.139 1.043 21
#> temporal[9] 0.005 0.055 -0.093 0.105 1.017 33
#>
#> Diagnostics:
#> Divergences: 0
#> Avg. acceptance: 0.926
#> Chains: 1
#> Iterations: 400
#> Warmup: 200
#> R-hat > 1.01 for 23 parameter(s), largest 1.099 (temporal[23]).
#> Bulk ESS < 100 for 30 parameter(s), smallest 12 (temporal[17]).
#> Run longer chains or reparameterise before using these estimates.
# }