Code
source("../dsan-globals/_globals.r")
DSAN 5650: Causal Inference for Computational Social Science
Summer 2025, Georgetown University
Today’s Planned Schedule:
Start | End | Topic | |
---|---|---|---|
Lecture | 6:30pm | 7:20pm | Multilevel Madness → |
7:20pm | 7:50pm | Applying \(\textsf{do}()\) → | |
Break! | 7:50pm | 8:00pm | |
8:00pm | 9:00pm | Closing Backdoor Paths → |
\[ \DeclareMathOperator*{\argmax}{argmax} \DeclareMathOperator*{\argmin}{argmin} \newcommand{\bigexp}[1]{\exp\mkern-4mu\left[ #1 \right]} \newcommand{\bigexpect}[1]{\mathbb{E}\mkern-4mu \left[ #1 \right]} \newcommand{\definedas}{\overset{\small\text{def}}{=}} \newcommand{\definedalign}{\overset{\phantom{\text{defn}}}{=}} \newcommand{\eqeventual}{\overset{\text{eventually}}{=}} \newcommand{\Err}{\text{Err}} \newcommand{\expect}[1]{\mathbb{E}[#1]} \newcommand{\expectsq}[1]{\mathbb{E}^2[#1]} \newcommand{\fw}[1]{\texttt{#1}} \newcommand{\given}{\mid} \newcommand{\green}[1]{\color{green}{#1}} \newcommand{\heads}{\outcome{heads}} \newcommand{\iid}{\overset{\text{\small{iid}}}{\sim}} \newcommand{\lik}{\mathcal{L}} \newcommand{\loglik}{\ell} \DeclareMathOperator*{\maximize}{maximize} \DeclareMathOperator*{\minimize}{minimize} \newcommand{\mle}{\textsf{ML}} \newcommand{\nimplies}{\;\not\!\!\!\!\implies} \newcommand{\orange}[1]{\color{orange}{#1}} \newcommand{\outcome}[1]{\textsf{#1}} \newcommand{\param}[1]{{\color{purple} #1}} \newcommand{\pgsamplespace}{\{\green{1},\green{2},\green{3},\purp{4},\purp{5},\purp{6}\}} \newcommand{\pedge}[2]{\require{enclose}\enclose{circle}{~{#1}~} \rightarrow \; \enclose{circle}{\kern.01em {#2}~\kern.01em}} \newcommand{\pnode}[1]{\require{enclose}\enclose{circle}{~{#1}~}} \newcommand{\purp}[1]{\color{purple}{#1}} \newcommand{\sign}{\text{Sign}} \newcommand{\spacecap}{\; \cap \;} \newcommand{\spacewedge}{\; \wedge \;} \newcommand{\tails}{\outcome{tails}} \newcommand{\Var}[1]{\text{Var}[#1]} \newcommand{\bigVar}[1]{\text{Var}\mkern-4mu \left[ #1 \right]} \]
source("../dsan-globals/_globals.r")
…“Lab” Time!
\[ \hat{\alpha}_j^{\text {multilevel }} = \frac{\frac{n_j}{\sigma_y^2} \bar{y}_j+\frac{1}{\sigma_\alpha^2} \bar{y}_{\text {all }}}{\frac{n_j}{\sigma_y^2}+\frac{1}{\sigma_\alpha^2}} \]
\[ \alpha_j \sim \mathcal{N}(\mu_\alpha, \sigma_\alpha), \text{ for }j = 1, \ldots, J \]
library(tidyverse) # For ggplot
── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
✔ dplyr 1.1.4 ✔ readr 2.1.5
✔ forcats 1.0.0 ✔ stringr 1.5.1
✔ lubridate 1.9.4 ✔ tibble 3.3.0
✔ purrr 1.0.4 ✔ tidyr 1.3.1
── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks stats::filter()
✖ dplyr::lag() masks stats::lag()
ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
library(extraDistr) # For rbern()
Attaching package: 'extraDistr'
The following object is masked from 'package:purrr':
rdunif
library(patchwork) # For side-by-side plotting
library(ggtext) # For colors in titles
library(rethinking)
Loading required package: cmdstanr
Warning: package 'cmdstanr' was built under R version 4.4.3
This is cmdstanr version 0.9.0
- CmdStanR documentation and vignettes: mc-stan.org/cmdstanr
- CmdStan path: /Users/jpj/.cmdstan/cmdstan-2.36.0
- CmdStan version: 2.36.0
Loading required package: posterior
This is posterior version 1.6.1
Attaching package: 'posterior'
The following objects are masked from 'package:stats':
mad, sd, var
The following objects are masked from 'package:base':
%in%, match
Loading required package: parallel
rethinking (Version 2.42)
Attaching package: 'rethinking'
The following objects are masked from 'package:extraDistr':
dbern, dlaplace, dpareto, rbern, rlaplace, rpareto
The following object is masked from 'package:purrr':
map
The following object is masked from 'package:stats':
rstudent
library(dagitty)
<- 10000 # For discrete RVs
n_d <- 300 # For continuous RVs n_c
library(rethinking)
library(dagitty)
library(ggdag)
<-dagitty("dag{
pipe_dag X[exposure]
Y[outcome]
X -> Y
X -> Z
Z -> Y
}")
coordinates(pipe_dag) <- list(
x=c(X=0, Z=0.5, Y=1),
y=c(X=1, Z=0.5, Y=1)
)drawdag(pipe_dag, cex=4, lwd=5, radius=10)
drawopenpaths(pipe_dag, lwd=5)
<- adjustmentSets(
adj_sets effect="direct"
pipe_dag,
)writeLines("Adjustment sets (direct effect):")
adj_setsset.seed(5650)
<- tibble(
cpipe_df X = rnorm(n_c),
Z = rbern(n_c, plogis(X)),
Y = rnorm(n_c, 2 * Z - 1)
)<- lm(Y ~ X, data=cpipe_df)
cpipe_lm <- round(cpipe_lm$coef['X'], 3)
cpipe_slope <- lm(Y ~ X, data=cpipe_df |> filter(Z == 0))
cpipe_z0_lm <- round(cpipe_z0_lm$coef['X'], 2)
cpipe_z0_slope <- paste0("<span style='color: #e69f00;'>Slope<sub>Z=0</sub> = ",cpipe_z0_slope,"</span>")
cpipe_z0_label <- lm(Y ~ X, data=cpipe_df |> filter(Z == 1))
cpipe_z1_lm <- round(cpipe_z1_lm$coef['X'], 2)
cpipe_z1_slope <- paste0("<span style='color: #56b4e9;'>Slope<sub>Z=1</sub> = ",cpipe_z1_slope,"</span>")
cpipe_z1_label <- paste0(cpipe_z0_label, " | ", cpipe_z1_label)
cpipe_z_texlabel <- min(cpipe_df$X)
cpipe_xmin <- max(cpipe_df$X)
cpipe_xmax ggplot() +
# Points
geom_point(
data=cpipe_df |> filter(Y > -3),
aes(x=X, y=Y, color=factor(Z)),
size=0.4*g_pointsize,
alpha=0.8
+
) # Overall lm
geom_smooth(
data=cpipe_df, aes(x=X, y=Y),
method = lm, se = FALSE,
linewidth = 3, color='white'
+
) geom_smooth(
data=cpipe_df, aes(x=X, y=Y),
method = lm, se = FALSE,
linewidth = 2.5, color='black'
+
) theme_dsan(base_size=18) +
theme(
plot.title = element_text(size=18),
plot.subtitle = element_markdown(size=16)
+
) coord_equal() +
labs(
title = paste0(
"Unstratified Slope = ",cpipe_slope
),x = "X", y = "Y", color = "Z"
)
Attaching package: 'ggdag'
The following object is masked from 'package:stats':
filter
Adjustment sets (direct effect):
{ Z }
`geom_smooth()` using formula = 'y ~ x'
`geom_smooth()` using formula = 'y ~ x'
library(rethinking)
library(dagitty)
library(ggdag)
<-dagitty("dag{
pipe_dag_closed X[exposure]
Y[outcome]
Z[adjustedNode]
X -> Y
X -> Z
Z -> Y
}")
coordinates(pipe_dag_closed) <- list(
x=c(X=0, Z=0.5, Y=1),
y=c(X=1, Z=0.5, Y=1)
)drawdag(pipe_dag_closed, cex=4, lwd=5, radius=10)
drawopenpaths(pipe_dag_closed, Z="Z", lwd=5)
<- adjustmentSets(
adj_sets_closed
pipe_dag_closed
)writeLines("Adjustment sets (direct effect):")
adj_sets_closedset.seed(5650)
<- tibble(
cpipe_df X = rnorm(n_c),
Z = rbern(n_c, plogis(X)),
Y = rnorm(n_c, 2 * Z - 1)
)<- lm(Y ~ X, data=cpipe_df)
cpipe_lm <- round(cpipe_lm$coef['X'], 3)
cpipe_slope <- lm(Y ~ X, data=cpipe_df |> filter(Z == 0))
cpipe_z0_lm <- round(cpipe_z0_lm$coef['X'], 2)
cpipe_z0_slope <- paste0("<span style='color: #e69f00;'>Slope<sub>Z=0</sub> = ",cpipe_z0_slope,"</span>")
cpipe_z0_label <- lm(Y ~ X, data=cpipe_df |> filter(Z == 1))
cpipe_z1_lm <- round(cpipe_z1_lm$coef['X'], 2)
cpipe_z1_slope <- paste0("<span style='color: #56b4e9;'>Slope<sub>Z=1</sub> = ",cpipe_z1_slope,"</span>")
cpipe_z1_label <- paste0(cpipe_z0_label, " | ", cpipe_z1_label)
cpipe_z_texlabel <- min(cpipe_df$X)
cpipe_xmin <- max(cpipe_df$X)
cpipe_xmax ggplot() +
# Points
geom_point(
data=cpipe_df |> filter(Y > -3),
aes(x=X, y=Y, color=factor(Z)),
size=0.6*g_pointsize,
alpha=0.8
+
) # Stratified lm
# (slightly larger black lines)
geom_smooth(
data=cpipe_df,
aes(x=X, y=Y, group=factor(Z)),
method=lm, se=FALSE, fullrange=TRUE,
linewidth=2.75, color='black'
+
) # (Colored lines)
geom_smooth(
data=cpipe_df,
aes(x=X, y=Y, color=factor(Z)),
method=lm, se=FALSE, fullrange=TRUE,
linewidth=2
+
) theme_dsan(base_size=18) +
theme(
plot.title = element_markdown(size=18),
plot.subtitle = element_markdown(size=16)
+
) coord_equal() +
labs(
title=cpipe_z_texlabel,
x = "X", y = "Y", color = "Z"
)
Adjustment sets (direct effect):
{}
`geom_smooth()` using formula = 'y ~ x'
`geom_smooth()` using formula = 'y ~ x'
library(rethinking)
library(dagitty)
library(ggdag)
<-dagitty("dag{
pipe_dag X[exposure]
Y[outcome]
X -> Y
Z -> X
Z -> Y
}")
coordinates(pipe_dag) <- list(
x=c(X=0, Z=0.5, Y=1),
y=c(X=1, Z=0.5, Y=1)
)drawdag(pipe_dag, cex=4, lwd=5, radius=10)
drawopenpaths(pipe_dag, lwd=5)
<- adjustmentSets(
adj_sets effect="direct"
pipe_dag,
)writeLines("Adjustment sets (direct effect):")
adj_setslibrary(ggtext)
set.seed(5650)
<- tibble(
cfork_df Z = rbern(n_c),
X = rnorm(n_c, 2 * Z - 1),
Y = rnorm(n_c, 2 * Z - 1)
)library(latex2exp)
<- lm(Y ~ X, data=cfork_df)
overall_lm <- round(overall_lm$coef['X'], 3)
overall_slope <- lm(Y ~ X, data=cfork_df |> filter(Z == 0))
z0_lm <- round(z0_lm$coef['X'], 2)
z0_slope <- paste0("<span style='color: #e69f00;'>Slope<sub>Z=0</sub> = ",z0_slope,"</span>")
z0_label <- lm(Y ~ X, data=cfork_df |> filter(Z == 1))
z1_lm <- round(z1_lm$coef['X'], 2)
z1_slope <- paste0("<span style='color: #56b4e9;'>Slope<sub>Z=1</sub> = ",z1_slope,"</span>")
z1_label <- paste0(z0_label, " | ", z1_label)
z_texlabel <- min(cfork_df$X)
cfork_xmin <- max(cfork_df$X)
cfork_xmax ggplot() +
# Points
geom_point(
data=cfork_df,
aes(x=X, y=Y, color=factor(Z)),
size=0.6*g_pointsize,
alpha=0.8
+
) # Overall lm
geom_smooth(
data=cfork_df, aes(x=X, y=Y),
method = lm, se = FALSE,
linewidth = 2.5, color='black'
+
) theme_dsan(base_size=18) +
theme(
plot.title = element_text(size=18),
plot.subtitle = element_markdown(size=16)
+
) coord_equal() +
labs(
title = paste0(
"Unstratified Slope = ",overall_slope
),x = "X", y = "Y", color = "Z"
)
Adjustment sets (direct effect):
{ Z }
`geom_smooth()` using formula = 'y ~ x'
library(rethinking)
library(dagitty)
library(ggdag)
<-dagitty("dag{
fork_dag_closed X[exposure]
Y[outcome]
Z[adjustedNode]
X -> Y
Z -> X
Z -> Y
}")
coordinates(fork_dag_closed) <- list(
x=c(X=0, Z=0.5, Y=1),
y=c(X=1, Z=0.5, Y=1)
)<- setVariableStatus(fork_dag_closed, "adjustedNode", "Z")
fork_dag_closed drawdag(fork_dag_closed, cex=4, lwd=5, radius=10)
drawopenpaths(fork_dag_closed, Z="Z", lwd=5)
library(ggtext)
set.seed(5650)
<- tibble(
cfork_df Z = rbern(n_c),
X = rnorm(n_c, 2 * Z - 1),
Y = rnorm(n_c, 2 * Z - 1)
)library(latex2exp)
<- lm(Y ~ X, data=cfork_df)
overall_lm <- round(overall_lm$coef['X'], 3)
overall_slope <- lm(Y ~ X, data=cfork_df |> filter(Z == 0))
z0_lm <- round(z0_lm$coef['X'], 2)
z0_slope <- paste0("<span style='color: #e69f00;'>Slope<sub>Z=0</sub> = ",z0_slope,"</span>")
z0_label <- lm(Y ~ X, data=cfork_df |> filter(Z == 1))
z1_lm <- round(z1_lm$coef['X'], 2)
z1_slope <- paste0("<span style='color: #56b4e9;'>Slope<sub>Z=1</sub> = ",z1_slope,"</span>")
z1_label <- paste0(z0_label, " | ", z1_label)
z_texlabel <- min(cfork_df$X)
cfork_xmin <- max(cfork_df$X)
cfork_xmax ggplot() +
# Points
geom_point(
data=cfork_df,
aes(x=X, y=Y, color=factor(Z)),
size=0.6*g_pointsize,
alpha=0.8
+
) # Overall lm
geom_smooth(
data=cfork_df, aes(x=X, y=Y),
method = lm, se = FALSE,
linewidth = 2.5, color='black'
+
) # Stratified lm
# (slightly larger black lines)
geom_smooth(
data=cfork_df,
aes(x=X, y=Y, group=factor(Z)),
method=lm, se=FALSE, fullrange=TRUE,
linewidth=2.75, color='black'
+
) # (Colored lines)
geom_smooth(
data=cfork_df,
aes(x=X, y=Y, color=factor(Z)),
method=lm, se=FALSE, fullrange=TRUE,
linewidth=2
+
) theme_dsan(base_size=18) +
theme(
plot.title = element_text(size=18),
plot.subtitle = element_markdown(size=16)
+
) coord_equal() +
labs(
title = paste0(
"Unstratified Slope = ",overall_slope
),subtitle=z_texlabel,
x = "X", y = "Y", color = "Z"
)
`geom_smooth()` using formula = 'y ~ x'
`geom_smooth()` using formula = 'y ~ x'
`geom_smooth()` using formula = 'y ~ x'
library(tidyverse)
library(extraDistr)
library(latex2exp)
set.seed(5650)
<- tibble(
cprox_df X = rnorm(n_c),
Z = rbern(n_c, plogis(X)),
Y = rnorm(n_c, 2 * Z - 1),
A = rbern(n_c, (1-Z)*0.86 + Z*0.14)
)<- lm(Y ~ X, data=cprox_df)
cprox_lm <- round(cprox_lm$coef['X'], 3)
cprox_slope <- lm(Y ~ X, data=cprox_df |> filter(A == 0))
cprox_a0_lm <- round(cprox_a0_lm$coef['X'], 2)
cprox_a0_slope <- paste0("<span style='color: #e69f00;'>Slope<sub>Z=0</sub> = ",cprox_a0_slope,"</span>")
cprox_a0_label # A == 1 lm
<- lm(Y ~ X, data=cprox_df |> filter(A == 1))
cprox_a1_lm <- round(cprox_a1_lm$coef['X'], 2)
cprox_a1_slope <- paste0("<span style='color: #56b4e9;'>Slope<sub>Z=1</sub> = ",cprox_a1_slope,"</span>")
cprox_a1_label <- paste0(cprox_a0_label, " | ", cprox_a1_label)
cprox_a_texlabel <- min(cprox_df$X)
cprox_xmin <- max(cprox_df$X)
cprox_xmax ggplot() +
# Points
geom_point(
data=cprox_df |> filter(Y > -3),
aes(x=X, y=Y, color=factor(A)),
size=0.5*g_pointsize,
alpha=0.8
+
) # Overall lm
geom_smooth(
data=cprox_df, aes(x=X, y=Y),
method = lm, se = FALSE,
linewidth = 2.5, color='black'
+
) # Stratified lm
# (slightly larger black lines)
geom_smooth(
data=cprox_df,
aes(x=X, y=Y, group=factor(A)),
method=lm, se=FALSE, fullrange=TRUE,
linewidth=2.75, color='black'
+
) # (Colored lines)
geom_smooth(
data=cprox_df,
aes(x=X, y=Y, color=factor(A)),
method=lm, se=FALSE, fullrange=TRUE,
linewidth=2
+
) theme_dsan(base_size=22) +
theme(
plot.title = element_text(size=22),
plot.subtitle = element_markdown(size=20)
+
) coord_equal() +
labs(
title = paste0(
"Unstratified Slope = ",cprox_slope
),subtitle=cprox_a_texlabel,
x = "X", y = "Y", color = "A"
)
`geom_smooth()` using formula = 'y ~ x'
`geom_smooth()` using formula = 'y ~ x'
`geom_smooth()` using formula = 'y ~ x'
library(rethinking)
library(dagitty)
library(ggdag)
<-dagitty("dag{
coll_dag X[exposure]
Y[outcome]
X -> Y
X -> Z
Y -> Z
}")
coordinates(coll_dag) <- list(
x=c(X=0, Z=0.5, Y=1),
y=c(X=1, Z=0.5, Y=1)
)drawdag(coll_dag, cex=4, lwd=5, radius=10)
drawopenpaths(coll_dag, lwd=5)
<- adjustmentSets(
adj_sets_coll effect="direct"
coll_dag,
)writeLines("Adjustment sets (direct effect):")
adj_sets_collset.seed(5650)
<- tibble(
ccoll_df X = rnorm(n_c),
Y = rnorm(n_c),
Z = rbern(n_c, plogis(2 * (X + Y - 1)))
)<- lm(Y ~ X, data=ccoll_df)
ccoll_lm <- round(ccoll_lm$coef['X'], 3)
ccoll_slope <- lm(Y ~ X, data=ccoll_df |> filter(Z == 0))
ccoll_z0_lm <- round(ccoll_z0_lm$coef['X'], 2)
ccoll_z0_slope <- paste0("<span style='color: #e69f00;'>Slope<sub>Z=0</sub> = ",ccoll_z0_slope,"</span>")
ccoll_z0_label <- lm(Y ~ X, data=ccoll_df |> filter(Z == 1))
ccoll_z1_lm <- round(ccoll_z1_lm$coef['X'], 2)
ccoll_z1_slope <- paste0("<span style='color: #56b4e9;'>Slope<sub>Z=1</sub> = ",ccoll_z1_slope,"</span>")
ccoll_z1_label <- paste0(ccoll_z0_label, " | ", ccoll_z1_label)
ccoll_z_texlabel <- min(ccoll_df$X)
ccoll_xmin <- max(ccoll_df$X)
ccoll_xmax ggplot() +
# Points
geom_point(
data=ccoll_df |> filter(Y > -3),
aes(x=X, y=Y, color=factor(Z)),
size=0.4*g_pointsize,
alpha=0.8
+
) # Overall lm
geom_smooth(
data=ccoll_df, aes(x=X, y=Y),
method = lm, se = FALSE,
linewidth = 3, color='white'
+
) geom_smooth(
data=ccoll_df, aes(x=X, y=Y),
method = lm, se = FALSE,
linewidth = 2.5, color='black'
+
) theme_dsan(base_size=18) +
theme(
plot.title = element_markdown(size=18),
plot.subtitle = element_markdown(size=16)
+
) coord_equal() +
labs(
title=ccoll_z_texlabel,
x = "X", y = "Y", color = "Z"
)
Adjustment sets (direct effect):
{}
`geom_smooth()` using formula = 'y ~ x'
`geom_smooth()` using formula = 'y ~ x'
library(rethinking)
library(dagitty)
library(ggdag)
<-dagitty("dag{
fork_dag_closed X[exposure]
Y[outcome]
Z[adjustedNode]
X -> Y
X -> Z
Y -> Z
}")
coordinates(fork_dag_closed) <- list(
x=c(X=0, Z=0.5, Y=1),
y=c(X=1, Z=0.5, Y=1)
)<- setVariableStatus(fork_dag_closed, "adjustedNode", "Z")
fork_dag_closed drawdag(fork_dag_closed, cex=4, lwd=5, radius=10)
drawopenpaths(fork_dag_closed, Z="Z", lwd=5)
set.seed(5650)
<- tibble(
ccoll_df X = rnorm(n_c),
Y = rnorm(n_c),
Z = rbern(n_c, plogis(2 * (X + Y - 1)))
)<- lm(Y ~ X, data=ccoll_df)
ccoll_lm <- round(ccoll_lm$coef['X'], 3)
ccoll_slope <- lm(Y ~ X, data=ccoll_df |> filter(Z == 0))
ccoll_z0_lm <- round(ccoll_z0_lm$coef['X'], 2)
ccoll_z0_slope <- paste0("<span style='color: #e69f00;'>Slope<sub>Z=0</sub> = ",ccoll_z0_slope,"</span>")
ccoll_z0_label <- lm(Y ~ X, data=ccoll_df |> filter(Z == 1))
ccoll_z1_lm <- round(ccoll_z1_lm$coef['X'], 2)
ccoll_z1_slope <- paste0("<span style='color: #56b4e9;'>Slope<sub>Z=1</sub> = ",ccoll_z1_slope,"</span>")
ccoll_z1_label <- paste0(ccoll_z0_label, " | ", ccoll_z1_label)
ccoll_z_texlabel <- min(ccoll_df$X)
ccoll_xmin <- max(ccoll_df$X)
ccoll_xmax ggplot() +
# Points
geom_point(
data=ccoll_df |> filter(Y > -3),
aes(x=X, y=Y, color=factor(Z)),
size=0.4*g_pointsize,
alpha=0.8
+
) # Stratified lm
# (slightly larger black lines)
geom_smooth(
data=ccoll_df,
aes(x=X, y=Y, group=factor(Z)),
method=lm, se=FALSE, fullrange=TRUE,
linewidth=2.75, color='black'
+
) # (Colored lines)
geom_smooth(
data=ccoll_df,
aes(x=X, y=Y, color=factor(Z)),
method=lm, se=FALSE, fullrange=TRUE,
linewidth=2
+
) theme_dsan(base_size=18) +
theme(
plot.title = element_markdown(size=18),
plot.subtitle = element_markdown(size=16)
+
) coord_equal() +
labs(
title=ccoll_z_texlabel,
x = "X", y = "Y", color = "Z"
)
`geom_smooth()` using formula = 'y ~ x'
`geom_smooth()` using formula = 'y ~ x'
set.seed(5650)
<- tibble(
grant_df newsworthy = runif(n_c, 0, 10),
rigorous = runif(n_c, 0, 10),
awarded = ifelse(newsworthy + rigorous > 10, 1, 0)
)<- lm(rigorous ~ newsworthy, data=grant_df)
grant_lm <- round(grant_lm$coef['newsworthy'], 3)
grant_slope <- lm(rigorous ~ newsworthy, data=grant_df |> filter(awarded == 0))
grant_z0_lm <- round(grant_z0_lm$coef['newsworthy'], 2)
grant_z0_slope <- lm(rigorous ~ newsworthy, data=grant_df |> filter(awarded == 1))
grant_z1_lm <- round(grant_z1_lm$coef['newsworthy'], 2)
grant_z1_slope <- paste0("<span style='color: #56b4e9;'>Slope<sub>+</sub> = ",grant_z1_slope,"</span>")
grant_z1_label <- grant_z1_label
grant_z_texlabel <- min(grant_df$newsworthy)
grant_xmin <- max(grant_df$newsworthy)
grant_xmax ggplot() +
# Points
geom_point(
data=grant_df |> filter(rigorous > -3),
aes(x=newsworthy, y=rigorous, color=factor(awarded)),
size=0.4*g_pointsize,
alpha=0.8
+
) # Stratified lm
# (slightly larger black lines)
geom_smooth(
data=grant_df |> filter(awarded == 1),
aes(x=newsworthy, y=rigorous, group=factor(awarded)),
method=lm, se=FALSE, fullrange=TRUE,
linewidth=2.75, color='black'
+
) # (Colored lines)
geom_smooth(
data=grant_df |> filter(awarded == 1),
aes(x=newsworthy, y=rigorous, color=factor(awarded)),
method=lm, se=FALSE, fullrange=TRUE,
linewidth=2
+
) theme_dsan(base_size=18) +
theme(
plot.title = element_markdown(size=18),
plot.subtitle = element_markdown(size=16)
+
) coord_equal() +
labs(
title=grant_z_texlabel,
x = "Newsworthy?", y = "Rigorous?", color = "awarded"
)set.seed(5650)
<- tibble(
grant_df newsworthy = rnorm(n_c, 5, 1),
rigorous = rnorm(n_c, 5, 1),
awarded = ifelse(newsworthy + rigorous > 12, 1, 0)
)<- lm(rigorous ~ newsworthy, data=grant_df)
grant_lm <- round(grant_lm$coef['newsworthy'], 3)
grant_slope <- lm(rigorous ~ newsworthy, data=grant_df |> filter(awarded == 0))
grant_z0_lm <- round(grant_z0_lm$coef['newsworthy'], 2)
grant_z0_slope <- lm(rigorous ~ newsworthy, data=grant_df |> filter(awarded == 1))
grant_z1_lm <- round(grant_z1_lm$coef['newsworthy'], 2)
grant_z1_slope <- paste0("<span style='color: #56b4e9;'>Slope<sub>+</sub> = ",grant_z1_slope,"</span>")
grant_z1_label <- grant_z1_label
grant_z_texlabel <- min(grant_df$newsworthy)
grant_xmin <- max(grant_df$newsworthy)
grant_xmax ggplot() +
# Points
geom_point(
data=grant_df |> filter(rigorous > -3),
aes(x=newsworthy, y=rigorous, color=factor(awarded)),
size=0.4*g_pointsize,
alpha=0.8
+
) # Stratified lm
# (slightly larger black lines)
geom_smooth(
data=grant_df |> filter(awarded == 1),
aes(x=newsworthy, y=rigorous, group=factor(awarded)),
method=lm, se=FALSE, fullrange=TRUE,
linewidth=2.75, color='black'
+
) # (Colored lines)
geom_smooth(
data=grant_df |> filter(awarded == 1),
aes(x=newsworthy, y=rigorous, color=factor(awarded)),
method=lm, se=FALSE, fullrange=TRUE,
linewidth=2
+
) theme_dsan(base_size=18) +
theme(
plot.title = element_markdown(size=18),
plot.subtitle = element_markdown(size=16)
+
) coord_equal() +
labs(
title=grant_z_texlabel,
x = "Newsworthy?", y = "Rigorous?", color = "awarded"
)
`geom_smooth()` using formula = 'y ~ x'
`geom_smooth()` using formula = 'y ~ x'
`geom_smooth()` using formula = 'y ~ x'
`geom_smooth()` using formula = 'y ~ x'
dagitty
: R interface to dagitty.net
<- dagitty("dag{
rt_dag X [exposure]
Y [outcome]
U [unobserved]
X -> Y
X <- U <- A -> C -> Y
U -> B <- C
}")
coordinates(rt_dag) <- list(
x=c(U=0, X=0, A=0.5, B=0.5, C=1, Y=1),
y=c(X=0.75, Y=0.75, B=0.5, U=0.25, C=0.25, A=0)
)drawdag(rt_dag, cex=2, lwd=4, radius=6)
Two backdoor paths!
\(X \leftarrow \require{enclose}\enclose{circle}{U} \leftarrow A \rightarrow C \rightarrow Y\): Open or closed?
\(X \leftarrow \require{enclose}\enclose{circle}{U} \rightarrow B \leftarrow C \rightarrow Y\): Open or closed?
adjustmentSets(rt_dag)
{ C }
{ A }