-
Notifications
You must be signed in to change notification settings - Fork 41
/
05-workflow.Rmd
642 lines (440 loc) · 16.8 KB
/
05-workflow.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
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
# Workflow
## Why workflow?
> "I think of workflow as one of my "secret" powers: one of the reasons that I've been able to accomplish so much is that I devote time to analysing and improving my workflow. I highly encourage you to do the same!" -Hadley Wickham (author)
- Workflow makes the process of writing Shiny apps more enjoyable, and helps your skills improve more quickly.
## Learning objectives
- The goal of this chapter is to help you improve three important Shiny workflows:
- Learn basic **development cycle** for creating apps,making changes, and quickly expirement with the results.
- Learn how to **debug** Shiny apps
- Learn how to write **self-contained reprexes**
![](images/05-workflow/workflow.png)
## Development workflow
- Why development workflow?
- Allows you to reduce the time between making a change and seeing the outcome
- The faster you can iterate and experiment, the faster you will become a better Shiny developer.
- Two main workflows to optimize here:
- creating apps (for first time),
- making changes and experimenting with the results faster (speeding up the iterative cycle).
## Creating apps
- Every shiny app has the same six lines of code.
```{r eval=FALSE}
library(shiny)
ui <- fluidPage(
)
server <- function(input, output, session) {
}
shinyApp(ui, server)
```
- Type `shinyapp` in `app.R` and you will see prompt to insert snippet (Shift + Tab).
- If you are using RStudio, you can create Shiny Web Application project easily via the file menu.
- `File` >> `New Project` >> `New Directory` >> `Shiny Web Application`
## Seeing your changes faster
> At most, you'll *create* a few apps a day, but you'll **run** apps hundreds of times, so mastering the development workflow is particularly important. - Hadley Wickham (author)
- Avoid clicking the "Run App" button
- Use keyboard shortcut: `Cmd/Ctrl + Shift + Enter`
- Turn auto reload on and run the app in the background as described [here](https://github.com/sol-eng/background-jobs/tree/master/shiny-job).
- Gives faster workflow: write some code, `Cmd/Ctrl + S` to save the file and experiment interactively.
- **Disadvantage:** harder to debug because the app is running in a separate process.
- Bigger apps: interactive testing >> automated testing (Chapter 21).
- Demo starting an app as a local job.
## Controlling the view
- Run in Viewer Pane: opens the app in the viewer pane . for smaller apps
- Run External: opens the app in your usual web browser.
- useful for larger apps
**Submenu used to change the view (click the drop-down)**
<center>
![](images/05-workflow/05-change-view-example.png)
</center>
## Debugging
> It's an eight line app, what could possibly go wrong? -Hadley Wickham (author)
> The process of systematically comparing your expectations to reality until you find the mismatch. -Hadley Wickham (author)
- something will go wrong definitely.
- it takes years of experience to write code that works the first time (So, we need a robust workflow for identifying and fixing mistakes)
- Specific focus to three debugging challenges to Shiny apps
## Three dubugging Challenges
<!-- A grammatically correct program may give you incorrect results due to logic errors. In case such errors (i.e. bugs) occur, you need to find out why and where they occur so that you can fix them. The procedure to identify and fix bugs is called “debugging”. -->
<!-- - R’s debugging tools include the traceback, browser, debug, debugonce, trace, and recover functions -->
<!-- - it takes years of experience in any language before you can reliably write code that works the first time. -->
<!-- - So, bugs happen. -->
<!-- - Three main cases of problems: -->
![](images/05-workflow/debugging_challenges.png)
### You get an unexpected error (easiest case).
- **Solution: A traceback is returned**
- Info that points to where the error occurred
- The interactive debugger is a powerful assistant for this process.
### You don’t get any errors
- **Solution: Use the interactive debugger**
- A tool to investigative and track down the root cause.
### All the values are correct, but they’re not updated when you expect
- **Solution: Most challenging problem**
- It’s unique to Shiny
- You can’t take advantage of your existing R debugging skills.
## Fixing errors: Tracebacks
- In R, every error is accompanied by a traceback, or call stack, which literally traces back through the sequence of calls that lead to the error
- The functions are printed in reverse order
- The traceback tool pinpoints the location of an error.
![](images/05-workflow//call_stack.png)
### Example of reading traceback
```{r}
f <- function(x) g(x)
g <- function(x) h(x)
h <- function(x) 2 * 2
f(3)
```
```{r, eval=FALSE}
f <- function(x) g(x)
g <- function(x) h(x)
h <- function(x) x * 2
f("a")
```
- This will generate an error below
```{r, eval=FALSE}
f("a")
#> Error in x * 2: non-numeric argument to binary operator
```
- The traceback is shown below:
- Top of the stack points to an error
```{r, eval=FALSE}
traceback()
#> 3: h(x)
#> 2: g(x)
#> 1: f("a")
```
- Flipping the traceback shows the better sequence(the top of the stack points to an error location)
- shows sequence of calls that lead to the error — f() called g() called h() (which errors)
```{r, eval=FALSE}
1: f("a")
2: g(x)
3: h(x)
```
## Tracebacks in Shiny
- You can’t use `traceback()` in Shiny because you can’t run code while an app is running.
- Shiny automatically prints the traceback for you.
```{r eval=FALSE, error= TRUE}
library(shiny)
f <- function(x) g(x)
g <- function(x) h(x)
h <- function(x) x * 2
ui <- fluidPage(
selectInput("n", "N", 1:10),
plotOutput("plot")
)
server <- function(input, output, session) {
output$plot <- renderPlot({
n <- f(input$n)
plot(head(cars, n))
}, res = 96)
}
shinyApp(ui, server)
```
- We will see an error below
```{r, eval=FALSE}
Error in *: non-numeric argument to binary operator
169: g [app.R#4]
168: f [app.R#3]
167: renderPlot [app.R#13]
165: func
125: drawPlot
111: <reactive:plotObj>
95: drawReactive
82: renderFunc
81: output$plot
1: runApp
```
- We can also flip the error
```{r, eval=FALSE}
Error in *: non-numeric argument to binary operator
1: runApp
81: output$plot
82: renderFunc
95: drawReactive
111: <reactive:plotObj>
125: drawPlot
165: func
167: renderPlot [app.R#13]
168: f [app.R#3]
169: g [app.R#4]
```
## Three components to a Shiny error stack
- **First**: few calls start the app. Ignore anything before the first runApp(); this is just the setup code to get the app running.
```{r, eval=FALSE}
1: runApp
#Sometimes, you may see other things before runAPP ignore them
#1: source
#3: print.shiny.appobj
#5: runApp
```
- **Second:** some internal Shiny code in charge of calling the reactive expression(output$plot is where the problem is) :
```{r, eval=FALSE}
81: output$plot
82: renderFunc
95: drawReactive
111: <reactive:plotObj>
125: drawPlot
165: func
```
- **Third**: Code that you have written
```{r, eval=FALSE}
167: renderPlot [app.R#13]
168: f [app.R#3]
169: g [app.R#4]
```
## Fixing errors: Interactive debugger
- When do we use?
- You have identified the error using `traceback` and want to figure out what’s causing it.
- Use the interactive debugger to debug your code
- Two ways to launch the debugger:
- Add a call to `browser()` in your source code
- This can also be done using a conditional statement
```{r, eval=FALSE}
if (input$value == "a") {
browser()
}
# Or maybe
if (my_reactive() < 0) {
browser()
}
```
- Add an RStudio breakpoint by clicking to the left of the line number
### Useful interactive debugger commands
Use these when you the debugger is up and running:
`n` - next step in the function
`c` - leaves debugger, continues the regular execution of the function
`Q` - stops debugging, terminates the function, and returns to global workspace
## Debugging reactivity
- Hardest problem to debug.
- We need other tools, which are not introduced in this chapter.
- Use "print" debugging to show your values.
- Use `message()` here: "standard output" vs "standard error"
## Getting help (using Reprex)
![](images/05-workflow//workflow.png)
- If you cant debug the error, it is time to ask for help at Shiny Community by creating Reprex
- A reprex is just some R code that works when you copy and paste it into a R session on another computer
- Good reprex, makes it easy for others to help you debug your app
- Below is an example of Shiny reprex
### How to make a reprex
- Create a single self-contained file that contains everything needed to run your code (e.g load all packages)
- Test it by restarting fresh R session and then running the code
- Potential problem is sharing your data
- Use built-in datasets(mpg),
- create sample datasets and illustrate the problem,
- use subset of the data with `dput()`
```{r}
(mydata <- data.frame(x = 1:5, y = c("a", "b", "c", "d", "e")))
```
```{r}
dput(mydata)
```
- Last resort is to provide complete app.R and the needed data files using Gihub or Zip files (if reading data from disk seems irreducible part of the problem)
- Make sure you use relative paths
- Format your code for to be easy read
- Use `styler` package if you adopt [tidyverse style guide](https://style.tidyverse.org)
### Making a minimal reprex
- Trim out all the code that’s ok (make the life of a helper much easier) rather than forcing a potential helper to understand your entire app.
- This process often lead you to discover what the problem is, so you don’t have to wait for help from someone else!
- A good way to find error code is to remove sections of code from your application, piece by piece, until the problem goes away
- If removing a particular piece of code makes the problem stop, it’s likely that that code is related to the problem.
#### Example of Bad reprex
- all the needed packages are not loaded
- The code is not style, making it uneasy to help
```{r, eval=FALSE}
library(shiny)
shinyApp(
ui = fluidPage(
uiOutput("interaction_slider"),
verbatimTextOutput("breaks")
),
server = function(input, output, session) {
df <- data.frame (dateTime = c("2019-08-20 16:00:00",
"2019-08-20 16:00:01",
"2019-08-20 16:00:02",
"2019-08-20 16:00:03",
"2019-08-20 16:00:04",
"2019-08-20 16:00:05"),
var1 = c(9, 8, 11, 14, 16, 1),
var2 = c(3, 4, 15, 12, 11, 19),
var3 = c(2, 11, 9, 7, 14, 1)
)
timeSeries <- as.xts(df[,2:4],order.by=strptime(df[,1], format="%Y-%m-%d %H:%M:%S"))
print (paste(min(time(timeSeries)),is.POSIXt(min(time(timeSeries))),sep=' '))
print (paste(max(time(timeSeries)),is.POSIXt(max(time(timeSeries))),sep=' '))
output$interaction_slider <- renderUI({
sliderInput(
"slider",
```
#### Making the bad reprex better(minimal)
- loaded needd packages
- The code is style, making it easy to help
```{r, eval=FALSE}
library(xts)
library(lubridate)
library(shiny)
ui <- fluidPage(
uiOutput("interaction_slider"),
verbatimTextOutput("breaks")
)
server <- function(input, output, session) {
df <- data.frame(
dateTime = c(
"2019-08-20 16:00:00",
"2019-08-20 16:00:01",
"2019-08-20 16:00:02",
"2019-08-20 16:00:03",
"2019-08-20 16:00:04",
"2019-08-20 16:00:05"
),
var1 = c(9, 8, 11, 14, 16, 1),
var2 = c(3, 4, 15, 12, 11, 19),
var3 = c(2, 11, 9, 7, 14, 1)
)
timeSeries <- as.xts(df[, 2:4],
order.by = strptime(df[, 1], format = "%Y-%m-%d %H:%M:%S")
)
print(paste(min(time(timeSeries)), is.POSIXt(min(time(timeSeries))), sep = " "))
print(paste(max(time(timeSeries)), is.POSIXt(max(time(timeSeries))), sep = " "))
output$interaction_slider <- renderUI({
sliderInput(
"slider",
"Select Range:",
min = min(time(timeSeries)),
max = max(time(timeSeries)),
value = c(min, max)
)
})
brks <- reactive({
req(input$slider)
seq(input$slider[1], input$slider[2], length.out = 10)
})
output$breaks <- brks
}
shinyApp(ui, server)
```
- Remove part of the code that is independent with the error(e.g two lines starting with `print()`, timeSeres and df)
- new server calls reduced:
```{r, eval=FALSE}
datetime <- Sys.time() + (86400 * 0:10)
server <- function(input, output, session) {
output$interaction_slider <- renderUI({
sliderInput(
"slider",
"Select Range:",
min = min(datetime),
max = max(datetime),
value = c(min, max)
)
})
brks <- reactive({
req(input$slider)
seq(input$slider[1], input$slider[2], length.out = 10)
})
output$breaks <- brks
}
```
- Next, the example uses a relatively sophisticated Shiny technique where the UI is generated in the server function.
- But the `renderUI()` doesn’t use any reactive inputs, so it should work the UI. this leads to new UI that generate the error:
```{r, eval=FALSE}
ui <- fluidPage(
sliderInput("slider",
"Select Range:",
min = min(datetime),
max = max(datetime),
value = c(min, max)
),
verbatimTextOutput("breaks")
)
#> Error: Type mismatch for `min`, `max`, and `value`.
#> i All values must have same type: either numeric, Date, or POSIXt.
```
- looking at each of the inputs we’re feeding to min, max, and value to see where the problem is:
```{r, eval=FALSE}
min(datetime)
#> [1] "2021-03-15 23:20:03 UTC"
max(datetime)
#> [1] "2021-03-25 23:20:03 UTC"
c(min, max)
#> [[1]]
#> function (..., na.rm = FALSE) .Primitive("min")
#>
#> [[2]]
#> function (..., na.rm = FALSE) .Primitive("max")
```
- Now the problem is obvious: we haven’t assigned min and max variables
```{r, eval=FALSE}
ui <- fluidPage(
sliderInput("slider",
"Select Range:",
min = min(datetime),
max = max(datetime),
value = range(datetime)
),
verbatimTextOutput("breaks")
)
```
## Meeting Videos
### Cohort 1
`r knitr::include_url("https://www.youtube.com/embed/I3Bn03_9Ogk")`
<details>
<summary> Meeting chat log </summary>
```
00:04:01 Russ Hyde: Hi Everyone. Welcome to this weeks bookclub meeting.
00:05:43 Diamantis Sellis: Hi, thanks for organizing!
00:05:52 [email protected]: Hello!
00:07:45 Russ Hyde: So. Shamsudeen should be presenting today.
00:12:21 Russ Hyde: Hi everyone. Are you able to hear Shamsuddeen ok?
00:12:27 priyanka gagneja: yes
00:12:31 Anne Hoffrichter: yes
00:12:32 Diamantis Sellis: yes
00:15:27 Jessica Mukiri: Hey have you just stared or the daylight savings taken effect yet?
00:17:28 Kent J: DST started on Sunday morning in Boston.
00:42:35 Russ Hyde: Is it options(shiny.fullstacktrace = TRUE)?
00:42:41 Russ Hyde: https://shiny.rstudio.com/reference/shiny/1.1.0/shiny-options.html
00:43:48 Russ Hyde: There's an example herer: https://shiny.rstudio.com/articles/debugging.html
01:08:13 Jessica Mukiri: Thanks everyone, have a good week.
```
</details>
### Cohort 2
`r knitr::include_url("https://www.youtube.com/embed/PPxi-IoAf7A")`
`r knitr::include_url("https://www.youtube.com/embed/OdTG862TChM")`
<details>
<summary> Meeting chat logs </summary>
```
# 2021-08-25
01:06:57 Kevin: pointblank pkg
#2021-09-01
00:12:41 Conor Tompkins: Is Kevin’s volume low for anyone else?
00:12:43 shane: Kevin are you able to bring your mic volume up a little?
00:12:45 shane: yeah
01:05:45 Kevin Gilds: https://twitter.com/EmilyRiederer/status/1430882791806410753?s=20
```
</details>
### Cohort 3
`r knitr::include_url("https://www.youtube.com/embed/-dHiUdsRxA4")`
### Cohort 4
`r knitr::include_url("https://www.youtube.com/embed/16qIoee5zGc")`
<details>
<summary>Meeting chat log</summary>
```
00:25:38 LUCIO ENRIQUE CORNEJO RAM REZ: all good
00:26:05 Trevin Flickinger: https://support.posit.co/hc/en-us/articles/200711853-Keyboard-Shortcuts-in-the-RStudio-IDE
00:26:20 Lydia Gibson: Hello
00:30:43 Matthew Efoli: https://appsilon.com/r-studio-shortcuts-and-tips-part-2/#:~:text=It%20is%20achieved%20by%20selecting,but%20it%20can%20be%20helpful.
00:43:33 Trevin Flickinger: https://adv-r.hadley.nz/debugging.html
00:57:18 Trevin Flickinger: https://wizardzines.com/zines/debugging-guide/
01:07:03 Trevin Flickinger: https://www.rstudio.com/resources/rstudioconf-2020/object-of-type-closure-is-not-subsettable/
01:07:20 Lydia Gibson: Thank you!
01:08:39 LUCIO ENRIQUE CORNEJO RAM REZ: thank you
01:09:17 Trevin Flickinger: I’m good with that
01:09:28 LUCIO ENRIQUE CORNEJO RAM REZ: works for me
01:09:42 Trevin Flickinger: I think most DSLC book clubs are on a two week break
01:10:44 LUCIO ENRIQUE CORNEJO RAM REZ: happy new year then :)
01:10:55 Trevin Flickinger: Happy new year all!
```
</details>
### Cohort 5
`r knitr::include_url("https://www.youtube.com/embed/URL")`
<details>
<summary>Meeting chat log</summary>
```
LOG
```
</details>