-
Notifications
You must be signed in to change notification settings - Fork 2
/
08-products.qmd
1673 lines (1308 loc) · 57.4 KB
/
08-products.qmd
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
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
# Data products
<div style="text-align:center;">
```{r, echo = F}
knitr::include_graphics("img/product.png")
```
</div>
What you’ll have learned by the end of the chapter: you'll know how to build data products using
Quarto and Shiny.
## Introduction
We are going to start by building data products using Quarto.
Quarto is a tool created by Posit, the company behind RStudio and Shiny. Quarto leverages pandoc to
convert between many document formats (for example, from `.md` to `.docx`) and makes it possible to
embed R, Python, Julia and Observable JS code into documents. It is not an R-specific tool, so it is
a program that you must install on your computer. So go to this
[link](https://quarto.org/docs/get-started/) and download and install Quarto.
We are going to start simple, with "static" data products. By static I mean products without any
sort of interactivity, so the user can look at them, read them, but not change them in any way.
These products are essentially going to be documents in the `.docx`, `.pptx` and `.pdf` formats,
but also `.html`. Thanks to Quarto, it is thus possible to programmatically create documents.
## A first taste of Quarto
A Quarto file looks very close to a standard Markdown file. So if you know Markdown, you will not have
many problems to switch to Quarto. If you don't know Markdown, no worries, its syntax is quite simple
and can be very quickly picked up.
Let's start with a basic Quarto source. Open your favorite text editor (doesn't have to be RStudio) and
create a file called `example.qmd` and copy the following lines in it:
````{verbatim}
---
title: "My Title"
author: "My name"
date: today
---
## This is a simple quarto document
```{r}
n <- 10
rnorm(n)
```
````
[This is the output](https://tiny-melomakarona-b87385.netlify.app/).
The first few lines of the document is where you can define the title of the document, the name of the author
and the date. For the date, I've use the `today` keyword to get today's date
but you could use a string to set the date to a specific day (for example, "2022-10-28").
The content in the document consists of a level 2 title (`## This is a simple quarto document`) and of an
R code chunk. Code chunks is were you will write code that gets then evaluated at render (compile) time.
To compile this file, run the following inside a terminal:
```
➤ quarto render example.qmd
```
If you're inside RStudio, you can also render the document by pressing `CTRL-SHIFT-K` or run the command:
```{r, eval = F}
quarto::quarto_render("example.qmd")
```
There are various ways to integrate Quarto with different editors:
- [VS Code](https://quarto.org/docs/get-started/hello/vscode.html)
- [RStudio](https://quarto.org/docs/get-started/hello/rstudio.html)
- [Jupyter](https://quarto.org/docs/get-started/hello/jupyter.html)
- [(Neo)Vim, Emacs, Sublime](https://quarto.org/docs/get-started/hello/text-editor.html#editor-modes)
Once the file is done rendering, you should find an `html` file in the same folder.
Open this html file inside a web browser and see the output. It is possible to run arbitrary R code
inside the code chunks:
````{verbatim}
---
title: "My Title"
author: "My name"
date: today
---
## This is a simple quarto document
```{r}
library(dplyr)
library(tidyr)
library(purrr)
library(ggplot2)
library(myPackage)
data("unemp")
unemp %>%
janitor::clean_names() %>%
filter(level == "Commune",
place_name %in% c("Luxembourg", "Esch-sur-Alzette", "Wiltz")) %>%
group_by(place_name) %>%
nest() %>%
mutate(plots = map2(.x = data, .y = place_name, ~ggplot(data = .x) +
theme_minimal() +
geom_line(aes(year, unemployment_rate_in_percent, group = 1)) +
labs(title = paste("Unemployment in", .y)))) %>%
pull(plots)
```
````
[This is what the output looks like.](https://scintillating-crostata-2df896.netlify.app/)
As you can see, it is quite easy to create a document with potentially hundreds of plots using what we've learned
until now. However, our document does not look great; for starters, we see the source code there, which we would like to hide.
People that will read this document might not be interested in the source code, but only in the plots. The other issue
is that when loading the `{dplyr}` package, users get some message informing them about some functions that get masked.
We would like to hide all of this. It turns out that code chunks have options, and we can use them to hide source code
and warning messages:
````{verbatim}
---
title: "My Title"
author: "My name"
date: today
---
## This is a simple quarto document
```{r}
#| echo: false
#| warning: false
library(dplyr)
library(tidyr)
library(purrr)
library(ggplot2)
library(myPackage)
data("unemp")
unemp %>%
janitor::clean_names() %>%
filter(level == "Commune",
place_name %in% c("Luxembourg", "Esch-sur-Alzette", "Wiltz")) %>%
group_by(place_name) %>%
nest() %>%
mutate(plots = map2(.x = data, .y = place_name, ~ggplot(data = .x) +
theme_minimal() +
geom_line(aes(year, unemployment_rate_in_percent, group = 1)) +
labs(title = paste("Unemployment in", .y)))) %>%
pull(plots)
```
````
[This is what the output looks like.](https://velvety-taffy-93cb66.netlify.app/)
Rendering this document will result in something nicer. We could also fold the code instead of completely removing
it. This is useful if we need to send the document to collaborators who might be interested in the source code as well.
However, code folding is something that only works in `html` outputs, and thus we need to specify the output format in the
header of the document (look at the three new lines after we define the data), and also remove the `echo: false` option
from the R chunk:
````{verbatim}
---
title: "My Title"
author: "My name"
date: today
format:
html:
code-fold: true
---
## This is a simple quarto document
```{r}
#| warning: false
library(dplyr)
library(tidyr)
library(purrr)
library(ggplot2)
library(myPackage)
data("unemp")
unemp %>%
janitor::clean_names() %>%
filter(level == "Commune",
place_name %in% c("Luxembourg", "Esch-sur-Alzette", "Wiltz")) %>%
group_by(place_name) %>%
nest() %>%
mutate(plots = map2(.x = data, .y = place_name, ~ggplot(data = .x) +
theme_minimal() +
geom_line(aes(year, unemployment_rate_in_percent, group = 1)) +
labs(title = paste("Unemployment in", .y)))) %>%
pull(plots)
```
````
[This is what the output looks like.](https://voluble-choux-674399.netlify.app/)
It is of course possible to write several R chunks:
````{verbatim}
---
title: "My Title"
author: "My name"
date: today
format:
html:
code-fold: true
---
## This is a simple quarto document
```{r}
#| warning: false
library(dplyr)
library(tidyr)
library(purrr)
library(ggplot2)
library(myPackage)
data("unemp")
unemp <- unemp %>%
janitor::clean_names() %>%
filter(level == "Commune")
```
There are `r length(unique(unemp$place_name))` communes in the dataset.
Below we plot the unemployment rate for 3 communes:
```{r}
unemp %>%
filter(place_name %in% c("Luxembourg", "Esch-sur-Alzette", "Wiltz")) %>%
group_by(place_name) %>%
nest() %>%
mutate(plots = map2(.x = data, .y = place_name, ~ggplot(data = .x) +
theme_minimal() +
geom_line(aes(year, unemployment_rate_in_percent, group = 1)) +
labs(title = paste("Unemployment in", .y)))) %>%
pull(plots)
```
````
[This is what the output looks like](https://relaxed-concha-89be95.netlify.app/).
### Python and Julia code chunks
It is possible, inside the same Quarto document, to define code chunks that run Python (or even Julia) code.
Put the following lines inside a file called `example2.qmd`
(to run the example below, you will need to have Python installed):
````{verbatim}
---
title: "R and Python"
author: "Bruno Rodrigues"
date: today
---
## This is a simple quarto document
```{r}
#| warning: false
library(dplyr)
library(tidyr)
library(purrr)
library(ggplot2)
library(myPackage)
data("unemp")
unemp <- unemp %>%
janitor::clean_names() %>%
filter(level == "Commune")
```
```{python}
print("hello from Python")
import sys
print(sys.version)
```
````
[This is what the output looks like.](https://phenomenal-clafoutis-2b05c1.netlify.app/)
If you have trouble rendering this line, make sure that you have the `jupyter` and `jupyterlab` modules installed.
It is also possible to pass objects from R to Python (and vice-versa):
````{verbatim}
---
title: "R and Python"
author: "Bruno Rodrigues"
date: today
---
## This is a simple quarto document
```{r}
#| warning: false
library(dplyr)
library(tidyr)
library(purrr)
library(ggplot2)
library(myPackage)
data("unemp")
unemp <- unemp %>%
janitor::clean_names() %>%
filter(level == "Commune")
```
The data that was loaded and cleaned from R can be accessed from Python using `r.unemp`:
```{python}
import pandas as pd
unemp_pd = pd.DataFrame(r.unemp)
unemp_pd.head
```
````
[This is what the output looks like.](https://endearing-gnome-80e9df.netlify.app/)
The HTML output is quite flexible, as it is possible to also integrate JS libraries. The following
example uses the `{g2r}` library (an R wrapper around the g2 javascript library) for creating
visualisations. To run the following code, make sure that you have the `{g2r}` package installed
(can only be install from github):
```{r, eval = F}
devtools::install_github("devOpifex/g2r")
```
The source file looks like this:
````{verbatim}
---
title: "Quarto and JS libraries"
author: "My name"
date: today
format:
html:
code-fold: true
---
## This is a simple quarto document showing basic plot interactivity using {g2r}
```{r}
#| warning: false
library(dplyr)
library(tidyr)
library(purrr)
library(g2r)
library(myPackage)
data("unemp")
unemp <- unemp %>%
janitor::clean_names() %>%
filter(level == "Commune")
```
There are `r length(unique(unemp$place_name))` communes in the dataset. Below we plot the unemployment rate for 3 communes:
```{r}
unemp %>%
filter(place_name %in% c("Luxembourg", "Esch-sur-Alzette", "Wiltz")) %>%
g2(data = .) %>%
fig_line(asp(year, unemployment_rate_in_percent, color = place_name))
```
````
[This is what the output looks like.](https://timely-marzipan-6380f0.netlify.app/)
It is possible to use other JS libraries, like here `DataTables`, wrapped inside the `{DT}` package:
````{verbatim}
---
title: "Quarto and JS libraries"
author: "My name"
date: today
format:
html:
toc: true
code-fold: true
---
## Basic plot interactivity using {g2r}
```{r}
#| warning: false
library(dplyr)
library(tidyr)
library(purrr)
library(g2r)
library(DT)
library(myPackage)
data("unemp")
unemp <- unemp %>%
mutate(year = as.character(year)) %>%
janitor::clean_names() %>%
filter(level == "Commune")
```
There are `r length(unique(unemp$place_name))` communes in the dataset. Below we plot the unemployment rate for 3 communes:
```{r}
unemp %>%
filter(place_name %in% c("Luxembourg", "Esch-sur-Alzette", "Wiltz")) %>%
g2(data = .) %>%
fig_line(asp(year, unemployment_rate_in_percent, color = place_name))
```
## Interactive tables with {DT}
```{r}
unemp %>%
DT::datatable(filter = "top")
```
## Others
You can find more widgets over [here](http://gallery.htmlwidgets.org/).
````
[This is what the output looks like.](https://playful-cucurucho-5cdcbb.netlify.app/)
The final example illustrates templating. It is possible to write code that generates qmd code:
````{verbatim}
---
title: "Templating with Quarto"
author: "Bruno Rodrigues"
date: today
format:
html:
toc: true
---
# Set up
The goal is to have a frequency table for each question in a survey. But we
do not want to have to do it by hand, so we define a function to create a
table, and then, using the templating capabilities of Quarto, write some
code to generate valid qmarkdown code. In the example below our survey only
has 4 questions, but the solution described trivially scales to an infinity
of questions. This is not the case if you’re solving this problem by hand.
Start by loading the data and defining some needed variables:
```{r}
#| warning: false
library(lubridate)
library(dplyr)
library(purrr)
library(rlang)
library(DT)
survey_data <- read.csv(
"https://gist.githubusercontent.com/b-rodrigues/0c2249dec5a9c9477e0d1ad9964a1340/raw/873bcc7532b8bad613235f029884df1d0b947c90/survey_example.csv"
)
```
Let’s take a look at the data:
```{r}
datatable(survey_data)
```
The column names are actually questions, so we save those in a variable:
```{r}
questions <- colnames(survey_data)
questions
```
Now we define question codes:
```{r}
codes <- paste0("var_", seq(1, 4))
codes
```
We create a lookup table that links questions to their codes:
```{r}
lookup <- bind_cols("codes" = codes, "questions" = questions)
datatable(lookup)
```
Finally, we replace the question names in the dataset by the code:
```{r}
colnames(survey_data) <- codes
datatable(survey_data)
```
Now, we define a function that creates a frequency table. This function has
two arguments: `dataset` and `var`. It uses the `dplyr::count()` function to
count each instance of the levels of `var` in `dataset`. Then it uses the
`knitr::kable()` function. This functions takes a data frame as an argument
and returns a table formatted in markdown code:
```{r}
create_table <- function(dataset, var){
dataset %>%
count(!!var) %>%
knitr::kable()
}
```
The next function is the one that does the magic: it takes only one argument
as an input, and generates valid markdown code using the `knitr::knit_expand()`
function. Any variable between `{{}}` gets replaced by its value (so
`{{question}}` gets replaced by the question that gets fetched from the
lookup table defined above). Using this function, we can now loop over
question codes, and what we get in return is valid markdown code that defines
a section with the question as the title, and our table.
```{r}
return_section <- function(var){
a <- knitr::knit_expand(text = c("## {{question}}", create_table(survey_data, var)),
question = lookup$questions[grepl(quo_name(var), lookup$codes)])
cat(a, sep = "\n")
}
```
Our codes are strings, so to be able to use them inside of `dplyr::count()`
we need to define them as bare string, or symbols. This can be done using the
`rlang::sym()` function. If this is confusing, try running `count(mtcars, "am")`
and you will see that it will not return what you want (compare to `count(mtcars, am)`).
This is also why we needed `rlang::quo_name()` in the function above, to convert
the symbol back to a string, which is what `grepl()` requires:
```{r}
sym_codes <- map(codes, sym)
```
Finally, we can create the sections. The line below uses `purrr::walk()`, which
is equivalent to `purrr::map()`, the difference being that we use `purrr::walk()`
when we are interested in the side effects of a function:
```{r, results="asis"}
walk(sym_codes, return_section)
```
````
[This is what the output looks like.](https://startling-shortbread-bc36ea.netlify.app/).
## Other output formats
### Word
Let’s now generate a Word document using Quarto. As you will see, this will be quite easy; but keep in mind that the basic
interactivity that we have seen with HTML outputs won’t be possible here (but templating will work). Render the following
source file to get back a `.docx` document (you don’t even need to have MS Word installed for it to work), and
take of what we changed from the previous file:
- Output changed from `html` to `docx`;
- No more `{DT}`, but `{pander}` instead to generated `.docx` tables
Here is the file:
````{verbatim}
---
title: "Templating with Quarto"
author: "Bruno Rodrigues"
date: today
format: docx
---
# Set up
The goal is to have a frequency table for each question in a survey. But we
do not want to have to do it by hand, so we define a function to create a
table, and then, using the templating capabilities of Quarto, write some
code to generate valid qmarkdown code. In the example below our survey only
has 4 questions, but the solution described trivially scales to an infinity
of questions. This is not the case if you’re solving this problem by hand.
Start by loading the data and defining some needed variables:
```{r}
#| warning: false
library(lubridate)
library(dplyr)
library(purrr)
library(pander)
library(rlang)
survey_data <- read.csv(
"https://gist.githubusercontent.com/b-rodrigues/0c2249dec5a9c9477e0d1ad9964a1340/raw/873bcc7532b8bad613235f029884df1d0b947c90/survey_example.csv"
)
```
Let’s take a look at the data:
```{r}
pander(head(survey_data))
```
The column names are actually questions, so we save those in a variable:
```{r}
questions <- colnames(survey_data)
questions
```
Now we define question codes:
```{r}
codes <- paste0("var_", seq(1, 4))
codes
```
We create a lookup table that links questions to their codes:
```{r}
lookup <- bind_cols("codes" = codes, "questions" = questions)
pander(lookup)
```
Finally, we replace the question names in the dataset by the code:
```{r}
colnames(survey_data) <- codes
pander(survey_data)
```
Now, we define a function that creates a frequency table. This function has
two arguments: `dataset` and `var`. It uses the `dplyr::count()` function to
count each instance of the levels of `var` in `dataset`. Then it uses the
`knitr::kable()` function. This functions takes a data frame as an argument
and returns a table formatted in markdown code:
```{r}
create_table <- function(dataset, var){
dataset %>%
count(!!var) %>%
knitr::kable()
}
```
The next function is the one that does the magic: it takes only one argument
as an input, and generates valid markdown code using the `knitr::knit_expand()`
function. Any variable between `{{}}` gets replaced by its value (so
`{{question}}` gets replaced by the question that gets fetched from the
lookup table defined above). Using this function, we can now loop over
question codes, and what we get in return is valid markdown code that defines
a section with the question as the title, and our table.
```{r}
return_section <- function(var){
a <- knitr::knit_expand(text = c("## {{question}}", create_table(survey_data, var)),
question = lookup$questions[grepl(quo_name(var), lookup$codes)])
cat(a, sep = "\n")
}
```
Our codes are strings, so to be able to use them inside of `dplyr::count()`
we need to define them as bare string, or symbols. This can be done using the
`rlang::sym()` function. If this is confusing, try running `count(mtcars, "am")`
and you will see that it will not return what you want (compare to `count(mtcars, am)`).
This is also why we needed `rlang::quo_name()` in the function above, to convert
the symbol back to a string, which is what `grepl()` requires:
```{r}
sym_codes <- map(codes, sym)
```
Finally, we can create the sections. The line below uses `purrr::walk()`, which
is equivalent to `purrr::map()`, the difference being that we use `purrr::walk()`
when we are interested in the side effects of a function:
```{r, results="asis"}
walk(sym_codes, return_section)
```
````
[You can download the output here.](https://github.com/b-rodrigues/rap4mads/blob/master/img/ex10.docx?raw=true)
Unlike with HTML outputs, it is also not possible to enable code folding, but you could hide the code completely using the "#| echo = false"
chunk option. If you wan to hide all the code without having to specify "#| echo = false" on each chunk you can also add the `execute` option
to the document header:
````{verbatim}
---
title: "Templating with Quarto"
author: "Bruno Rodrigues"
date: today
format: docx
execute:
echo: false
---
````
You can use a document as a template for Word documents generated with Quarto. For this, you must create a new Word file, and update the
styles. This document, with the updated styles, can then be referenced in the header to act as a template:
````{verbatim}
---
title: "Using a custom Word style"
author: "Bruno Rodrigues"
date: today
format:
docx:
reference-doc: fancy_template.docx
execute:
echo: false
---
# Introduction
## MS Word is great (lol)
This is normal text that is unreadable.
````
Just put `fancy_template.docx` in the same folder as your source qmd file. You can download the template I’ve used from
[here](https://github.com/b-rodrigues/rap4mads/blob/master/img/fancy_template.docx?raw=true) to test things out.
For more details, visit this [page](https://quarto.org/docs/output-formats/ms-word-templates.html).
### Presentations
It is also possible to create presentations using Quarto. There are output formats as well: HTML, PDF and Powerpoint. I will
not discuss this here, because it is quite easy to get started, simply [follow along](https://quarto.org/docs/presentations/).
### PDF
I do need to discuss the PDF output a little bit. In order to generate PDF files, Quarto uses the `pdflatex` compiler (or rather
pandoc, called by Quarto, uses `pdflatex`). `pdflatex` compiles `.tex` source files to PDF, so what Quarto does (by leveraging pandoc)
is first converting a `.qmd` file to a `.tex` file, and then call `pdflatex` to compile it. `.tex` files are the file extension of the
Latex typesetting language, extensively used in science. It makes it easy to write complex mathematical formulas, like this one:
\begin{align*}
S(\omega)
&= \frac{\alpha g^2}{\omega^5} e^{[ -0.74\bigl\{\frac{\omega U_\omega 19.5}{g}\bigr\}^{\!-4}\,]} \\
&= \frac{\alpha g^2}{\omega^5} \exp\Bigl[ -0.74\Bigl\{\frac{\omega U_\omega 19.5}{g}\Bigr\}^{\!-4}\,\Bigr]
\end{align*}
Latex is a bit unwieldly, so using Markdown to write scientific documents is becoming more and more popular. However, Latex still
has an edge when it comes to tables. But thankfully, it is possible to simply embed the Latex code that produces these tables in Markdown,
and there are packages that export regression table directly to PDF. In any case, in order to compile to PDF, you need to install
Texlive. Installing Texlive is frankly a mess, but thankfully there is a very simple alternative called TinyTex. TinyTex is both
available as an R package or as a standalone installation, and was put together by the author of RMarkdown (in a sense, Quarto is
a spiritual successor to RMarkdown). This package installs a self-contained Texlive installation locally, which can then be used
to compile PDF documents (from, or outside of R/RStudio). I highly recommend you use Tinytex. Instructions can be found
[here](https://yihui.org/tinytex/). Once you’ve installed TinyTex, you can try to compile the following example document
(the first time you run this, it might take some time, as the required packages get installed):
````{verbatim}
---
title: "PDF example with table"
format: pdf
---
## A PDF document using Quarto
In the code below, we fit several models and then use the `{modelsummary}`
package to print a nicely formatted table with minimal effort:
```{r}
library(modelsummary)
url <- 'https://vincentarelbundock.github.io/Rdatasets/csv/HistData/Guerry.csv'
dat <- read.csv(url)
models <- list(
"OLS 1" = lm(Donations ~ Literacy + Clergy, data = dat),
"Poisson 1" = glm(Donations ~ Literacy + Commerce, family = poisson, data = dat),
"OLS 2" = lm(Crime_pers ~ Literacy + Clergy, data = dat),
"Poisson 2" = glm(Crime_pers ~ Literacy + Commerce, family = poisson, data = dat),
"OLS 3" = lm(Crime_prop ~ Literacy + Clergy, data = dat)
)
modelsummary(models)
```
And an equation, for good measure:
\begin{align*}
S(\omega)
&= \frac{\alpha g^2}{\omega^5} e^{[ -0.74\bigl\{\frac{\omega U_\omega 19.5}{g}\bigr\}^{\!-4}\,]} \\
&= \frac{\alpha g^2}{\omega^5} \exp\Bigl[ -0.74\Bigl\{\frac{\omega U_\omega 19.5}{g}\Bigr\}^{\!-4}\,\Bigr]
\end{align*}
````
[This is what the output looks like (scroll down to page 2).](https://github.com/b-rodrigues/rap4mads/blob/master/img/ex12.pdf?raw=TRUE).
It is possible to author, many, many, different types of documents using Quarto. For more formats, consult this
[page](https://quarto.org/docs/gallery/). Quarto is still very new -- it was officially anounced in July of 2022 by Posit-- so
much more content will arrive. There are still many features of Quarto that we have not explored, so take your time to read
its documentation in detail.
## Interactive web applications with {shiny}
`{shiny}` is a package developed by Posit to build interactive web applications. These apps can be quite "simple"
(for example, an app that shows a graph but in which the user can choose the variable to plot), but can be arbitrarily
complex. Some people even go as far as make [games with {shiny}](https://shiny.rstudio.com/gallery/hex-memory.html).
[A version for Python](https://shiny.rstudio.com/py/) is also in alpha, and you can already experiment with it.
In this section, I will give a very, very short introduction to `{shiny}`. This is because `{shiny}` is so feature-rich,
that I could spend 20 hours teaching you and even then we would not have seen everything. That being said, we can with
only some cursory knowledge build some useful apps. These apps can run locally on your machine, but they're really only
useful if deploy them on a server, so that users can then use these web apps on their browsers.
### The basic structure of a Shiny app
Shiny apps are always made of at least 2 parts: a *server* and a *ui*. In general, each of these parts are in separate
scripts called `server.R` and `ui.R`. It is possible to have another script, called `global.R`, where you can define
variables that you want to be available for both the server and the ui, and to every user of your app.
Let's start by building a very basic app. This app will allow users to visualize unemployment data for Luxembourg.
For now, let's say that we want users only to be able to select communes, but not variables. The example code
below is based on this [official example](https://shiny.rstudio.com/gallery/telephones-by-region.html)
(this is how I recommend you learn by the way. Take a look at the different example there are and adapt them
to suit your needs! You can find the examples [here](https://shiny.rstudio.com/gallery/#demos)).
Create a folder called something like `my_app` and then create three scripts in it:
- `global.R`
- `server.R`
- `ui.R`
Let's start with `global.R`:
```{r, eval = F}
library(myPackage)
library(dplyr)
library(ggplot2)
data("unemp")
```
In the `global.R` file, we load the required packages and data. This is now available everywhere. Let's
continue with the `server.R` script:
```{r, eval = F}
server <- function(session, input, output) {
filtered_data <- reactive(
unemp %>%
filter(place_name %in% input$place_name_selected)
)
output$unemp_plot <- renderPlot({
ggplot(data = filtered_data()) +
theme_minimal() +
geom_line(aes(year, unemployment_rate_in_percent, color = place_name)) +
labs(title = paste("Unemployment in", paste(input$place_name_selected, collapse = ", ")))
})
}
```
Several things need to be commented here: first, the script contains a single function, called
`server()`. This function take three arguments, `session`, `input` and `output`. I won't go into
details here, but you should know that you will never call the `server()` function yourself, and
that these arguments are required so the function can... function. I will leave a reference at the
end of this section with more details. The next important thing is that we defined an object called
`filtered_data`. This is a reactive object. What this means is that this object should get
recomputed every time the user interacts with it. But how does the user interact with it? By
choosing the `place_name` he or she wants to see! The predicate inside `filter()` is `place_name
%in% input$place_name_selected`. Where does that `input$place_name_selected` come from? This comes
from the `ui` (that we have not written yet). But the idea is that the user will be able to chose
place names from a list, and this list will be called `place_name_selected` and will contain the
place names that the user wants to see.
Finally, we define a new object called `output$unemp_plot`. The goal of the `server()` function is
to compute things that will be part of the `output` list. This list, and the objects it contains,
get then rendered in the `ui`. `unemp_plot` is a ggplot graph that uses the reactive data set
we defined first. Notice the `()` after `filtered_data` inside the ggplot call. These are required;
this is how we say that the reactive object must be recomputed. If the plot does not get rendered,
the reactive data set does not get computed, since it never gets called.
Ok so now to the ui. Let's take inspiration from the same example again:
```{r, eval = F}
ui <- function(request){
fluidPage(
titlePanel("Unemployment in Luxembourg"),
sidebarLayout(
sidebarPanel(
selectizeInput("place_name_selected", "Select place:",
choices=unique(unemp$place_name),
multiple = TRUE,
selected = c("Rumelange", "Dudelange"),
options = list(
plugins = list("remove_button"),
create = TRUE,
persist = FALSE # keep created choices in dropdown
)
),
hr(),
helpText("Original data from STATEC")
),
mainPanel(
plotOutput("unemp_plot")
)
)
)
}
```
I've added some useful things to the ui. First of all, I made it a function of an argument,
`request`. This is useful for bookmarking the state of the variable. We'll add a bookmark button
later. The ui is divided into two parts, a sidebar panel, and a main panel. The sidebar panel is
where you will typically add dropdown menus, checkboxes, radio buttons, etc, for the users to make
various selections. In the main panel, you will show the result of their selections. In the sidebar
panel I add a `selectizeInput()` to create a dynamic dropdown list using the `selectize` JS
library, included with `{shiny}`. The available choices are all the unique place names contained in
our data, I allow users to select multiple place names, by default two communes are selected and
using the `options` argument I need little "remove" buttons in the selected commune names. Finally,
in the main panel I use the `plotOutput()` function to render the plot. Notice that I use the name
of the plot defined in the server, "unemp_plot". Finally, to run this, add a new script, called
`app.R` and add the following line in it:
```{r, eval = F}
shiny::runApp(".")
```
You can now run this script in RStudio, or from any R console, and this should open a web browser
with your app.
<div style="text-align:center;">
<video width="640" height="480" controls>
<source src="img/shiny_1.mp4" type="video/mp4">
</video>
</div>
Believe it or not, but this app contains almost every ingredient you need to know to build shiny apps.
But of course, there are many, many other widgets that you can use to give your users even more
ways to interact with applications.
### Slightly more advanced shiny
Let's take a look at another, more complex example. Because this second example is much more
complex, let's first take a look at a video of the app in action:
<div style="text-align:center;">
<video width="640" height="480" controls>
<source src="img/shiny_2.mp4" type="video/mp4">
</video>
</div>
The global file will be almost the same as before:
```{r, eval = F}
library(myPackage)
library(dplyr)
library(ggplot2)
library(g2r)
data("unemp")
enableBookmarking(store = "url")
```