Skip to content

Commit bf6dd01

Browse files
update exercises for contin. ps
1 parent 8f1ee7e commit bf6dd01

File tree

1 file changed

+21
-46
lines changed

1 file changed

+21
-46
lines changed

exercises/14-bonus-continuous-pscores-exercises.qmd

Lines changed: 21 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -76,33 +76,28 @@ wait_times <- eight |>
7676
First, let’s calculate the propensity score model, which will be the denominator in our stabilized weights (more to come on that soon). We’ll fit a model using `lm()` for `avg_spostmin` with our covariates, then use the fitted predictions of `avg_spostmin` (`.fitted`, `.sigma`) to calculate the density using `dnorm()`.
7777

7878
1. Fit a model using `lm()` with `avg_spostmin` as the outcome and the confounders identified in the DAG.
79-
2. Use `augment()` to add model predictions to the data frame
80-
3. In `dnorm()`, use `.fitted` as the mean and the mean of `.sigma` as the SD to calculate the propensity score for the denominator.
79+
2. Use `augment()` to add model predictions to the data frame.
80+
3. In `wt_ate()`, calculate the weights using `avg_postmin`, `.fitted`, and `.sigma`.
8181

8282
```{r}
83-
denominator_model <- lm(
83+
post_time_model <- lm(
8484
__________,
8585
data = wait_times
8686
)
8787
88-
denominators <- denominator_model |>
88+
wait_times_wts <- post_time_model |>
8989
______(data = wait_times) |>
90-
mutate(
91-
denominator = dnorm(
92-
avg_spostmin,
93-
mean = ______,
94-
sd = mean(______, na.rm = TRUE)
95-
)
96-
) |>
97-
select(date, denominator)
90+
mutate(wts = ______(
91+
______, ______, .sigma = ______
92+
))
9893
```
9994

10095
# Your Turn 2
10196

10297
As with the example in the slides, we have a lot of extreme values for our weights
10398

10499
```{r}
105-
denominators |>
100+
wait_times_wts |>
106101
mutate(wts = 1 / denominator) |>
107102
ggplot(aes(wts)) +
108103
geom_density(col = "#E69F00", fill = "#E69F0095") +
@@ -113,58 +108,37 @@ denominators |>
113108

114109
Let’s now fit the marginal density to use for stabilized weights:
115110

116-
1. Fit an intercept-only model of posted weight times to use as the numerator model
117-
2. Calculate the numerator weights using `dnorm()` as above.
118-
3. Finally, calculate the stabilized weights, `swts`, using the `numerator` and `denominator` weights
111+
1. Re-fit the above using stabilized weights
119112

120113
```{r}
121-
numerator_model <- lm(
122-
___ ~ ___,
123-
data = wait_times
124-
)
125-
126-
numerators <- numerator_model |>
114+
wait_times_swts <- post_time_model |>
127115
augment(data = wait_times) |>
128-
mutate(
129-
numerator = dnorm(
130-
avg_spostmin,
131-
___,
132-
mean(___, na.rm = TRUE)
133-
)
134-
) |>
135-
select(date, numerator)
136-
137-
wait_times_wts <- wait_times |>
138-
left_join(numerators, by = "date") |>
139-
left_join(denominators, by = "date") |>
140-
mutate(swts = ___)
116+
mutate(swts = _____(
117+
_____,
118+
_____,
119+
.sigma = .sigma,
120+
_____ = _____
121+
))
141122
```
142123

143124
Take a look at the weights now that we've stabilized them:
144125

145126
```{r}
146-
ggplot(wait_times_wts, aes(swts)) +
127+
ggplot(wait_times_swts, aes(swts)) +
147128
geom_density(col = "#E69F00", fill = "#E69F0095") +
148129
scale_x_log10() +
149130
theme_minimal(base_size = 20) +
150131
xlab("Stabilized Weights")
151132
```
152133

153-
Note that their mean is now about 1! That means the psuedo-population created by the weights is the same size as the observed population (the number of days we have wait time data, in this case).
154-
155-
```{r}
156-
round(mean(wait_times_wts$swts), digits = 2)
157-
```
158-
159-
160134
# Your Turn 3
161135

162136
Now, let's fit the outcome model!
163137

164138
1. Estimate the relationship between posted wait times and actual wait times using the stabilized weights we just created.
165139

166140
```{r}
167-
lm(___ ~ ___, weights = ___, data = wait_times_wts) |>
141+
lm(___ ~ ___, weights = ___, data = wait_times_swts) |>
168142
tidy() |>
169143
filter(term == "avg_spostmin") |>
170144
mutate(estimate = estimate * 10)
@@ -199,7 +173,8 @@ boot_estimate
199173

200174
# Take aways
201175

202-
* We can calculate propensity scores for continuous exposures. Here, we use `dnorm(true_value, predicted_value, mean(estimated_sigma, rm.na = TRUE))` to use the normal density to transform predictions to a propensity-like scale. We can also use other approaches like quantile binning of the exposure, calculating probability-based propensity scores using categorical regression models.
203-
* Continuous exposures are prone to mispecification and usually need to be stabilized. A simple stabilization is to invert the propensity score by stabilization weights using an intercept-only model such as `lm(exposure ~ 1)`
176+
* We can calculate propensity scores for continuous exposures. `wt_ate()` uses `dnorm()` to use the normal density to transform predictions to a propensity-like scale; we need to give `wt_ate()` `.sigma` as to calculate do this. We can also use other approaches like quantile binning of the exposure, calculating probability-based propensity scores using categorical regression models.
177+
* Continuous exposures are prone to mispecification and usually need to be stabilized. A simple stabilization is to invert the propensity score by stabilization weights using an intercept-only model such as `lm(exposure ~ 1)`. `wt_ate()` can do this for you automatically with `stabilize = TRUE`. This also applies to other types of exposures.
204178
* Stabilization is useful for any type of exposure where the weights are unbounded. Weights like the ATO, making them less susceptible to extreme weights.
205179
* Using propensity scores for continuous exposures in outcome models is identical to using them with binary exposures.
180+
* Because propensity scores for continuous exposures are prone to positivity violation, check the bootstrap distribution of your estimate for skew and to see if the mean estimate is different from your regression model. If these problems are present, you may need to use another approach like g-computation.

0 commit comments

Comments
 (0)