generated from jtr13/bookdown-template
-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy path05-tutorial.Rmd
454 lines (337 loc) · 20.8 KB
/
05-tutorial.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
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
# Create Shiny App from scratch{#tutorial}
We'll build a Shiny App that takes a csv file containing data for 6 samples and the raw counts for 1000 genes. Each sample is a column and each gene is a row. One sample will be plotted on the x-axis and another sample on the y-axis. We'll also want to be able to display a table of the selected columns but that will be for the challenge.
```{r head_data, echo=FALSE}
dat <- read.csv("ShinyApps/demo/data-files/shiny_sample_data.csv", row.names = 1)
head(dat, 10)
```
*Top ten rows of the raw data used to build the shiny app*
## Setting up a shiny app
Create one blank R script (Click `File` > `New File` > `R Script`). Save it inside the `demo` folder with the name `app.R`. We've now seen how to create a blank app with the ui and the server. We should be able to make out the layout of the app now. The top of the window is where the title panel is located and beneath it are two panels, laid out with the sidebar to the left and the larger main panel to the right.
## Using widgets
We are going to add two widgets to the ui. These will control which samples gets plotted to the x and y axis of the plot and will be located in the sidebar panel. We will use `selectInput` to create these widgets but these could easily be swapped out for other widgets such radio-buttons. To see what widgets are available, look at the [Shiny widget gallery](http://shiny.rstudio.com/gallery/widget-gallery.html) or consult the [Shiny cheatsheet](https://rstudio.github.io/cheatsheets/shiny.pdf).
To start off with, let's add just one widget to the sidebar.
```{r, eval = FALSE}
ui <- fluidPage(
titlePanel("My first app"),
sidebarLayout(
sidebarPanel("This is a sidebar panel",
selectInput(inputId = "x_select", label = "Select x-axis",
choices = list("sample1",
"sample2",
"sample3",
"sample4",
"sample5",
"sample6"),
selected = "sample1")
),
mainPanel("And this is the main panel")
)
)
```
Let's break down the `selectInput` function. `inputId` is the name of widget, in this case `x_select` is the name that'll be used in the server object to access the current value of the widget. The `labels` argument takes a string that will be used to label above the widget.
`choices` takes a list of values and can be a bit confusing to understand. For simplicity's sake, we have given an unnamed list, which will directly display the values themselves. `"sample1"` happens to be a column name in the data the app will be using, so this widget can be used to select which column gets plotted. However, if the names of the choices needs to be different from the values, a named list can be used.
For example:
```{r, eval = F}
choices = list("Sample 1" = "sample1",
"Sample 2" = "sample2",
"Sample 3" = "sample3",
"Sample 4" = "sample4",
"Sample 5" = "sample5",
"Sample 6" = "sample6")
```
In `"Sample 1" = "sample1"` the string `"Sample 1"` is the text that will appear in the widget, whereas `"sample1"` is the value that will be returned from the widget. Using this would make a much neater looking widget. Think of the name as purely cosmetic and what you'll see while the value is what's important and will be used behind the scenes for calculations. You could change the names to whatever you wanted without having on effect the app.
Finally, `selected` chooses which value the widget will start on when the app is run.
Now to add the second widget. To show the difference between an unnamed and a named list, the second widget will use a named list for `choices`.
```{r, eval = FALSE}
ui <- fluidPage(
titlePanel("My first app"),
sidebarLayout(
sidebarPanel("This is a sidebar panel",
selectInput(inputId = "x_select", label = "Select x-axis",
choices = list("sample1",
"sample2",
"sample3",
"sample4",
"sample5",
"sample6"),
selected = "sample1"),
selectInput(inputId = "y_select", label = "Select y-axis",
choices = list("Sample 1" = "sample1",
"Sample 2" = "sample2",
"Sample 3" = "sample3",
"Sample 4" = "sample4",
"Sample 5" = "sample5",
"Sample 6" = "sample6"),
selected = "sample2")
),
mainPanel("And this is the main panel")
)
)
```
This widget is nearly identical to the first, all that has changed is the name of the widget, the use of a named list and which sample has been pre-selected. This widget has been called `y_select` and it will start on the value `sample2` so that the columns selected for the x and y axis will be different.
#### Adding some outputs
There's one last thing to do before moving onto the server object. The app has inputs now, but it doesn't have any outputs! For this app, we will use three outputs. Two are going to be purely diagnostic so we can check that our `selectInputs` are working. They will not be included in our final app. The one output that will remain in our final app is `plotOutput`, as we want to generate a plot and it will be located in the main panel. There are numerous types of outputs Shiny can generate - to find out what other outputs Shiny is capable of, consult the [Shiny cheatsheet](https://rstudio.github.io/cheatsheets/shiny.pdf) or the [Shiny website](https://shiny.rstudio.com/reference/shiny/latest/).
```{r, eval = F}
ui <- fluidPage(
titlePanel("My first app"),
sidebarLayout(
sidebarPanel("This is a sidebar panel",
selectInput(inputId = "x_select", label = "Select x-axis",
choices = list("sample1",
"sample2",
"sample3",
"sample4",
"sample5",
"sample6"),
selected = "sample1"),
selectInput(inputId = "y_select", label = "Select y-axis",
choices = list("Sample 1" = "sample1",
"Sample 2" = "sample2",
"Sample 3" = "sample3",
"Sample 4" = "sample4",
"Sample 5" = "sample5",
"Sample 6" = "sample6"),
selected = "sample2")
),
mainPanel("And this is the main panel",
textOutput(outputId = "check_x_select"),
textOutput(outputId = "check_y_select"),
plotOutput(outputId = "my_plot")
)
)
)
```
`my_plot` is the ID of the `plotoutput` and it tells the server object file where to direct data. Same goes for the two `textOutputs`. Run the app and see what you'll get. Does it look any different?
At the moment, the app will display nothing because we haven't told it do any calculations and generate a plot or display text. To do that, we need to move on to the server side.
## Data processing on the server side
The app now has a full UI but nothing is happening on the backend. To make the app interactive, that's where the server object comes into play. Think of it as the object that contains an R analysis. The structure of this object can vary immensely depending on the complexity of the app itself.
First, we need to check on our two `selectInput` widgets, see if they are working correctly, before we move on to plotting. When building a Shiny app, it's useful to have create outputs to check to see if parts of the app are doing what they should be, for both the ui and the server code. This step can be omitted, but we'll go through it to demonstrate what the `selectInputs` are doing.
```{r, eval = F}
server <- function(input, output) {
output$check_x_select <- renderText({
print(input$x_select)
})
output$check_y_select <- renderText({
print(input$y_select)
})
}
```
Now run the app.
There are a number of `render` functions in Shiny, `renderTable`, `renderText`, `renderPrint`, etc, depending on what type of data needs to be output. If you want to check what render options exist, got the [Shiny website reference page](http://shiny.rstudio.com/reference/shiny/latest/).
Whenever a `render` function is called, it needs to be directed to an output that exists in the ui object. In the case of `output$check_x_select`, we have pointed the `renderText` function to `check_x_select`, which was the id of a `textOutput` in the UI. This means that the result of `renderText` is sent to the `check_x_select` output.
Inside the `renderText` function, we `print` the value of the `x_select`. To access a value of a input widget, we use `input$input_Id`. Once the app is running, every time the value of the input changes, a render function that depends on it will be re-run. So if we changed the value of the `x_select`, only the first `renderText` statement is re-calculated but the second one, which doesn't depend on `x_select` will not be re-calculated.
Notice that despite the difference between the two `selectInputs` (one uses a named list and the other doesn't), that the values returned are the same. Once again, using a named list changes the appearance of the ui but not the actual values of the widgets.
Now that we've seen that our widgets are working correctly, we can remove the diagnostic lines of code from both the ui and server. You can either comment them out or delete them. Your app should currently look like this:
```{r, eval = F}
library(shiny)
ui <- fluidPage(
titlePanel("My first app"),
sidebarLayout(
sidebarPanel("This is a sidebar panel",
selectInput(inputId = "x_select", label = "Select x-axis",
choices = list("sample1",
"sample2",
"sample3",
"sample4",
"sample5",
"sample6"),
selected = "sample1"),
selectInput(inputId = "y_select", label = "Select y-axis",
choices = list("Sample 1" = "sample1",
"Sample 2" = "sample2",
"Sample 3" = "sample3",
"Sample 4" = "sample4",
"Sample 5" = "sample5",
"Sample 6" = "sample6"),
selected = "sample2")
),
mainPanel("And this is the main panel",
plotOutput(outputId = "my_plot")
)
)
)
server <- function(input, output) {
}
shinyApp(ui = ui, server = server)
```
The final app will be rather simple. It will take one `render` functions and send the output to the UI. To get going, let's create an empty `renderPlot`.
```{r, eval = F}
server <- function(input, output) {
output$my_plot <- renderPlot({
})
}
```
Now let's give the `render` function some data to display, the first two columns from the csv `shiny_sample_data.csv`.
```{r, eval = F}
server <- function(input, output) {
output$my_plot <- renderPlot({
#Read in the sample csv
dat <- read.csv("data-files/shiny_sample_data.csv", row.names = 1)
#Pull out the first and second columns in dat
x_axis <- dat[, "sample1"]
y_axis <- dat[, "sample2"]
#Create a basic dot plot
plot(x = x_axis, y_axis)
})
}
```
**The complete working code looks like:**
```{r, echo=T, eval = F}
titlePanel("My first app")
#Changed up the input and output IDs so that they don't conflict with the earlier app.
sidebarLayout(
sidebarPanel("App input control panel",
selectInput("x_select1", label = h3("Select x-axis"),
choices = list("sample1",
"sample2",
"sample3",
"sample4",
"sample5",
"sample6"),
selected = "sample1"),
selectInput(inputId = "y_select1", label = "Select y-axis",
choices = list("Sample 1" = "sample1",
"Sample 2" = "sample2",
"Sample 3" = "sample3",
"Sample 4" = "sample4",
"Sample 5" = "sample5",
"Sample 6" = "sample6"),
selected = "sample2")
),
mainPanel("App output panel",
plotOutput("plot1", width = 600, height = 600)
)
)
output$plot1 <- renderPlot({
dat <- read.csv("data-files/shiny_sample_data.csv", row.names = 1)
#Pull out the first and second columns in dat
x_axis <- dat[, "sample1"]
y_axis <- dat[, "sample2"]
plot(x = x_axis, y = y_axis)
})
```
The app has a plot now...but it isn't interactive, if we change the widget values, it has no effect on what is plotted on the figure. That's because the first two columns were selected directly in the `renderPlot` function. However, we could change to call the widget values instead using `input`.
```{r, eval = F}
server <- function(input, output) {
output$my_plot <- renderPlot({
#Read in the sample csv
dat <- read.csv("data-files/shiny_sample_data.csv", row.names = 1)
#Pull out a column for the x and y axis each
x_axis <- dat[, input$x_select]
y_axis <- dat[, input$y_select]
#Create a basic dot plot
plot(x = x_axis, y_axis)
})
}
```
**The complete working app code looks like:**
```{r, eval = F}
library(shiny)
library(utils)
ui <- fluidPage(
titlePanel("My first app"),
sidebarLayout(
sidebarPanel("This is a sidebar panel",
selectInput(inputId = "x_select", label = "Select x-axis",
choices = list("sample1",
"sample2",
"sample3",
"sample4",
"sample5",
"sample6"),
selected = "sample1"),
selectInput(inputId = "y_select", label = "Select y-axis",
choices = list("Sample 1" = "sample1",
"Sample 2" = "sample2",
"Sample 3" = "sample3",
"Sample 4" = "sample4",
"Sample 5" = "sample5",
"Sample 6" = "sample6"),
selected = "sample2")
),
mainPanel("And this is the main panel",
textOutput(outputId = "check_x_select"),
textOutput(outputId = "check_y_select"),
plotOutput(outputId = "my_plot", width = 600, height = 600)
)
)
)
server <- function(input, output) {
output$check_x_select <- renderText({
print(input$x_select)
})
output$check_y_select <- renderText({
print(input$y_select)
})
output$my_plot <- renderPlot({
#Read in the sample csv
dat <- read.csv("data-files/shiny_sample_data.csv", row.names = 1)
#Pull out a column for the x and y axis each
x_axis <- dat[, input$x_select]
y_axis <- dat[, input$y_select]
#Create a basic dot plot
plot(x = x_axis, y_axis)
})
}
shinyApp(ui = ui, server = server)
```
### Activity 4: Modily x- and y-axes labels{- .activity}
Can you figure out how to change the axis titles to the name of the selected sample. One of `selectInput` widgets also need to be changed to a named list. Other than that, you're good to go.
> Hint:
>
> plot(x = blah, y = plah, xlab = "Label for the x-axis", ylab = "Label for the y-axis")
---
Now imagine you had an experiment where you had many conditions and you needed to plot each one against each other. Instead of plotting each one individually, with a Shiny app, you could easily visualise the different conditions without having to write code for each one of them and then download them.
<!-- If you want to get a shareable link of your app, so that other people can access it over the web: `http://biotraining.erc.monash.edu:3838/your_username/demo/`. -->
<!-- Make sure to change the url to have your biotraining account name in place of `your_username`. -->
### Challenge 6: Modify the app (breakout). {- .challenge}
Our collaborator saw the app we have created to show the relationship between two samples. They want to view the expression of individual genes in the samples in a tabular format. Modify your app to display the tabular data in the final output.
```{r, eval = F}
library(ggplot2)
# 1. Add ui
titlePanel("My slightly more complicated app")
sidebarLayout(
sidebarPanel("App input control panel",
selectInput("x_select3", label = h3("Select x-axis"),
choices = list("sample1",
"sample2",
"sample3",
"sample4",
"sample5",
"sample6"),
selected = "sample1"),
selectInput("y_select3", label = h3("Select y-axis"),
choices = list("sample1",
"sample2",
"sample3",
"sample4",
"sample5",
"sample6"),
selected = "sample2")
# 2. Add sliderInput
),
mainPanel("App output panel",
plotOutput("plot3", width = 600, height = 600)
# 3. Add tableOutput
)
)
# 4. Add server logic
# 5. Redirect output to a variable to be called in the mainPanel
```
1. The first step is to sort out the ui.
2. We want to print the number of rows in the selected data-set as decided by a slider. Create a slider input widget.
> Hint: The code for a slider looks something like this:
> sliderInput("inputIDyouwant", label = "Slider", min = , max = , value = 50)
>
> To find out more about how a slider works check the [Shiny widget gallery](http://shiny.rstudio.com/gallery/widget-gallery.html)
>
3. Create a new output called a `tableOutput` to print the data in tabular format.
> Hint: The code for it looks like:
> tableOutput(outputId = "table1")
4. Define the server function.
5. You'll need to direct the result of a `renderTable` function to the new output.
> Hint:
> The table that is displayed is a subset of the full csv. It shows only the two currently selected columns. Remember that a dataframe can be subsetted to show only certain columns using `x[, c(column_a, column_c, column_z)]`
>
> Inside the `renderTable` call, you'll need a function that shows you the top of a table. The `head` function is ideal for this. If you aren't familiar with how head works, check the help manual for `head` with `?head` to see the arguements it takes.
An important thing to note is that only one thing is returned from inside a render function (usually the last line inside that function). Therefore a variable, such as `dat` created inside the `renderPlot` **will not exist** outside that `renderPlot` function. You'll need to read the csv of data back in again in the `renderTable` function. For an ever further challenge, create a reactive function and pass the reactive variable to the two `render` functions. Read the csv file only once in the application