Skip to content

Commit

Permalink
Update getting started vignette with story
Browse files Browse the repository at this point in the history
  • Loading branch information
EeethB committed Sep 11, 2023
1 parent 656c9a1 commit 669d503
Show file tree
Hide file tree
Showing 2 changed files with 104 additions and 32 deletions.
2 changes: 1 addition & 1 deletion R/plot.initial_graph.R
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ plot.initial_graph <- function(x,
vertex.color = "#e8c2ff",
vertex.label = v_labels,
vertex.label.color = "black",
vertex.size = 35,
vertex.size = 20,
edge.label = edge_labels,
edge.curved = curve,
edge.arrow.size = 1,
Expand Down
134 changes: 103 additions & 31 deletions vignettes/graph-examples.Rmd
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
---
title: "Commonly-used graph examples"
title: "Common procedures"
output: rmarkdown::html_vignette
vignette: >
%\VignetteIndexEntry{Commonly-used graph examples}
%\VignetteIndexEntry{Common procedures}
%\VignetteEngine{knitr::rmarkdown}
%\VignetteEncoding{UTF-8}
---
Expand All @@ -11,7 +11,8 @@ vignette: >
knitr::opts_chunk$set(
fig.align = "center",
collapse = TRUE,
comment = "#>"
comment = "#>",
fig.dim = c(5, 5)
)
```

Expand All @@ -21,24 +22,38 @@ library(graphicalMCP)

# Introduction

## Multiple comparison procedures
In confirmatory clinical trials with multiple endpoints, regulations mandate the strong control of the family-wise error rate to reduce the risk of false positives. Multiple comparison procedures (MCPs) are used to spread the total significance out over multiple hypotheses. In this article we'll demonstrate how to go from a few standard procedures to more advanced procedures using the graphical approach.

In confirmatory clinical trials with multiple endpoints,
# Simple procedures

Any valid graph can be made with `graph_create()`, but some patterns have been established over time. We demonstrate how to run some of these patterns with graphicalMCP.
## Bonferroni

# Bonferroni-Holm
Probably the most common MCP is the Bonferroni correction. It simply divides the alpha level evenly between all hypotheses. No significance adjustment is made if a hypothesis is rejected. The Bonferroni correction can be performed easily without a graphical approach, but here is a graphical example of it.

```{r bonferroni-holm, fig.dim=c(6, 6)}
```{r bonferroni}
transitions <- matrix(0, 5, 5)
bonferroni_graph <- graph_create(rep(1 / 5, 5), transitions)
plot(bonferroni_graph, layout = igraph::layout_in_circle, vertex.size = 30)
```

## Holm

The Holm procedure starts with identical weights to Bonferroni, but weights are updated uniformly when a hypothesis is rejected. Because of this updating, it is always at least as powerful as the Bonferroni procedure.

```{r bonferroni-holm}
transitions <- matrix(1 / 4, 5, 5)
diag(transitions) <- 0
bonferroni_holm_graph <- graph_create(rep(1 / 5, 5), transitions)
holm_graph <- graph_create(rep(1 / 5, 5), transitions)
plot(bonferroni_holm_graph)
plot(holm_graph, layout = igraph::layout_in_circle, vertex.size = 30)
```

# Fixed sequence
## Fixed sequence

Another common procedure because of its simplicity is the fixed sequence procedure. It places more emphasis on rejecting hypotheses when most hypotheses are expected to have p-values only slightly below alpha, where the reduced weights of the Holm procedure would cause most or all hypotheses to not be rejected from the start.

```{r fixed-sequence, fig.dim=c(6, 2)}
fixed_sequence_graph <- graph_create(
Expand All @@ -51,12 +66,20 @@ fixed_sequence_graph <- graph_create(
)
)
plot(fixed_sequence_graph, layout = "grid", nrow = 1)
plot(fixed_sequence_graph, nrow = 1, asp = .1)
```

# Simple successive
<!-- This paragraph needs work -->

These procedures are popular because they control the FWER while being simple, allowing them to be easily understood and communicated about. However, they're not always the procedures which fit the study best, and using more complex procedures can make communication difficult. The graphical approach can enable more powerful procedures to be used while enabling clinical teams to understand and communicate the procedures.

```{r simple-successive, fig.dim=c(5, 5)}
# Complex procedures

## Simple successive

The simple successive procedure was introduced by [names] in their [year] paper, [*title*]. It is designed for experiments involving four hypotheses: Two pairs of hypotheses, each containing a primary and a related secondary hypothesis. Initial weight is split equally between pairs and is passed from one pair to the other only when both hypotheses in a pair are rejected.

```{r simple-successive}
simple_successive_graph <- graph_create(
c(.5, .5, 0, 0),
rbind(
Expand All @@ -67,7 +90,7 @@ simple_successive_graph <- graph_create(
)
)
plot(simple_successive_graph, layout = "grid", nrow = 2)
plot(simple_successive_graph, layout = "grid", nrow = 2, vertex.size = 30)
```

General successive graphs are a good example where multiple variations may be useful with slight differences in starting edge weights. Variable edge weights are not currently supported, but they can be done for a particular graph with a light wrapper.
Expand All @@ -86,29 +109,33 @@ simple_successive_var <- function(gamma) {
}
```

Then multiple variations can be created and compared easily.
Then multiple variations can be created and compared easily. A similar approach could be used for deviating from the equal split of hypothesis weights as well.

```{r simple-successive-var, fig.show="hold", fig.align='default', out.width="47%"}
plot(
simple_successive_var(.75),
layout = "grid",
nrow = 2,
vertex.label.cex = .7,
edge.label.cex = .7
vertex.label.cex = 1.4,
edge.label.cex = 1.4,
vertex.size = 30
)
plot(
simple_successive_var(.9),
layout = "grid",
nrow = 2,
vertex.label.cex = .7,
edge.label.cex = .7
vertex.label.cex = 1.4,
edge.label.cex = 1.4,
vertex.size = 30
)
```

# Huque-Alosh-Bhore (2011)
## Huque-Alosh-Bhore (2011)

In 2011, Huque, Alosh, and Bhore proposed another size 4 procedure. What's this one particularly meant for?

```{r huque-alosh-bhore, fig.dim=c(5, 5)}
```{r huque-alosh-bhore}
hab_2011_graph <- graph_create(
c(1, 0, 0, 0),
rbind(
Expand All @@ -119,10 +146,10 @@ hab_2011_graph <- graph_create(
)
)
plot(hab_2011_graph, layout = "grid")
plot(hab_2011_graph, layout = "grid", vertex.size = 30)
```

# Wiens-Dmitrienko (2005)
## Wiens-Dmitrienko (2005)

```{r wiens-dmitrienko, fig.dim=c(6, 2)}
wd_2005_graph <- graph_create(
Expand All @@ -138,13 +165,17 @@ plot(
wd_2005_graph,
layout = "grid",
nrow = 1,
edge_curves = c(pairs = -6, "H3|H1" = -6)
edge_curves = c(pairs = -4, "H3|H1" = -4),
vertex.size = 30,
asp = .1
)
```

# Parallel gate-keeping
## Parallel gate-keeping

```{r parallel-gatekeeping, fig.dim=c(5, 5)}
In [year], [names] introduced the parallel gate-keeping procedure. It is useful when...?

```{r parallel-gatekeeping}
par_gate_graph <- graph_create(
c(.5, .5, 0, 0),
rbind(
Expand All @@ -156,12 +187,20 @@ par_gate_graph <- graph_create(
)
plot(par_gate_graph, layout = "grid", nrow = 2)
plot(par_gate_graph, layout = "grid", nrow = 2, vertex.size = 30)
```

## Improved parallel gate-keeping

One shortcoming of the parallel gate-keeping approach is in the case where e.g. H2, H3, and H4 are found to be significant. Once these three have been deleted, H1 is left with only 0.5 weight. This makes the graph sub-optimal.

```{r parallel-gatekeeping-update}
plot(graph_update(par_gate_graph, 2:4), vertex.size = 30)
```

# Improved parallel gate-keeping
The graph can be made optimal by adding epsilon edges going back up from the secondary to the primary hypotheses. Epsilon edges have some infinitesimal weight which creates a connection between hypotheses that effectively only propagates weight when it's the only edge left exiting a hypothesis. This yields the improved parallel gate-keeping procedure.

```{r improved-parallel-gatekeeping, fig.dim=c(5, 5)}
```{r improved-parallel-gatekeeping}
imp_par_gate_graph <- graph_create(
c(.5, .5, 0, 0),
rbind(
Expand All @@ -172,6 +211,39 @@ imp_par_gate_graph <- graph_create(
)
)
plot(
imp_par_gate_graph,
layout = "grid",
nrow = 2,
eps = .001,
vertex.size = 30
)
```

```{r improved-parallel-gatekeeping-update, fig.show="hold", fig.align='default', out.width="30%"}
plot(
graph_update(imp_par_gate_graph, c(2)),
vertex.size = 60,
vertex.label.cex = 2.5,
edge.label.cex = 2.5,
eps = 1e-3
)
plot(
graph_update(imp_par_gate_graph, c(2, 4)),
vertex.size = 60,
vertex.label.cex = 2.5,
edge.label.cex = 2.5
)
plot(imp_par_gate_graph, layout = "grid", nrow = 2)
plot(
graph_update(imp_par_gate_graph, c(2, 4, 3)),
vertex.size = 60,
vertex.label.cex = 2.5,
edge.label.cex = 2.5
)
```

# Conclusion

These examples are just a few of the unlimited possibilities enabled by the graphical approach to MCPs. Graphs can be customized based on priorities in trial design, marginal power of the endpoints, and other assumptions, such as expected or known correlation between endpoints. The graphical approach allows these complex procedures to maximize the chances of having a successful trial while enabling better communication and insight about the procedures.

0 comments on commit 669d503

Please sign in to comment.