├── .gitignore ├── R ├── packages.R ├── rcod_lecture0 │ ├── img │ │ └── intro2R │ │ │ ├── args.png │ │ │ ├── bbox.PNG │ │ │ ├── codeiscoming.jpg │ │ │ ├── conversion.PNG │ │ │ ├── cran.PNG │ │ │ ├── ctrlflow.png │ │ │ ├── datastr1.png │ │ │ ├── datastr2.png │ │ │ ├── datatyp1.png │ │ │ ├── environment.png │ │ │ ├── f1.PNG │ │ │ ├── f2.PNG │ │ │ ├── for.PNG │ │ │ ├── function.PNG │ │ │ ├── if.PNG │ │ │ ├── ifelse.PNG │ │ │ ├── joins.png │ │ │ ├── kid.jpg │ │ │ ├── loops.PNG │ │ │ ├── math.jpg │ │ │ ├── matrix.PNG │ │ │ ├── matrix2.PNG │ │ │ ├── messages.jpg │ │ │ ├── numbersets.png │ │ │ ├── numbersets2.PNG │ │ │ ├── numbersets3.PNG │ │ │ ├── panes.png │ │ │ ├── panes2.PNG │ │ │ ├── prog1.jpg │ │ │ ├── prog2.PNG │ │ │ ├── prog3.jpeg │ │ │ ├── repeat.PNG │ │ │ ├── rgui.PNG │ │ │ ├── rlogo.jpg │ │ │ ├── rproj.PNG │ │ │ ├── rstudiologo.png │ │ │ ├── tidypipeline.PNG │ │ │ ├── topprogr.PNG │ │ │ └── while.PNG │ ├── rcod_lecture0_intro2R.Rmd │ ├── rcod_lecture0_intro2R.html │ ├── rcod_lecture0_rmarkdown.Rmd │ └── rcod_lecture0_rmarkdown.html ├── rcod_lecture1_tidybasics.R ├── rcod_lecture2_tidywrang.R ├── rcod_lecture3_tidyviz.R ├── rcod_lecture4_tidyfunprog.R ├── rcod_lecture5_tidymodels.R ├── rcod_lecture6_flexUI.Rmd ├── rcod_lecture6_flexUI.html ├── rcod_lecture6_flexUI_emptytemplate.Rmd ├── rcod_lecture6_flexUI_emptytemplate.html ├── rcod_lecture6_flexUI_fulltemplate1.Rmd ├── rcod_lecture6_flexUI_fulltemplate1.html ├── rcod_lecture6_flexUI_fulltemplate2.Rmd ├── rcod_lecture6_flexUI_fulltemplate2.html ├── rcod_lecture7_flexShiny.Rmd ├── rcod_lecture7_flexShiny.html ├── rcod_lecture7_flexShiny_emptytemplate.Rmd ├── rcod_lecture7_flexShiny_fulltemplate.Rmd ├── rcod_lecture8_flex2prod.Rmd ├── rcod_lecture8_flex2prod.html ├── rcod_lectureExtra_advancedEDA.R ├── rcod_lextureExtra_chatgptdashboard.txt └── utils.R ├── README.Rmd ├── README.md ├── dashboards ├── auth.txt ├── css │ ├── auth.css │ ├── styles-dark.css │ └── styles-default.css ├── img │ └── logo.png ├── rcod_lecture6_salesdash01_static.Rmd ├── rcod_lecture6_salesdash01_static.html ├── rcod_lecture7_salesdash02_reactive.Rmd ├── rcod_lecture7_salesdash03_timeplot.Rmd ├── rcod_lecture7_salesdash04_radiobut.Rmd ├── rcod_lecture7_salesdash05_apply.Rmd ├── rcod_lecture7_salesdash06_valuebox.Rmd ├── rcod_lecture7_salesdash07_auth.Rmd ├── rcod_lecture7_salesdash08_forecast.Rmd ├── rcod_lecture7_salesdash09_css.Rmd └── salesdash_result.png ├── data ├── bikes_database.db └── dirty_data.xlsx ├── general-infos ├── rcod_description.Rmd ├── rcod_description.html ├── rcod_description.pdf ├── rcod_description_business.Rmd ├── rcod_description_business.html ├── rcod_description_business.pdf ├── rcod_description_business_ita.Rmd ├── rcod_description_business_ita.html ├── rcod_description_business_ita.pdf ├── rcod_syllabus.Rmd ├── rcod_syllabus.html └── rcod_syllabus.pdf ├── rcoding-course.Rproj └── resources ├── advancedR.pdf ├── base-r.pdf ├── data-transformation.pdf ├── data-visualization.pdf ├── dsR_cheatsheet.pdf ├── factors.pdf ├── lubridate.pdf ├── package-development.pdf ├── parallel_computation.pdf ├── presentation ├── r_best_practices.html ├── r_best_practices.qmd └── r_best_practices_files │ └── libs │ ├── clipboard │ └── clipboard.min.js │ ├── quarto-html │ ├── popper.min.js │ ├── quarto-html.min.css │ ├── quarto-syntax-highlighting.css │ ├── tabby.min.js │ ├── tippy.css │ └── tippy.umd.min.js │ └── revealjs │ ├── dist │ ├── reset.css │ ├── reveal.css │ ├── reveal.esm.js │ ├── reveal.esm.js.map │ ├── reveal.js │ ├── reveal.js.map │ └── theme │ │ ├── fonts │ │ ├── league-gothic │ │ │ ├── LICENSE │ │ │ ├── league-gothic.css │ │ │ ├── league-gothic.eot │ │ │ ├── league-gothic.ttf │ │ │ └── league-gothic.woff │ │ └── source-sans-pro │ │ │ ├── LICENSE │ │ │ ├── source-sans-pro-italic.eot │ │ │ ├── source-sans-pro-italic.ttf │ │ │ ├── source-sans-pro-italic.woff │ │ │ ├── source-sans-pro-regular.eot │ │ │ ├── source-sans-pro-regular.ttf │ │ │ ├── source-sans-pro-regular.woff │ │ │ ├── source-sans-pro-semibold.eot │ │ │ ├── source-sans-pro-semibold.ttf │ │ │ ├── source-sans-pro-semibold.woff │ │ │ ├── source-sans-pro-semibolditalic.eot │ │ │ ├── source-sans-pro-semibolditalic.ttf │ │ │ ├── source-sans-pro-semibolditalic.woff │ │ │ └── source-sans-pro.css │ │ └── quarto.css │ └── plugin │ ├── highlight │ ├── highlight.esm.js │ ├── highlight.js │ ├── monokai.css │ ├── plugin.js │ └── zenburn.css │ ├── markdown │ ├── markdown.esm.js │ ├── markdown.js │ └── plugin.js │ ├── math │ ├── katex.js │ ├── math.esm.js │ ├── math.js │ ├── mathjax2.js │ ├── mathjax3.js │ └── plugin.js │ ├── notes │ ├── notes.esm.js │ ├── notes.js │ ├── plugin.js │ └── speaker-view.html │ ├── pdf-export │ ├── pdfexport.js │ └── plugin.yml │ ├── quarto-line-highlight │ ├── line-highlight.css │ ├── line-highlight.js │ └── plugin.yml │ ├── quarto-support │ ├── footer.css │ ├── plugin.yml │ └── support.js │ ├── reveal-menu │ ├── menu.css │ ├── menu.js │ ├── plugin.yml │ ├── quarto-menu.css │ └── quarto-menu.js │ ├── search │ ├── plugin.js │ ├── search.esm.js │ └── search.js │ └── zoom │ ├── plugin.js │ ├── zoom.esm.js │ └── zoom.js ├── purrr.pdf ├── regex.pdf ├── rmarkdown.pdf ├── rstudio-ide.pdf ├── shiny.pdf ├── strings.pdf └── tidyr.pdf /.gitignore: -------------------------------------------------------------------------------- 1 | .Rproj.user 2 | .Rhistory 3 | .RData 4 | .Ruserdata 5 | exam/ -------------------------------------------------------------------------------- /R/packages.R: -------------------------------------------------------------------------------- 1 | # Packages 2 | pkgs <- c( 3 | "tidyverse", 4 | "lubridate", 5 | "readxl", 6 | "tidymodels", 7 | "odbc", 8 | "DBI", 9 | "RSQLite", 10 | "plotly", 11 | "patchwork", 12 | "DT", 13 | "timetk", 14 | "performance", 15 | "DALEX", 16 | "modelStudio", 17 | "scales", 18 | "rmarkdown", 19 | "flexdashboard", 20 | "shiny", 21 | "shinyWidgets", 22 | "shinyjs", 23 | "shinymanager", 24 | "nycflights13", 25 | "rvg", 26 | "ggiraph", 27 | "ggrepel", 28 | "gganimate", 29 | "gapminder", 30 | "ggside", 31 | "ggdist", 32 | "grafify" 33 | ) 34 | install_and_load(pkgs) 35 | -------------------------------------------------------------------------------- /R/rcod_lecture0/img/intro2R/args.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcozanotti/rcoding-course/4482384dcccfe58a8a8b6207e02f3ae5016382d0/R/rcod_lecture0/img/intro2R/args.png -------------------------------------------------------------------------------- /R/rcod_lecture0/img/intro2R/bbox.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcozanotti/rcoding-course/4482384dcccfe58a8a8b6207e02f3ae5016382d0/R/rcod_lecture0/img/intro2R/bbox.PNG -------------------------------------------------------------------------------- /R/rcod_lecture0/img/intro2R/codeiscoming.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcozanotti/rcoding-course/4482384dcccfe58a8a8b6207e02f3ae5016382d0/R/rcod_lecture0/img/intro2R/codeiscoming.jpg -------------------------------------------------------------------------------- /R/rcod_lecture0/img/intro2R/conversion.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcozanotti/rcoding-course/4482384dcccfe58a8a8b6207e02f3ae5016382d0/R/rcod_lecture0/img/intro2R/conversion.PNG -------------------------------------------------------------------------------- /R/rcod_lecture0/img/intro2R/cran.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcozanotti/rcoding-course/4482384dcccfe58a8a8b6207e02f3ae5016382d0/R/rcod_lecture0/img/intro2R/cran.PNG -------------------------------------------------------------------------------- /R/rcod_lecture0/img/intro2R/ctrlflow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcozanotti/rcoding-course/4482384dcccfe58a8a8b6207e02f3ae5016382d0/R/rcod_lecture0/img/intro2R/ctrlflow.png -------------------------------------------------------------------------------- /R/rcod_lecture0/img/intro2R/datastr1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcozanotti/rcoding-course/4482384dcccfe58a8a8b6207e02f3ae5016382d0/R/rcod_lecture0/img/intro2R/datastr1.png -------------------------------------------------------------------------------- /R/rcod_lecture0/img/intro2R/datastr2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcozanotti/rcoding-course/4482384dcccfe58a8a8b6207e02f3ae5016382d0/R/rcod_lecture0/img/intro2R/datastr2.png -------------------------------------------------------------------------------- /R/rcod_lecture0/img/intro2R/datatyp1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcozanotti/rcoding-course/4482384dcccfe58a8a8b6207e02f3ae5016382d0/R/rcod_lecture0/img/intro2R/datatyp1.png -------------------------------------------------------------------------------- /R/rcod_lecture0/img/intro2R/environment.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcozanotti/rcoding-course/4482384dcccfe58a8a8b6207e02f3ae5016382d0/R/rcod_lecture0/img/intro2R/environment.png -------------------------------------------------------------------------------- /R/rcod_lecture0/img/intro2R/f1.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcozanotti/rcoding-course/4482384dcccfe58a8a8b6207e02f3ae5016382d0/R/rcod_lecture0/img/intro2R/f1.PNG -------------------------------------------------------------------------------- /R/rcod_lecture0/img/intro2R/f2.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcozanotti/rcoding-course/4482384dcccfe58a8a8b6207e02f3ae5016382d0/R/rcod_lecture0/img/intro2R/f2.PNG -------------------------------------------------------------------------------- /R/rcod_lecture0/img/intro2R/for.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcozanotti/rcoding-course/4482384dcccfe58a8a8b6207e02f3ae5016382d0/R/rcod_lecture0/img/intro2R/for.PNG -------------------------------------------------------------------------------- /R/rcod_lecture0/img/intro2R/function.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcozanotti/rcoding-course/4482384dcccfe58a8a8b6207e02f3ae5016382d0/R/rcod_lecture0/img/intro2R/function.PNG -------------------------------------------------------------------------------- /R/rcod_lecture0/img/intro2R/if.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcozanotti/rcoding-course/4482384dcccfe58a8a8b6207e02f3ae5016382d0/R/rcod_lecture0/img/intro2R/if.PNG -------------------------------------------------------------------------------- /R/rcod_lecture0/img/intro2R/ifelse.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcozanotti/rcoding-course/4482384dcccfe58a8a8b6207e02f3ae5016382d0/R/rcod_lecture0/img/intro2R/ifelse.PNG -------------------------------------------------------------------------------- /R/rcod_lecture0/img/intro2R/joins.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcozanotti/rcoding-course/4482384dcccfe58a8a8b6207e02f3ae5016382d0/R/rcod_lecture0/img/intro2R/joins.png -------------------------------------------------------------------------------- /R/rcod_lecture0/img/intro2R/kid.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcozanotti/rcoding-course/4482384dcccfe58a8a8b6207e02f3ae5016382d0/R/rcod_lecture0/img/intro2R/kid.jpg -------------------------------------------------------------------------------- /R/rcod_lecture0/img/intro2R/loops.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcozanotti/rcoding-course/4482384dcccfe58a8a8b6207e02f3ae5016382d0/R/rcod_lecture0/img/intro2R/loops.PNG -------------------------------------------------------------------------------- /R/rcod_lecture0/img/intro2R/math.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcozanotti/rcoding-course/4482384dcccfe58a8a8b6207e02f3ae5016382d0/R/rcod_lecture0/img/intro2R/math.jpg -------------------------------------------------------------------------------- /R/rcod_lecture0/img/intro2R/matrix.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcozanotti/rcoding-course/4482384dcccfe58a8a8b6207e02f3ae5016382d0/R/rcod_lecture0/img/intro2R/matrix.PNG -------------------------------------------------------------------------------- /R/rcod_lecture0/img/intro2R/matrix2.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcozanotti/rcoding-course/4482384dcccfe58a8a8b6207e02f3ae5016382d0/R/rcod_lecture0/img/intro2R/matrix2.PNG -------------------------------------------------------------------------------- /R/rcod_lecture0/img/intro2R/messages.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcozanotti/rcoding-course/4482384dcccfe58a8a8b6207e02f3ae5016382d0/R/rcod_lecture0/img/intro2R/messages.jpg -------------------------------------------------------------------------------- /R/rcod_lecture0/img/intro2R/numbersets.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcozanotti/rcoding-course/4482384dcccfe58a8a8b6207e02f3ae5016382d0/R/rcod_lecture0/img/intro2R/numbersets.png -------------------------------------------------------------------------------- /R/rcod_lecture0/img/intro2R/numbersets2.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcozanotti/rcoding-course/4482384dcccfe58a8a8b6207e02f3ae5016382d0/R/rcod_lecture0/img/intro2R/numbersets2.PNG -------------------------------------------------------------------------------- /R/rcod_lecture0/img/intro2R/numbersets3.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcozanotti/rcoding-course/4482384dcccfe58a8a8b6207e02f3ae5016382d0/R/rcod_lecture0/img/intro2R/numbersets3.PNG -------------------------------------------------------------------------------- /R/rcod_lecture0/img/intro2R/panes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcozanotti/rcoding-course/4482384dcccfe58a8a8b6207e02f3ae5016382d0/R/rcod_lecture0/img/intro2R/panes.png -------------------------------------------------------------------------------- /R/rcod_lecture0/img/intro2R/panes2.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcozanotti/rcoding-course/4482384dcccfe58a8a8b6207e02f3ae5016382d0/R/rcod_lecture0/img/intro2R/panes2.PNG -------------------------------------------------------------------------------- /R/rcod_lecture0/img/intro2R/prog1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcozanotti/rcoding-course/4482384dcccfe58a8a8b6207e02f3ae5016382d0/R/rcod_lecture0/img/intro2R/prog1.jpg -------------------------------------------------------------------------------- /R/rcod_lecture0/img/intro2R/prog2.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcozanotti/rcoding-course/4482384dcccfe58a8a8b6207e02f3ae5016382d0/R/rcod_lecture0/img/intro2R/prog2.PNG -------------------------------------------------------------------------------- /R/rcod_lecture0/img/intro2R/prog3.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcozanotti/rcoding-course/4482384dcccfe58a8a8b6207e02f3ae5016382d0/R/rcod_lecture0/img/intro2R/prog3.jpeg -------------------------------------------------------------------------------- /R/rcod_lecture0/img/intro2R/repeat.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcozanotti/rcoding-course/4482384dcccfe58a8a8b6207e02f3ae5016382d0/R/rcod_lecture0/img/intro2R/repeat.PNG -------------------------------------------------------------------------------- /R/rcod_lecture0/img/intro2R/rgui.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcozanotti/rcoding-course/4482384dcccfe58a8a8b6207e02f3ae5016382d0/R/rcod_lecture0/img/intro2R/rgui.PNG -------------------------------------------------------------------------------- /R/rcod_lecture0/img/intro2R/rlogo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcozanotti/rcoding-course/4482384dcccfe58a8a8b6207e02f3ae5016382d0/R/rcod_lecture0/img/intro2R/rlogo.jpg -------------------------------------------------------------------------------- /R/rcod_lecture0/img/intro2R/rproj.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcozanotti/rcoding-course/4482384dcccfe58a8a8b6207e02f3ae5016382d0/R/rcod_lecture0/img/intro2R/rproj.PNG -------------------------------------------------------------------------------- /R/rcod_lecture0/img/intro2R/rstudiologo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcozanotti/rcoding-course/4482384dcccfe58a8a8b6207e02f3ae5016382d0/R/rcod_lecture0/img/intro2R/rstudiologo.png -------------------------------------------------------------------------------- /R/rcod_lecture0/img/intro2R/tidypipeline.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcozanotti/rcoding-course/4482384dcccfe58a8a8b6207e02f3ae5016382d0/R/rcod_lecture0/img/intro2R/tidypipeline.PNG -------------------------------------------------------------------------------- /R/rcod_lecture0/img/intro2R/topprogr.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcozanotti/rcoding-course/4482384dcccfe58a8a8b6207e02f3ae5016382d0/R/rcod_lecture0/img/intro2R/topprogr.PNG -------------------------------------------------------------------------------- /R/rcod_lecture0/img/intro2R/while.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcozanotti/rcoding-course/4482384dcccfe58a8a8b6207e02f3ae5016382d0/R/rcod_lecture0/img/intro2R/while.PNG -------------------------------------------------------------------------------- /R/rcod_lecture0/rcod_lecture0_rmarkdown.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: 'R Coding - Lecture 0: Introduction to RMarkdown' 3 | author: "Marco Zanotti" 4 | date: "" 5 | output: 6 | html_document: 7 | toc: yes 8 | toc_float: 9 | collapsed: yes 10 | smooth_scroll: yes 11 | toc_depth: 3 12 | highlight: tango 13 | theme: lumen 14 | fig_caption: yes 15 | --- 16 | 17 | 28 | 29 | ## RMardown 30 | 31 | So far, you’ve learned the tools to get your data into R, tidy it into a 32 | form convenient for analysis, and then understand your data through 33 | transformation, visualization and modelling. However, it doesn’t matter 34 | how great your analysis is unless you can explain it to others: 35 | you need to **communicate** your results. 36 | 37 | R Markdown provides an authoring framework for data science. 38 | 39 | You can use a single R Markdown file to both 40 | - save and execute code, and 41 | - generate high quality reports that can be shared with an audience. 42 | 43 | R Markdown was designed for easier reproducibility, since both the 44 | computing code and narratives are in the same document, and results 45 | are automatically generated from the source code. R Markdown supports 46 | dozens of static and dynamic/interactive output formats. 47 | 48 | There are three basic components of an R Markdown document: 49 | the metadata, text, and code. The metadata is written between 50 | the pair of three dashes ---. The syntax for the metadata is YAML 51 | (YAML Ain’t Markup Language, https://en.wikipedia.org/wiki/YAML), 52 | so sometimes it is also called the YAML metadata or the YAML frontmatter. 53 | 54 | The body of a document follows the metadata. The syntax for text 55 | (also known as prose or narratives) is Markdown. 56 | 57 | 58 | ### How to Create an RMarkdown Document 59 | 60 | Open a new .Rmd file in the RStudio IDE by going to File > New File > R Markdown. 61 | 62 | 63 | ### RMarkdown Workflow 64 | 65 | 1. Open a new .Rmd file in the RStudio IDE by going to 66 | File > New File > R Markdown 67 | 2. Embed code in chunks. Run code by line, by chunk, or all at once. 68 | 3. Write text and add tables, figures, images, and citations 69 | Format with Markdown syntax or the RStudio Visual Markdown Editor 70 | 4. Set output format(s) and options in the YAML header. Customize themes or 71 | add parameters to execute or add interactivity with Shiny 72 | 5. Save and render the whole document. Knit periodically to preview your 73 | work as you write 74 | 6. Share your work! 75 | 76 | 77 | ### Rendering 78 | 79 | The usual way to compile an R Markdown document is to click the Knit 80 | button as shown in Figure 2.1, and the corresponding keyboard shortcut 81 | is Ctrl + Shift + K (Cmd + Shift + K on macOS). Under the hood, RStudio 82 | calls the function rmarkdown::render() to render the document in a new 83 | R session. Please note the emphasis here, which often confuses R 84 | Markdown users. Rendering an Rmd document in a new R session means 85 | that none of the objects in your current R session (e.g., those you 86 | created in your R console) are available to that session. Reproducibility 87 | is the main reason that RStudio uses a new R session to render your 88 | Rmd documents: in most cases, you may want your documents to continue 89 | to work the next time you open R, or in other people’s computing 90 | environments. 91 | 92 | If you must render a document in the current R session, you can also 93 | call rmarkdown::render() by yourself, and pass the path of the Rmd 94 | file to this function. The second argument of this function is the 95 | output format, which defaults to the first output format you specify 96 | in the YAML metadata (if it is missing, the default is html_document). 97 | 98 | There are two types of output formats in the rmarkdown package: 99 | documents, and presentations. 100 | 101 | 102 | ### Learning RMarkdown 103 | 104 | To master RMarkdown follow 105 | [RMarkdown the Definitive Guide](https://bookdown.org/yihui/rmarkdown/) 106 | 107 | If you prefer a video introduction to R Markdown, I recommend that 108 | you check out the website https://rmarkdown.rstudio.com, and watch 109 | the videos in the “Get Started” section, which cover the basics of 110 | R Markdown. 111 | 112 | 113 | ## Headers 114 | 115 | To create a header to an RMarkdown document you can `#`. 116 | The number of `#` used represents the level of the header. 117 | 118 | 119 | ## Text 120 | 121 | Text can simply be add writing normally as a text document. 122 | 123 | **Bold text** is added with `**some text**` and *italic text* with `*some text*`. 124 | 125 | One can also adapts the text to the output of an R command, such as 126 | 'nrow(cars)' and 'ncol(cars)'. To include the output of an R command 127 | within the text, use backticks and r. 128 | For example... the iris dataset contains `r nrow(iris)` rows and 129 | `r ncol(iris)` columns. 130 | 131 | 132 | ## Comments 133 | 134 | 140 | To add comments to an RMarkdown document you can use ``, as 141 | in html. 142 | 143 | 144 | ## Chunks 145 | 146 | Chunks are the places where code is used and they are created with the 147 | following syntax 148 | 149 | ` ```{r chunk_name} # some code ``` ` 150 | 151 | Chunks can be inserted quickly using the keyboard shortcut Ctrl + Alt + I 152 | (macOS: Cmd + Option + I), or via the Insert menu in the editor toolbar. 153 | 154 | Each chunk has a name. The name must be in the form 'r + some text', 155 | for instance 'r data'. Within a chunk you can write R commands. 156 | 157 | ```{r data, echo = TRUE, eval = TRUE} 158 | str(iris) 159 | ``` 160 | 161 | Chunks have options that controls how r commands and outputs are displayed 162 | in the html document. The are few fundamental options that are always used: 163 | - `eval`: whether to evaluate a code chunk. 164 | - `echo`: whether to echo the source code in the output document 165 | (someone may not prefer reading your smart source code but only results). 166 | - `include`: whether to include anything from a code chunk in the output document. 167 | - `warning`, `message`, and `error`: whether to show warnings, messages, and 168 | errors in the output document. 169 | - `fig.width` and `fig.height`: the (graphical device) size of R plots in inches. 170 | - `out.width` and `out.height`: the output size of R plots in the output document. 171 | - `fig.align`: the alignment of plots. It can be 'left', 'center', or 'right'. 172 | - `fig.cap`: the figure caption. 173 | 174 | ```{r packages, include=FALSE, message=FALSE} 175 | library(tidyverse) 176 | ``` 177 | 178 | 179 | ## Figures 180 | 181 | By default, figures produced by R code will be placed immediately after 182 | the code chunk they were generated from. 183 | 184 | ```{r plot, echo = TRUE, fig.width = 5, fig.height = 4, fig.align='center'} 185 | plot(iris$Sepal.Length, iris$Petal.Length, xlab = "Sepal Lenght", ylab = "Petal Lenght") 186 | ``` 187 | 188 | 189 | ## Statistics within Chunks 190 | 191 | One can simply perform statistical analysis within code chunks. 192 | 193 | ```{r summary, echo = TRUE} 194 | summary(iris) 195 | ``` 196 | 197 | Math expression can be inserted by surrounding text into $. 198 | 199 | I now want to fit the linear regression model to my data of the form 200 | 201 | $$Y = \beta_0 + \beta_1 X + \varepsilon$$ 202 | 203 | To do this, I simply use the function `lm`. 204 | 205 | ```{r lm, echo = TRUE} 206 | model <- lm(Petal.Length ~ Sepal.Length, data = iris) 207 | ``` 208 | 209 | The model output is the following: 210 | 211 | ```{r lm out, echo = TRUE} 212 | summary(model) 213 | ``` 214 | 215 | We see that $\hat{\beta}_0$ is `r round(model$coefficients[1],2)` while the 216 | slope is equal to `r round(model$coefficients[2],2)`. 217 | 218 | -------------------------------------------------------------------------------- /R/rcod_lecture6_flexUI.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Flexdashboard UI" 3 | output: 4 | flexdashboard::flex_dashboard: 5 | orientation: columns 6 | vertical_layout: fill 7 | --- 8 | 9 | 10 | 11 | Column {data-width=500} 12 | --------------------------------------------------------------- 13 | 14 | ### Overview 15 | 16 | The goal of flexdashboard is to make it easy to create interactive 17 | dashboards for R, using R Markdown: 18 | 19 | - Use **R Markdown** to publish a group of related data visualizations 20 | as a dashboard 21 | - Support for a **wide variety of components** including htmlwidgets; base, 22 | lattice, and grid graphics; tabular data; gauges and value boxes; and 23 | text annotations 24 | - Flexible and easy to specify **row and column-based layouts**. Components 25 | are intelligently re-sized to fill the browser and adapted for display 26 | on mobile devices 27 | - **Storyboard** layouts for presenting sequences of visualizations and 28 | related commentary 29 | - Optionally use **Shiny** to drive visualizations dynamically 30 | - Optionally use **bslib** to easily customize main colors, fonts, and more 31 | 32 | 33 | 34 | Column {data-width=500} 35 | --------------------------------------------------------------- 36 | 37 | ### Tutorial 38 | 39 | Follow the: 40 | 41 | 1. [Flexdashboard UI Tutorial](https://pkgs.rstudio.com/flexdashboard/articles/using.html) 42 | 2. [Sample Layouts Tutorial](https://pkgs.rstudio.com/flexdashboard/articles/layouts.html) 43 | 3. [Theming Tutorial]() 44 | 45 | Throughout these tutorials you can learn about: 46 | 47 | - Layouts 48 | - Components 49 | - Sizing 50 | - Paging 51 | - Storyboards 52 | - Sidebars 53 | - Mobile 54 | - Appearance & Theming 55 | -------------------------------------------------------------------------------- /R/rcod_lecture6_flexUI_emptytemplate.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Flexdashboard UI Tutorial" 3 | output: 4 | flexdashboard::flex_dashboard: 5 | orientation: columns 6 | vertical_layout: fill 7 | --- 8 | 9 | ```{r setup, include=FALSE} 10 | library(flexdashboard) 11 | ``` 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /R/rcod_lecture6_flexUI_fulltemplate1.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Flexdashboard UI Tutorial" 3 | output: 4 | flexdashboard::flex_dashboard: 5 | orientation: rows 6 | vertical_layout: scroll 7 | --- 8 | 9 | ```{r setup, include=FALSE} 10 | library(flexdashboard) 11 | ``` 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | Sidebar {.sidebar} 27 | ----------------------------------------------------------------------- 28 | 29 | Button Inputs 30 | 31 | - Input 1 32 | 33 | - Input 2 34 | 35 | 36 | Column {data-height=650 .tabset} 37 | ----------------------------------------------------------------------- 38 | 39 | ### Chart A-1 40 | 41 | ```{r} 42 | 43 | ``` 44 | 45 | ### Chart A-2 46 | 47 | ```{r} 48 | 49 | ``` 50 | 51 | 52 | Column {data-height=350 .tabset .tabset-fade} 53 | ----------------------------------------------------------------------- 54 | 55 | ### Chart B 56 | 57 | ```{r} 58 | 59 | ``` 60 | 61 | ### Chart C 62 | 63 | ```{r} 64 | 65 | ``` 66 | 67 | ### Chart D 68 | 69 | ```{r} 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 | -------------------------------------------------------------------------------- /R/rcod_lecture6_flexUI_fulltemplate2.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Flexdashboard UI Tutorial" 3 | output: 4 | flexdashboard::flex_dashboard: 5 | orientation: rows 6 | vertical_layout: scroll 7 | --- 8 | 9 | ```{r setup, include=FALSE} 10 | library(flexdashboard) 11 | ``` 12 | 13 | Sidebar {.sidebar} 14 | ======================================================================= 15 | 16 | Button Inputs 17 | 18 | - Input 1 19 | 20 | - Input 2 21 | 22 | 23 | Page 1 24 | ======================================================================= 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | Column {data-height=650 .tabset} 37 | ----------------------------------------------------------------------- 38 | 39 | ### Chart A-1 40 | 41 | ```{r} 42 | 43 | ``` 44 | 45 | ### Chart A-2 46 | 47 | ```{r} 48 | 49 | ``` 50 | 51 | 52 | Column {data-height=350 .tabset .tabset-fade} 53 | ----------------------------------------------------------------------- 54 | 55 | ### Chart B 56 | 57 | ```{r} 58 | 59 | ``` 60 | 61 | ### Chart C 62 | 63 | ```{r} 64 | 65 | ``` 66 | 67 | ### Chart D 68 | 69 | ```{r} 70 | 71 | ``` 72 | 73 | 74 | 75 | Page 2 {data-orientation=columns} 76 | ======================================================================= 77 | 78 | 79 | Column {data-height=200} 80 | ----------------------------------------------------------------------- 81 | 82 | ### Chart E 83 | 84 | ```{r} 85 | 86 | ``` 87 | 88 | Column {data-height=200} 89 | ----------------------------------------------------------------------- 90 | 91 | ### Chart F 92 | 93 | ```{r} 94 | 95 | ``` 96 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /R/rcod_lecture7_flexShiny.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Flexdashboard Reactivity with Shiny" 3 | output: 4 | flexdashboard::flex_dashboard: 5 | orientation: columns 6 | vertical_layout: fill 7 | --- 8 | 9 | 10 | 11 | Column {data-width=500} 12 | --------------------------------------------------------------- 13 | 14 | ### Overview 15 | 16 | By adding **Shiny** to a flexdashboard, you can create dashboards that enable 17 | viewers to change underlying parameters and see the results immediately, 18 | or that update themselves incrementally as their underlying data changes 19 | (see reactiveFileReader and reactivePoll). This is done by adding 20 | **runtime: shiny** to a standard flexdashboard and then adding one or more 21 | input controls and/or reactive expressions that dynamically drive the 22 | appearance of the components within the dashboard. 23 | 24 | Using Shiny with flexdashboard turns a static R Markdown report into an 25 | Interactive Document. It’s important to note that interactive documents 26 | need to be deployed to a Shiny Server to be shared broadly (whereas static 27 | R Markdown documents are standalone web pages that can be attached to emails 28 | or served from any standard web server). 29 | 30 | Note that the **shinydashboard** package provides another way to create 31 | dashboards with Shiny. 32 | 33 | 34 | 35 | 36 | 37 | Column {data-width=500} 38 | --------------------------------------------------------------- 39 | 40 | ### Tutorial 41 | 42 | Follow the: 43 | 44 | [Flexdashboard using Shiny](https://pkgs.rstudio.com/flexdashboard/articles/shiny.html) 45 | 46 | Throughout this tutorial you can learn about: 47 | 48 | - Basic Shiny Components 49 | - Inputs 50 | - Rendering Functions 51 | 52 | Moreover, follow the rcod_lecture7_shiny1 and rcod_lecture7_shiny2. 53 | 54 | -------------------------------------------------------------------------------- /R/rcod_lecture7_flexShiny_emptytemplate.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Flexdashboard Shiny Tutorial" 3 | output: 4 | flexdashboard::flex_dashboard: 5 | orientation: columns 6 | vertical_layout: fill 7 | runtime: shiny 8 | --- 9 | 10 | ```{r setup, include=FALSE} 11 | source("utils.R") 12 | source("packages.R") 13 | ``` 14 | 15 | ```{r} 16 | # Make data 17 | set.seed(123) 18 | sales_data_tbl <- tibble( 19 | date = seq.Date(from = ymd("2018-01-01"), to = today(), length.out = 90), 20 | class = sample(c("marketing", "sales", "research"), size = 90, replace = TRUE), 21 | sales = runif(n = 90, min = 1, max = 100) 22 | ) 23 | ``` 24 | 25 | 32 | 33 | 34 | 35 | 36 | 37 | Sidebar {.sidebar} 38 | ------------------------ 39 | 40 | ```{r} 41 | checkboxGroupInput( 42 | inputId = "checkbox_group", 43 | label = h4("Checkbox Group"), 44 | choices = c( 45 | "Marketing" = "marketing", 46 | "R&D" = "research", 47 | "Sales" = "sales" 48 | ), 49 | selected = c("marketing", "research", "sales") 50 | ) 51 | 52 | dateRangeInput( 53 | inputId = "date_range", 54 | label = h4("Date Range"), 55 | start = "2018-01-01", 56 | end = today(), 57 | min = min(sales_data_tbl$date), 58 | max = max(sales_data_tbl$date), 59 | startview = "year" 60 | ) 61 | 62 | sliderInput( 63 | inputId = "slider_input", 64 | label = h4("Slider Range"), 65 | min = 1, 66 | max = 100, 67 | value = c(40, 60), 68 | step = 1, 69 | round = TRUE, 70 | pre = "$" 71 | ) 72 | ``` 73 | 74 | ```{r reset} 75 | actionButton( 76 | inputId = "reset", 77 | label = "Reset", 78 | icon = icon("sync") 79 | ) 80 | 81 | observeEvent( 82 | eventExpr = input$reset, 83 | handlerExpr = { 84 | 85 | updateCheckboxGroupInput( 86 | session = session, 87 | inputId = "checkbox_group", 88 | selected = c("marketing", "research", "sales") 89 | ) 90 | 91 | updateDateRangeInput( 92 | session = session, 93 | inputId = "date_range", 94 | start = min(sales_data_tbl$date), 95 | end = today() 96 | ) 97 | 98 | updateSliderInput( 99 | session = session, 100 | inputId = "slider_input", 101 | value = c(40, 60) 102 | ) 103 | 104 | } 105 | ) 106 | ``` 107 | 108 | 109 | 110 | Column {data-width=400} 111 | ----------------------- 112 | 113 | ### Section - Checkbox Output 114 | 115 | ```{r} 116 | output$checkbox_values <- renderPrint(input$checkbox_group) 117 | 118 | textOutput(outputId = "checkbox_values") 119 | ``` 120 | 121 | 122 | ### Section - Date Range 123 | 124 | ```{r} 125 | output$date_range_values <- renderPrint(input$date_range) 126 | 127 | textOutput(outputId = "date_range_values") 128 | ``` 129 | 130 | 131 | ### Section - Slider Output 132 | 133 | ```{r} 134 | output$slider_values <- renderPrint(input$slider_input) 135 | 136 | textOutput(outputId = "slider_values") 137 | ``` 138 | 139 | 140 | 141 | Column {data-width=600} 142 | ----------------------- 143 | 144 | ```{r} 145 | # Reactive Filter 146 | sales_data_filtered <- reactive({ 147 | sales_data_tbl %>% 148 | filter(class %in% input$checkbox_group) %>% 149 | filter(date %>% between(ymd(input$date_range[1]), ymd(input$date_range[2]))) %>% 150 | filter(sales %>% between(input$slider_input[1], input$slider_input[2])) 151 | }) 152 | ``` 153 | 154 | 155 | ### Section - Reactive Output 156 | 157 | ```{r} 158 | output$tab_output <- renderTable(sales_data_filtered()) 159 | tableOutput(outputId = "tab_output") 160 | ``` 161 | 162 | 163 | ### Section - Reactive Summary 164 | 165 | ```{r} 166 | output$dt_output_2 <- DT::renderDataTable(expr = { 167 | sales_data_filtered() %>% 168 | group_by(class) %>% 169 | summarise(total_revenue = sum(sales)) %>% 170 | ungroup() %>% 171 | mutate(total_revenue = scales::dollar(total_revenue)) 172 | }, options = list(scrollY = "250px")) 173 | 174 | DTOutput(outputId = "dt_output_2") 175 | ``` 176 | 177 | -------------------------------------------------------------------------------- /R/rcod_lecture7_flexShiny_fulltemplate.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Flexdashboard Shiny Tutorial" 3 | output: 4 | flexdashboard::flex_dashboard: 5 | orientation: columns 6 | vertical_layout: fill 7 | runtime: shiny 8 | --- 9 | 10 | ```{r setup, include=FALSE} 11 | source("utils.R") 12 | source("packages.R") 13 | ``` 14 | 15 | ```{r data} 16 | # create some sample data 17 | set.seed(123) 18 | sales_data_tbl <- tibble( 19 | date = seq.Date(from = ymd("2018-01-01"), to = today(), length.out = 90), 20 | class = sample(c("marketing", "sales", "research"), size = 90, replace = TRUE), 21 | sales = runif(n = 90, min = 1, max = 100) 22 | ) 23 | ``` 24 | 25 | 26 | 27 | 28 | Sidebar {.sidebar} 29 | ------------------------ 30 | 31 | ```{r inputs} 32 | checkboxGroupInput( 33 | inputId = "checkbox_group", 34 | label = h4("Checkbox Group"), 35 | choices = c( 36 | "Marketing" = "marketing", 37 | "R&D" = "research", 38 | "Sales" = "sales" 39 | ), 40 | selected = c("marketing", "research", "sales") 41 | ) 42 | 43 | dateRangeInput( 44 | inputId = "date_range", 45 | label = h4("Date Range"), 46 | start = "2018-01-01", 47 | end = today(), 48 | min = min(sales_data_tbl$date), 49 | max = max(sales_data_tbl$date), 50 | startview = "year" 51 | ) 52 | 53 | sliderInput( 54 | inputId = "slider_input", 55 | label = h4("Slider Range"), 56 | min = 1, 57 | max = 100, 58 | value = c(40, 60), 59 | step = 1, 60 | round = TRUE, 61 | pre = "$" 62 | ) 63 | ``` 64 | 65 | 66 | ```{r reset} 67 | actionButton( 68 | inputId = "reset", 69 | label = "Reset", 70 | icon = icon("sync") 71 | ) 72 | 73 | observeEvent( 74 | eventExpr = input$reset, 75 | handlerExpr = { 76 | 77 | updateCheckboxGroupInput( 78 | session = session, 79 | inputId = "checkbox_group", 80 | selected = c("marketing", "research", "sales") 81 | ) 82 | 83 | updateDateRangeInput( 84 | session = session, 85 | inputId = "date_range", 86 | start = min(sales_data_tbl$date), 87 | end = today() 88 | ) 89 | 90 | updateSliderInput( 91 | session = session, 92 | inputId = "slider_input", 93 | value = c(40, 60) 94 | ) 95 | 96 | } 97 | ) 98 | ``` 99 | 100 | 101 | 102 | Column {data-width=400} 103 | ----------------------- 104 | 105 | ### Section - Checkbox Output 106 | 107 | ```{r} 108 | output$checkbox_values <- renderPrint(input$checkbox_group) 109 | 110 | textOutput(outputId = "checkbox_values") 111 | ``` 112 | 113 | ### Section - Date Range 114 | 115 | ```{r} 116 | output$date_range_values <- renderPrint(input$date_range) 117 | 118 | textOutput(outputId = "date_range_values") 119 | ``` 120 | 121 | ### Section - Slider Output 122 | 123 | ```{r} 124 | output$slider_values <- renderPrint(input$slider_input) 125 | 126 | textOutput(outputId = "slider_values") 127 | ``` 128 | 129 | 130 | 131 | Column {data-width=600} 132 | ----------------------- 133 | 134 | ```{r} 135 | # Reactive Filter 136 | sales_data_filtered <- reactive({ 137 | sales_data_tbl %>% 138 | filter(class %in% input$checkbox_group) %>% 139 | filter(date %>% between(ymd(input$date_range[1]), ymd(input$date_range[2]))) %>% 140 | filter(sales %>% between(input$slider_input[1], input$slider_input[2])) 141 | }) 142 | ``` 143 | 144 | 145 | ### Section - Reactive Output 146 | 147 | ```{r} 148 | output$tab_output <- renderTable(sales_data_filtered()) 149 | tableOutput(outputId = "tab_output") 150 | # problem with date type with basic table (it renders only numeric or character) 151 | 152 | # output$dt_output_1 <- DT::renderDataTable(expr = { 153 | # sales_data_filtered() %>% 154 | # mutate(sales = scales::dollar(sales)) 155 | # }, options = list(scrollY = "250px")) 156 | # 157 | # DTOutput(outputId = "dt_output_1") 158 | ``` 159 | 160 | 161 | ### Section - Reactive Summary 162 | 163 | ```{r} 164 | output$dt_output_2 <- DT::renderDataTable(expr = { 165 | sales_data_filtered() %>% 166 | group_by(class) %>% 167 | summarise(total_revenue = sum(sales)) %>% 168 | ungroup() %>% 169 | mutate(total_revenue = scales::dollar(total_revenue)) 170 | }, options = list(scrollY = "250px")) 171 | 172 | DTOutput(outputId = "dt_output_2") 173 | ``` 174 | 175 | -------------------------------------------------------------------------------- /R/rcod_lecture8_flex2prod.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Flexdashboard in Production" 3 | output: 4 | flexdashboard::flex_dashboard: 5 | orientation: columns 6 | vertical_layout: fill 7 | --- 8 | 9 | 10 | 11 | Column {data-width=500} 12 | --------------------------------------------------------------- 13 | 14 | ### Static HTML 15 | 16 | Flexdashboards without Shiny interactive components are just HTML pages 17 | hence can be shared as any other basic HTML file: attaching 18 | it to and e-mail, sending as documents into your business chat or even 19 | uploading it to a website. 20 | 21 | One of the most efficient way to share you dashboard is indeed to host it 22 | on the web (for free) using [GitHub Pages](https://pages.github.com/). 23 | 24 | Follow these simple steps to deploy your HTML Flexdashboard to the web: 25 | 26 | 1. Login to [GitHub](https://github.com/) and create a new **public** repository 27 | 2. From the green code tab of your new repo, copy the HTTPS link 28 | 3. Locally, through RStudio, create a new **RStudio Project** from Version 29 | Control and link it to your GitHub repo using the HTTPS link 30 | 4. Place your *flexdashboard.Rmd* file into the project folder, knit, commit 31 | and push the new files to GitHub 32 | 5. From the settings of your GitHub repo, go to **Pages** and activate them 33 | 6. Your dashboard is now available at 34 | https://user-name.github.io/repo-name/dashboard-name.html 35 | 36 | 37 | Column {data-width=500} 38 | --------------------------------------------------------------- 39 | 40 | ### Reactive Rmd 41 | 42 | Using Shiny with flexdashboard turns a static R Markdown report into an 43 | interactive document. It’s important to note that interactive documents 44 | cannot be shared as static HTML files, but need to be deployed as a service 45 | to a web server. 46 | 47 | The easiest way is through 48 | [Shiny Server](https://www.rstudio.com/products/shiny/shiny-server/) 49 | or directly via [Shinyapps.io](https://www.shinyapps.io/) (but the 50 | free version limits you to 5 dashboards and small online time). 51 | Deploying your dashboard allows you to share it broadly with no limits. 52 | 53 | Follow these simple steps to deploy your Rmd Flexdashboard via Shiny Server: 54 | 55 | 1. Install **Shiny R package** as admin 56 | 2. Install **Shiny Server** (once installed it starts automatically) 57 | 3. Install all the **R packages** required by your flexdashboard as an admin 58 | (for example, on Ubuntu shell run 59 | `sudo su - -c "R -e \"install.packages('package_name', repos='https://cran.rstudio.com/')\""` 60 | ) 61 | 4. To host the app just copy the directory of your dashboard to 62 | **/srv/shiny-server/**. At this point, the Shiny service automatically points 63 | to the .Rmd of the dashboard and deploys hosts it on 64 | http://localhost:3838/dashboard-directory-name/ 65 | 66 | 67 | Notes: 68 | 69 | The default configuration file is installed at 70 | **/etc/shiny-server/shiny-server.conf** if it doesn't already exist. 71 | The default configuration creates a single server listening on port 3838, 72 | serving any application contained within **/srv/shiny-server/** at the root 73 | URL (/). 74 | See also the 75 | [Shiny Server Basic Installation Guide](https://www.rstudio.com/products/shiny/download-server/) 76 | and the 77 | [Shiny Server Administrator Guide](https://docs.rstudio.com/shiny-server/). 78 | 79 | 80 | To host your app to the web you need some host web server as 81 | [AWS Services](https://www.charlesbordet.com/en/guide-shiny-aws/#). 82 | 83 | -------------------------------------------------------------------------------- /R/rcod_lectureExtra_advancedEDA.R: -------------------------------------------------------------------------------- 1 | # R Coding ---- 2 | 3 | # Lecture Extra: Explorative Data Analysis -------------------------------- 4 | # Marco Zanotti 5 | 6 | # Goals: 7 | # - janitor 8 | # - skimr 9 | # - dlookr 10 | # - SmartEDA 11 | # - explore 12 | # - DataExplorer 13 | # - radiant 14 | 15 | library(tidyverse) 16 | 17 | # PROs: 18 | # 19 | # 1) Faster insights with less code for experienced R users. Exploring a fresh new 20 | # dataset is exciting. Instead of searching for syntax at Stackoverflow, use all 21 | # your attention searching for interesting patterns in your data, using just a 22 | # handful easy to remember functions. Your code is easy to understand - even for 23 | # non R users. 24 | # 25 | # 2) Instant success for new R users. It is said that R has a steep learning curve, 26 | # especially if you come from a GUI for your statistical analysis. Instead of 27 | # learning a lot of R syntax before you can explore data, these packages 28 | # enable you to have instant success. You can start with just one function and 29 | # learn other R syntax later step by step. 30 | # 31 | # 3) Interactive data exploration (univariate, bivariate, multivariate). A target 32 | # can be defined (binary / categorical / numerical). 33 | # 34 | # 4) Generate an Automated Report with one line of code. The target can be binary, 35 | # categorical or numeric. 36 | 37 | 38 | 39 | # janitor ----------------------------------------------------------------- 40 | 41 | library(readxl) 42 | library(janitor) 43 | 44 | roster_raw <- read_excel("data/dirty_data.xlsx") 45 | roster_raw 46 | 47 | roster_raw <- roster_raw |> 48 | row_to_names(row_number = 1) |> 49 | clean_names() 50 | roster_raw 51 | 52 | read_excel("data/dirty_data.xlsx", skip = 1, .name_repair = make_clean_names) 53 | 54 | roster <- roster_raw |> 55 | remove_empty(c("rows", "cols")) |> 56 | remove_constant(na.rm = TRUE, quiet = FALSE) |> # remove the column of all "Yes" values 57 | mutate( 58 | hire_date = convert_to_date( 59 | hire_date, # handle the mixed-format dates 60 | character_fun = lubridate::mdy 61 | ), 62 | cert = dplyr::coalesce(certification, certification_2) 63 | ) |> 64 | select(-certification, -certification_2) 65 | 66 | roster |> get_dupes(contains("name")) 67 | 68 | # tabulation 69 | roster |> tabyl(subject) # one variable 70 | 71 | roster |> 72 | filter(hire_date > as.Date("1950-01-01")) |> 73 | tabyl(employee_status, full_time) # two variables 74 | 75 | roster |> 76 | tabyl(full_time, subject, employee_status, show_missing_levels = FALSE) # three variables 77 | 78 | roster |> 79 | tabyl(employee_status, full_time) |> 80 | adorn_totals("row") |> 81 | adorn_percentages("row") |> 82 | adorn_pct_formatting() |> 83 | adorn_ns() |> 84 | adorn_title("combined") 85 | 86 | 87 | 88 | # skimr ------------------------------------------------------------------- 89 | 90 | library(skimr) 91 | 92 | skim(iris) 93 | skim(iris) |> summary() 94 | 95 | 96 | 97 | # dlookr ------------------------------------------------------------------ 98 | 99 | library(nycflights13) 100 | library(dlookr) 101 | 102 | flights 103 | 104 | diagnose(flights) 105 | diagnose_numeric(flights) 106 | diagnose_category(flights) 107 | diagnose_outlier(flights) 108 | 109 | flights |> 110 | plot_outlier( 111 | diagnose_outlier(flights) |> 112 | filter(outliers_ratio >= 10) |> 113 | select(variables) |> 114 | unlist() 115 | ) 116 | 117 | describe(flights) 118 | 119 | flights |> normality(arr_delay) 120 | flights |> plot_normality(arr_delay) 121 | 122 | correlate(flights) 123 | flights |> select(arr_delay, dep_delay, distance) |> correlate() |> plot() 124 | 125 | flights |> 126 | mutate( 127 | dep_delay_minmax = transform(dep_delay, method = "minmax"), 128 | arr_delay_minmax = transform(arr_delay, method = "minmax") 129 | ) |> 130 | select(dep_delay, arr_delay, dep_delay_minmax, arr_delay_minmax) 131 | 132 | # find_na(flights) 133 | # find_outliers(flights) 134 | # imputate_na(flights, method = "mean") 135 | # imputate_outlier(flights, method = "mean") 136 | 137 | 138 | # automatic reports 139 | flights |> 140 | diagnose_web_report( 141 | subtitle = "flights", output_dir = "./", output_file = "Diagn.html", theme = "blue" 142 | ) 143 | flights |> 144 | diagnose_paged_report( 145 | subtitle = "flights", output_dir = "./", output_file = "Diagn.pdf", theme = "blue" 146 | ) 147 | 148 | flights |> 149 | eda_web_report( 150 | target = "arr_delay", subtitle = "flights", 151 | output_dir = "./", output_file = "EDA.html", 152 | theme = "blue" 153 | ) 154 | flights |> 155 | eda_paged_report( 156 | target = "arr_delay", subtitle = "flights", 157 | output_dir = "./", output_file = "EDA.pdf", 158 | theme = "blue" 159 | ) 160 | 161 | flights |> 162 | transformation_web_report( 163 | target = "arr_delay", subtitle = "flights", 164 | output_dir = "./", output_file = "transform.html", 165 | theme = "blue" 166 | ) 167 | flights |> 168 | transformation_paged_report( 169 | target = "arr_delay", subtitle = "flights", 170 | output_dir = "./", output_file = "transform.pdf", 171 | theme = "blue" 172 | ) 173 | 174 | 175 | 176 | # SmartEDA ---------------------------------------------------------------- 177 | 178 | library(ISLR) 179 | library(SmartEDA) 180 | 181 | Carseats <- ISLR::Carseats 182 | 183 | ExpData(data = Carseats, type = 1) # overview of the data 184 | ExpData(data = Carseats, type = 2) # structure of the data 185 | 186 | # numeric variables 187 | ExpNumStat( 188 | Carseats, 189 | by = "A", 190 | gp = NULL, 191 | Qnt = seq(0, 1, 0.1), 192 | MesofShape = 2, 193 | Outlier = TRUE, 194 | round = 2 195 | ) 196 | ExpNumViz( 197 | Carseats, 198 | target = "Price", 199 | type = 2, 200 | nlim = 25, 201 | Page = c(2, 2) 202 | ) 203 | 204 | # categorical variables 205 | ExpCTable( 206 | Carseats, 207 | Target = NULL, 208 | margin = 1, 209 | clim = 10, 210 | nlim = 5, 211 | round = 2, 212 | bin = NULL, 213 | per = TRUE 214 | ) 215 | ExpCatViz( 216 | Carseats, 217 | target = "Urban", 218 | fname = NULL, 219 | clim = 10, 220 | col = NULL, 221 | margin = 2, 222 | Page = c(2, 1), 223 | sample = 2 224 | ) 225 | 226 | # variable importance 227 | ExpCatStat( 228 | Carseats, 229 | Target = "Urban", 230 | result = "Stat", 231 | clim = 10, 232 | nlim = 5, 233 | bins = 10, 234 | Pclass = "Yes", 235 | plot = TRUE, 236 | top = 10, 237 | Round = 2 238 | ) 239 | 240 | # normality 241 | ExpOutQQ( 242 | Carseats, 243 | nlim = 10, 244 | fname = NULL, 245 | Page = c(2, 2), 246 | sample = 4 247 | ) 248 | 249 | # outliers 250 | ExpOutliers( 251 | Carseats, 252 | varlist = c("Sales", "CompPrice", "Income"), 253 | method = "boxplot", 254 | capping = c(0.1, 0.9) 255 | ) 256 | 257 | 258 | # automatic report 259 | ExpReport( 260 | Carseats, 261 | Target = "Urban", 262 | label = NULL, 263 | op_file = "test.html", 264 | op_dir = getwd(), 265 | sc = 2, 266 | sn = 2, 267 | Rc = "Yes" 268 | ) 269 | 270 | 271 | 272 | # explore ----------------------------------------------------------------- 273 | 274 | library(explore) 275 | 276 | describe(iris) 277 | 278 | explore(iris) 279 | 280 | report(iris, output_dir = getwd()) 281 | report(iris, output_dir = getwd(), target = "Species") 282 | 283 | iris_new <- iris 284 | iris_new$is_versicolor <- ifelse(iris_new$Species == "versicolor", 1, 0) 285 | iris_new$Species <- NULL 286 | iris_new |> explain_tree(target = is_versicolor) # explain target using a decision tree 287 | iris_new |> explain_logreg(target = is_versicolor) # explain target using a logistic regression 288 | 289 | 290 | 291 | # DataExplorer ------------------------------------------------------------ 292 | 293 | library(DataExplorer) 294 | 295 | introduce(airquality) 296 | plot_intro(airquality) 297 | 298 | plot_missing(airquality) 299 | 300 | plot_bar(diamonds) 301 | plot_bar(diamonds, with = "price") 302 | plot_bar(diamonds, by = "cut") 303 | 304 | plot_histogram(diamonds) 305 | plot_density(diamonds) 306 | 307 | plot_qq(diamonds) 308 | plot_qq(diamonds, by = "cut") 309 | 310 | plot_correlation(diamonds) 311 | plot_boxplot(diamonds, by = "cut") 312 | 313 | plot_scatterplot(split_columns(diamonds)$continuous, by = "price", sampled_rows = 1000L) 314 | 315 | plot_prcomp(diamonds, maxcat = 5L) 316 | 317 | 318 | # automatic report 319 | create_report(airquality) 320 | create_report(diamonds, y = "price") 321 | 322 | 323 | 324 | # radiant ----------------------------------------------------------------- 325 | 326 | library(radiant) 327 | radiant() 328 | 329 | -------------------------------------------------------------------------------- /R/rcod_lextureExtra_chatgptdashboard.txt: -------------------------------------------------------------------------------- 1 | 2 | We want to build a flexdashboard using chatgpt. 3 | 4 | Start by creating an empty .Rmd then use the chatgpt prompt: 5 | - be as precise as possible in your questions 6 | - start simple and build the code progressively 7 | 8 | 9 | (1) 10 | I want to build a dashboard in R to analyze the iris dataset. 11 | I want to use the flexdashboard package. Please, create an empty .Rmd 12 | template with a just a YAML header and a sidebar. 13 | 14 | (2) 15 | Now add also two columns to this and correct the YAML. 16 | 17 | (3) 18 | Now add a check box input to the sidebar to select the iris species. 19 | 20 | (4) 21 | Now add a ggplot graph into the first column plotting the petal length vs 22 | the petal width for the species selected by the input. 23 | 24 | 25 | (5) 26 | Now add to the second coluumn a ggplot graph into the first column 27 | plotting the boxplot of sepal length for the different species and coloring 28 | also by species. 29 | -------------------------------------------------------------------------------- /R/utils.R: -------------------------------------------------------------------------------- 1 | # Helper Functions 2 | 3 | 4 | # Function to check packages already loaded into NAMESPACE 5 | check_namespace <- function(pkgs) { 6 | 7 | pkgs_notloaded <- pkgs[!pkgs %in% loadedNamespaces()] 8 | if (length(pkgs_notloaded) == 0) { 9 | res <- NULL 10 | } else { 11 | res <- pkgs_notloaded 12 | } 13 | return(res) 14 | 15 | } 16 | 17 | 18 | # Function to install and load the specified packages 19 | install_and_load <- function(pkgs, repos = getOption("repos")) { 20 | 21 | pkgs_inst <- pkgs[!pkgs %in% installed.packages()] 22 | 23 | if (length(pkgs_inst) == 0) { 24 | lapply(pkgs, library, character.only = TRUE, quietly = TRUE) 25 | check_res <- check_namespace(pkgs) 26 | if (is.null(check_res)) { 27 | res <- "All packages correctly installed and loaded." 28 | } else { 29 | res <- paste0( 30 | "Problems loading packages ", 31 | paste0(check_res, collapse = ", "), 32 | "." 33 | ) 34 | } 35 | 36 | } else { 37 | 38 | inst_res <- vector("character", length(pkgs_inst)) 39 | 40 | for (i in seq_along(pkgs_inst)) { 41 | inst_res_tmp <- tryCatch( 42 | utils::install.packages(pkgs_inst[i], dependencies = TRUE, repos = repos, quiet = TRUE), 43 | error = function(e) e, 44 | warning = function(w) w 45 | ) 46 | if (!is.null(inst_res_tmp)) { 47 | inst_res[i] <- inst_res_tmp$message 48 | } 49 | } 50 | 51 | pkgs_err <- pkgs_inst[!inst_res == ""] 52 | if (length(pkgs_err) == 0) { 53 | lapply(pkgs, library, character.only = TRUE, quietly = TRUE) 54 | check_res <- check_namespace(pkgs) 55 | if (is.null(check_res)) { 56 | res <- "All packages correctly installed and loaded." 57 | } else { 58 | res <- paste0( 59 | "Problems loading packages ", 60 | paste0(check_res, collapse = ", "), 61 | "." 62 | ) 63 | } 64 | } else { 65 | pkgs_noerr <- pkgs[!pkgs %in% pkgs_err] 66 | lapply(pkgs_noerr, library, character.only = TRUE, quietly = TRUE) 67 | check_res <- check_namespace(pkgs_noerr) 68 | if (is.null(check_res)) { 69 | res <- paste0( 70 | "Problems installing packages ", 71 | paste0(pkgs_err, collapse = ", "), 72 | "." 73 | ) 74 | } else { 75 | res <- c( 76 | paste0( 77 | "Problems installing packages ", 78 | paste0(pkgs_err, collapse = ", "), 79 | "." 80 | ), 81 | paste0( 82 | "Problems loading packages ", 83 | paste0(check_res, collapse = ", "), 84 | "." 85 | ) 86 | ) 87 | } 88 | } 89 | 90 | } 91 | 92 | message(toupper( 93 | paste0( 94 | "\n\n\n", 95 | "\n==================================================================", 96 | "\nResults:\n ", 97 | res, 98 | "\n==================================================================" 99 | ) 100 | )) 101 | return(invisible(res)) 102 | 103 | } 104 | 105 | 106 | # Functions for rcod_lecture7_salesdash08_forecast 107 | # function to perform time series aggregation 108 | aggregate_time_series <- function(data, time_unit = "month") { 109 | 110 | output_tbl <- data %>% 111 | mutate(date = floor_date(order.date, unit = time_unit)) %>% 112 | group_by(date) %>% 113 | summarize(total_sales = sum(extended_price)) %>% 114 | ungroup() %>% 115 | mutate(label_text = str_glue("Date: {date} 116 | Revenue: {scales::dollar(total_sales)}")) 117 | 118 | return(output_tbl) 119 | 120 | } 121 | 122 | # function to plot the time series 123 | plot_time_series <- function(data) { 124 | 125 | g <- data %>% 126 | ggplot(aes(date, total_sales)) + 127 | geom_line(color = "#2c3e50") + 128 | geom_point(aes(text = label_text), color = "#2c3e50", size = 0.1) + 129 | geom_smooth(method = "loess", span = 0.2, size = 1) + 130 | expand_limits(y = 0) + 131 | scale_y_continuous(labels = scales::dollar_format()) + 132 | labs(x = "", y = "") 133 | 134 | ggplotly(g, tooltip = "text") 135 | 136 | } 137 | 138 | # function to generate the forecast with ML models (Elastic Net or XGBoost) 139 | generate_forecast <- function(data, n_future = 12, seed = NULL) { 140 | 141 | # frequency 142 | time_scale <- data %>% 143 | tk_index() %>% 144 | tk_get_timeseries_summary() %>% 145 | pull(scale) 146 | 147 | # recipe 148 | ml_rcp <- recipe(total_sales ~ ., data = data %>% select(-label_text)) %>% 149 | step_timeseries_signature(date) %>% 150 | step_normalize(date_index.num) %>% 151 | step_rm(matches("(iso)|(xts)|(lbl)|(hour)|(minute)|(second)|(hour12)|(am.pm)")) %>% 152 | step_rm(date) 153 | 154 | # future data 155 | future_tbl <- data %>% 156 | future_frame(.date_var = date, .length_out = n_future) 157 | 158 | if (time_scale %in% c("quarter", "year")) { 159 | 160 | model <- linear_reg( 161 | mode = "regression", 162 | penalty = 0.05, 163 | mixture = 0.5 164 | ) %>% 165 | set_engine(engine = "glmnet") 166 | 167 | } else { 168 | 169 | model <- boost_tree( 170 | mode = "regression", 171 | mtry = 20, 172 | trees = 500, 173 | min_n = 3, 174 | tree_depth = 8, 175 | learn_rate = 0.01, 176 | loss_reduction = 0.01 177 | ) %>% 178 | set_engine(engine = "xgboost") 179 | 180 | } 181 | 182 | set.seed(seed) 183 | wkfl_fit <- workflow() %>% 184 | add_recipe(ml_rcp) %>% 185 | add_model(model) %>% 186 | fit(data = data %>% select(-label_text)) 187 | 188 | prediction_tbl <- wkfl_fit %>% 189 | predict(new_data = future_tbl) %>% 190 | bind_cols(future_tbl) %>% 191 | rename(total_sales = .pred) %>% 192 | mutate(label_text = str_glue("Date: {date} 193 | Revenue: {scales::dollar(total_sales)}")) %>% 194 | add_column(key = "Prediction") 195 | 196 | output_tbl <- data %>% 197 | add_column(key = "Actual") %>% 198 | relocate(total_sales, .before = date) %>% 199 | bind_rows(prediction_tbl) 200 | 201 | return(output_tbl) 202 | 203 | } 204 | 205 | # function to plot the forecast results 206 | plot_forecast <- function(data) { 207 | 208 | time_scale <- data %>% 209 | tk_index() %>% 210 | tk_get_timeseries_summary() %>% 211 | pull(scale) 212 | 213 | # Only 1 Prediction - points 214 | n_predictions <- data %>% 215 | filter(key == "Prediction") %>% 216 | nrow() 217 | 218 | g <- data %>% 219 | ggplot(aes(date, total_sales, color = key)) + 220 | geom_line() + 221 | scale_y_continuous(labels = scales::dollar_format()) + 222 | expand_limits(y = 0) + 223 | labs(x = "", y = "") 224 | 225 | # Yearly - LM Smoother 226 | if (time_scale %in% c("quarter", "year")) { 227 | g <- g + 228 | geom_smooth(method = "lm", size = 0.5) 229 | } else { 230 | g <- g + geom_smooth(method = "loess", span = 0.2, size = 0.5) 231 | } 232 | 233 | # Only 1 Prediction 234 | if (n_predictions == 1) { 235 | g <- g + geom_point(aes(text = label_text), size = 1) 236 | } else { 237 | g <- g + geom_point(aes(text = label_text), size = 0.01) 238 | } 239 | 240 | ggplotly(g, tooltip = "text") 241 | 242 | } 243 | -------------------------------------------------------------------------------- /README.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | output: github_document 3 | editor_options: 4 | markdown: 5 | wrap: 72 6 | --- 7 | 8 | ```{r, include = FALSE} 9 | knitr::opts_chunk$set( 10 | collapse = TRUE, 11 | comment = "#>" 12 | ) 13 | ``` 14 | 15 | # R Coding 16 | 17 | *Overview* 18 | 19 | The course aims at providing technical skills about coding/scripting 20 | aspects for data analysis and to manage persistent data storage of 21 | sources and results involved in analysis. On the one side, the Python 22 | programming language and the R framework are illustrated. The goal is to 23 | deal with essential notions about data structures and control structures 24 | of both Python and R. On the other side, the goal is to present the core 25 | notions of relational databases, such as keys, integrity, and 26 | primary/foreign key constraints, as well as the SQL language for data 27 | definition, manipulation, and query. Recent and innovative NoSQL 28 | solutions are also discussed, with special focus on a document-oriented 29 | system called MongoDB. 30 | 31 | The **R Coding** part of the course aims at teaching **R Programming**, 32 | focusing on how to code with modern and state-of-the-art R packages. 33 | 34 | 35 | ## General Information 36 | 37 | - [Course Descriprion (for Students)](https://marcozanotti.github.io/rcoding-course/general-infos/rcod_description.html) 38 | - [Course Descriprion (for Business Experts)](https://marcozanotti.github.io/rcoding-course/general-infos/rcod_description_business.html) 39 | - [Syllabus](https://marcozanotti.github.io/rcoding-course/general-infos/rcod_syllabus.html) 40 | 41 | 42 | ## Materials 43 | 44 | - [Introduction to R](https://marcozanotti.github.io/rcoding-course/R/rcod_lecture0/rcod_lecture0_intro2R.html) 45 | - [Introduction to RMarkdown](https://marcozanotti.github.io/rcoding-course/R/rcod_lecture0/rcod_lecture0_rmarkdown.html) 46 | - [💯 R Best Practices](https://marcozanotti.github.io/rcoding-course/resources/presentation/r_best_practices.html) 47 | - [Tutorials (lectures)](https://github.com/marcozanotti/rcoding-course/tree/master/R) 48 | - [Other Resources](https://github.com/marcozanotti/rcoding-course/tree/master/resources) 49 | 50 | 51 | ## Suggested References 52 | 53 | Git & GitHub: 54 | 55 | - [Pro Git](https://git-scm.com/book/en/v2) 56 | - [Happy Git with R](https://happygitwithr.com/index.html) 57 | 58 | R Programming: 59 | 60 | - [R for Data Science](https://r4ds.had.co.nz/) 61 | - [Efficient R Programming](https://csgillespie.github.io/efficientR/index.html) 62 | - [R Packages](https://r-pkgs.org/index.html) 63 | - [Advanced R](https://adv-r.hadley.nz/) 64 | 65 | RMarkdown & Shiny: 66 | 67 | - [RMarkdown: The Definitive Guide](https://bookdown.org/yihui/rmarkdown/) 68 | - [Mastering Shiny](https://mastering-shiny.org/) 69 | - [Shiny Tutorials](https://shiny.rstudio.com/tutorial/) 70 | 71 | Everything with R: 72 | 73 | - [Big Book of R](https://www.bigbookofr.com/) 74 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # R Coding 3 | 4 | *Overview* 5 | 6 | The course aims at providing technical skills about coding/scripting 7 | aspects for data analysis and to manage persistent data storage of 8 | sources and results involved in analysis. On the one side, the Python 9 | programming language and the R framework are illustrated. The goal is to 10 | deal with essential notions about data structures and control structures 11 | of both Python and R. On the other side, the goal is to present the core 12 | notions of relational databases, such as keys, integrity, and 13 | primary/foreign key constraints, as well as the SQL language for data 14 | definition, manipulation, and query. Recent and innovative NoSQL 15 | solutions are also discussed, with special focus on a document-oriented 16 | system called MongoDB. 17 | 18 | The **R Coding** part of the course aims at teaching **R Programming**, 19 | focusing on how to code with modern and state-of-the-art R packages. 20 | 21 | ## General Information 22 | 23 | - [Course Descriprion (for 24 | Students)](https://marcozanotti.github.io/rcoding-course/general-infos/rcod_description.html) 25 | - [Course Descriprion (for Business 26 | Experts)](https://marcozanotti.github.io/rcoding-course/general-infos/rcod_description_business.html) 27 | - [Syllabus](https://marcozanotti.github.io/rcoding-course/general-infos/rcod_syllabus.html) 28 | 29 | ## Materials 30 | 31 | - [Introduction to 32 | R](https://marcozanotti.github.io/rcoding-course/R/rcod_lecture0/rcod_lecture0_intro2R.html) 33 | - [Introduction to 34 | RMarkdown](https://marcozanotti.github.io/rcoding-course/R/rcod_lecture0/rcod_lecture0_rmarkdown.html) 35 | - [💯 R Best 36 | Practices](https://marcozanotti.github.io/rcoding-course/resources/presentation/r_best_practices.html) 37 | - [Tutorials 38 | (lectures)](https://github.com/marcozanotti/rcoding-course/tree/master/R) 39 | - [Other 40 | Resources](https://github.com/marcozanotti/rcoding-course/tree/master/resources) 41 | 42 | ## Suggested References 43 | 44 | Git & GitHub: 45 | 46 | - [Pro Git](https://git-scm.com/book/en/v2) 47 | - [Happy Git with R](https://happygitwithr.com/index.html) 48 | 49 | R Programming: 50 | 51 | - [R for Data Science](https://r4ds.had.co.nz/) 52 | - [Efficient R 53 | Programming](https://csgillespie.github.io/efficientR/index.html) 54 | - [R Packages](https://r-pkgs.org/index.html) 55 | - [Advanced R](https://adv-r.hadley.nz/) 56 | 57 | RMarkdown & Shiny: 58 | 59 | - [RMarkdown: The Definitive 60 | Guide](https://bookdown.org/yihui/rmarkdown/) 61 | - [Mastering Shiny](https://mastering-shiny.org/) 62 | - [Shiny Tutorials](https://shiny.rstudio.com/tutorial/) 63 | 64 | Everything with R: 65 | 66 | - [Big Book of R](https://www.bigbookofr.com/) 67 | -------------------------------------------------------------------------------- /dashboards/auth.txt: -------------------------------------------------------------------------------- 1 | user,password 2 | test,testpw 3 | my_new_user,my_new_password 4 | -------------------------------------------------------------------------------- /dashboards/css/auth.css: -------------------------------------------------------------------------------- 1 | /* AUTH PANEL */ 2 | .panel-auth { 3 | position: fixed; 4 | top:0; 5 | bottom: 0; 6 | left: 0; 7 | right: 0; 8 | background-color: #FFF; 9 | opacity: 1; 10 | z-index: 99997; 11 | overflow-x: hidden; 12 | overflow-y: scroll; 13 | } -------------------------------------------------------------------------------- /dashboards/css/styles-dark.css: -------------------------------------------------------------------------------- 1 | /* AUTH PANEL */ 2 | .panel-auth { 3 | position: fixed; 4 | top:0; 5 | bottom: 0; 6 | left: 0; 7 | right: 0; 8 | background-color: #FFF; 9 | opacity: 1; 10 | z-index: 99997; 11 | overflow-x: hidden; 12 | overflow-y: scroll; 13 | } 14 | 15 | /* FONTS */ 16 | @import url('https://fonts.googleapis.com/css?family=Montserrat:600|Roboto:300&display=swap'); 17 | 18 | .navbar-brand { 19 | font-family: "Montserrat", sans-serif; 20 | } 21 | 22 | h1, h2, h3, h4, h5, h6, .h1, .h2, .h3, .h4, .h5, .h6 { 23 | font-family: "Montserrat", sans-serif; 24 | color: #FFFFFF; 25 | } 26 | 27 | body { 28 | font-family: 'Roboto', sans-serif; 29 | color: #2c3e50; 30 | background-color: #090909; 31 | } 32 | 33 | /* NAVBAR */ 34 | .navbar-inverse { 35 | background-color: #18bc9c; 36 | border-color: #fff; 37 | } 38 | 39 | .navbar-logo { 40 | width: 50px; 41 | } 42 | 43 | 44 | /* SIDEBAR */ 45 | .section.sidebar { 46 | background-color: #090909; 47 | } 48 | 49 | @media screen and (min-width: 767px) { 50 | #dashboard-container { 51 | padding-left: 350px !important; 52 | } 53 | 54 | .sidebar { 55 | width: 350px !important; 56 | } 57 | } 58 | 59 | 60 | @media screen and (max-width: 767px) { 61 | .chart-shim { 62 | overflow-y: scroll; 63 | } 64 | .level3:first-child { 65 | background-color: #090909; 66 | } 67 | } 68 | 69 | 70 | 71 | /* SWITCH */ 72 | .bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-success, .bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-success { 73 | color: #fff; 74 | background: #18bc9c; 75 | } 76 | 77 | .bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-primary, .bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-primary { 78 | color: #fff; 79 | background: #2c3e50; 80 | } 81 | 82 | /* BUTTONS */ 83 | .btn-default { 84 | color: #ffffff; 85 | background-color: #2c3e50; 86 | border-color: #2c3e50 87 | } 88 | 89 | .btn-default:focus,.btn-default.focus { 90 | color: #ffffff; 91 | background-color: #233140; 92 | border-color: #233140 93 | } 94 | 95 | .btn-default:hover { 96 | color: #ffffff; 97 | background-color: #233140; 98 | border-color: #233140 99 | } 100 | 101 | .btn-default:active,.btn-default.active,.open>.dropdown-toggle.btn-default { 102 | color: #ffffff; 103 | background-color: #233140; 104 | border-color: #233140 105 | } 106 | 107 | .btn-default:active:hover,.btn-default.active:hover,.open>.dropdown-toggle.btn-default:hover,.btn-default:active:focus,.btn-default.active:focus,.open>.dropdown-toggle.btn-default:focus,.btn-default:active.focus,.btn-default.active.focus,.open>.dropdown-toggle.btn-default.focus { 108 | color: #ffffff; 109 | background-color: #233140; 110 | border-color: #233140 111 | } 112 | 113 | .btn-default:active,.btn-default.active,.open>.dropdown-toggle.btn-default { 114 | background-image: none 115 | } 116 | 117 | .btn-default.disabled:hover,.btn-default[disabled]:hover,fieldset[disabled] .btn-default:hover,.btn-default.disabled:focus,.btn-default[disabled]:focus,fieldset[disabled] .btn-default:focus,.btn-default.disabled.focus,.btn-default[disabled].focus,fieldset[disabled] .btn-default.focus { 118 | background-color: #222222; 119 | border-color: #222222 120 | } 121 | 122 | .btn-default .badge { 123 | color: #222222; 124 | background-color: #ffffff 125 | } 126 | 127 | 128 | /* BUTTON - SUCCESS */ 129 | 130 | .btn-success { 131 | color: #ffffff; 132 | background-color: #18BC9C; 133 | border-color: #18BC9C 134 | } 135 | 136 | .btn-success:focus,.btn-success.focus { 137 | color: #ffffff; 138 | background-color: #16AB8E; 139 | border-color: #16AB8E 140 | } 141 | 142 | .btn-success:hover { 143 | color: #ffffff; 144 | background-color: #16AB8E; 145 | border-color: #16AB8E 146 | } 147 | 148 | .btn-success:active,.btn-success.active,.open>.dropdown-toggle.btn-success { 149 | color: #ffffff; 150 | background-color: #16AB8E; 151 | border-color: #16AB8E 152 | } 153 | 154 | .btn-success:active:hover,.btn-success.active:hover,.open>.dropdown-toggle.btn-success:hover,.btn-success:active:focus,.btn-success.active:focus,.open>.dropdown-toggle.btn-success:focus,.btn-success:active.focus,.btn-success.active.focus,.open>.dropdown-toggle.btn-success.focus { 155 | color: #ffffff; 156 | background-color: #16AB8E; 157 | border-color: #16AB8E 158 | } 159 | 160 | .btn-success:active,.btn-success.active,.open>.dropdown-toggle.btn-success { 161 | background-image: none 162 | } 163 | 164 | .btn-success.disabled:hover,.btn-success[disabled]:hover,fieldset[disabled] .btn-success:hover,.btn-success.disabled:focus,.btn-success[disabled]:focus,fieldset[disabled] .btn-success:focus,.btn-success.disabled.focus,.btn-success[disabled].focus,fieldset[disabled] .btn-success.focus { 165 | background-color: #3fb618; 166 | border-color: #3fb618 167 | } 168 | 169 | .btn-success .badge { 170 | color: #3fb618; 171 | background-color: #ffffff 172 | } 173 | 174 | /* VALUE BOXES */ 175 | 176 | .bg-danger { 177 | background-color: #E31A1C; 178 | } 179 | 180 | .bg-success { 181 | background-color: #18bc9c; 182 | } 183 | 184 | .bg-warning { 185 | background-color: #FF7F00; 186 | } 187 | 188 | 189 | 190 | 191 | -------------------------------------------------------------------------------- /dashboards/css/styles-default.css: -------------------------------------------------------------------------------- 1 | /* AUTH PANEL */ 2 | .panel-auth { 3 | position: fixed; 4 | top:0; 5 | bottom: 0; 6 | left: 0; 7 | right: 0; 8 | background-color: #FFF; 9 | opacity: 1; 10 | z-index: 99997; 11 | overflow-x: hidden; 12 | overflow-y: scroll; 13 | } 14 | 15 | /* FONTS */ 16 | @import url('https://fonts.googleapis.com/css?family=Montserrat:600|Roboto:300&display=swap'); 17 | 18 | .navbar-brand { 19 | font-family: "Montserrat", sans-serif; 20 | } 21 | 22 | h1, h2, h3, h4, h5, h6, .h1, .h2, .h3, .h4, .h5, .h6 { 23 | font-family: "Montserrat", sans-serif; 24 | color: #2c3e50; 25 | } 26 | 27 | body { 28 | font-family: 'Roboto', sans-serif; 29 | color: #2c3e50; 30 | } 31 | 32 | /* NAVBAR */ 33 | .navbar-inverse { 34 | background-color: #2c3e50; 35 | border-color: #fff; 36 | } 37 | 38 | .navbar-logo { 39 | width: 50px; 40 | } 41 | 42 | 43 | /* SIDEBAR */ 44 | .section.sidebar { 45 | background-color: #dddddd; 46 | } 47 | 48 | @media screen and (min-width: 767px) { 49 | #dashboard-container { 50 | padding-left: 350px !important; 51 | } 52 | 53 | .sidebar { 54 | width: 350px !important; 55 | } 56 | } 57 | 58 | 59 | @media screen and (max-width: 767px) { 60 | .chart-shim { 61 | overflow-y: scroll; 62 | } 63 | } 64 | 65 | 66 | 67 | /* SWITCH */ 68 | .bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-success, .bootstrap-switch .bootstrap-switch-handle-on.bootstrap-switch-success { 69 | color: #fff; 70 | background: #18bc9c; 71 | } 72 | 73 | 74 | /* BUTTONS */ 75 | .btn-default { 76 | color: #ffffff; 77 | background-color: #2c3e50; 78 | border-color: #2c3e50 79 | } 80 | 81 | .btn-default:focus,.btn-default.focus { 82 | color: #ffffff; 83 | background-color: #233140; 84 | border-color: #233140 85 | } 86 | 87 | .btn-default:hover { 88 | color: #ffffff; 89 | background-color: #233140; 90 | border-color: #233140 91 | } 92 | 93 | .btn-default:active,.btn-default.active,.open>.dropdown-toggle.btn-default { 94 | color: #ffffff; 95 | background-color: #233140; 96 | border-color: #233140 97 | } 98 | 99 | .btn-default:active:hover,.btn-default.active:hover,.open>.dropdown-toggle.btn-default:hover,.btn-default:active:focus,.btn-default.active:focus,.open>.dropdown-toggle.btn-default:focus,.btn-default:active.focus,.btn-default.active.focus,.open>.dropdown-toggle.btn-default.focus { 100 | color: #ffffff; 101 | background-color: #233140; 102 | border-color: #233140 103 | } 104 | 105 | .btn-default:active,.btn-default.active,.open>.dropdown-toggle.btn-default { 106 | background-image: none 107 | } 108 | 109 | .btn-default.disabled:hover,.btn-default[disabled]:hover,fieldset[disabled] .btn-default:hover,.btn-default.disabled:focus,.btn-default[disabled]:focus,fieldset[disabled] .btn-default:focus,.btn-default.disabled.focus,.btn-default[disabled].focus,fieldset[disabled] .btn-default.focus { 110 | background-color: #222222; 111 | border-color: #222222 112 | } 113 | 114 | .btn-default .badge { 115 | color: #222222; 116 | background-color: #ffffff 117 | } 118 | 119 | 120 | /* BUTTON - SUCCESS */ 121 | 122 | .btn-success { 123 | color: #ffffff; 124 | background-color: #18BC9C; 125 | border-color: #18BC9C 126 | } 127 | 128 | .btn-success:focus,.btn-success.focus { 129 | color: #ffffff; 130 | background-color: #16AB8E; 131 | border-color: #16AB8E 132 | } 133 | 134 | .btn-success:hover { 135 | color: #ffffff; 136 | background-color: #16AB8E; 137 | border-color: #16AB8E 138 | } 139 | 140 | .btn-success:active,.btn-success.active,.open>.dropdown-toggle.btn-success { 141 | color: #ffffff; 142 | background-color: #16AB8E; 143 | border-color: #16AB8E 144 | } 145 | 146 | .btn-success:active:hover,.btn-success.active:hover,.open>.dropdown-toggle.btn-success:hover,.btn-success:active:focus,.btn-success.active:focus,.open>.dropdown-toggle.btn-success:focus,.btn-success:active.focus,.btn-success.active.focus,.open>.dropdown-toggle.btn-success.focus { 147 | color: #ffffff; 148 | background-color: #16AB8E; 149 | border-color: #16AB8E 150 | } 151 | 152 | .btn-success:active,.btn-success.active,.open>.dropdown-toggle.btn-success { 153 | background-image: none 154 | } 155 | 156 | .btn-success.disabled:hover,.btn-success[disabled]:hover,fieldset[disabled] .btn-success:hover,.btn-success.disabled:focus,.btn-success[disabled]:focus,fieldset[disabled] .btn-success:focus,.btn-success.disabled.focus,.btn-success[disabled].focus,fieldset[disabled] .btn-success.focus { 157 | background-color: #3fb618; 158 | border-color: #3fb618 159 | } 160 | 161 | .btn-success .badge { 162 | color: #3fb618; 163 | background-color: #ffffff 164 | } 165 | 166 | /* VALUE BOXES */ 167 | 168 | .bg-danger { 169 | background-color: #E31A1C; 170 | } 171 | 172 | .bg-success { 173 | background-color: #18bc9c; 174 | } 175 | 176 | .bg-warning { 177 | background-color: #FF7F00; 178 | } 179 | 180 | 181 | 182 | 183 | -------------------------------------------------------------------------------- /dashboards/img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcozanotti/rcoding-course/4482384dcccfe58a8a8b6207e02f3ae5016382d0/dashboards/img/logo.png -------------------------------------------------------------------------------- /dashboards/rcod_lecture6_salesdash01_static.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Sales Dashboard" 3 | output: 4 | flexdashboard::flex_dashboard: 5 | orientation: columns 6 | vertical_layout: fill 7 | --- 8 | 9 | 15 | 16 | ```{r setup, include=FALSE, message=FALSE} 17 | source("../R/utils.R") 18 | source("../R/packages.R") 19 | ``` 20 | 21 | ```{r import and clean data} 22 | path_to_data <- "../data/bikes_database.db" 23 | con <- dbConnect(RSQLite::SQLite(), path_to_data) 24 | 25 | # dbListTables(con) 26 | bikes_tbl <- tbl(con, "bikes") 27 | bikeshops_tbl <- tbl(con, "bikeshops") 28 | orderlines_tbl <- tbl(con, "orderlines") 29 | 30 | processed_data_tbl <- orderlines_tbl %>% 31 | left_join(bikeshops_tbl, by = c("customer.id" = "bikeshop.id")) %>% 32 | left_join(bikes_tbl, by = c("product.id" = "bike.id")) %>% 33 | mutate(extended_price = quantity * price) %>% 34 | collect() 35 | 36 | processed_data_tbl <- processed_data_tbl %>% 37 | mutate(order.date = ymd(order.date)) %>% 38 | separate(location, into = c("city", "state"), sep = ", ") %>% 39 | separate( 40 | description, 41 | into = c("category_1", "category_2", "frame_material"), 42 | sep = " - " 43 | ) %>% 44 | select( 45 | order.date, order.id, order.line, state, quantity, price, 46 | extended_price, category_1:frame_material 47 | ) 48 | # processed_data_tbl 49 | 50 | dbDisconnect(con) 51 | ``` 52 | 53 | 54 | 55 | Column {data-width=100} 56 | --------------------------------------------------------------- 57 | 58 | ### By State 59 | 60 | ```{r} 61 | geo_plot_tbl <- processed_data_tbl %>% 62 | group_by(state) %>% 63 | summarise(total_revenue = sum(extended_price)) %>% 64 | ungroup() %>% 65 | mutate(label_text = str_glue("State: {state} 66 | Revenue: {scales::dollar(total_revenue)}")) 67 | ``` 68 | 69 | ```{r} 70 | geo_plot_tbl %>% 71 | plot_geo(locationmode = "USA-states") %>% 72 | add_trace( 73 | z = ~ total_revenue, 74 | locations = ~ state, 75 | color = ~ total_revenue, 76 | text = ~ label_text, 77 | colors = "Blues" 78 | ) %>% 79 | layout( 80 | geo = list( 81 | scope = "usa", 82 | projection = list(type = "albers usa"), 83 | showlakes = TRUE, 84 | lakecolor = toRGB("white") 85 | ) 86 | ) 87 | ``` 88 | -------------------------------------------------------------------------------- /dashboards/rcod_lecture7_salesdash02_reactive.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Sales Dashboard" 3 | output: 4 | flexdashboard::flex_dashboard: 5 | orientation: columns 6 | vertical_layout: fill 7 | runtime: shiny 8 | --- 9 | 10 | 21 | 22 | ```{r setup, include=FALSE, message=FALSE} 23 | source("../R/utils.R") 24 | source("../R/packages.R") 25 | ``` 26 | 27 | ```{r data} 28 | path_to_data <- "../data/bikes_database.db" 29 | con <- dbConnect(RSQLite::SQLite(), path_to_data) 30 | 31 | # dbListTables(con) 32 | bikes_tbl <- tbl(con, "bikes") 33 | bikeshops_tbl <- tbl(con, "bikeshops") 34 | orderlines_tbl <- tbl(con, "orderlines") 35 | 36 | processed_data_tbl <- orderlines_tbl %>% 37 | left_join(bikeshops_tbl, by = c("customer.id" = "bikeshop.id")) %>% 38 | left_join(bikes_tbl, by = c("product.id" = "bike.id")) %>% 39 | mutate(extended_price = quantity * price) %>% 40 | collect() 41 | 42 | processed_data_tbl <- processed_data_tbl %>% 43 | mutate(order.date = ymd(order.date)) %>% 44 | separate(location, into = c("city", "state"), sep = ", ") %>% 45 | separate( 46 | description, 47 | into = c("category_1", "category_2", "frame_material"), 48 | sep = " - " 49 | ) %>% 50 | select( 51 | order.date, order.id, order.line, state, quantity, price, 52 | extended_price, category_1:frame_material 53 | ) 54 | # processed_data_tbl 55 | 56 | dbDisconnect(con) 57 | ``` 58 | 59 | 60 | 61 | Column {.sidebar} 62 | --------------------------------------------------------------- 63 | 64 | ```{r inputs} 65 | dateRangeInput( 66 | inputId = "date_range_1", 67 | label = h4("Date Range"), 68 | start = min(processed_data_tbl$order.date), 69 | end = max(processed_data_tbl$order.date), 70 | min = min(processed_data_tbl$order.date), 71 | max = max(processed_data_tbl$order.date) 72 | ) 73 | 74 | shinyWidgets::checkboxGroupButtons( 75 | inputId = "checkbox_category_1", 76 | label = h4("Bike Type"), 77 | choices = unique(processed_data_tbl$category_1), 78 | selected = unique(processed_data_tbl$category_1), 79 | checkIcon = list( 80 | yes = icon("ok", lib = "glyphicon"), 81 | no = icon("remove", lib = "glyphicon") 82 | ) 83 | ) 84 | 85 | shinyWidgets::pickerInput( 86 | inputId = "picker_category_2", 87 | label = h4("Bike Family"), 88 | choices = unique(processed_data_tbl$category_2), 89 | selected = unique(processed_data_tbl$category_2), 90 | multiple = TRUE, 91 | options = list( 92 | `actions-box` = TRUE, 93 | size = 10, 94 | `selected-text-format` = "count > 3" 95 | ) 96 | ) 97 | 98 | br() # break rule 99 | hr() # horizontal rule 100 | br() # break rule 101 | 102 | actionButton(inputId = "reset", label = "Reset", icon = icon("sync")) 103 | 104 | observeEvent(eventExpr = input$reset, handlerExpr = { 105 | 106 | updateDateRangeInput( 107 | session = session, 108 | inputId = "date_range_1", 109 | start = min(processed_data_tbl$order.date), 110 | end = max(processed_data_tbl$order.date) 111 | ) 112 | 113 | updateCheckboxGroupButtons( 114 | session = session, 115 | inputId = "checkbox_category_1", 116 | selected = unique(processed_data_tbl$category_1)) 117 | 118 | updatePickerInput( 119 | session = session, 120 | inputId = "picker_category_2", 121 | selected = unique(processed_data_tbl$category_2)) 122 | 123 | }) 124 | 125 | # renderPrint(input$picker_date_1) 126 | 127 | # renderPrint(input$picker_category_2) 128 | 129 | # renderText(input$checkbox_category_1) 130 | 131 | # input <- list( 132 | # date_range_1 = c(min(processed_data_tbl$order.date), max(processed_data_tbl$order.date)), 133 | # checkbox_category_1 = unique(processed_data_tbl$category_1), 134 | # picker_category_2 = unique(processed_data_tbl$category_2) 135 | # ) 136 | ``` 137 | 138 | 139 | 140 | Column {data-width=100} 141 | --------------------------------------------------------------- 142 | 143 | ### By State 144 | 145 | ```{r} 146 | geo_plot_tbl <- reactive({ 147 | # geo_plot_tbl <- 148 | processed_data_tbl %>% 149 | filter(between(order.date, input$date_range_1[1], input$date_range_1[2])) %>% 150 | filter(category_1 %in% input$checkbox_category_1) %>% 151 | filter(category_2 %in% input$picker_category_2) %>% 152 | group_by(state) %>% 153 | summarise(total_revenue = sum(extended_price)) %>% 154 | ungroup() %>% 155 | mutate(label_text = str_glue("State: {state} 156 | Revenue: {scales::dollar(total_revenue)}")) 157 | }) 158 | ``` 159 | 160 | ```{r} 161 | output$plotly_1 <- renderPlotly(expr = { 162 | geo_plot_tbl() %>% 163 | plot_geo(locationmode = "USA-states") %>% 164 | add_trace( 165 | z = ~ total_revenue, 166 | locations = ~ state, 167 | color = ~ total_revenue, 168 | text = ~ label_text, 169 | colors = "Blues" 170 | ) %>% 171 | layout( 172 | geo = list( 173 | scope = "usa", 174 | projection = list(type = "albers usa"), 175 | showlakes = TRUE, 176 | lakecolor = toRGB("white") 177 | ) 178 | ) 179 | }) 180 | 181 | plotlyOutput(outputId = "plotly_1") 182 | ``` 183 | 184 | -------------------------------------------------------------------------------- /dashboards/rcod_lecture7_salesdash03_timeplot.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Sales Dashboard" 3 | output: 4 | flexdashboard::flex_dashboard: 5 | orientation: columns 6 | vertical_layout: fill 7 | runtime: shiny 8 | --- 9 | 10 | 14 | 15 | ```{r setup, include=FALSE, message=FALSE} 16 | source("../R/utils.R") 17 | source("../R/packages.R") 18 | ``` 19 | 20 | ```{r data} 21 | path_to_data <- "../data/bikes_database.db" 22 | con <- dbConnect(RSQLite::SQLite(), path_to_data) 23 | 24 | # dbListTables(con) 25 | bikes_tbl <- tbl(con, "bikes") 26 | bikeshops_tbl <- tbl(con, "bikeshops") 27 | orderlines_tbl <- tbl(con, "orderlines") 28 | 29 | processed_data_tbl <- orderlines_tbl %>% 30 | left_join(bikeshops_tbl, by = c("customer.id" = "bikeshop.id")) %>% 31 | left_join(bikes_tbl, by = c("product.id" = "bike.id")) %>% 32 | mutate(extended_price = quantity * price) %>% 33 | collect() 34 | 35 | processed_data_tbl <- processed_data_tbl %>% 36 | mutate(order.date = ymd(order.date)) %>% 37 | separate(location, into = c("city", "state"), sep = ", ") %>% 38 | separate( 39 | description, 40 | into = c("category_1", "category_2", "frame_material"), 41 | sep = " - " 42 | ) %>% 43 | select( 44 | order.date, order.id, order.line, state, quantity, price, 45 | extended_price, category_1:frame_material 46 | ) 47 | # processed_data_tbl 48 | 49 | dbDisconnect(con) 50 | ``` 51 | 52 | 53 | 54 | Column {.sidebar} 55 | --------------------------------------------------------------- 56 | 57 | ```{r inputs} 58 | dateRangeInput( 59 | inputId = "date_range_1", 60 | label = h4("Date Range"), 61 | start = min(processed_data_tbl$order.date), 62 | end = max(processed_data_tbl$order.date), 63 | min = min(processed_data_tbl$order.date), 64 | max = max(processed_data_tbl$order.date) 65 | ) 66 | 67 | shinyWidgets::checkboxGroupButtons( 68 | inputId = "checkbox_category_1", 69 | label = h4("Bike Type"), 70 | choices = unique(processed_data_tbl$category_1), 71 | selected = unique(processed_data_tbl$category_1), 72 | checkIcon = list( 73 | yes = icon("ok", lib = "glyphicon"), 74 | no = icon("remove", lib = "glyphicon") 75 | ) 76 | ) 77 | 78 | shinyWidgets::pickerInput( 79 | inputId = "picker_category_2", 80 | label = h4("Bike Family"), 81 | choices = unique(processed_data_tbl$category_2), 82 | selected = unique(processed_data_tbl$category_2), 83 | multiple = TRUE, 84 | options = list( 85 | `actions-box` = TRUE, 86 | size = 10, 87 | `selected-text-format` = "count > 3" 88 | ) 89 | ) 90 | 91 | br() # break rule 92 | hr() # horizontal rule 93 | br() # break rule 94 | 95 | actionButton(inputId = "reset", label = "Reset", icon = icon("sync")) 96 | 97 | observeEvent(eventExpr = input$reset, handlerExpr = { 98 | 99 | updateDateRangeInput( 100 | session = session, 101 | inputId = "date_range_1", 102 | start = min(processed_data_tbl$order.date), 103 | end = max(processed_data_tbl$order.date) 104 | ) 105 | 106 | updateCheckboxGroupButtons( 107 | session = session, 108 | inputId = "checkbox_category_1", 109 | selected = unique(processed_data_tbl$category_1)) 110 | 111 | updatePickerInput( 112 | session = session, 113 | inputId = "picker_category_2", 114 | selected = unique(processed_data_tbl$category_2)) 115 | 116 | }) 117 | 118 | # renderPrint(input$picker_date_1) 119 | 120 | # renderPrint(input$picker_category_2) 121 | 122 | # renderText(input$checkbox_category_1) 123 | 124 | # input <- list( 125 | # date_range_1 = c(min(processed_data_tbl$order.date), max(processed_data_tbl$order.date)), 126 | # checkbox_category_1 = unique(processed_data_tbl$category_1), 127 | # picker_category_2 = unique(processed_data_tbl$category_2) 128 | # ) 129 | ``` 130 | 131 | 132 | 133 | Column {data-width=600} 134 | --------------------------------------------------------------- 135 | 136 | ### By State 137 | 138 | ```{r} 139 | geo_plot_tbl <- reactive({ 140 | # geo_plot_tbl <- 141 | processed_data_tbl %>% 142 | filter(between(order.date, input$date_range_1[1], input$date_range_1[2])) %>% 143 | filter(category_1 %in% input$checkbox_category_1) %>% 144 | filter(category_2 %in% input$picker_category_2) %>% 145 | group_by(state) %>% 146 | summarise(total_revenue = sum(extended_price)) %>% 147 | ungroup() %>% 148 | mutate(label_text = str_glue("State: {state} 149 | Revenue: {scales::dollar(total_revenue)}")) 150 | }) 151 | ``` 152 | 153 | ```{r} 154 | output$plotly_1 <- renderPlotly(expr = { 155 | geo_plot_tbl() %>% 156 | plot_geo(locationmode = "USA-states") %>% 157 | add_trace( 158 | z = ~ total_revenue, 159 | locations = ~ state, 160 | color = ~ total_revenue, 161 | text = ~ label_text, 162 | colors = "Blues" 163 | ) %>% 164 | layout( 165 | geo = list( 166 | scope = "usa", 167 | projection = list(type = "albers usa"), 168 | showlakes = TRUE, 169 | lakecolor = toRGB("white") 170 | ) 171 | ) 172 | }) 173 | 174 | plotlyOutput(outputId = "plotly_1") 175 | ``` 176 | 177 | 178 | 179 | Column {data-width=400} 180 | ------------------------------------------------------------------- 181 | 182 | ### Over Time 183 | 184 | ```{r} 185 | time_unit <- "month" 186 | 187 | time_plot_tbl <- processed_data_tbl %>% 188 | mutate(date = floor_date(order.date, unit = time_unit)) %>% 189 | group_by(date) %>% 190 | summarize(total_sales = sum(extended_price)) %>% 191 | ungroup() %>% 192 | mutate(label_text = str_glue("Date: {date} 193 | Revenue: {scales::dollar(total_sales)}")) 194 | ``` 195 | 196 | ```{r} 197 | g <- time_plot_tbl %>% 198 | ggplot(aes(date, total_sales)) + 199 | geom_line(color = "#2c3e50") + 200 | geom_point(aes(text = label_text), color = "#2c3e50", size = 0.1) + 201 | geom_smooth(method = "loess", span = 0.2) + 202 | expand_limits(y = 0) + 203 | scale_y_continuous(labels = scales::dollar_format()) + 204 | labs(x = "", y = "") 205 | 206 | ggplotly(g, tooltip = "text") 207 | ``` 208 | 209 | -------------------------------------------------------------------------------- /dashboards/rcod_lecture7_salesdash04_radiobut.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Sales Dashboard" 3 | output: 4 | flexdashboard::flex_dashboard: 5 | orientation: columns 6 | vertical_layout: fill 7 | runtime: shiny 8 | --- 9 | 10 | 16 | 17 | ```{r setup, include=FALSE, message=FALSE} 18 | source("../R/utils.R") 19 | source("../R/packages.R") 20 | ``` 21 | 22 | ```{r data} 23 | path_to_data <- "../data/bikes_database.db" 24 | con <- dbConnect(RSQLite::SQLite(), path_to_data) 25 | 26 | # dbListTables(con) 27 | bikes_tbl <- tbl(con, "bikes") 28 | bikeshops_tbl <- tbl(con, "bikeshops") 29 | orderlines_tbl <- tbl(con, "orderlines") 30 | 31 | processed_data_tbl <- orderlines_tbl %>% 32 | left_join(bikeshops_tbl, by = c("customer.id" = "bikeshop.id")) %>% 33 | left_join(bikes_tbl, by = c("product.id" = "bike.id")) %>% 34 | mutate(extended_price = quantity * price) %>% 35 | collect() 36 | 37 | processed_data_tbl <- processed_data_tbl %>% 38 | mutate(order.date = ymd(order.date)) %>% 39 | separate(location, into = c("city", "state"), sep = ", ") %>% 40 | separate( 41 | description, 42 | into = c("category_1", "category_2", "frame_material"), 43 | sep = " - " 44 | ) %>% 45 | select( 46 | order.date, order.id, order.line, state, quantity, price, 47 | extended_price, category_1:frame_material 48 | ) 49 | # processed_data_tbl 50 | 51 | dbDisconnect(con) 52 | ``` 53 | 54 | 55 | 56 | Column {.sidebar} 57 | --------------------------------------------------------------- 58 | 59 | ```{r inputs} 60 | dateRangeInput( 61 | inputId = "date_range_1", 62 | label = h4("Date Range"), 63 | start = min(processed_data_tbl$order.date), 64 | end = max(processed_data_tbl$order.date), 65 | min = min(processed_data_tbl$order.date), 66 | max = max(processed_data_tbl$order.date) 67 | ) 68 | 69 | shinyWidgets::checkboxGroupButtons( 70 | inputId = "checkbox_category_1", 71 | label = h4("Bike Type"), 72 | choices = unique(processed_data_tbl$category_1), 73 | selected = unique(processed_data_tbl$category_1), 74 | checkIcon = list( 75 | yes = icon("ok", lib = "glyphicon"), 76 | no = icon("remove", lib = "glyphicon") 77 | ) 78 | ) 79 | 80 | shinyWidgets::pickerInput( 81 | inputId = "picker_category_2", 82 | label = h4("Bike Family"), 83 | choices = unique(processed_data_tbl$category_2), 84 | selected = unique(processed_data_tbl$category_2), 85 | multiple = TRUE, 86 | options = list( 87 | `actions-box` = TRUE, 88 | size = 10, 89 | `selected-text-format` = "count > 3" 90 | ) 91 | ) 92 | 93 | br() # break rule 94 | hr() # horizontal rule 95 | br() # break rule 96 | 97 | actionButton(inputId = "reset", label = "Reset", icon = icon("sync")) 98 | 99 | observeEvent(eventExpr = input$reset, handlerExpr = { 100 | 101 | updateDateRangeInput( 102 | session = session, 103 | inputId = "date_range_1", 104 | start = min(processed_data_tbl$order.date), 105 | end = max(processed_data_tbl$order.date) 106 | ) 107 | 108 | updateCheckboxGroupButtons( 109 | session = session, 110 | inputId = "checkbox_category_1", 111 | selected = unique(processed_data_tbl$category_1)) 112 | 113 | updatePickerInput( 114 | session = session, 115 | inputId = "picker_category_2", 116 | selected = unique(processed_data_tbl$category_2)) 117 | 118 | updateRadioGroupButtons( 119 | session = session, 120 | inputId = "time_unit", 121 | selected = "month" 122 | ) 123 | 124 | }) 125 | 126 | # renderPrint(input$picker_date_1) 127 | 128 | # renderPrint(input$picker_category_2) 129 | 130 | # renderText(input$checkbox_category_1) 131 | 132 | # input <- list( 133 | # date_range_1 = c(min(processed_data_tbl$order.date), max(processed_data_tbl$order.date)), 134 | # checkbox_category_1 = unique(processed_data_tbl$category_1), 135 | # picker_category_2 = unique(processed_data_tbl$category_2) 136 | # ) 137 | ``` 138 | 139 | 140 | 141 | Column {data-width=600} 142 | --------------------------------------------------------------- 143 | 144 | ### By State 145 | 146 | ```{r} 147 | geo_plot_tbl <- reactive({ 148 | # geo_plot_tbl <- 149 | processed_data_tbl %>% 150 | filter(between(order.date, input$date_range_1[1], input$date_range_1[2])) %>% 151 | filter(category_1 %in% input$checkbox_category_1) %>% 152 | filter(category_2 %in% input$picker_category_2) %>% 153 | group_by(state) %>% 154 | summarise(total_revenue = sum(extended_price)) %>% 155 | ungroup() %>% 156 | mutate(label_text = str_glue("State: {state} 157 | Revenue: {scales::dollar(total_revenue)}")) 158 | }) 159 | ``` 160 | 161 | ```{r} 162 | output$plotly_1 <- renderPlotly(expr = { 163 | geo_plot_tbl() %>% 164 | plot_geo(locationmode = "USA-states") %>% 165 | add_trace( 166 | z = ~ total_revenue, 167 | locations = ~ state, 168 | color = ~ total_revenue, 169 | text = ~ label_text, 170 | colors = "Blues" 171 | ) %>% 172 | layout( 173 | geo = list( 174 | scope = "usa", 175 | projection = list(type = "albers usa"), 176 | showlakes = TRUE, 177 | lakecolor = toRGB("white") 178 | ) 179 | ) 180 | }) 181 | 182 | plotlyOutput(outputId = "plotly_1") 183 | ``` 184 | 185 | 186 | 187 | Column {data-width=400} 188 | ------------------------------------------------------------------- 189 | 190 | ### Over Time 191 | 192 | ```{r} 193 | shinyWidgets::radioGroupButtons( 194 | inputId = "time_unit", 195 | label = "Time Unit", 196 | choices = c("D" = "day", "W" = "week", "M" = "month", "Q" = "quarter", "Y" = "year"), 197 | selected = "month", 198 | status = "primary", 199 | justified = TRUE, 200 | checkIcon = list(yes = icon("ok", lib = "glyphicon"), no = NULL) 201 | ) 202 | 203 | # input <- c(input, list(time_unit = "month")) 204 | ``` 205 | 206 | ```{r} 207 | time_plot_tbl <- reactive({ 208 | # time_plot_tbl <- 209 | processed_data_tbl %>% 210 | filter(between(order.date, input$date_range_1[1], input$date_range_1[2])) %>% 211 | filter(category_1 %in% input$checkbox_category_1) %>% 212 | filter(category_2 %in% input$picker_category_2) %>% 213 | mutate(date = floor_date(order.date, unit = input$time_unit)) %>% 214 | group_by(date) %>% 215 | summarize(total_sales = sum(extended_price)) %>% 216 | ungroup() %>% 217 | mutate(label_text = str_glue("Date: {date} 218 | Revenue: {scales::dollar(total_sales)}")) 219 | }) 220 | ``` 221 | 222 | ```{r} 223 | output$plotly_2 <- renderPlotly({ 224 | 225 | g <- time_plot_tbl() %>% 226 | ggplot(aes(date, total_sales)) + 227 | geom_line(color = "#2c3e50") + 228 | geom_point(aes(text = label_text), color = "#2c3e50", size = 0.1) + 229 | geom_smooth(method = "loess", span = 0.2) + 230 | expand_limits(y = 0) + 231 | scale_y_continuous(labels = scales::dollar_format()) + 232 | labs(x = "", y = "") 233 | 234 | ggplotly(g, tooltip = "text") %>% 235 | layout(margin = list(b = 200)) 236 | 237 | }) 238 | 239 | plotlyOutput(outputId = "plotly_2") 240 | ``` 241 | 242 | -------------------------------------------------------------------------------- /dashboards/rcod_lecture7_salesdash05_apply.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Sales Dashboard" 3 | output: 4 | flexdashboard::flex_dashboard: 5 | orientation: columns 6 | vertical_layout: fill 7 | runtime: shiny 8 | --- 9 | 10 | 17 | 18 | ```{r setup, include=FALSE, message=FALSE} 19 | source("../R/utils.R") 20 | source("../R/packages.R") 21 | ``` 22 | 23 | ```{r data} 24 | path_to_data <- "../data/bikes_database.db" 25 | con <- dbConnect(RSQLite::SQLite(), path_to_data) 26 | 27 | # dbListTables(con) 28 | bikes_tbl <- tbl(con, "bikes") 29 | bikeshops_tbl <- tbl(con, "bikeshops") 30 | orderlines_tbl <- tbl(con, "orderlines") 31 | 32 | processed_data_tbl <- orderlines_tbl %>% 33 | left_join(bikeshops_tbl, by = c("customer.id" = "bikeshop.id")) %>% 34 | left_join(bikes_tbl, by = c("product.id" = "bike.id")) %>% 35 | mutate(extended_price = quantity * price) %>% 36 | collect() 37 | 38 | processed_data_tbl <- processed_data_tbl %>% 39 | mutate(order.date = ymd(order.date)) %>% 40 | separate(location, into = c("city", "state"), sep = ", ") %>% 41 | separate( 42 | description, 43 | into = c("category_1", "category_2", "frame_material"), 44 | sep = " - " 45 | ) %>% 46 | select( 47 | order.date, order.id, order.line, state, quantity, price, 48 | extended_price, category_1:frame_material 49 | ) 50 | # processed_data_tbl 51 | 52 | dbDisconnect(con) 53 | ``` 54 | 55 | 56 | 57 | Column {.sidebar} 58 | --------------------------------------------------------------- 59 | 60 | ```{r} 61 | useShinyjs(rmd = TRUE) # use Shiny JavaScript to allow delay on buttons 62 | 63 | dateRangeInput( 64 | inputId = "date_range_1", 65 | label = h4("Date Range"), 66 | start = min(processed_data_tbl$order.date), 67 | end = max(processed_data_tbl$order.date), 68 | min = min(processed_data_tbl$order.date), 69 | max = max(processed_data_tbl$order.date) 70 | ) 71 | 72 | shinyWidgets::checkboxGroupButtons( 73 | inputId = "checkbox_category_1", 74 | label = h4("Bike Type"), 75 | choices = unique(processed_data_tbl$category_1), 76 | selected = unique(processed_data_tbl$category_1), 77 | checkIcon = list( 78 | yes = icon("ok", lib = "glyphicon"), 79 | no = icon("remove", lib = "glyphicon") 80 | ) 81 | ) 82 | 83 | shinyWidgets::pickerInput( 84 | inputId = "picker_category_2", 85 | label = h4("Bike Family"), 86 | choices = unique(processed_data_tbl$category_2), 87 | selected = unique(processed_data_tbl$category_2), 88 | multiple = TRUE, 89 | options = list( 90 | `actions-box` = TRUE, 91 | size = 10, 92 | `selected-text-format` = "count > 3" 93 | ) 94 | ) 95 | 96 | br() # break rule 97 | hr() # horizontal rule 98 | br() # break rule 99 | 100 | actionButton(inputId = "apply", label = "Apply", icon = icon("play")) 101 | 102 | actionButton(inputId = "reset", label = "Reset", icon = icon("sync")) 103 | 104 | observeEvent(eventExpr = input$reset, handlerExpr = { 105 | 106 | updateDateRangeInput( 107 | session = session, 108 | inputId = "date_range_1", 109 | start = min(processed_data_tbl$order.date), 110 | end = max(processed_data_tbl$order.date) 111 | ) 112 | 113 | updateCheckboxGroupButtons( 114 | session = session, 115 | inputId = "checkbox_category_1", 116 | selected = unique(processed_data_tbl$category_1)) 117 | 118 | updatePickerInput( 119 | session = session, 120 | inputId = "picker_category_2", 121 | selected = unique(processed_data_tbl$category_2)) 122 | 123 | updateRadioGroupButtons( 124 | session = session, 125 | inputId = "time_unit", 126 | selected = "month" 127 | ) 128 | 129 | shinyjs::delay(ms = 300, expr = {shinyjs::click(id = "apply")}) 130 | 131 | }) 132 | 133 | # renderPrint(input$picker_date_1) 134 | 135 | # renderPrint(input$picker_category_2) 136 | 137 | # renderText(input$checkbox_category_1) 138 | 139 | # input <- list( 140 | # date_range_1 = c(min(processed_data_tbl$order.date), max(processed_data_tbl$order.date)), 141 | # checkbox_category_1 = unique(processed_data_tbl$category_1), 142 | # picker_category_2 = unique(processed_data_tbl$category_2) 143 | # ) 144 | ``` 145 | 146 | ```{r} 147 | processed_data_filtered_tbl <- eventReactive( 148 | eventExpr = input$apply, 149 | valueExpr = { 150 | # processed_data_filtered_tbl <- 151 | processed_data_tbl %>% 152 | filter(between(order.date, input$date_range_1[1], input$date_range_1[2])) %>% 153 | filter(category_1 %in% input$checkbox_category_1) %>% 154 | filter(category_2 %in% input$picker_category_2) 155 | }, 156 | ignoreNULL = FALSE 157 | ) 158 | ``` 159 | 160 | 161 | 162 | Column {data-width=600} 163 | --------------------------------------------------------------- 164 | 165 | ### By State 166 | 167 | ```{r} 168 | geo_plot_tbl <- reactive({ 169 | # geo_plot_tbl <- 170 | processed_data_filtered_tbl() %>% 171 | group_by(state) %>% 172 | summarise(total_revenue = sum(extended_price)) %>% 173 | ungroup() %>% 174 | mutate(label_text = str_glue("State: {state} 175 | Revenue: {scales::dollar(total_revenue)}")) 176 | }) 177 | ``` 178 | 179 | ```{r} 180 | output$plotly_1 <- renderPlotly(expr = { 181 | geo_plot_tbl() %>% 182 | plot_geo(locationmode = "USA-states") %>% 183 | add_trace( 184 | z = ~ total_revenue, 185 | locations = ~ state, 186 | color = ~ total_revenue, 187 | text = ~ label_text, 188 | colors = "Blues" 189 | ) %>% 190 | layout( 191 | geo = list( 192 | scope = "usa", 193 | projection = list(type = "albers usa"), 194 | showlakes = TRUE, 195 | lakecolor = toRGB("white") 196 | ) 197 | ) 198 | }) 199 | 200 | plotlyOutput(outputId = "plotly_1") 201 | ``` 202 | 203 | 204 | 205 | Column {data-width=400} 206 | ------------------------------------------------------------------- 207 | 208 | ### Over Time 209 | 210 | ```{r} 211 | shinyWidgets::radioGroupButtons( 212 | inputId = "time_unit", 213 | label = "Time Unit", 214 | choices = c("D" = "day", "W" = "week", "M" = "month", "Q" = "quarter", "Y" = "year"), 215 | selected = "month", 216 | status = "primary", 217 | justified = TRUE, 218 | checkIcon = list(yes = icon("ok", lib = "glyphicon"), no = NULL) 219 | ) 220 | 221 | # input <- c(input, list(time_unit = "month")) 222 | ``` 223 | 224 | ```{r} 225 | time_plot_tbl <- reactive({ 226 | # time_plot_tbl <- 227 | processed_data_filtered_tbl() %>% 228 | mutate(date = floor_date(order.date, unit = input$time_unit)) %>% 229 | group_by(date) %>% 230 | summarize(total_sales = sum(extended_price)) %>% 231 | ungroup() %>% 232 | mutate(label_text = str_glue("Date: {date} 233 | Revenue: {scales::dollar(total_sales)}")) 234 | }) 235 | ``` 236 | 237 | ```{r} 238 | output$plotly_2 <- renderPlotly({ 239 | 240 | g <- time_plot_tbl() %>% 241 | ggplot(aes(date, total_sales)) + 242 | geom_line(color = "#2c3e50") + 243 | geom_point(aes(text = label_text), color = "#2c3e50", size = 0.1) + 244 | geom_smooth(method = "loess", span = 0.2) + 245 | expand_limits(y = 0) + 246 | scale_y_continuous(labels = scales::dollar_format()) + 247 | labs(x = "", y = "") 248 | 249 | ggplotly(g, tooltip = "text") %>% 250 | layout(margin = list(b = 200)) 251 | 252 | }) 253 | 254 | plotlyOutput(outputId = "plotly_2") 255 | ``` 256 | 257 | -------------------------------------------------------------------------------- /dashboards/rcod_lecture7_salesdash06_valuebox.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Sales Dashboard" 3 | output: 4 | flexdashboard::flex_dashboard: 5 | orientation: rows 6 | vertical_layout: fill 7 | runtime: shiny 8 | --- 9 | 10 | 15 | 16 | ```{r setup, include=FALSE, message=FALSE} 17 | source("../R/utils.R") 18 | source("../R/packages.R") 19 | ``` 20 | 21 | ```{r data} 22 | path_to_data <- "../data/bikes_database.db" 23 | con <- dbConnect(RSQLite::SQLite(), path_to_data) 24 | 25 | # dbListTables(con) 26 | bikes_tbl <- tbl(con, "bikes") 27 | bikeshops_tbl <- tbl(con, "bikeshops") 28 | orderlines_tbl <- tbl(con, "orderlines") 29 | 30 | processed_data_tbl <- orderlines_tbl %>% 31 | left_join(bikeshops_tbl, by = c("customer.id" = "bikeshop.id")) %>% 32 | left_join(bikes_tbl, by = c("product.id" = "bike.id")) %>% 33 | mutate(extended_price = quantity * price) %>% 34 | collect() 35 | 36 | processed_data_tbl <- processed_data_tbl %>% 37 | mutate(order.date = ymd(order.date)) %>% 38 | separate(location, into = c("city", "state"), sep = ", ") %>% 39 | separate( 40 | description, 41 | into = c("category_1", "category_2", "frame_material"), 42 | sep = " - " 43 | ) %>% 44 | select( 45 | order.date, order.id, order.line, state, quantity, price, 46 | extended_price, category_1:frame_material 47 | ) 48 | # processed_data_tbl 49 | 50 | dbDisconnect(con) 51 | ``` 52 | 53 | 54 | 55 | Column {.sidebar} 56 | --------------------------------------------------------------- 57 | 58 | ```{r} 59 | useShinyjs(rmd = TRUE) # use Shiny JavaScript to allow delay on buttons 60 | 61 | dateRangeInput( 62 | inputId = "date_range_1", 63 | label = h4("Date Range"), 64 | start = min(processed_data_tbl$order.date), 65 | end = max(processed_data_tbl$order.date), 66 | min = min(processed_data_tbl$order.date), 67 | max = max(processed_data_tbl$order.date) 68 | ) 69 | 70 | shinyWidgets::checkboxGroupButtons( 71 | inputId = "checkbox_category_1", 72 | label = h4("Bike Type"), 73 | choices = unique(processed_data_tbl$category_1), 74 | selected = unique(processed_data_tbl$category_1), 75 | checkIcon = list( 76 | yes = icon("ok", lib = "glyphicon"), 77 | no = icon("remove", lib = "glyphicon") 78 | ) 79 | ) 80 | 81 | shinyWidgets::pickerInput( 82 | inputId = "picker_category_2", 83 | label = h4("Bike Family"), 84 | choices = unique(processed_data_tbl$category_2), 85 | selected = unique(processed_data_tbl$category_2), 86 | multiple = TRUE, 87 | options = list( 88 | `actions-box` = TRUE, 89 | size = 10, 90 | `selected-text-format` = "count > 3" 91 | ) 92 | ) 93 | 94 | br() # break rule 95 | hr() # horizontal rule 96 | br() # break rule 97 | 98 | actionButton(inputId = "apply", label = "Apply", icon = icon("play")) 99 | 100 | actionButton(inputId = "reset", label = "Reset", icon = icon("sync")) 101 | 102 | observeEvent(eventExpr = input$reset, handlerExpr = { 103 | 104 | updateDateRangeInput( 105 | session = session, 106 | inputId = "date_range_1", 107 | start = min(processed_data_tbl$order.date), 108 | end = max(processed_data_tbl$order.date) 109 | ) 110 | 111 | updateCheckboxGroupButtons( 112 | session = session, 113 | inputId = "checkbox_category_1", 114 | selected = unique(processed_data_tbl$category_1)) 115 | 116 | updatePickerInput( 117 | session = session, 118 | inputId = "picker_category_2", 119 | selected = unique(processed_data_tbl$category_2)) 120 | 121 | updateRadioGroupButtons( 122 | session = session, 123 | inputId = "time_unit", 124 | selected = "month" 125 | ) 126 | 127 | shinyjs::delay(ms = 300, expr = {shinyjs::click(id = "apply")}) 128 | 129 | }) 130 | 131 | # renderPrint(input$picker_date_1) 132 | 133 | # renderPrint(input$picker_category_2) 134 | 135 | # renderText(input$checkbox_category_1) 136 | 137 | # input <- list( 138 | # date_range_1 = c(min(processed_data_tbl$order.date), max(processed_data_tbl$order.date)), 139 | # checkbox_category_1 = unique(processed_data_tbl$category_1), 140 | # picker_category_2 = unique(processed_data_tbl$category_2) 141 | # ) 142 | ``` 143 | 144 | ```{r} 145 | processed_data_filtered_tbl <- eventReactive( 146 | eventExpr = input$apply, 147 | valueExpr = { 148 | # processed_data_filtered_tbl <- 149 | processed_data_tbl %>% 150 | filter(between(order.date, input$date_range_1[1], input$date_range_1[2])) %>% 151 | filter(category_1 %in% input$checkbox_category_1) %>% 152 | filter(category_2 %in% input$picker_category_2) 153 | }, 154 | ignoreNULL = FALSE 155 | ) 156 | ``` 157 | 158 | 159 | 160 | Row {data-height=150} 161 | --------------------------------------------------------------- 162 | 163 | ```{r} 164 | summary_values_tbl <- reactive({ 165 | # summary_values_tbl <- 166 | processed_data_filtered_tbl() %>% 167 | summarize( 168 | health_metric = unique(order.id) %>% length(), 169 | wealth_metric = sum(extended_price), 170 | wise_metric = mean(price) %>% round(0) 171 | ) %>% 172 | mutate( 173 | health_metric = scales::number(health_metric, big.mark = ","), 174 | wealth_metric = scales::dollar(wealth_metric, suffix = "M"), 175 | wise_metric = scales::dollar(wise_metric) 176 | ) 177 | }) 178 | ``` 179 | 180 | ### Health 181 | 182 | ```{r} 183 | renderValueBox({ 184 | valueBox( 185 | value = summary_values_tbl()$health_metric, 186 | caption = "Orders", 187 | icon = "fa-heartbeat", 188 | color = "success") 189 | }) 190 | ``` 191 | 192 | ### Wealthy 193 | 194 | ```{r} 195 | renderValueBox({ 196 | valueBox( 197 | value = summary_values_tbl()$wealth_metric, 198 | caption = "Sales", 199 | icon = "fa-money-check-alt", 200 | color = "primary") 201 | }) 202 | ``` 203 | 204 | ### Wise 205 | 206 | ```{r} 207 | renderValueBox({ 208 | valueBox( 209 | value = summary_values_tbl()$wise_metric, 210 | caption = "Average Price", 211 | icon = "fa-brain", 212 | color = "info") 213 | }) 214 | ``` 215 | 216 | 217 | 218 | Row {data-height=850} 219 | --------------------------------------------------------------- 220 | 221 | ### By State 222 | 223 | ```{r} 224 | geo_plot_tbl <- reactive({ 225 | # geo_plot_tbl <- 226 | processed_data_filtered_tbl() %>% 227 | group_by(state) %>% 228 | summarise(total_revenue = sum(extended_price)) %>% 229 | ungroup() %>% 230 | mutate(label_text = str_glue("State: {state} 231 | Revenue: {scales::dollar(total_revenue)}")) 232 | }) 233 | ``` 234 | 235 | ```{r} 236 | output$plotly_1 <- renderPlotly(expr = { 237 | geo_plot_tbl() %>% 238 | plot_geo(locationmode = "USA-states") %>% 239 | add_trace( 240 | z = ~ total_revenue, 241 | locations = ~ state, 242 | color = ~ total_revenue, 243 | text = ~ label_text, 244 | colors = "Blues" 245 | ) %>% 246 | layout( 247 | geo = list( 248 | scope = "usa", 249 | projection = list(type = "albers usa"), 250 | showlakes = TRUE, 251 | lakecolor = toRGB("white") 252 | ) 253 | ) 254 | }) 255 | 256 | plotlyOutput(outputId = "plotly_1") 257 | ``` 258 | 259 | 260 | 261 | ### Over Time 262 | 263 | ```{r} 264 | shinyWidgets::radioGroupButtons( 265 | inputId = "time_unit", 266 | label = "Time Unit", 267 | choices = c("D" = "day", "W" = "week", "M" = "month", "Q" = "quarter", "Y" = "year"), 268 | selected = "month", 269 | status = "primary", 270 | justified = TRUE, 271 | checkIcon = list(yes = icon("ok", lib = "glyphicon"), no = NULL) 272 | ) 273 | 274 | # input <- c(input, list(time_unit = "month")) 275 | ``` 276 | 277 | ```{r} 278 | time_plot_tbl <- reactive({ 279 | # time_plot_tbl <- 280 | processed_data_filtered_tbl() %>% 281 | mutate(date = floor_date(order.date, unit = input$time_unit)) %>% 282 | group_by(date) %>% 283 | summarize(total_sales = sum(extended_price)) %>% 284 | ungroup() %>% 285 | mutate(label_text = str_glue("Date: {date} 286 | Revenue: {scales::dollar(total_sales)}")) 287 | }) 288 | ``` 289 | 290 | ```{r} 291 | output$plotly_2 <- renderPlotly({ 292 | 293 | g <- time_plot_tbl() %>% 294 | ggplot(aes(date, total_sales)) + 295 | geom_line(color = "#2c3e50") + 296 | geom_point(aes(text = label_text), color = "#2c3e50", size = 0.1) + 297 | geom_smooth(method = "loess", span = 0.2) + 298 | expand_limits(y = 0) + 299 | scale_y_continuous(labels = scales::dollar_format()) + 300 | labs(x = "", y = "") 301 | 302 | ggplotly(g, tooltip = "text") %>% 303 | layout(margin = list(b = 200)) 304 | 305 | }) 306 | 307 | plotlyOutput(outputId = "plotly_2") 308 | ``` 309 | 310 | -------------------------------------------------------------------------------- /dashboards/rcod_lecture7_salesdash07_auth.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Sales Dashboard" 3 | output: 4 | flexdashboard::flex_dashboard: 5 | orientation: rows 6 | vertical_layout: fill 7 | css: css/auth.css # try without css (Cascading Style Sheets) 8 | runtime: shiny 9 | --- 10 | 11 | 17 | 18 | ```{r setup, include=FALSE, message=FALSE} 19 | source("../R/utils.R") 20 | source("../R/packages.R") 21 | ``` 22 | 23 | ```{r auth} 24 | credentials <- read.csv("auth.txt") 25 | shinymanager::auth_ui(id = "auth") 26 | auth <- shiny::callModule( 27 | module = shinymanager::auth_server, 28 | id = "auth", 29 | check_credentials = shinymanager::check_credentials(credentials) 30 | ) 31 | ``` 32 | 33 | ```{r data} 34 | path_to_data <- "../data/bikes_database.db" 35 | con <- dbConnect(RSQLite::SQLite(), path_to_data) 36 | 37 | # dbListTables(con) 38 | bikes_tbl <- tbl(con, "bikes") 39 | bikeshops_tbl <- tbl(con, "bikeshops") 40 | orderlines_tbl <- tbl(con, "orderlines") 41 | 42 | processed_data_tbl <- orderlines_tbl %>% 43 | left_join(bikeshops_tbl, by = c("customer.id" = "bikeshop.id")) %>% 44 | left_join(bikes_tbl, by = c("product.id" = "bike.id")) %>% 45 | mutate(extended_price = quantity * price) %>% 46 | collect() 47 | 48 | processed_data_tbl <- processed_data_tbl %>% 49 | mutate(order.date = ymd(order.date)) %>% 50 | separate(location, into = c("city", "state"), sep = ", ") %>% 51 | separate( 52 | description, 53 | into = c("category_1", "category_2", "frame_material"), 54 | sep = " - " 55 | ) %>% 56 | select( 57 | order.date, order.id, order.line, state, quantity, price, 58 | extended_price, category_1:frame_material 59 | ) 60 | # processed_data_tbl 61 | 62 | dbDisconnect(con) 63 | ``` 64 | 65 | 66 | 67 | Column {.sidebar} 68 | --------------------------------------------------------------- 69 | 70 | ```{r} 71 | useShinyjs(rmd = TRUE) # use Shiny JavaScript to allow delay on buttons 72 | 73 | dateRangeInput( 74 | inputId = "date_range_1", 75 | label = h4("Date Range"), 76 | start = min(processed_data_tbl$order.date), 77 | end = max(processed_data_tbl$order.date), 78 | min = min(processed_data_tbl$order.date), 79 | max = max(processed_data_tbl$order.date) 80 | ) 81 | 82 | shinyWidgets::checkboxGroupButtons( 83 | inputId = "checkbox_category_1", 84 | label = h4("Bike Type"), 85 | choices = unique(processed_data_tbl$category_1), 86 | selected = unique(processed_data_tbl$category_1), 87 | checkIcon = list( 88 | yes = icon("ok", lib = "glyphicon"), 89 | no = icon("remove", lib = "glyphicon") 90 | ) 91 | ) 92 | 93 | shinyWidgets::pickerInput( 94 | inputId = "picker_category_2", 95 | label = h4("Bike Family"), 96 | choices = unique(processed_data_tbl$category_2), 97 | selected = unique(processed_data_tbl$category_2), 98 | multiple = TRUE, 99 | options = list( 100 | `actions-box` = TRUE, 101 | size = 10, 102 | `selected-text-format` = "count > 3" 103 | ) 104 | ) 105 | 106 | br() # break rule 107 | hr() # horizontal rule 108 | br() # break rule 109 | 110 | actionButton(inputId = "apply", label = "Apply", icon = icon("play")) 111 | 112 | actionButton(inputId = "reset", label = "Reset", icon = icon("sync")) 113 | 114 | observeEvent(eventExpr = input$reset, handlerExpr = { 115 | 116 | updateDateRangeInput( 117 | session = session, 118 | inputId = "date_range_1", 119 | start = min(processed_data_tbl$order.date), 120 | end = max(processed_data_tbl$order.date) 121 | ) 122 | 123 | updateCheckboxGroupButtons( 124 | session = session, 125 | inputId = "checkbox_category_1", 126 | selected = unique(processed_data_tbl$category_1)) 127 | 128 | updatePickerInput( 129 | session = session, 130 | inputId = "picker_category_2", 131 | selected = unique(processed_data_tbl$category_2)) 132 | 133 | updateRadioGroupButtons( 134 | session = session, 135 | inputId = "time_unit", 136 | selected = "month" 137 | ) 138 | 139 | shinyjs::delay(ms = 300, expr = {shinyjs::click(id = "apply")}) 140 | 141 | }) 142 | 143 | # renderPrint(input$picker_date_1) 144 | 145 | # renderPrint(input$picker_category_2) 146 | 147 | # renderText(input$checkbox_category_1) 148 | 149 | # input <- list( 150 | # date_range_1 = c(min(processed_data_tbl$order.date), max(processed_data_tbl$order.date)), 151 | # checkbox_category_1 = unique(processed_data_tbl$category_1), 152 | # picker_category_2 = unique(processed_data_tbl$category_2) 153 | # ) 154 | ``` 155 | 156 | ```{r} 157 | processed_data_filtered_tbl <- eventReactive( 158 | eventExpr = input$apply, 159 | valueExpr = { 160 | # processed_data_filtered_tbl <- 161 | processed_data_tbl %>% 162 | filter(between(order.date, input$date_range_1[1], input$date_range_1[2])) %>% 163 | filter(category_1 %in% input$checkbox_category_1) %>% 164 | filter(category_2 %in% input$picker_category_2) 165 | }, 166 | ignoreNULL = FALSE 167 | ) 168 | ``` 169 | 170 | 171 | 172 | Row {data-height=150} 173 | --------------------------------------------------------------- 174 | 175 | ```{r} 176 | summary_values_tbl <- reactive({ 177 | # summary_values_tbl <- 178 | processed_data_filtered_tbl() %>% 179 | summarize( 180 | health_metric = unique(order.id) %>% length(), 181 | wealth_metric = sum(extended_price), 182 | wise_metric = mean(price) %>% round(0) 183 | ) %>% 184 | mutate( 185 | health_metric = scales::number(health_metric, big.mark = ","), 186 | wealth_metric = scales::dollar(wealth_metric, suffix = "M"), 187 | wise_metric = scales::dollar(wise_metric) 188 | ) 189 | }) 190 | ``` 191 | 192 | ### Health 193 | 194 | ```{r} 195 | renderValueBox({ 196 | valueBox( 197 | value = summary_values_tbl()$health_metric, 198 | caption = "Orders", 199 | icon = "fa-heartbeat", 200 | color = "success") 201 | }) 202 | ``` 203 | 204 | ### Wealthy 205 | 206 | ```{r} 207 | renderValueBox({ 208 | valueBox( 209 | value = summary_values_tbl()$wealth_metric, 210 | caption = "Sales", 211 | icon = "fa-money-check-alt", 212 | color = "primary") 213 | }) 214 | ``` 215 | 216 | ### Wise 217 | 218 | ```{r} 219 | renderValueBox({ 220 | valueBox( 221 | value = summary_values_tbl()$wise_metric, 222 | caption = "Average Price", 223 | icon = "fa-brain", 224 | color = "info") 225 | }) 226 | ``` 227 | 228 | 229 | 230 | Row {data-height=850} 231 | --------------------------------------------------------------- 232 | 233 | ### By State 234 | 235 | ```{r} 236 | geo_plot_tbl <- reactive({ 237 | # geo_plot_tbl <- 238 | processed_data_filtered_tbl() %>% 239 | group_by(state) %>% 240 | summarise(total_revenue = sum(extended_price)) %>% 241 | ungroup() %>% 242 | mutate(label_text = str_glue("State: {state} 243 | Revenue: {scales::dollar(total_revenue)}")) 244 | }) 245 | ``` 246 | 247 | ```{r} 248 | output$plotly_1 <- renderPlotly(expr = { 249 | geo_plot_tbl() %>% 250 | plot_geo(locationmode = "USA-states") %>% 251 | add_trace( 252 | z = ~ total_revenue, 253 | locations = ~ state, 254 | color = ~ total_revenue, 255 | text = ~ label_text, 256 | colors = "Blues" 257 | ) %>% 258 | layout( 259 | geo = list( 260 | scope = "usa", 261 | projection = list(type = "albers usa"), 262 | showlakes = TRUE, 263 | lakecolor = toRGB("white") 264 | ) 265 | ) 266 | }) 267 | 268 | plotlyOutput(outputId = "plotly_1") 269 | ``` 270 | 271 | 272 | 273 | ### Over Time 274 | 275 | ```{r} 276 | shinyWidgets::radioGroupButtons( 277 | inputId = "time_unit", 278 | label = "Time Unit", 279 | choices = c("D" = "day", "W" = "week", "M" = "month", "Q" = "quarter", "Y" = "year"), 280 | selected = "month", 281 | status = "primary", 282 | justified = TRUE, 283 | checkIcon = list(yes = icon("ok", lib = "glyphicon"), no = NULL) 284 | ) 285 | 286 | # input <- c(input, list(time_unit = "month")) 287 | ``` 288 | 289 | ```{r} 290 | time_plot_tbl <- reactive({ 291 | # time_plot_tbl <- 292 | processed_data_filtered_tbl() %>% 293 | mutate(date = floor_date(order.date, unit = input$time_unit)) %>% 294 | group_by(date) %>% 295 | summarize(total_sales = sum(extended_price)) %>% 296 | ungroup() %>% 297 | mutate(label_text = str_glue("Date: {date} 298 | Revenue: {scales::dollar(total_sales)}")) 299 | }) 300 | ``` 301 | 302 | ```{r} 303 | output$plotly_2 <- renderPlotly({ 304 | 305 | g <- time_plot_tbl() %>% 306 | ggplot(aes(date, total_sales)) + 307 | geom_line(color = "#2c3e50") + 308 | geom_point(aes(text = label_text), color = "#2c3e50", size = 0.1) + 309 | geom_smooth(method = "loess", span = 0.2) + 310 | expand_limits(y = 0) + 311 | scale_y_continuous(labels = scales::dollar_format()) + 312 | labs(x = "", y = "") 313 | 314 | ggplotly(g, tooltip = "text") %>% 315 | layout(margin = list(b = 200)) 316 | 317 | }) 318 | 319 | plotlyOutput(outputId = "plotly_2") 320 | ``` 321 | 322 | -------------------------------------------------------------------------------- /dashboards/salesdash_result.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcozanotti/rcoding-course/4482384dcccfe58a8a8b6207e02f3ae5016382d0/dashboards/salesdash_result.png -------------------------------------------------------------------------------- /data/bikes_database.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcozanotti/rcoding-course/4482384dcccfe58a8a8b6207e02f3ae5016382d0/data/bikes_database.db -------------------------------------------------------------------------------- /data/dirty_data.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcozanotti/rcoding-course/4482384dcccfe58a8a8b6207e02f3ae5016382d0/data/dirty_data.xlsx -------------------------------------------------------------------------------- /general-infos/rcod_description.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "R Coding" 3 | author: "Marco Zanotti" 4 | date: "" 5 | output: 6 | pdf_document: default 7 | rmdformats::downcute: 8 | highlight: tango 9 | --- 10 | 11 | ```{r setup, include=FALSE} 12 | knitr::opts_chunk$set(echo = TRUE) 13 | ``` 14 | 15 |   16 | 17 | The course aims at providing technical skills about coding/scripting 18 | aspects for data analysis and to manage persistent data storage of 19 | sources and results involved in analysis. On the one side, the R framework is 20 | illustrated. The goal is to deal first with essential notions about data 21 | structures and control structures of R, then to learn how to code with modern 22 | and state-of-the-art R packages. 23 | 24 | 25 | ## Course Structure 26 | 27 | The **R Coding** part of the course aims at teaching **R Programming**. 28 | The state-of-the-art and modern R packages are presented from a very practical 29 | point of view, throughout R tutorials on each main topic. 30 | 31 | * 10 hours: practical lectures on the main contents 32 | 33 | 34 | ## Contents 35 | 36 | * Tidyverse Basics 37 | * Tidyverse Wrangling 38 | * Tidyverse Visualization 39 | * Tidyverse Functional Programming 40 | * Tidymodels 41 | * Flexdashboard UI 42 | * Flexdashboard Reactivity - Shiny 43 | * Flexdashboard in Production 44 | 45 | 46 | \pagebreak 47 | 48 | 49 | ## Duration & Calendar 50 | 51 | The course is divided into 5 lectures (2 hours each). 52 | 53 | 1. Day 2023-11-07, 10.30 - 12.30 54 | 2. Day yyyy-mm-dd, 10.30 - 12.30 55 | 3. Day yyyy-mm-dd, 10.30 - 12.30 56 | 4. Day yyyy-mm-dd, 10.30 - 12.30 57 | 5. Day yyyy-mm-dd, 10.30 - 12.30 58 | 59 | Lectures take place online. 60 | 61 | 62 | ## Requirements 63 | 64 | Requirements: 65 | 66 | * Lecture 0 - Introduction to R 67 | * Lecture 0 - RMarkdown 68 | 69 | 70 | For more information about the course contact zanottimarco17@gmail.com 71 | or look at the 72 | [Course Syllabus](https://marcozanotti.github.io/rcoding-course/general-infos/rcod_syllabus.html) 73 | -------------------------------------------------------------------------------- /general-infos/rcod_description.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcozanotti/rcoding-course/4482384dcccfe58a8a8b6207e02f3ae5016382d0/general-infos/rcod_description.pdf -------------------------------------------------------------------------------- /general-infos/rcod_description_business.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "R Coding & BI for Business" 3 | author: "Marco Zanotti" 4 | date: "" 5 | output: 6 | pdf_document: default 7 | rmdformats::downcute: 8 | highlight: tango 9 | --- 10 | 11 | ```{r setup, include=FALSE} 12 | knitr::opts_chunk$set(echo = TRUE) 13 | ``` 14 | 15 | ## Purposes 16 | 17 | The course aims at providing technical skills about coding/scripting 18 | aspects for data analysis and to manage persistent data storage of 19 | sources and results involved in analysis. On the one side, the R framework is 20 | illustrated. The goal is to deal first with essential notions about data 21 | structures and control structures of R, then to learn how to code with modern 22 | and state-of-the-art R packages. 23 | 24 | The **R Coding** part of the course aims at teaching **R Programming**. 25 | The state-of-the-art and modern R packages are presented from a very practical 26 | point of view, throughout R tutorials on each main topic. 27 | The **BI** part of the course aims at teaching how to create modern data 28 | analysis dashboard using **Shiny** and **flexdashboard**. 29 | 30 | 31 | ## Contents 32 | 33 | * Tidyverse Basics 34 | * Tidyverse Wrangling 35 | * Tidyverse Visualization 36 | * Tidyverse Functional Programming 37 | * Tidymodels 38 | * Shiny Basics 39 | * Flexdashboard UI 40 | * Shiny Reactivity 41 | * Advanced Shiny Components 42 | 43 | Specific business needs and adjustments may be discussed. 44 | 45 | 46 | ## Duration 47 | 48 | The course has a duration of 20 hours. 49 | 50 | 51 | ## Beneficiaries 52 | 53 | This course is intended for data scientist, data analyst, statisticians, 54 | IT specialists, developers, project managers and business leaders who want 55 | to develop the most in-demand R & BI programming skills. 56 | 57 | -------------------------------------------------------------------------------- /general-infos/rcod_description_business.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcozanotti/rcoding-course/4482384dcccfe58a8a8b6207e02f3ae5016382d0/general-infos/rcod_description_business.pdf -------------------------------------------------------------------------------- /general-infos/rcod_description_business_ita.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Programmazione R & BI per il Business" 3 | author: "Marco Zanotti" 4 | date: "" 5 | output: 6 | pdf_document: default 7 | rmdformats::downcute: 8 | highlight: tango 9 | --- 10 | 11 | ```{r setup, include=FALSE} 12 | knitr::opts_chunk$set(echo = TRUE) 13 | ``` 14 | 15 | ## Obiettivi 16 | 17 | Il corso ha un duplice obiettivo: fornire competenze tecniche su 18 | programmazione e scripting per l'analisi dei dati con il linguaggio R; 19 | apprendere le tecniche per la creazione di dashboard per l'analisi dei dati. 20 | 21 | Durante la parte di **Programmazione R** verranno presentate le basi del 22 | linguaggio R e le moderne librerie di sviluppo. Durante la parte di **BI**, 23 | invece, verranno presentate le tecniche per la creazione di dashboard 24 | con l'utilizzo del framework **Shiny**. 25 | 26 | 27 | ## Contenuti 28 | 29 | * Tidyverse Basics 30 | * Tidyverse Wrangling 31 | * Tidyverse Visualization 32 | * Tidyverse Functional Programming 33 | * Tidymodels 34 | * Shiny Basics 35 | * Flexdashboard UI 36 | * Shiny Reactivity 37 | * Advanced Shiny Components 38 | 39 | Eventuali esigenze aziendali riguardo a tematiche di interesse 40 | possono essere valutate insieme al docente per rimodulare il 41 | corso sulla base dei desiderata del cliente. 42 | 43 | 44 | ## Durata 45 | 46 | Il corso ha una durata di 20 ore. 47 | 48 | 49 | ## Destinatari 50 | 51 | I destinatari di questo corso sono data scientist, data analyst, 52 | statistici, professionisti IT, sviluppatori, capi progetto che 53 | vogliono acquisire know how di programmazione R e BI per la data analysis. 54 | 55 | -------------------------------------------------------------------------------- /general-infos/rcod_description_business_ita.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcozanotti/rcoding-course/4482384dcccfe58a8a8b6207e02f3ae5016382d0/general-infos/rcod_description_business_ita.pdf -------------------------------------------------------------------------------- /general-infos/rcod_syllabus.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "R Coding \n - Course Syllabus -" 3 | author: "Marco Zanotti" 4 | date: "" 5 | output: 6 | pdf_document: default 7 | rmdformats::downcute: 8 | highlight: tango 9 | toc_depth: 2 10 | --- 11 | 12 | ```{r setup, include=FALSE} 13 | knitr::opts_chunk$set(echo = TRUE) 14 | ``` 15 | 16 | 17 | ## Lecture 0: Introduction to R 18 | 19 | **Mathematics**: 20 | - Vectors & Matrices 21 | - Matrix Algebra 22 | - Functions 23 | 24 | **Programming**: 25 | - What is a Programming Language? 26 | - Programming & Natural Language: Similarities and Differences 27 | 28 | **R**: 29 | - What is the R Language? 30 | - CRAN & Packages 31 | - Objects and Functions 32 | - R Environment 33 | - R messages 34 | - The R GUI 35 | - The RStudio IDE 36 | - Working with the RStudio IDE 37 | - How to write R code: style guidelines 38 | 39 | **Basic R**: 40 | - Assignment Operator and Variables 41 | - Some fundamental R commands 42 | - Arithmetic with R 43 | - Data Types 44 | - Data Structures 45 | - Accessing values & Subsetting 46 | 47 | **Intermediate R**: 48 | - Relational Operators 49 | - Logical Operators 50 | - Conditional subsetting 51 | - Conditional Statements 52 | - Looping Together 53 | - Functions 54 | 55 | **Extra Topics**: 56 | - Tidy Data 57 | - Importing Data with R 58 | - Statistics 59 | 60 | 61 | ## Lecture 0: RMarkdown 62 | 63 | **RMarkdown** 64 | 65 | 66 | ## Lecture 1: Tidyverse Basics 67 | 68 | **Tidyverse**: 69 | - Core Tidyverse 70 | - Import 71 | - Wrangle 72 | - Program 73 | - Model 74 | 75 | **R Code Evaluation Methods**: 76 | - Standard Evaluation 77 | - Non-Standard Evaluation 78 | - Tidy Evaluation 79 | 80 | **Pipe Operator**: 81 | - Basic Piping 82 | - Argument Placeholder 83 | - Re-using Placeholder for Attributes 84 | 85 | **Tibble**: 86 | - Tibble Structure 87 | 88 | **Stringr**: 89 | - Modify 90 | - Count Patterns 91 | - Detect Patterns 92 | - Extract Patterns 93 | - Removing & Replacing Patterns 94 | - Trimming 95 | - Testing Patterns 96 | - Base R Functions 97 | 98 | **Forcats**: 99 | - Count Levels 100 | - Order Levels 101 | - Modify Levels 102 | 103 | **Lubridate**: 104 | - Parse 105 | - Differences 106 | - Extract Time Periods 107 | - Round 108 | 109 | **Readr**: 110 | - Readr 111 | - Readxl 112 | 113 | 114 | ## Lecture 2: Tidyverse Wrangling 115 | 116 | **Tidyr**: 117 | - Column Headers 118 | - Multiple Variables 119 | - Variables in Rows and Columns 120 | - Multiple Types 121 | - One Type in Multiple Tables 122 | 123 | **Dplyr**: 124 | - Rows 125 | - Columns 126 | - Groups 127 | - Joins 128 | - Utilities 129 | 130 | **Dbplyr**: 131 | - Dplyr database backend 132 | 133 | 134 | ## Lecture 3: Tidyverse Visualization 135 | 136 | **Ggplot2**: 137 | - Initialization 138 | - Aesthetics Mappings 139 | - Geometries 140 | - Statistical Transformations 141 | - Position Adjustments 142 | - Coordinate Systems 143 | - Facets 144 | - Other Useful Graphical Designs 145 | 146 | **Plotly**: 147 | - Ggplotly conversion 148 | 149 | 150 | ## Lecture 4: Tidyverse Functional Programming 151 | 152 | **Purrr**: 153 | - For Loop vs Functionals 154 | - The Map Functions 155 | - The Power of Mapping 156 | - Mapping Over Multiple Arguments 157 | - Invoking Different Functions 158 | 159 | 160 | ## Lecture 5: Tidymodels 161 | 162 | **Tidymodels**: 163 | - Recipes 164 | - Engines 165 | - Modelling 166 | - Predicting 167 | 168 | 169 | ## Lecture 6: Flexdashboard UI 170 | 171 | **Static UI**: 172 | - Layouts 173 | - Components 174 | - Sizing 175 | - Paging 176 | - Storyboards 177 | - Sidebars 178 | 179 | 180 | ## Lecture 7: Flexdashboard UI - Shiny 181 | 182 | **Interactive UI**: 183 | - Basic Shiny Components 184 | - Inputs 185 | - Rendering Functions 186 | - Shinywidgets, Shinyjs, Shinymanager 187 | 188 | -------------------------------------------------------------------------------- /general-infos/rcod_syllabus.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcozanotti/rcoding-course/4482384dcccfe58a8a8b6207e02f3ae5016382d0/general-infos/rcod_syllabus.pdf -------------------------------------------------------------------------------- /rcoding-course.Rproj: -------------------------------------------------------------------------------- 1 | Version: 1.0 2 | 3 | RestoreWorkspace: Default 4 | SaveWorkspace: Default 5 | AlwaysSaveHistory: Default 6 | 7 | EnableCodeIndexing: Yes 8 | UseSpacesForTab: Yes 9 | NumSpacesForTab: 2 10 | Encoding: UTF-8 11 | 12 | RnwWeave: Sweave 13 | LaTeX: pdfLaTeX 14 | -------------------------------------------------------------------------------- /resources/advancedR.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcozanotti/rcoding-course/4482384dcccfe58a8a8b6207e02f3ae5016382d0/resources/advancedR.pdf -------------------------------------------------------------------------------- /resources/base-r.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcozanotti/rcoding-course/4482384dcccfe58a8a8b6207e02f3ae5016382d0/resources/base-r.pdf -------------------------------------------------------------------------------- /resources/data-transformation.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcozanotti/rcoding-course/4482384dcccfe58a8a8b6207e02f3ae5016382d0/resources/data-transformation.pdf -------------------------------------------------------------------------------- /resources/data-visualization.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcozanotti/rcoding-course/4482384dcccfe58a8a8b6207e02f3ae5016382d0/resources/data-visualization.pdf -------------------------------------------------------------------------------- /resources/dsR_cheatsheet.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcozanotti/rcoding-course/4482384dcccfe58a8a8b6207e02f3ae5016382d0/resources/dsR_cheatsheet.pdf -------------------------------------------------------------------------------- /resources/factors.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcozanotti/rcoding-course/4482384dcccfe58a8a8b6207e02f3ae5016382d0/resources/factors.pdf -------------------------------------------------------------------------------- /resources/lubridate.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcozanotti/rcoding-course/4482384dcccfe58a8a8b6207e02f3ae5016382d0/resources/lubridate.pdf -------------------------------------------------------------------------------- /resources/package-development.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcozanotti/rcoding-course/4482384dcccfe58a8a8b6207e02f3ae5016382d0/resources/package-development.pdf -------------------------------------------------------------------------------- /resources/parallel_computation.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcozanotti/rcoding-course/4482384dcccfe58a8a8b6207e02f3ae5016382d0/resources/parallel_computation.pdf -------------------------------------------------------------------------------- /resources/presentation/r_best_practices_files/libs/clipboard/clipboard.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * clipboard.js v2.0.10 3 | * https://clipboardjs.com/ 4 | * 5 | * Licensed MIT © Zeno Rocha 6 | */ 7 | !function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.ClipboardJS=e():t.ClipboardJS=e()}(this,function(){return n={686:function(t,e,n){"use strict";n.d(e,{default:function(){return o}});var e=n(279),i=n.n(e),e=n(370),u=n.n(e),e=n(817),c=n.n(e);function a(t){try{return document.execCommand(t)}catch(t){return}}var f=function(t){t=c()(t);return a("cut"),t};var l=function(t){var e,n,o,r=1 code.sourceCode > span { 40 | color: #003B4F; 41 | } 42 | 43 | code span { 44 | color: #003B4F; 45 | } 46 | 47 | code.sourceCode > span { 48 | color: #003B4F; 49 | } 50 | 51 | div.sourceCode, 52 | div.sourceCode pre.sourceCode { 53 | color: #003B4F; 54 | } 55 | 56 | code span.ot { 57 | color: #003B4F; 58 | } 59 | 60 | code span.at { 61 | color: #657422; 62 | } 63 | 64 | code span.ss { 65 | color: #20794D; 66 | } 67 | 68 | code span.an { 69 | color: #5E5E5E; 70 | } 71 | 72 | code span.fu { 73 | color: #4758AB; 74 | } 75 | 76 | code span.st { 77 | color: #20794D; 78 | } 79 | 80 | code span.cf { 81 | color: #003B4F; 82 | } 83 | 84 | code span.op { 85 | color: #5E5E5E; 86 | } 87 | 88 | code span.er { 89 | color: #AD0000; 90 | } 91 | 92 | code span.bn { 93 | color: #AD0000; 94 | } 95 | 96 | code span.al { 97 | color: #AD0000; 98 | } 99 | 100 | code span.va { 101 | color: #111111; 102 | } 103 | 104 | code span.pp { 105 | color: #AD0000; 106 | } 107 | 108 | code span.in { 109 | color: #5E5E5E; 110 | } 111 | 112 | code span.vs { 113 | color: #20794D; 114 | } 115 | 116 | code span.wa { 117 | color: #5E5E5E; 118 | font-style: italic; 119 | } 120 | 121 | code span.do { 122 | color: #5E5E5E; 123 | font-style: italic; 124 | } 125 | 126 | code span.im { 127 | color: #00769E; 128 | } 129 | 130 | code span.ch { 131 | color: #20794D; 132 | } 133 | 134 | code span.dt { 135 | color: #AD0000; 136 | } 137 | 138 | code span.fl { 139 | color: #AD0000; 140 | } 141 | 142 | code span.co { 143 | color: #5E5E5E; 144 | } 145 | 146 | code span.cv { 147 | color: #5E5E5E; 148 | font-style: italic; 149 | } 150 | 151 | code span.cn { 152 | color: #8f5902; 153 | } 154 | 155 | code span.sc { 156 | color: #5E5E5E; 157 | } 158 | 159 | code span.dv { 160 | color: #AD0000; 161 | } 162 | 163 | code span.kw { 164 | color: #003B4F; 165 | } 166 | 167 | .prevent-inlining { 168 | content: ".tippy-arrow{bottom:0}.tippy-box[data-placement^=top]>.tippy-arrow:before{bottom:-7px;left:0;border-width:8px 8px 0;border-top-color:initial;transform-origin:center top}.tippy-box[data-placement^=bottom]>.tippy-arrow{top:0}.tippy-box[data-placement^=bottom]>.tippy-arrow:before{top:-7px;left:0;border-width:0 8px 8px;border-bottom-color:initial;transform-origin:center bottom}.tippy-box[data-placement^=left]>.tippy-arrow{right:0}.tippy-box[data-placement^=left]>.tippy-arrow:before{border-width:8px 0 8px 8px;border-left-color:initial;right:-7px;transform-origin:center left}.tippy-box[data-placement^=right]>.tippy-arrow{left:0}.tippy-box[data-placement^=right]>.tippy-arrow:before{left:-7px;border-width:8px 8px 8px 0;border-right-color:initial;transform-origin:center right}.tippy-box[data-inertia][data-state=visible]{transition-timing-function:cubic-bezier(.54,1.5,.38,1.11)}.tippy-arrow{width:16px;height:16px;color:#333}.tippy-arrow:before{content:"";position:absolute;border-color:transparent;border-style:solid}.tippy-content{position:relative;padding:5px 9px;z-index:1} -------------------------------------------------------------------------------- /resources/presentation/r_best_practices_files/libs/revealjs/dist/reset.css: -------------------------------------------------------------------------------- 1 | /* http://meyerweb.com/eric/tools/css/reset/ 2 | v4.0 | 20180602 3 | License: none (public domain) 4 | */ 5 | 6 | html, body, div, span, applet, object, iframe, 7 | h1, h2, h3, h4, h5, h6, p, blockquote, pre, 8 | a, abbr, acronym, address, big, cite, code, 9 | del, dfn, em, img, ins, kbd, q, s, samp, 10 | small, strike, strong, sub, sup, tt, var, 11 | b, u, i, center, 12 | dl, dt, dd, ol, ul, li, 13 | fieldset, form, label, legend, 14 | table, caption, tbody, tfoot, thead, tr, th, td, 15 | article, aside, canvas, details, embed, 16 | figure, figcaption, footer, header, hgroup, 17 | main, menu, nav, output, ruby, section, summary, 18 | time, mark, audio, video { 19 | margin: 0; 20 | padding: 0; 21 | border: 0; 22 | font-size: 100%; 23 | font: inherit; 24 | vertical-align: baseline; 25 | } 26 | /* HTML5 display-role reset for older browsers */ 27 | article, aside, details, figcaption, figure, 28 | footer, header, hgroup, main, menu, nav, section { 29 | display: block; 30 | } -------------------------------------------------------------------------------- /resources/presentation/r_best_practices_files/libs/revealjs/dist/theme/fonts/league-gothic/LICENSE: -------------------------------------------------------------------------------- 1 | SIL Open Font License (OFL) 2 | http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=OFL 3 | -------------------------------------------------------------------------------- /resources/presentation/r_best_practices_files/libs/revealjs/dist/theme/fonts/league-gothic/league-gothic.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'League Gothic'; 3 | src: url('./league-gothic.eot'); 4 | src: url('./league-gothic.eot?#iefix') format('embedded-opentype'), 5 | url('./league-gothic.woff') format('woff'), 6 | url('./league-gothic.ttf') format('truetype'); 7 | 8 | font-weight: normal; 9 | font-style: normal; 10 | } 11 | -------------------------------------------------------------------------------- /resources/presentation/r_best_practices_files/libs/revealjs/dist/theme/fonts/league-gothic/league-gothic.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcozanotti/rcoding-course/4482384dcccfe58a8a8b6207e02f3ae5016382d0/resources/presentation/r_best_practices_files/libs/revealjs/dist/theme/fonts/league-gothic/league-gothic.eot -------------------------------------------------------------------------------- /resources/presentation/r_best_practices_files/libs/revealjs/dist/theme/fonts/league-gothic/league-gothic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcozanotti/rcoding-course/4482384dcccfe58a8a8b6207e02f3ae5016382d0/resources/presentation/r_best_practices_files/libs/revealjs/dist/theme/fonts/league-gothic/league-gothic.ttf -------------------------------------------------------------------------------- /resources/presentation/r_best_practices_files/libs/revealjs/dist/theme/fonts/league-gothic/league-gothic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcozanotti/rcoding-course/4482384dcccfe58a8a8b6207e02f3ae5016382d0/resources/presentation/r_best_practices_files/libs/revealjs/dist/theme/fonts/league-gothic/league-gothic.woff -------------------------------------------------------------------------------- /resources/presentation/r_best_practices_files/libs/revealjs/dist/theme/fonts/source-sans-pro/LICENSE: -------------------------------------------------------------------------------- 1 | SIL Open Font License 2 | 3 | Copyright 2010, 2012 Adobe Systems Incorporated (http://www.adobe.com/), with Reserved Font Name ‘Source’. All Rights Reserved. Source is a trademark of Adobe Systems Incorporated in the United States and/or other countries. 4 | 5 | This Font Software is licensed under the SIL Open Font License, Version 1.1. 6 | This license is copied below, and is also available with a FAQ at: http://scripts.sil.org/OFL 7 | 8 | —————————————————————————————- 9 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 10 | —————————————————————————————- 11 | 12 | PREAMBLE 13 | The goals of the Open Font License (OFL) are to stimulate worldwide development of collaborative font projects, to support the font creation efforts of academic and linguistic communities, and to provide a free and open framework in which fonts may be shared and improved in partnership with others. 14 | 15 | The OFL allows the licensed fonts to be used, studied, modified and redistributed freely as long as they are not sold by themselves. The fonts, including any derivative works, can be bundled, embedded, redistributed and/or sold with any software provided that any reserved names are not used by derivative works. The fonts and derivatives, however, cannot be released under any other type of license. The requirement for fonts to remain under this license does not apply to any document created using the fonts or their derivatives. 16 | 17 | DEFINITIONS 18 | “Font Software” refers to the set of files released by the Copyright Holder(s) under this license and clearly marked as such. This may include source files, build scripts and documentation. 19 | 20 | “Reserved Font Name” refers to any names specified as such after the copyright statement(s). 21 | 22 | “Original Version” refers to the collection of Font Software components as distributed by the Copyright Holder(s). 23 | 24 | “Modified Version” refers to any derivative made by adding to, deleting, or substituting—in part or in whole—any of the components of the Original Version, by changing formats or by porting the Font Software to a new environment. 25 | 26 | “Author” refers to any designer, engineer, programmer, technical writer or other person who contributed to the Font Software. 27 | 28 | PERMISSION & CONDITIONS 29 | Permission is hereby granted, free of charge, to any person obtaining a copy of the Font Software, to use, study, copy, merge, embed, modify, redistribute, and sell modified and unmodified copies of the Font Software, subject to the following conditions: 30 | 31 | 1) Neither the Font Software nor any of its individual components, in Original or Modified Versions, may be sold by itself. 32 | 33 | 2) Original or Modified Versions of the Font Software may be bundled, redistributed and/or sold with any software, provided that each copy contains the above copyright notice and this license. These can be included either as stand-alone text files, human-readable headers or in the appropriate machine-readable metadata fields within text or binary files as long as those fields can be easily viewed by the user. 34 | 35 | 3) No Modified Version of the Font Software may use the Reserved Font Name(s) unless explicit written permission is granted by the corresponding Copyright Holder. This restriction only applies to the primary font name as presented to the users. 36 | 37 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font Software shall not be used to promote, endorse or advertise any Modified Version, except to acknowledge the contribution(s) of the Copyright Holder(s) and the Author(s) or with their explicit written permission. 38 | 39 | 5) The Font Software, modified or unmodified, in part or in whole, must be distributed entirely under this license, and must not be distributed under any other license. The requirement for fonts to remain under this license does not apply to any document created using the Font Software. 40 | 41 | TERMINATION 42 | This license becomes null and void if any of the above conditions are not met. 43 | 44 | DISCLAIMER 45 | THE FONT SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE. -------------------------------------------------------------------------------- /resources/presentation/r_best_practices_files/libs/revealjs/dist/theme/fonts/source-sans-pro/source-sans-pro-italic.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcozanotti/rcoding-course/4482384dcccfe58a8a8b6207e02f3ae5016382d0/resources/presentation/r_best_practices_files/libs/revealjs/dist/theme/fonts/source-sans-pro/source-sans-pro-italic.eot -------------------------------------------------------------------------------- /resources/presentation/r_best_practices_files/libs/revealjs/dist/theme/fonts/source-sans-pro/source-sans-pro-italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcozanotti/rcoding-course/4482384dcccfe58a8a8b6207e02f3ae5016382d0/resources/presentation/r_best_practices_files/libs/revealjs/dist/theme/fonts/source-sans-pro/source-sans-pro-italic.ttf -------------------------------------------------------------------------------- /resources/presentation/r_best_practices_files/libs/revealjs/dist/theme/fonts/source-sans-pro/source-sans-pro-italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcozanotti/rcoding-course/4482384dcccfe58a8a8b6207e02f3ae5016382d0/resources/presentation/r_best_practices_files/libs/revealjs/dist/theme/fonts/source-sans-pro/source-sans-pro-italic.woff -------------------------------------------------------------------------------- /resources/presentation/r_best_practices_files/libs/revealjs/dist/theme/fonts/source-sans-pro/source-sans-pro-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcozanotti/rcoding-course/4482384dcccfe58a8a8b6207e02f3ae5016382d0/resources/presentation/r_best_practices_files/libs/revealjs/dist/theme/fonts/source-sans-pro/source-sans-pro-regular.eot -------------------------------------------------------------------------------- /resources/presentation/r_best_practices_files/libs/revealjs/dist/theme/fonts/source-sans-pro/source-sans-pro-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcozanotti/rcoding-course/4482384dcccfe58a8a8b6207e02f3ae5016382d0/resources/presentation/r_best_practices_files/libs/revealjs/dist/theme/fonts/source-sans-pro/source-sans-pro-regular.ttf -------------------------------------------------------------------------------- /resources/presentation/r_best_practices_files/libs/revealjs/dist/theme/fonts/source-sans-pro/source-sans-pro-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcozanotti/rcoding-course/4482384dcccfe58a8a8b6207e02f3ae5016382d0/resources/presentation/r_best_practices_files/libs/revealjs/dist/theme/fonts/source-sans-pro/source-sans-pro-regular.woff -------------------------------------------------------------------------------- /resources/presentation/r_best_practices_files/libs/revealjs/dist/theme/fonts/source-sans-pro/source-sans-pro-semibold.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcozanotti/rcoding-course/4482384dcccfe58a8a8b6207e02f3ae5016382d0/resources/presentation/r_best_practices_files/libs/revealjs/dist/theme/fonts/source-sans-pro/source-sans-pro-semibold.eot -------------------------------------------------------------------------------- /resources/presentation/r_best_practices_files/libs/revealjs/dist/theme/fonts/source-sans-pro/source-sans-pro-semibold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcozanotti/rcoding-course/4482384dcccfe58a8a8b6207e02f3ae5016382d0/resources/presentation/r_best_practices_files/libs/revealjs/dist/theme/fonts/source-sans-pro/source-sans-pro-semibold.ttf -------------------------------------------------------------------------------- /resources/presentation/r_best_practices_files/libs/revealjs/dist/theme/fonts/source-sans-pro/source-sans-pro-semibold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcozanotti/rcoding-course/4482384dcccfe58a8a8b6207e02f3ae5016382d0/resources/presentation/r_best_practices_files/libs/revealjs/dist/theme/fonts/source-sans-pro/source-sans-pro-semibold.woff -------------------------------------------------------------------------------- /resources/presentation/r_best_practices_files/libs/revealjs/dist/theme/fonts/source-sans-pro/source-sans-pro-semibolditalic.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcozanotti/rcoding-course/4482384dcccfe58a8a8b6207e02f3ae5016382d0/resources/presentation/r_best_practices_files/libs/revealjs/dist/theme/fonts/source-sans-pro/source-sans-pro-semibolditalic.eot -------------------------------------------------------------------------------- /resources/presentation/r_best_practices_files/libs/revealjs/dist/theme/fonts/source-sans-pro/source-sans-pro-semibolditalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcozanotti/rcoding-course/4482384dcccfe58a8a8b6207e02f3ae5016382d0/resources/presentation/r_best_practices_files/libs/revealjs/dist/theme/fonts/source-sans-pro/source-sans-pro-semibolditalic.ttf -------------------------------------------------------------------------------- /resources/presentation/r_best_practices_files/libs/revealjs/dist/theme/fonts/source-sans-pro/source-sans-pro-semibolditalic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcozanotti/rcoding-course/4482384dcccfe58a8a8b6207e02f3ae5016382d0/resources/presentation/r_best_practices_files/libs/revealjs/dist/theme/fonts/source-sans-pro/source-sans-pro-semibolditalic.woff -------------------------------------------------------------------------------- /resources/presentation/r_best_practices_files/libs/revealjs/dist/theme/fonts/source-sans-pro/source-sans-pro.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'Source Sans Pro'; 3 | src: url('./source-sans-pro-regular.eot'); 4 | src: url('./source-sans-pro-regular.eot?#iefix') format('embedded-opentype'), 5 | url('./source-sans-pro-regular.woff') format('woff'), 6 | url('./source-sans-pro-regular.ttf') format('truetype'); 7 | font-weight: normal; 8 | font-style: normal; 9 | } 10 | 11 | @font-face { 12 | font-family: 'Source Sans Pro'; 13 | src: url('./source-sans-pro-italic.eot'); 14 | src: url('./source-sans-pro-italic.eot?#iefix') format('embedded-opentype'), 15 | url('./source-sans-pro-italic.woff') format('woff'), 16 | url('./source-sans-pro-italic.ttf') format('truetype'); 17 | font-weight: normal; 18 | font-style: italic; 19 | } 20 | 21 | @font-face { 22 | font-family: 'Source Sans Pro'; 23 | src: url('./source-sans-pro-semibold.eot'); 24 | src: url('./source-sans-pro-semibold.eot?#iefix') format('embedded-opentype'), 25 | url('./source-sans-pro-semibold.woff') format('woff'), 26 | url('./source-sans-pro-semibold.ttf') format('truetype'); 27 | font-weight: 600; 28 | font-style: normal; 29 | } 30 | 31 | @font-face { 32 | font-family: 'Source Sans Pro'; 33 | src: url('./source-sans-pro-semibolditalic.eot'); 34 | src: url('./source-sans-pro-semibolditalic.eot?#iefix') format('embedded-opentype'), 35 | url('./source-sans-pro-semibolditalic.woff') format('woff'), 36 | url('./source-sans-pro-semibolditalic.ttf') format('truetype'); 37 | font-weight: 600; 38 | font-style: italic; 39 | } 40 | -------------------------------------------------------------------------------- /resources/presentation/r_best_practices_files/libs/revealjs/plugin/highlight/monokai.css: -------------------------------------------------------------------------------- 1 | /* 2 | Monokai style - ported by Luigi Maselli - http://grigio.org 3 | */ 4 | 5 | .hljs { 6 | display: block; 7 | overflow-x: auto; 8 | padding: 0.5em; 9 | background: #272822; 10 | color: #ddd; 11 | } 12 | 13 | .hljs-tag, 14 | .hljs-keyword, 15 | .hljs-selector-tag, 16 | .hljs-literal, 17 | .hljs-strong, 18 | .hljs-name { 19 | color: #f92672; 20 | } 21 | 22 | .hljs-code { 23 | color: #66d9ef; 24 | } 25 | 26 | .hljs-class .hljs-title { 27 | color: white; 28 | } 29 | 30 | .hljs-attribute, 31 | .hljs-symbol, 32 | .hljs-regexp, 33 | .hljs-link { 34 | color: #bf79db; 35 | } 36 | 37 | .hljs-string, 38 | .hljs-bullet, 39 | .hljs-subst, 40 | .hljs-title, 41 | .hljs-section, 42 | .hljs-emphasis, 43 | .hljs-type, 44 | .hljs-built_in, 45 | .hljs-builtin-name, 46 | .hljs-selector-attr, 47 | .hljs-selector-pseudo, 48 | .hljs-addition, 49 | .hljs-variable, 50 | .hljs-template-tag, 51 | .hljs-template-variable { 52 | color: #a6e22e; 53 | } 54 | 55 | .hljs-comment, 56 | .hljs-quote, 57 | .hljs-deletion, 58 | .hljs-meta { 59 | color: #75715e; 60 | } 61 | 62 | .hljs-keyword, 63 | .hljs-selector-tag, 64 | .hljs-literal, 65 | .hljs-doctag, 66 | .hljs-title, 67 | .hljs-section, 68 | .hljs-type, 69 | .hljs-selector-id { 70 | font-weight: bold; 71 | } 72 | -------------------------------------------------------------------------------- /resources/presentation/r_best_practices_files/libs/revealjs/plugin/highlight/zenburn.css: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Zenburn style from voldmar.ru (c) Vladimir Epifanov 4 | based on dark.css by Ivan Sagalaev 5 | 6 | */ 7 | 8 | .hljs { 9 | display: block; 10 | overflow-x: auto; 11 | padding: 0.5em; 12 | background: #3f3f3f; 13 | color: #dcdcdc; 14 | } 15 | 16 | .hljs-keyword, 17 | .hljs-selector-tag, 18 | .hljs-tag { 19 | color: #e3ceab; 20 | } 21 | 22 | .hljs-template-tag { 23 | color: #dcdcdc; 24 | } 25 | 26 | .hljs-number { 27 | color: #8cd0d3; 28 | } 29 | 30 | .hljs-variable, 31 | .hljs-template-variable, 32 | .hljs-attribute { 33 | color: #efdcbc; 34 | } 35 | 36 | .hljs-literal { 37 | color: #efefaf; 38 | } 39 | 40 | .hljs-subst { 41 | color: #8f8f8f; 42 | } 43 | 44 | .hljs-title, 45 | .hljs-name, 46 | .hljs-selector-id, 47 | .hljs-selector-class, 48 | .hljs-section, 49 | .hljs-type { 50 | color: #efef8f; 51 | } 52 | 53 | .hljs-symbol, 54 | .hljs-bullet, 55 | .hljs-link { 56 | color: #dca3a3; 57 | } 58 | 59 | .hljs-deletion, 60 | .hljs-string, 61 | .hljs-built_in, 62 | .hljs-builtin-name { 63 | color: #cc9393; 64 | } 65 | 66 | .hljs-addition, 67 | .hljs-comment, 68 | .hljs-quote, 69 | .hljs-meta { 70 | color: #7f9f7f; 71 | } 72 | 73 | 74 | .hljs-emphasis { 75 | font-style: italic; 76 | } 77 | 78 | .hljs-strong { 79 | font-weight: bold; 80 | } 81 | -------------------------------------------------------------------------------- /resources/presentation/r_best_practices_files/libs/revealjs/plugin/math/katex.js: -------------------------------------------------------------------------------- 1 | /** 2 | * A plugin which enables rendering of math equations inside 3 | * of reveal.js slides. Essentially a thin wrapper for KaTeX. 4 | * 5 | * @author Hakim El Hattab 6 | * @author Gerhard Burger 7 | */ 8 | export const KaTeX = () => { 9 | let deck; 10 | 11 | let defaultOptions = { 12 | version: 'latest', 13 | delimiters: [ 14 | {left: '$$', right: '$$', display: true}, // Note: $$ has to come before $ 15 | {left: '$', right: '$', display: false}, 16 | {left: '\\(', right: '\\)', display: false}, 17 | {left: '\\[', right: '\\]', display: true} 18 | ], 19 | ignoredTags: ['script', 'noscript', 'style', 'textarea', 'pre'] 20 | } 21 | 22 | const loadCss = src => { 23 | let link = document.createElement('link'); 24 | link.rel = 'stylesheet'; 25 | link.href = src; 26 | document.head.appendChild(link); 27 | }; 28 | 29 | /** 30 | * Loads a JavaScript file and returns a Promise for when it is loaded 31 | * Credits: https://aaronsmith.online/easily-load-an-external-script-using-javascript/ 32 | */ 33 | const loadScript = src => { 34 | return new Promise((resolve, reject) => { 35 | const script = document.createElement('script') 36 | script.type = 'text/javascript' 37 | script.onload = resolve 38 | script.onerror = reject 39 | script.src = src 40 | document.head.append(script) 41 | }) 42 | }; 43 | 44 | async function loadScripts(urls) { 45 | for(const url of urls) { 46 | await loadScript(url); 47 | } 48 | } 49 | 50 | return { 51 | id: 'katex', 52 | 53 | init: function (reveal) { 54 | 55 | deck = reveal; 56 | 57 | let revealOptions = deck.getConfig().katex || {}; 58 | 59 | let options = {...defaultOptions, ...revealOptions}; 60 | const {local, version, extensions, ...katexOptions} = options; 61 | 62 | let baseUrl = options.local || 'https://cdn.jsdelivr.net/npm/katex'; 63 | let versionString = options.local ? '' : '@' + options.version; 64 | 65 | let cssUrl = baseUrl + versionString + '/dist/katex.min.css'; 66 | let katexUrl = baseUrl + versionString + '/dist/katex.min.js'; 67 | let mhchemUrl = baseUrl + versionString + '/dist/contrib/mhchem.min.js' 68 | let karUrl = baseUrl + versionString + '/dist/contrib/auto-render.min.js'; 69 | 70 | let katexScripts = [katexUrl]; 71 | if(options.extensions && options.extensions.includes("mhchem")) { 72 | katexScripts.push(mhchemUrl); 73 | } 74 | katexScripts.push(karUrl); 75 | 76 | const renderMath = () => { 77 | renderMathInElement(reveal.getSlidesElement(), katexOptions); 78 | deck.layout(); 79 | } 80 | 81 | loadCss(cssUrl); 82 | 83 | // For some reason dynamically loading with defer attribute doesn't result in the expected behavior, the below code does 84 | loadScripts(katexScripts).then(() => { 85 | if( deck.isReady() ) { 86 | renderMath(); 87 | } 88 | else { 89 | deck.on( 'ready', renderMath.bind( this ) ); 90 | } 91 | }); 92 | 93 | } 94 | } 95 | 96 | }; 97 | -------------------------------------------------------------------------------- /resources/presentation/r_best_practices_files/libs/revealjs/plugin/math/mathjax2.js: -------------------------------------------------------------------------------- 1 | /** 2 | * A plugin which enables rendering of math equations inside 3 | * of reveal.js slides. Essentially a thin wrapper for MathJax. 4 | * 5 | * @author Hakim El Hattab 6 | */ 7 | export const MathJax2 = () => { 8 | 9 | // The reveal.js instance this plugin is attached to 10 | let deck; 11 | 12 | let defaultOptions = { 13 | messageStyle: 'none', 14 | tex2jax: { 15 | inlineMath: [ [ '$', '$' ], [ '\\(', '\\)' ] ], 16 | skipTags: [ 'script', 'noscript', 'style', 'textarea', 'pre' ] 17 | }, 18 | skipStartupTypeset: true 19 | }; 20 | 21 | function loadScript( url, callback ) { 22 | 23 | let head = document.querySelector( 'head' ); 24 | let script = document.createElement( 'script' ); 25 | script.type = 'text/javascript'; 26 | script.src = url; 27 | 28 | // Wrapper for callback to make sure it only fires once 29 | let finish = () => { 30 | if( typeof callback === 'function' ) { 31 | callback.call(); 32 | callback = null; 33 | } 34 | } 35 | 36 | script.onload = finish; 37 | 38 | // IE 39 | script.onreadystatechange = () => { 40 | if ( this.readyState === 'loaded' ) { 41 | finish(); 42 | } 43 | } 44 | 45 | // Normal browsers 46 | head.appendChild( script ); 47 | 48 | } 49 | 50 | return { 51 | id: 'mathjax2', 52 | 53 | init: function( reveal ) { 54 | 55 | deck = reveal; 56 | 57 | let revealOptions = deck.getConfig().mathjax2 || deck.getConfig().math || {}; 58 | 59 | let options = { ...defaultOptions, ...revealOptions }; 60 | let mathjax = options.mathjax || 'https://cdn.jsdelivr.net/npm/mathjax@2/MathJax.js'; 61 | let config = options.config || 'TeX-AMS_HTML-full'; 62 | let url = mathjax + '?config=' + config; 63 | 64 | options.tex2jax = { ...defaultOptions.tex2jax, ...revealOptions.tex2jax }; 65 | 66 | options.mathjax = options.config = null; 67 | 68 | loadScript( url, function() { 69 | 70 | MathJax.Hub.Config( options ); 71 | 72 | // Typeset followed by an immediate reveal.js layout since 73 | // the typesetting process could affect slide height 74 | MathJax.Hub.Queue( [ 'Typeset', MathJax.Hub, deck.getRevealElement() ] ); 75 | MathJax.Hub.Queue( deck.layout ); 76 | 77 | // Reprocess equations in slides when they turn visible 78 | deck.on( 'slidechanged', function( event ) { 79 | 80 | MathJax.Hub.Queue( [ 'Typeset', MathJax.Hub, event.currentSlide ] ); 81 | 82 | } ); 83 | 84 | } ); 85 | 86 | } 87 | } 88 | 89 | }; 90 | -------------------------------------------------------------------------------- /resources/presentation/r_best_practices_files/libs/revealjs/plugin/math/mathjax3.js: -------------------------------------------------------------------------------- 1 | /** 2 | * A plugin which enables rendering of math equations inside 3 | * of reveal.js slides. Essentially a thin wrapper for MathJax 3 4 | * 5 | * @author Hakim El Hattab 6 | * @author Gerhard Burger 7 | */ 8 | export const MathJax3 = () => { 9 | 10 | // The reveal.js instance this plugin is attached to 11 | let deck; 12 | 13 | let defaultOptions = { 14 | tex: { 15 | inlineMath: [ [ '$', '$' ], [ '\\(', '\\)' ] ] 16 | }, 17 | options: { 18 | skipHtmlTags: [ 'script', 'noscript', 'style', 'textarea', 'pre' ] 19 | }, 20 | startup: { 21 | ready: () => { 22 | MathJax.startup.defaultReady(); 23 | MathJax.startup.promise.then(() => { 24 | Reveal.layout(); 25 | }); 26 | } 27 | } 28 | }; 29 | 30 | function loadScript( url, callback ) { 31 | 32 | let script = document.createElement( 'script' ); 33 | script.type = "text/javascript" 34 | script.id = "MathJax-script" 35 | script.src = url; 36 | script.async = true 37 | 38 | // Wrapper for callback to make sure it only fires once 39 | script.onload = () => { 40 | if (typeof callback === 'function') { 41 | callback.call(); 42 | callback = null; 43 | } 44 | }; 45 | 46 | document.head.appendChild( script ); 47 | 48 | } 49 | 50 | return { 51 | id: 'mathjax3', 52 | init: function(reveal) { 53 | 54 | deck = reveal; 55 | 56 | let revealOptions = deck.getConfig().mathjax3 || {}; 57 | let options = {...defaultOptions, ...revealOptions}; 58 | options.tex = {...defaultOptions.tex, ...revealOptions.tex} 59 | options.options = {...defaultOptions.options, ...revealOptions.options} 60 | options.startup = {...defaultOptions.startup, ...revealOptions.startup} 61 | 62 | let url = options.mathjax || 'https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js'; 63 | options.mathjax = null; 64 | 65 | window.MathJax = options; 66 | 67 | loadScript( url, function() { 68 | // Reprocess equations in slides when they turn visible 69 | Reveal.addEventListener( 'slidechanged', function( event ) { 70 | MathJax.typeset(); 71 | } ); 72 | } ); 73 | 74 | } 75 | } 76 | 77 | }; 78 | -------------------------------------------------------------------------------- /resources/presentation/r_best_practices_files/libs/revealjs/plugin/math/plugin.js: -------------------------------------------------------------------------------- 1 | import {KaTeX} from "./katex"; 2 | import {MathJax2} from "./mathjax2"; 3 | import {MathJax3} from "./mathjax3"; 4 | 5 | const defaultTypesetter = MathJax2; 6 | 7 | /*! 8 | * This plugin is a wrapper for the MathJax2, 9 | * MathJax3 and KaTeX typesetter plugins. 10 | */ 11 | export default Plugin = Object.assign( defaultTypesetter(), { 12 | KaTeX, 13 | MathJax2, 14 | MathJax3 15 | } ); -------------------------------------------------------------------------------- /resources/presentation/r_best_practices_files/libs/revealjs/plugin/notes/plugin.js: -------------------------------------------------------------------------------- 1 | import speakerViewHTML from './speaker-view.html'; 2 | 3 | import { marked } from 'marked'; 4 | 5 | /** 6 | * Handles opening of and synchronization with the reveal.js 7 | * notes window. 8 | * 9 | * Handshake process: 10 | * 1. This window posts 'connect' to notes window 11 | * - Includes URL of presentation to show 12 | * 2. Notes window responds with 'connected' when it is available 13 | * 3. This window proceeds to send the current presentation state 14 | * to the notes window 15 | */ 16 | const Plugin = () => { 17 | 18 | let connectInterval; 19 | let speakerWindow = null; 20 | let deck; 21 | 22 | /** 23 | * Opens a new speaker view window. 24 | */ 25 | function openSpeakerWindow() { 26 | 27 | // If a window is already open, focus it 28 | if( speakerWindow && !speakerWindow.closed ) { 29 | speakerWindow.focus(); 30 | } 31 | else { 32 | speakerWindow = window.open( 'about:blank', 'reveal.js - Notes', 'width=1100,height=700' ); 33 | speakerWindow.marked = marked; 34 | speakerWindow.document.write( speakerViewHTML ); 35 | 36 | if( !speakerWindow ) { 37 | alert( 'Speaker view popup failed to open. Please make sure popups are allowed and reopen the speaker view.' ); 38 | return; 39 | } 40 | 41 | connect(); 42 | } 43 | 44 | } 45 | 46 | /** 47 | * Reconnect with an existing speaker view window. 48 | */ 49 | function reconnectSpeakerWindow( reconnectWindow ) { 50 | 51 | if( speakerWindow && !speakerWindow.closed ) { 52 | speakerWindow.focus(); 53 | } 54 | else { 55 | speakerWindow = reconnectWindow; 56 | window.addEventListener( 'message', onPostMessage ); 57 | onConnected(); 58 | } 59 | 60 | } 61 | 62 | /** 63 | * Connect to the notes window through a postmessage handshake. 64 | * Using postmessage enables us to work in situations where the 65 | * origins differ, such as a presentation being opened from the 66 | * file system. 67 | */ 68 | function connect() { 69 | 70 | const presentationURL = deck.getConfig().url; 71 | 72 | const url = typeof presentationURL === 'string' ? presentationURL : 73 | window.location.protocol + '//' + window.location.host + window.location.pathname + window.location.search; 74 | 75 | // Keep trying to connect until we get a 'connected' message back 76 | connectInterval = setInterval( function() { 77 | speakerWindow.postMessage( JSON.stringify( { 78 | namespace: 'reveal-notes', 79 | type: 'connect', 80 | state: deck.getState(), 81 | url 82 | } ), '*' ); 83 | }, 500 ); 84 | 85 | window.addEventListener( 'message', onPostMessage ); 86 | 87 | } 88 | 89 | /** 90 | * Calls the specified Reveal.js method with the provided argument 91 | * and then pushes the result to the notes frame. 92 | */ 93 | function callRevealApi( methodName, methodArguments, callId ) { 94 | 95 | let result = deck[methodName].apply( deck, methodArguments ); 96 | speakerWindow.postMessage( JSON.stringify( { 97 | namespace: 'reveal-notes', 98 | type: 'return', 99 | result, 100 | callId 101 | } ), '*' ); 102 | 103 | } 104 | 105 | /** 106 | * Posts the current slide data to the notes window. 107 | */ 108 | function post( event ) { 109 | 110 | let slideElement = deck.getCurrentSlide(), 111 | notesElement = slideElement.querySelector( 'aside.notes' ), 112 | fragmentElement = slideElement.querySelector( '.current-fragment' ); 113 | 114 | let messageData = { 115 | namespace: 'reveal-notes', 116 | type: 'state', 117 | notes: '', 118 | markdown: false, 119 | whitespace: 'normal', 120 | state: deck.getState() 121 | }; 122 | 123 | // Look for notes defined in a slide attribute 124 | if( slideElement.hasAttribute( 'data-notes' ) ) { 125 | messageData.notes = slideElement.getAttribute( 'data-notes' ); 126 | messageData.whitespace = 'pre-wrap'; 127 | } 128 | 129 | // Look for notes defined in a fragment 130 | if( fragmentElement ) { 131 | let fragmentNotes = fragmentElement.querySelector( 'aside.notes' ); 132 | if( fragmentNotes ) { 133 | notesElement = fragmentNotes; 134 | } 135 | else if( fragmentElement.hasAttribute( 'data-notes' ) ) { 136 | messageData.notes = fragmentElement.getAttribute( 'data-notes' ); 137 | messageData.whitespace = 'pre-wrap'; 138 | 139 | // In case there are slide notes 140 | notesElement = null; 141 | } 142 | } 143 | 144 | // Look for notes defined in an aside element 145 | if( notesElement ) { 146 | messageData.notes = notesElement.innerHTML; 147 | messageData.markdown = typeof notesElement.getAttribute( 'data-markdown' ) === 'string'; 148 | } 149 | 150 | speakerWindow.postMessage( JSON.stringify( messageData ), '*' ); 151 | 152 | } 153 | 154 | function onPostMessage( event ) { 155 | 156 | let data = JSON.parse( event.data ); 157 | if( data && data.namespace === 'reveal-notes' && data.type === 'connected' ) { 158 | clearInterval( connectInterval ); 159 | onConnected(); 160 | } 161 | else if( data && data.namespace === 'reveal-notes' && data.type === 'call' ) { 162 | callRevealApi( data.methodName, data.arguments, data.callId ); 163 | } 164 | 165 | } 166 | 167 | /** 168 | * Called once we have established a connection to the notes 169 | * window. 170 | */ 171 | function onConnected() { 172 | 173 | // Monitor events that trigger a change in state 174 | deck.on( 'slidechanged', post ); 175 | deck.on( 'fragmentshown', post ); 176 | deck.on( 'fragmenthidden', post ); 177 | deck.on( 'overviewhidden', post ); 178 | deck.on( 'overviewshown', post ); 179 | deck.on( 'paused', post ); 180 | deck.on( 'resumed', post ); 181 | 182 | // Post the initial state 183 | post(); 184 | 185 | } 186 | 187 | return { 188 | id: 'notes', 189 | 190 | init: function( reveal ) { 191 | 192 | deck = reveal; 193 | 194 | if( !/receiver/i.test( window.location.search ) ) { 195 | 196 | // If the there's a 'notes' query set, open directly 197 | if( window.location.search.match( /(\?|\&)notes/gi ) !== null ) { 198 | openSpeakerWindow(); 199 | } 200 | else { 201 | // Keep listening for speaker view hearbeats. If we receive a 202 | // heartbeat from an orphaned window, reconnect it. This ensures 203 | // that we remain connected to the notes even if the presentation 204 | // is reloaded. 205 | window.addEventListener( 'message', event => { 206 | 207 | if( !speakerWindow && typeof event.data === 'string' ) { 208 | let data; 209 | 210 | try { 211 | data = JSON.parse( event.data ); 212 | } 213 | catch( error ) {} 214 | 215 | if( data && data.namespace === 'reveal-notes' && data.type === 'heartbeat' ) { 216 | reconnectSpeakerWindow( event.source ); 217 | } 218 | } 219 | }); 220 | } 221 | 222 | // Open the notes when the 's' key is hit 223 | deck.addKeyBinding({keyCode: 83, key: 'S', description: 'Speaker notes view'}, function() { 224 | openSpeakerWindow(); 225 | } ); 226 | 227 | } 228 | 229 | }, 230 | 231 | open: openSpeakerWindow 232 | }; 233 | 234 | }; 235 | 236 | export default Plugin; 237 | -------------------------------------------------------------------------------- /resources/presentation/r_best_practices_files/libs/revealjs/plugin/pdf-export/pdfexport.js: -------------------------------------------------------------------------------- 1 | var PdfExport = ( function( _Reveal ){ 2 | 3 | var Reveal = _Reveal; 4 | var setStylesheet = null; 5 | var installAltKeyBindings = null; 6 | 7 | function getRevealJsPath(){ 8 | var regex = /\b[^/]+\/reveal.css$/i; 9 | var script = Array.from( document.querySelectorAll( 'link' ) ).find( function( e ){ 10 | return e.attributes.href && e.attributes.href.value.search( regex ) >= 0; 11 | }); 12 | if( !script ){ 13 | console.error( 'reveal.css could not be found in included elements. Did you rename this file?' ); 14 | return ''; 15 | } 16 | return script.attributes.href.value.replace( regex, '' ); 17 | } 18 | 19 | function setStylesheet3( pdfExport ){ 20 | var link = document.querySelector( '#print' ); 21 | if( !link ){ 22 | link = document.createElement( 'link' ); 23 | link.rel = 'stylesheet'; 24 | link.id = 'print'; 25 | document.querySelector( 'head' ).appendChild( link ); 26 | } 27 | var style = 'paper'; 28 | if( pdfExport ){ 29 | style = 'pdf'; 30 | } 31 | link.href = getRevealJsPath() + 'css/print/' + style + '.css'; 32 | } 33 | 34 | function setStylesheet4( pdfExport ){ 35 | } 36 | 37 | function installAltKeyBindings3(){ 38 | } 39 | 40 | function installAltKeyBindings4(){ 41 | if( isPrintingPDF() ){ 42 | var config = Reveal.getConfig(); 43 | var shortcut = config.pdfExportShortcut || 'E'; 44 | window.addEventListener( 'keydown', function( e ){ 45 | if( e.target.nodeName.toUpperCase() == 'BODY' 46 | && ( e.key.toUpperCase() == shortcut.toUpperCase() || e.keyCode == shortcut.toUpperCase().charCodeAt( 0 ) ) ){ 47 | e.preventDefault(); 48 | togglePdfExport(); 49 | return false; 50 | } 51 | }, true ); 52 | } 53 | } 54 | 55 | function isPrintingPDF(){ 56 | return ( /print-pdf/gi ).test( window.location.search ); 57 | } 58 | 59 | function togglePdfExport(){ 60 | var url_doc = new URL( document.URL ); 61 | var query_doc = new URLSearchParams( url_doc.searchParams ); 62 | if( isPrintingPDF() ){ 63 | query_doc.delete( 'print-pdf' ); 64 | }else{ 65 | query_doc.set( 'print-pdf', '' ); 66 | } 67 | url_doc.search = ( query_doc.toString() ? '?' + query_doc.toString() : '' ); 68 | window.location.href = url_doc.toString(); 69 | } 70 | 71 | function installKeyBindings(){ 72 | var config = Reveal.getConfig(); 73 | var shortcut = config.pdfExportShortcut || 'E'; 74 | Reveal.addKeyBinding({ 75 | keyCode: shortcut.toUpperCase().charCodeAt( 0 ), 76 | key: shortcut.toUpperCase(), 77 | description: 'PDF export mode' 78 | }, togglePdfExport ); 79 | installAltKeyBindings(); 80 | } 81 | 82 | function install(){ 83 | installKeyBindings(); 84 | setStylesheet( isPrintingPDF() ); 85 | } 86 | 87 | var Plugin = { 88 | } 89 | 90 | if( Reveal && Reveal.VERSION && Reveal.VERSION.length && Reveal.VERSION[ 0 ] == '3' ){ 91 | // reveal 3.x 92 | setStylesheet = setStylesheet3; 93 | installAltKeyBindings = installAltKeyBindings3; 94 | install(); 95 | }else{ 96 | // must be reveal 4.x 97 | setStylesheet = setStylesheet4; 98 | installAltKeyBindings = installAltKeyBindings4; 99 | Plugin.id = 'pdf-export'; 100 | Plugin.init = function( _Reveal ){ 101 | Reveal = _Reveal; 102 | install(); 103 | }; 104 | } 105 | 106 | return Plugin; 107 | 108 | })( Reveal ); 109 | -------------------------------------------------------------------------------- /resources/presentation/r_best_practices_files/libs/revealjs/plugin/pdf-export/plugin.yml: -------------------------------------------------------------------------------- 1 | name: PdfExport 2 | script: pdfexport.js 3 | -------------------------------------------------------------------------------- /resources/presentation/r_best_practices_files/libs/revealjs/plugin/quarto-line-highlight/line-highlight.css: -------------------------------------------------------------------------------- 1 | .reveal 2 | div.sourceCode 3 | pre 4 | code.has-line-highlights 5 | > span:not(.highlight-line) { 6 | opacity: 0.4; 7 | } 8 | 9 | .reveal pre.numberSource { 10 | padding-left: 0; 11 | } 12 | 13 | .reveal pre.numberSource code > span { 14 | left: -2.1em; 15 | } 16 | 17 | pre.numberSource code > span > a:first-child::before { 18 | left: -0.7em; 19 | } 20 | 21 | .reveal pre > code:not(:first-child).fragment { 22 | position: absolute; 23 | top: 0; 24 | left: 0; 25 | width: 100%; 26 | box-sizing: border-box; 27 | } 28 | 29 | .reveal div.sourceCode pre code { 30 | min-height: 100%; 31 | } 32 | -------------------------------------------------------------------------------- /resources/presentation/r_best_practices_files/libs/revealjs/plugin/quarto-line-highlight/plugin.yml: -------------------------------------------------------------------------------- 1 | # adapted from https://github.com/hakimel/reveal.js/tree/master/plugin/highlight 2 | name: QuartoLineHighlight 3 | script: line-highlight.js 4 | stylesheet: line-highlight.css 5 | -------------------------------------------------------------------------------- /resources/presentation/r_best_practices_files/libs/revealjs/plugin/quarto-support/footer.css: -------------------------------------------------------------------------------- 1 | .reveal .slide-logo { 2 | display: block; 3 | position: fixed; 4 | bottom: 0; 5 | right: 12px; 6 | max-height: 2.2rem; 7 | height: 100%; 8 | width: auto; 9 | } 10 | 11 | .reveal .footer { 12 | display: block; 13 | position: fixed; 14 | bottom: 18px; 15 | width: 100%; 16 | margin: 0 auto; 17 | text-align: center; 18 | font-size: 18px; 19 | z-index: 2; 20 | } 21 | 22 | .reveal .footer > * { 23 | margin-top: 0; 24 | margin-bottom: 0; 25 | } 26 | 27 | .reveal .slide .footer { 28 | display: none; 29 | } 30 | 31 | .reveal .slide-number { 32 | bottom: 10px; 33 | right: 10px; 34 | font-size: 16px; 35 | background-color: transparent; 36 | } 37 | 38 | .reveal.has-logo .slide-number { 39 | bottom: initial; 40 | top: 8px; 41 | right: 8px; 42 | } 43 | 44 | .reveal .slide-number .slide-number-delimiter { 45 | margin: 0; 46 | } 47 | 48 | .reveal .slide-menu-button { 49 | left: 8px; 50 | bottom: 8px; 51 | } 52 | 53 | .reveal .slide-chalkboard-buttons { 54 | position: fixed; 55 | left: 12px; 56 | bottom: 8px; 57 | z-index: 30; 58 | font-size: 24px; 59 | } 60 | 61 | .reveal .slide-chalkboard-buttons.slide-menu-offset { 62 | left: 54px; 63 | } 64 | 65 | .reveal .slide-chalkboard-buttons > span { 66 | margin-right: 14px; 67 | cursor: pointer; 68 | } 69 | 70 | @media screen and (max-width: 800px) { 71 | .reveal .slide-logo { 72 | max-height: 1.1rem; 73 | bottom: -2px; 74 | right: 10px; 75 | } 76 | .reveal .footer { 77 | font-size: 14px; 78 | bottom: 12px; 79 | } 80 | .reveal .slide-number { 81 | font-size: 12px; 82 | bottom: 7px; 83 | } 84 | .reveal .slide-menu-button .fas::before { 85 | height: 1.3rem; 86 | width: 1.3rem; 87 | vertical-align: -0.125em; 88 | background-size: 1.3rem 1.3rem; 89 | } 90 | 91 | .reveal .slide-chalkboard-buttons .fas::before { 92 | height: 0.95rem; 93 | width: 0.95rem; 94 | background-size: 0.95rem 0.95rem; 95 | vertical-align: -0em; 96 | } 97 | 98 | .reveal .slide-chalkboard-buttons.slide-menu-offset { 99 | left: 36px; 100 | } 101 | .reveal .slide-chalkboard-buttons > span { 102 | margin-right: 9px; 103 | } 104 | } 105 | 106 | html.print-pdf .reveal .slide-menu-button, 107 | html.print-pdf .reveal .slide-chalkboard-buttons { 108 | display: none; 109 | } 110 | -------------------------------------------------------------------------------- /resources/presentation/r_best_practices_files/libs/revealjs/plugin/quarto-support/plugin.yml: -------------------------------------------------------------------------------- 1 | name: QuartoSupport 2 | script: support.js 3 | stylesheet: footer.css 4 | config: 5 | smaller: false 6 | -------------------------------------------------------------------------------- /resources/presentation/r_best_practices_files/libs/revealjs/plugin/reveal-menu/plugin.yml: -------------------------------------------------------------------------------- 1 | name: RevealMenu 2 | script: [menu.js, quarto-menu.js] 3 | stylesheet: [menu.css, quarto-menu.css] 4 | config: 5 | menu: 6 | side: "left" 7 | useTextContentForMissingTitles: true 8 | markers: false 9 | loadIcons: false 10 | -------------------------------------------------------------------------------- /resources/presentation/r_best_practices_files/libs/revealjs/plugin/reveal-menu/quarto-menu.css: -------------------------------------------------------------------------------- 1 | .slide-menu-wrapper .slide-tool-item { 2 | display: block; 3 | text-align: left; 4 | padding: 10px 18px; 5 | color: #aaa; 6 | cursor: pointer; 7 | border-top: solid 1px #555; 8 | } 9 | 10 | .slide-menu-wrapper .slide-tool-item a { 11 | text-decoration: none; 12 | } 13 | 14 | .slide-menu-wrapper .slide-tool-item kbd { 15 | font-family: monospace; 16 | margin-right: 10px; 17 | padding: 3px 8px; 18 | color: inherit; 19 | border: 1px solid; 20 | border-radius: 5px; 21 | border-color: #555; 22 | } 23 | 24 | .slide-menu-wrapper .slide-menu-toolbar > li.active-toolbar-button { 25 | text-decoration: none; 26 | } 27 | 28 | .reveal .slide-menu-button { 29 | left: 8px; 30 | bottom: 8px; 31 | } 32 | 33 | .reveal .slide-menu-button .fas::before, 34 | .reveal .slide-chalkboard-buttons .fas::before, 35 | .slide-menu-wrapper .slide-menu-toolbar .fas::before { 36 | display: inline-block; 37 | height: 2.2rem; 38 | width: 2.2rem; 39 | content: ""; 40 | vertical-align: -0.125em; 41 | background-repeat: no-repeat; 42 | background-size: 2.2rem 2.2rem; 43 | } 44 | 45 | .reveal .slide-chalkboard-buttons .fas::before { 46 | height: 1.45rem; 47 | width: 1.45rem; 48 | background-size: 1.45rem 1.45rem; 49 | vertical-align: 0.1em; 50 | } 51 | 52 | .slide-menu-wrapper .slide-menu-toolbar .fas::before { 53 | height: 1.8rem; 54 | width: 1.8rem; 55 | background-size: 1.8rem 1.8rem; 56 | } 57 | 58 | .slide-menu-wrapper .slide-menu-toolbar .fa-images::before { 59 | background-image: url('data:image/svg+xml,'); 60 | } 61 | 62 | .slide-menu-wrapper .slide-menu-toolbar .fa-gear::before { 63 | background-image: url('data:image/svg+xml,'); 64 | } 65 | 66 | .slide-menu-wrapper .slide-menu-toolbar .fa-times::before { 67 | background-image: url('data:image/svg+xml,'); 68 | } 69 | -------------------------------------------------------------------------------- /resources/presentation/r_best_practices_files/libs/revealjs/plugin/reveal-menu/quarto-menu.js: -------------------------------------------------------------------------------- 1 | window.revealMenuToolHandler = function (handler) { 2 | return function (event) { 3 | event.preventDefault(); 4 | handler(); 5 | Reveal.getPlugin("menu").closeMenu(); 6 | }; 7 | }; 8 | 9 | window.RevealMenuToolHandlers = { 10 | fullscreen: revealMenuToolHandler(function () { 11 | const element = document.documentElement; 12 | const requestMethod = 13 | element.requestFullscreen || 14 | element.webkitRequestFullscreen || 15 | element.webkitRequestFullScreen || 16 | element.mozRequestFullScreen || 17 | element.msRequestFullscreen; 18 | if (requestMethod) { 19 | requestMethod.apply(element); 20 | } 21 | }), 22 | speakerMode: revealMenuToolHandler(function () { 23 | Reveal.getPlugin("notes").open(); 24 | }), 25 | keyboardHelp: revealMenuToolHandler(function () { 26 | Reveal.toggleHelp(true); 27 | }), 28 | overview: revealMenuToolHandler(function () { 29 | Reveal.toggleOverview(true); 30 | }), 31 | toggleChalkboard: revealMenuToolHandler(function () { 32 | RevealChalkboard.toggleChalkboard(); 33 | }), 34 | toggleNotesCanvas: revealMenuToolHandler(function () { 35 | RevealChalkboard.toggleNotesCanvas(); 36 | }), 37 | downloadDrawings: revealMenuToolHandler(function () { 38 | RevealChalkboard.download(); 39 | }), 40 | }; 41 | -------------------------------------------------------------------------------- /resources/presentation/r_best_practices_files/libs/revealjs/plugin/search/plugin.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Handles finding a text string anywhere in the slides and showing the next occurrence to the user 3 | * by navigatating to that slide and highlighting it. 4 | * 5 | * @author Jon Snyder , February 2013 6 | */ 7 | 8 | const Plugin = () => { 9 | 10 | // The reveal.js instance this plugin is attached to 11 | let deck; 12 | 13 | let searchElement; 14 | let searchButton; 15 | let searchInput; 16 | 17 | let matchedSlides; 18 | let currentMatchedIndex; 19 | let searchboxDirty; 20 | let hilitor; 21 | 22 | function render() { 23 | 24 | searchElement = document.createElement( 'div' ); 25 | searchElement.classList.add( 'searchbox' ); 26 | searchElement.style.position = 'absolute'; 27 | searchElement.style.top = '10px'; 28 | searchElement.style.right = '10px'; 29 | searchElement.style.zIndex = 10; 30 | 31 | //embedded base64 search icon Designed by Sketchdock - http://www.sketchdock.com/: 32 | searchElement.innerHTML = ` 33 | `; 34 | 35 | searchInput = searchElement.querySelector( '.searchinput' ); 36 | searchInput.style.width = '240px'; 37 | searchInput.style.fontSize = '14px'; 38 | searchInput.style.padding = '4px 6px'; 39 | searchInput.style.color = '#000'; 40 | searchInput.style.background = '#fff'; 41 | searchInput.style.borderRadius = '2px'; 42 | searchInput.style.border = '0'; 43 | searchInput.style.outline = '0'; 44 | searchInput.style.boxShadow = '0 2px 18px rgba(0, 0, 0, 0.2)'; 45 | searchInput.style['-webkit-appearance'] = 'none'; 46 | 47 | deck.getRevealElement().appendChild( searchElement ); 48 | 49 | // searchButton.addEventListener( 'click', function(event) { 50 | // doSearch(); 51 | // }, false ); 52 | 53 | searchInput.addEventListener( 'keyup', function( event ) { 54 | switch (event.keyCode) { 55 | case 13: 56 | event.preventDefault(); 57 | doSearch(); 58 | searchboxDirty = false; 59 | break; 60 | default: 61 | searchboxDirty = true; 62 | } 63 | }, false ); 64 | 65 | closeSearch(); 66 | 67 | } 68 | 69 | function openSearch() { 70 | if( !searchElement ) render(); 71 | 72 | searchElement.style.display = 'inline'; 73 | searchInput.focus(); 74 | searchInput.select(); 75 | } 76 | 77 | function closeSearch() { 78 | if( !searchElement ) render(); 79 | 80 | searchElement.style.display = 'none'; 81 | if(hilitor) hilitor.remove(); 82 | } 83 | 84 | function toggleSearch() { 85 | if( !searchElement ) render(); 86 | 87 | if (searchElement.style.display !== 'inline') { 88 | openSearch(); 89 | } 90 | else { 91 | closeSearch(); 92 | } 93 | } 94 | 95 | function doSearch() { 96 | //if there's been a change in the search term, perform a new search: 97 | if (searchboxDirty) { 98 | var searchstring = searchInput.value; 99 | 100 | if (searchstring === '') { 101 | if(hilitor) hilitor.remove(); 102 | matchedSlides = null; 103 | } 104 | else { 105 | //find the keyword amongst the slides 106 | hilitor = new Hilitor("slidecontent"); 107 | matchedSlides = hilitor.apply(searchstring); 108 | currentMatchedIndex = 0; 109 | } 110 | } 111 | 112 | if (matchedSlides) { 113 | //navigate to the next slide that has the keyword, wrapping to the first if necessary 114 | if (matchedSlides.length && (matchedSlides.length <= currentMatchedIndex)) { 115 | currentMatchedIndex = 0; 116 | } 117 | if (matchedSlides.length > currentMatchedIndex) { 118 | deck.slide(matchedSlides[currentMatchedIndex].h, matchedSlides[currentMatchedIndex].v); 119 | currentMatchedIndex++; 120 | } 121 | } 122 | } 123 | 124 | // Original JavaScript code by Chirp Internet: www.chirp.com.au 125 | // Please acknowledge use of this code by including this header. 126 | // 2/2013 jon: modified regex to display any match, not restricted to word boundaries. 127 | function Hilitor(id, tag) { 128 | 129 | var targetNode = document.getElementById(id) || document.body; 130 | var hiliteTag = tag || "EM"; 131 | var skipTags = new RegExp("^(?:" + hiliteTag + "|SCRIPT|FORM)$"); 132 | var colors = ["#ff6", "#a0ffff", "#9f9", "#f99", "#f6f"]; 133 | var wordColor = []; 134 | var colorIdx = 0; 135 | var matchRegex = ""; 136 | var matchingSlides = []; 137 | 138 | this.setRegex = function(input) 139 | { 140 | input = input.replace(/^[^\w]+|[^\w]+$/g, "").replace(/[^\w'-]+/g, "|"); 141 | matchRegex = new RegExp("(" + input + ")","i"); 142 | } 143 | 144 | this.getRegex = function() 145 | { 146 | return matchRegex.toString().replace(/^\/\\b\(|\)\\b\/i$/g, "").replace(/\|/g, " "); 147 | } 148 | 149 | // recursively apply word highlighting 150 | this.hiliteWords = function(node) 151 | { 152 | if(node == undefined || !node) return; 153 | if(!matchRegex) return; 154 | if(skipTags.test(node.nodeName)) return; 155 | 156 | if(node.hasChildNodes()) { 157 | for(var i=0; i < node.childNodes.length; i++) 158 | this.hiliteWords(node.childNodes[i]); 159 | } 160 | if(node.nodeType == 3) { // NODE_TEXT 161 | var nv, regs; 162 | if((nv = node.nodeValue) && (regs = matchRegex.exec(nv))) { 163 | //find the slide's section element and save it in our list of matching slides 164 | var secnode = node; 165 | while (secnode != null && secnode.nodeName != 'SECTION') { 166 | secnode = secnode.parentNode; 167 | } 168 | 169 | var slideIndex = deck.getIndices(secnode); 170 | var slidelen = matchingSlides.length; 171 | var alreadyAdded = false; 172 | for (var i=0; i < slidelen; i++) { 173 | if ( (matchingSlides[i].h === slideIndex.h) && (matchingSlides[i].v === slideIndex.v) ) { 174 | alreadyAdded = true; 175 | } 176 | } 177 | if (! alreadyAdded) { 178 | matchingSlides.push(slideIndex); 179 | } 180 | 181 | if(!wordColor[regs[0].toLowerCase()]) { 182 | wordColor[regs[0].toLowerCase()] = colors[colorIdx++ % colors.length]; 183 | } 184 | 185 | var match = document.createElement(hiliteTag); 186 | match.appendChild(document.createTextNode(regs[0])); 187 | match.style.backgroundColor = wordColor[regs[0].toLowerCase()]; 188 | match.style.fontStyle = "inherit"; 189 | match.style.color = "#000"; 190 | 191 | var after = node.splitText(regs.index); 192 | after.nodeValue = after.nodeValue.substring(regs[0].length); 193 | node.parentNode.insertBefore(match, after); 194 | } 195 | } 196 | }; 197 | 198 | // remove highlighting 199 | this.remove = function() 200 | { 201 | var arr = document.getElementsByTagName(hiliteTag); 202 | var el; 203 | while(arr.length && (el = arr[0])) { 204 | el.parentNode.replaceChild(el.firstChild, el); 205 | } 206 | }; 207 | 208 | // start highlighting at target node 209 | this.apply = function(input) 210 | { 211 | if(input == undefined || !input) return; 212 | this.remove(); 213 | this.setRegex(input); 214 | this.hiliteWords(targetNode); 215 | return matchingSlides; 216 | }; 217 | 218 | } 219 | 220 | return { 221 | 222 | id: 'search', 223 | 224 | init: reveal => { 225 | 226 | deck = reveal; 227 | deck.registerKeyboardShortcut( 'CTRL + Shift + F', 'Search' ); 228 | 229 | document.addEventListener( 'keydown', function( event ) { 230 | if( event.key == "F" && (event.ctrlKey || event.metaKey) ) { //Control+Shift+f 231 | event.preventDefault(); 232 | toggleSearch(); 233 | } 234 | }, false ); 235 | 236 | }, 237 | 238 | open: openSearch 239 | 240 | } 241 | }; 242 | 243 | export default Plugin; -------------------------------------------------------------------------------- /resources/presentation/r_best_practices_files/libs/revealjs/plugin/zoom/plugin.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * reveal.js Zoom plugin 3 | */ 4 | const Plugin = { 5 | 6 | id: 'zoom', 7 | 8 | init: function( reveal ) { 9 | 10 | reveal.getRevealElement().addEventListener( 'mousedown', function( event ) { 11 | var defaultModifier = /Linux/.test( window.navigator.platform ) ? 'ctrl' : 'alt'; 12 | 13 | var modifier = ( reveal.getConfig().zoomKey ? reveal.getConfig().zoomKey : defaultModifier ) + 'Key'; 14 | var zoomLevel = ( reveal.getConfig().zoomLevel ? reveal.getConfig().zoomLevel : 2 ); 15 | 16 | if( event[ modifier ] && !reveal.isOverview() ) { 17 | event.preventDefault(); 18 | 19 | zoom.to({ 20 | x: event.clientX, 21 | y: event.clientY, 22 | scale: zoomLevel, 23 | pan: false 24 | }); 25 | } 26 | } ); 27 | 28 | }, 29 | 30 | destroy: () => { 31 | 32 | zoom.reset(); 33 | 34 | } 35 | 36 | }; 37 | 38 | export default () => Plugin; 39 | 40 | /*! 41 | * zoom.js 0.3 (modified for use with reveal.js) 42 | * http://lab.hakim.se/zoom-js 43 | * MIT licensed 44 | * 45 | * Copyright (C) 2011-2014 Hakim El Hattab, http://hakim.se 46 | */ 47 | var zoom = (function(){ 48 | 49 | // The current zoom level (scale) 50 | var level = 1; 51 | 52 | // The current mouse position, used for panning 53 | var mouseX = 0, 54 | mouseY = 0; 55 | 56 | // Timeout before pan is activated 57 | var panEngageTimeout = -1, 58 | panUpdateInterval = -1; 59 | 60 | // Check for transform support so that we can fallback otherwise 61 | var supportsTransforms = 'transform' in document.body.style; 62 | 63 | if( supportsTransforms ) { 64 | // The easing that will be applied when we zoom in/out 65 | document.body.style.transition = 'transform 0.8s ease'; 66 | } 67 | 68 | // Zoom out if the user hits escape 69 | document.addEventListener( 'keyup', function( event ) { 70 | if( level !== 1 && event.keyCode === 27 ) { 71 | zoom.out(); 72 | } 73 | } ); 74 | 75 | // Monitor mouse movement for panning 76 | document.addEventListener( 'mousemove', function( event ) { 77 | if( level !== 1 ) { 78 | mouseX = event.clientX; 79 | mouseY = event.clientY; 80 | } 81 | } ); 82 | 83 | /** 84 | * Applies the CSS required to zoom in, prefers the use of CSS3 85 | * transforms but falls back on zoom for IE. 86 | * 87 | * @param {Object} rect 88 | * @param {Number} scale 89 | */ 90 | function magnify( rect, scale ) { 91 | 92 | var scrollOffset = getScrollOffset(); 93 | 94 | // Ensure a width/height is set 95 | rect.width = rect.width || 1; 96 | rect.height = rect.height || 1; 97 | 98 | // Center the rect within the zoomed viewport 99 | rect.x -= ( window.innerWidth - ( rect.width * scale ) ) / 2; 100 | rect.y -= ( window.innerHeight - ( rect.height * scale ) ) / 2; 101 | 102 | if( supportsTransforms ) { 103 | // Reset 104 | if( scale === 1 ) { 105 | document.body.style.transform = ''; 106 | } 107 | // Scale 108 | else { 109 | var origin = scrollOffset.x +'px '+ scrollOffset.y +'px', 110 | transform = 'translate('+ -rect.x +'px,'+ -rect.y +'px) scale('+ scale +')'; 111 | 112 | document.body.style.transformOrigin = origin; 113 | document.body.style.transform = transform; 114 | } 115 | } 116 | else { 117 | // Reset 118 | if( scale === 1 ) { 119 | document.body.style.position = ''; 120 | document.body.style.left = ''; 121 | document.body.style.top = ''; 122 | document.body.style.width = ''; 123 | document.body.style.height = ''; 124 | document.body.style.zoom = ''; 125 | } 126 | // Scale 127 | else { 128 | document.body.style.position = 'relative'; 129 | document.body.style.left = ( - ( scrollOffset.x + rect.x ) / scale ) + 'px'; 130 | document.body.style.top = ( - ( scrollOffset.y + rect.y ) / scale ) + 'px'; 131 | document.body.style.width = ( scale * 100 ) + '%'; 132 | document.body.style.height = ( scale * 100 ) + '%'; 133 | document.body.style.zoom = scale; 134 | } 135 | } 136 | 137 | level = scale; 138 | 139 | if( document.documentElement.classList ) { 140 | if( level !== 1 ) { 141 | document.documentElement.classList.add( 'zoomed' ); 142 | } 143 | else { 144 | document.documentElement.classList.remove( 'zoomed' ); 145 | } 146 | } 147 | } 148 | 149 | /** 150 | * Pan the document when the mosue cursor approaches the edges 151 | * of the window. 152 | */ 153 | function pan() { 154 | var range = 0.12, 155 | rangeX = window.innerWidth * range, 156 | rangeY = window.innerHeight * range, 157 | scrollOffset = getScrollOffset(); 158 | 159 | // Up 160 | if( mouseY < rangeY ) { 161 | window.scroll( scrollOffset.x, scrollOffset.y - ( 1 - ( mouseY / rangeY ) ) * ( 14 / level ) ); 162 | } 163 | // Down 164 | else if( mouseY > window.innerHeight - rangeY ) { 165 | window.scroll( scrollOffset.x, scrollOffset.y + ( 1 - ( window.innerHeight - mouseY ) / rangeY ) * ( 14 / level ) ); 166 | } 167 | 168 | // Left 169 | if( mouseX < rangeX ) { 170 | window.scroll( scrollOffset.x - ( 1 - ( mouseX / rangeX ) ) * ( 14 / level ), scrollOffset.y ); 171 | } 172 | // Right 173 | else if( mouseX > window.innerWidth - rangeX ) { 174 | window.scroll( scrollOffset.x + ( 1 - ( window.innerWidth - mouseX ) / rangeX ) * ( 14 / level ), scrollOffset.y ); 175 | } 176 | } 177 | 178 | function getScrollOffset() { 179 | return { 180 | x: window.scrollX !== undefined ? window.scrollX : window.pageXOffset, 181 | y: window.scrollY !== undefined ? window.scrollY : window.pageYOffset 182 | } 183 | } 184 | 185 | return { 186 | /** 187 | * Zooms in on either a rectangle or HTML element. 188 | * 189 | * @param {Object} options 190 | * - element: HTML element to zoom in on 191 | * OR 192 | * - x/y: coordinates in non-transformed space to zoom in on 193 | * - width/height: the portion of the screen to zoom in on 194 | * - scale: can be used instead of width/height to explicitly set scale 195 | */ 196 | to: function( options ) { 197 | 198 | // Due to an implementation limitation we can't zoom in 199 | // to another element without zooming out first 200 | if( level !== 1 ) { 201 | zoom.out(); 202 | } 203 | else { 204 | options.x = options.x || 0; 205 | options.y = options.y || 0; 206 | 207 | // If an element is set, that takes precedence 208 | if( !!options.element ) { 209 | // Space around the zoomed in element to leave on screen 210 | var padding = 20; 211 | var bounds = options.element.getBoundingClientRect(); 212 | 213 | options.x = bounds.left - padding; 214 | options.y = bounds.top - padding; 215 | options.width = bounds.width + ( padding * 2 ); 216 | options.height = bounds.height + ( padding * 2 ); 217 | } 218 | 219 | // If width/height values are set, calculate scale from those values 220 | if( options.width !== undefined && options.height !== undefined ) { 221 | options.scale = Math.max( Math.min( window.innerWidth / options.width, window.innerHeight / options.height ), 1 ); 222 | } 223 | 224 | if( options.scale > 1 ) { 225 | options.x *= options.scale; 226 | options.y *= options.scale; 227 | 228 | magnify( options, options.scale ); 229 | 230 | if( options.pan !== false ) { 231 | 232 | // Wait with engaging panning as it may conflict with the 233 | // zoom transition 234 | panEngageTimeout = setTimeout( function() { 235 | panUpdateInterval = setInterval( pan, 1000 / 60 ); 236 | }, 800 ); 237 | 238 | } 239 | } 240 | } 241 | }, 242 | 243 | /** 244 | * Resets the document zoom state to its default. 245 | */ 246 | out: function() { 247 | clearTimeout( panEngageTimeout ); 248 | clearInterval( panUpdateInterval ); 249 | 250 | magnify( { x: 0, y: 0 }, 1 ); 251 | 252 | level = 1; 253 | }, 254 | 255 | // Alias 256 | magnify: function( options ) { this.to( options ) }, 257 | reset: function() { this.out() }, 258 | 259 | zoomLevel: function() { 260 | return level; 261 | } 262 | } 263 | 264 | })(); 265 | -------------------------------------------------------------------------------- /resources/presentation/r_best_practices_files/libs/revealjs/plugin/zoom/zoom.esm.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * reveal.js Zoom plugin 3 | */ 4 | var e={id:"zoom",init:function(e){e.getRevealElement().addEventListener("mousedown",(function(n){var o=/Linux/.test(window.navigator.platform)?"ctrl":"alt",i=(e.getConfig().zoomKey?e.getConfig().zoomKey:o)+"Key",d=e.getConfig().zoomLevel?e.getConfig().zoomLevel:2;n[i]&&!e.isOverview()&&(n.preventDefault(),t.to({x:n.clientX,y:n.clientY,scale:d,pan:!1}))}))},destroy:function(){t.reset()}},t=function(){var e=1,n=0,o=0,i=-1,d=-1,l="transform"in document.body.style;function s(t,n){var o=r();if(t.width=t.width||1,t.height=t.height||1,t.x-=(window.innerWidth-t.width*n)/2,t.y-=(window.innerHeight-t.height*n)/2,l)if(1===n)document.body.style.transform="";else{var i=o.x+"px "+o.y+"px",d="translate("+-t.x+"px,"+-t.y+"px) scale("+n+")";document.body.style.transformOrigin=i,document.body.style.transform=d}else 1===n?(document.body.style.position="",document.body.style.left="",document.body.style.top="",document.body.style.width="",document.body.style.height="",document.body.style.zoom=""):(document.body.style.position="relative",document.body.style.left=-(o.x+t.x)/n+"px",document.body.style.top=-(o.y+t.y)/n+"px",document.body.style.width=100*n+"%",document.body.style.height=100*n+"%",document.body.style.zoom=n);e=n,document.documentElement.classList&&(1!==e?document.documentElement.classList.add("zoomed"):document.documentElement.classList.remove("zoomed"))}function c(){var t=.12*window.innerWidth,i=.12*window.innerHeight,d=r();owindow.innerHeight-i&&window.scroll(d.x,d.y+(1-(window.innerHeight-o)/i)*(14/e)),nwindow.innerWidth-t&&window.scroll(d.x+(1-(window.innerWidth-n)/t)*(14/e),d.y)}function r(){return{x:void 0!==window.scrollX?window.scrollX:window.pageXOffset,y:void 0!==window.scrollY?window.scrollY:window.pageYOffset}}return l&&(document.body.style.transition="transform 0.8s ease"),document.addEventListener("keyup",(function(n){1!==e&&27===n.keyCode&&t.out()})),document.addEventListener("mousemove",(function(t){1!==e&&(n=t.clientX,o=t.clientY)})),{to:function(n){if(1!==e)t.out();else{if(n.x=n.x||0,n.y=n.y||0,n.element){var o=n.element.getBoundingClientRect();n.x=o.left-20,n.y=o.top-20,n.width=o.width+40,n.height=o.height+40}void 0!==n.width&&void 0!==n.height&&(n.scale=Math.max(Math.min(window.innerWidth/n.width,window.innerHeight/n.height),1)),n.scale>1&&(n.x*=n.scale,n.y*=n.scale,s(n,n.scale),!1!==n.pan&&(i=setTimeout((function(){d=setInterval(c,1e3/60)}),800)))}},out:function(){clearTimeout(i),clearInterval(d),s({x:0,y:0},1),e=1},magnify:function(e){this.to(e)},reset:function(){this.out()},zoomLevel:function(){return e}}}();export default function(){return e} 5 | -------------------------------------------------------------------------------- /resources/presentation/r_best_practices_files/libs/revealjs/plugin/zoom/zoom.js: -------------------------------------------------------------------------------- 1 | !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e="undefined"!=typeof globalThis?globalThis:e||self).RevealZoom=t()}(this,(function(){"use strict"; 2 | /*! 3 | * reveal.js Zoom plugin 4 | */var e={id:"zoom",init:function(e){e.getRevealElement().addEventListener("mousedown",(function(o){var n=/Linux/.test(window.navigator.platform)?"ctrl":"alt",i=(e.getConfig().zoomKey?e.getConfig().zoomKey:n)+"Key",d=e.getConfig().zoomLevel?e.getConfig().zoomLevel:2;o[i]&&!e.isOverview()&&(o.preventDefault(),t.to({x:o.clientX,y:o.clientY,scale:d,pan:!1}))}))},destroy:function(){t.reset()}},t=function(){var e=1,o=0,n=0,i=-1,d=-1,l="transform"in document.body.style;function s(t,o){var n=r();if(t.width=t.width||1,t.height=t.height||1,t.x-=(window.innerWidth-t.width*o)/2,t.y-=(window.innerHeight-t.height*o)/2,l)if(1===o)document.body.style.transform="";else{var i=n.x+"px "+n.y+"px",d="translate("+-t.x+"px,"+-t.y+"px) scale("+o+")";document.body.style.transformOrigin=i,document.body.style.transform=d}else 1===o?(document.body.style.position="",document.body.style.left="",document.body.style.top="",document.body.style.width="",document.body.style.height="",document.body.style.zoom=""):(document.body.style.position="relative",document.body.style.left=-(n.x+t.x)/o+"px",document.body.style.top=-(n.y+t.y)/o+"px",document.body.style.width=100*o+"%",document.body.style.height=100*o+"%",document.body.style.zoom=o);e=o,document.documentElement.classList&&(1!==e?document.documentElement.classList.add("zoomed"):document.documentElement.classList.remove("zoomed"))}function c(){var t=.12*window.innerWidth,i=.12*window.innerHeight,d=r();nwindow.innerHeight-i&&window.scroll(d.x,d.y+(1-(window.innerHeight-n)/i)*(14/e)),owindow.innerWidth-t&&window.scroll(d.x+(1-(window.innerWidth-o)/t)*(14/e),d.y)}function r(){return{x:void 0!==window.scrollX?window.scrollX:window.pageXOffset,y:void 0!==window.scrollY?window.scrollY:window.pageYOffset}}return l&&(document.body.style.transition="transform 0.8s ease"),document.addEventListener("keyup",(function(o){1!==e&&27===o.keyCode&&t.out()})),document.addEventListener("mousemove",(function(t){1!==e&&(o=t.clientX,n=t.clientY)})),{to:function(o){if(1!==e)t.out();else{if(o.x=o.x||0,o.y=o.y||0,o.element){var n=o.element.getBoundingClientRect();o.x=n.left-20,o.y=n.top-20,o.width=n.width+40,o.height=n.height+40}void 0!==o.width&&void 0!==o.height&&(o.scale=Math.max(Math.min(window.innerWidth/o.width,window.innerHeight/o.height),1)),o.scale>1&&(o.x*=o.scale,o.y*=o.scale,s(o,o.scale),!1!==o.pan&&(i=setTimeout((function(){d=setInterval(c,1e3/60)}),800)))}},out:function(){clearTimeout(i),clearInterval(d),s({x:0,y:0},1),e=1},magnify:function(e){this.to(e)},reset:function(){this.out()},zoomLevel:function(){return e}}}();return function(){return e}})); 5 | -------------------------------------------------------------------------------- /resources/purrr.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcozanotti/rcoding-course/4482384dcccfe58a8a8b6207e02f3ae5016382d0/resources/purrr.pdf -------------------------------------------------------------------------------- /resources/regex.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcozanotti/rcoding-course/4482384dcccfe58a8a8b6207e02f3ae5016382d0/resources/regex.pdf -------------------------------------------------------------------------------- /resources/rmarkdown.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcozanotti/rcoding-course/4482384dcccfe58a8a8b6207e02f3ae5016382d0/resources/rmarkdown.pdf -------------------------------------------------------------------------------- /resources/rstudio-ide.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcozanotti/rcoding-course/4482384dcccfe58a8a8b6207e02f3ae5016382d0/resources/rstudio-ide.pdf -------------------------------------------------------------------------------- /resources/shiny.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcozanotti/rcoding-course/4482384dcccfe58a8a8b6207e02f3ae5016382d0/resources/shiny.pdf -------------------------------------------------------------------------------- /resources/strings.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcozanotti/rcoding-course/4482384dcccfe58a8a8b6207e02f3ae5016382d0/resources/strings.pdf -------------------------------------------------------------------------------- /resources/tidyr.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcozanotti/rcoding-course/4482384dcccfe58a8a8b6207e02f3ae5016382d0/resources/tidyr.pdf --------------------------------------------------------------------------------