forked from markdunning/shiny-bioinformatics
-
Notifications
You must be signed in to change notification settings - Fork 3
/
interactive-plots.Rmd
299 lines (223 loc) · 11.7 KB
/
interactive-plots.Rmd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
---
title: "Interactive Plots in Shiny Apps"
author: "Matt Eldridge"
date: '`r format(Sys.time(), "Last modified: %d %b %Y")`'
output: html_document
runtime: shiny
---
```{r setup, include = FALSE}
knitr::opts_chunk$set(echo = TRUE)
```
### Introduction
In this section we briefly introduce creating Shiny applications with interactive plots. These are figures where clicking on a point within a plot will trigger a selection event that causes an update to another part of the application.
We will show how this can be achieved with static plots generated by **ggplot2** and also with two JavaScript libraries, **Plotly** and **HighCharts**, which allow for even more interactivity including zooming and tooltips.
For the examples shown here we use the [results of a differential expression analysis](NKI-DE-results.csv) using limma, comparing ER negative and positive status groups (see [DE.R](DE.R) script).
### ggplot2
The following Shiny application shows a Volcano plot of the log P-value versus the log fold change.
It is possible when using ggplot2 (and base) graphics to handle mouse click events within a Shiny application. This can be very useful for allowing a user to select data points of interest and display more detailed information about the items selected.
Here we add a `click` argument to `plotOutput` to listen to mouse click events and update a table with details of the data points near to the cursor. You can also listen to double-click and hover (mouse-over) events in exactly the same way. Note the way in which the table is updated in response to a mouse click as reflected in a change in the value of `input$volcanoPlotClick`.
`nearPoints` is a convenient function for extracting the rows of the data frame on which the plot is based for the points near where the mouse was clicked. In the code below, `nearPoints` is used within the `renderDataTable` function to create the data frame that populates the table.
```{r message = FALSE}
library(shiny)
library(tidyverse)
ui <- fluidPage(
titlePanel("Volcano Plot"),
fluidRow(
column(
width = 6,
plotOutput("volcanoPlot", click = "volcanoPlotSelection", height = "500px")
),
column(
width = 6,
dataTableOutput("selectedProbesTable")
)
)
)
server <- function(input, output) {
differentialExpressionResults <-
read.csv("NKI-DE-results.csv", stringsAsFactors = FALSE) %>%
mutate(
probe.type = factor(ifelse(grepl("^Contig", probe), "EST", "mRNA")),
minusLog10Pvalue = -log10(adj.P.Val)
)
output$volcanoPlot <- renderPlot({
ggplot(differentialExpressionResults, aes(x = logFC, y = minusLog10Pvalue, colour = probe.type)) +
geom_point() +
xlab("log fold change") +
ylab("-log10(P-value)") +
theme(legend.position = "bottom")
})
output$selectedProbesTable <- renderDataTable(
nearPoints(differentialExpressionResults, input$volcanoPlotSelection) %>%
transmute(
probe,
gene = HUGO.gene.symbol,
`log fold change` = signif(logFC, digits = 2),
`p-value` = signif(adj.P.Val, digits = 2)
),
options = list(dom = "tip", pageLength = 10, searching = FALSE)
)
}
shinyApp(ui, server, options = list(height = 600))
```
Note that if you are using base graphics the `nearPoints` function needs to be provided with details of what columns were used for the x- and y-axes using the `xvar` and `yvar` arguments; for ggplot2 Shiny was able to infer these.
#### Exercise
Try changing the code above to handle mouse-over (hover) and brush events (brushing involves drawing a box in the plot area) instead of mouse clicks (hint: see help page for `plotOutput` function).
#### Useful links
* [Introduction to interactive plots](https://shiny.rstudio.com/articles/plot-interaction.html) from RStudio
* More [advanced guide](https://shiny.rstudio.com/articles/plot-interaction-advanced.html) to interactive plots from RStudio
### Plotly
[Plotly](https://plot.ly/r) is an R package for creating interactive web-based graphs via the open source JavaScript graphing library [plotly.js](https://plot.ly/javascript/) (which in turn is built on D3.js). Although developed by a company of the same name, it is open source.
One really great feature of the plotly R package is the ability to convert a ggplot figure into an interactive plot using the `ggplotly` function. This enables the resulting plot to be even more interactive with features such as zooming, panning, tooltips, toggling the display of subsets of data by clicking on legend items, and a save as PNG option.
In the following example, we've changed the above Shiny application to use `ggplotly`. In this case, selection is facilitated by drawing a box around data points of interest ('brushing'). Handling of click, hover and relayout (e.g. zoom) events are also supported.
```{r message = FALSE}
library(shiny)
library(plotly)
library(tidyverse)
ui <- fluidPage(
titlePanel("Volcano Plotly"),
fluidRow(
column(
width = 7,
plotlyOutput("volcanoPlot", height = "500px")
),
column(
width = 5,
dataTableOutput("selectedProbesTable")
)
)
)
server <- function(input, output) {
differentialExpressionResults <-
read.csv("NKI-DE-results.csv", stringsAsFactors = FALSE) %>%
mutate(
probe.type = factor(ifelse(grepl("^Contig", probe), "EST", "mRNA")),
minusLog10Pvalue = -log10(adj.P.Val),
tooltip = ifelse(is.na(HUGO.gene.symbol), probe, paste(HUGO.gene.symbol, " (", probe, ")", sep = ""))
) %>%
sample_n(1000)
output$volcanoPlot <- renderPlotly({
plot <- differentialExpressionResults %>%
ggplot(aes(x = logFC,
y = minusLog10Pvalue,
colour = probe.type,
text = tooltip,
key = row.names(differentialExpressionResults))) +
geom_point() +
xlab("log fold change") +
ylab("-log10(P-value)")
plot %>%
ggplotly(tooltip = "tooltip") %>%
layout(dragmode = "select")
})
output$selectedProbesTable <- renderDataTable({
eventData <- event_data("plotly_selected")
selectedData <- differentialExpressionResults %>% slice(0)
if (!is.null(eventData)) selectedData <- differentialExpressionResults[eventData$key,]
selectedData %>%
transmute(
probe,
gene = HUGO.gene.symbol,
`log fold change` = signif(logFC, digits = 2),
`p-value` = signif(adj.P.Val, digits = 2)
)
},
options = list(dom = "tip", pageLength = 10, searching = FALSE)
)
}
shinyApp(ui, server, options = list(height = 600))
```
As with other client-side JavaScript libraries, the processing required for the interactivity is being handled within the web browser. Plots containing many thousands of data points can become quite unresponsive. Here we sampled 1000 points randomly from the 25,000 in our original data frame. In practice it might make more sense to partition the data into a subset that needs to be displayed, e.g. genes with either low p-value or high fold changes, and a subset from which data points can be sampled to make up a manageable data set for including in the plot.
#### Useful links
* Plotly guide on [event handling within Shiny](https://plot.ly/r/shinyapp-plotly-events) applications
* [Plotly Shiny gallery](https://plot.ly/r/shiny-gallery)
### HighCharts using highcharter R wrapper
[HighCharts](https://www.highcharts.com) is a popular commercial interactive JavaScript charting library. It is free for non-commercial use. There are at least two R wrapper packages for HighCharts. Here we use the [highcharter](http://jkunst.com/highcharter) package.
The array of [configuration parameters](http://api.highcharts.com/highcharts) for HighCharts is somewhat bewildering, providing control over almost all aspects of plotting. The documentation is comprehensive, at least that is if you're programming directly in JavaScript. The refernce manual for the highcharter package has improved considerably but there is still quite a steep learning curve involved in using the package and working out how to specify a configuration parameter is not always straightforward.
Event handling for tooltips and mouse clicks is also more complicated and involves creating small JavaScript functions.
```{r message = FALSE}
library(shiny)
library(tidyverse)
library(highcharter)
ui <- fluidPage(
titlePanel("Volcano HighChart"),
fluidRow(
column(
width = 7,
highchartOutput("volcanoPlot", height = "500px")
),
column(
width = 5,
dataTableOutput("selectedProbesTable")
)
)
)
server <- function(input, output) {
differentialExpressionResults <-
read.csv("NKI-DE-results.csv", stringsAsFactors = FALSE) %>%
mutate(
probe.type = factor(ifelse(grepl("^Contig", probe), "EST", "mRNA")),
minusLog10Pvalue = -log10(adj.P.Val),
tooltip = ifelse(is.na(HUGO.gene.symbol), probe, paste(HUGO.gene.symbol, " (", probe, ")", sep = ""))
) %>%
sample_n(1000)
output$volcanoPlot <- renderHighchart({
plot <- highchart() %>%
hc_chart(animation = TRUE, zoomType = "xy") %>%
hc_xAxis(title = list(text = "log fold change")) %>%
hc_yAxis(title = list(text = "-log10(P-value)")) %>%
hc_colors(c('rgba(67, 67, 72, 0.6)', 'rgba(124, 181, 236, 0.6)')) %>%
hc_legend(layout = "horizontal", align = "center", verticalAlign = "bottom") %>%
hc_exporting(enabled = TRUE, filename = "volcanoPlot.svg") %>%
hc_tooltip(
animation = FALSE,
formatter = JS("function() { return (this.point.tooltip) }")) %>%
hc_plotOptions(series = list(
cursor = "pointer",
point = list(events = list(click = JS("function() { Shiny.onInputChange('volcanoPlot_clicked', this.id) }")))))
for (probeType in levels(differentialExpressionResults$probe.type)) {
seriesData <- differentialExpressionResults %>%
filter(probe.type == probeType) %>%
select(id = probe, x = logFC, y = minusLog10Pvalue, tooltip) %>%
list_parse
plot <- plot %>%
hc_add_series(
data = seriesData,
name = probeType,
type = "scatter",
marker = list(symbol = "circle"),
stickyTracking = FALSE
)
}
plot
})
output$selectedProbesTable <- renderDataTable({
selectedData <- differentialExpressionResults %>% slice(0)
clicked <- input$volcanoPlot_clicked
if (!is.null(clicked))
selectedData <- differentialExpressionResults %>% filter(probe == clicked)
selectedData %>%
transmute(
probe,
gene = HUGO.gene.symbol,
`log fold change` = signif(logFC, digits = 2),
`p-value` = signif(adj.P.Val, digits = 2)
)
},
options = list(dom = "t", searching = FALSE)
)
}
shinyApp(ui, server, options = list(height = 575))
```
#### Useful links
* [Highcharter website](http://jkunst.com/highcharter) providing lots of examples with code
* [Highcharter reference manual](https://cran.r-project.org/web/packages/highcharter/highcharter.pdf) from CRAN project page
* [HighCharts API](http://api.highcharts.com/highcharts) documentation
### Other packages
Several interactive visualization packages that use JavaScript libraries can be used within Shiny applications. These include:
* [Google charts](https://developers.google.com/chart) via the [googleVis](https://github.com/mages/googleVis) R package
* [HighCharts](https://www.highcharts.com) via the [rChart](https://github.com/ramnathv/rCharts) R package
* [d3heatmap](https://github.com/rstudio/d3heatmap) R package that uses the [D3](https://d3js.org) Javascript library.
Several example shiny applications that showcase these and other interactive visualization packages can be found here:
* [RStudio gallery](https://shiny.rstudio.com/gallery/)
* [htmlwidgets for R](http://www.htmlwidgets.org/index.html)