class: center, middle, inverse, title-slide # flipbookr building blocks ## what lies beneath ### Gina Reynolds, December 2019 --- ```r library(flipbookr) library(tidyverse) ``` ``` ## ── Attaching packages ────────────────────────────────────── tidyverse 1.2.1 ── ``` ``` ## ✓ ggplot2 3.2.1 ✓ purrr 0.3.3 ## ✓ tibble 2.1.3 ✓ dplyr 0.8.3 ## ✓ tidyr 1.0.0 ✓ stringr 1.4.0 ## ✓ readr 1.3.1 ✓ forcats 0.4.0 ``` ``` ## ── Conflicts ───────────────────────────────────────── tidyverse_conflicts() ── ## x dplyr::filter() masks stats::filter() ## x dplyr::lag() masks stats::lag() ``` ```r knitr::opts_chunk$set(fig.width = 6, message = F, warning = F, comment = "", cache = F) ``` ```r xaringanthemer::mono_accent( code_highlight_color = "rgba(255, 211, 0, .35)", base_color = "#03418A") ``` ``` [1] "xaringan-themer.css" ``` --- We'll use the tidyverse packages to create a pipeline, and flipbookr to walk through the steps in this pipeline. ```r library(tidyverse) library(flipbookr) ``` The input code to be "flipbookified" is below: ```r cars %>% #BREAK ggplot() + aes(x = speed) + aes(color = speed) + #BREAK2 geom_rug() #BREAK ``` --- Let's first look at the result of using flipbookr to bring this code. --- class: split-40 count: false .column[.content[ ```r *cars ``` ]] .column[.content[ ``` speed dist 1 4 2 2 4 10 3 7 4 4 7 22 5 8 16 6 9 10 7 10 18 8 10 26 9 10 34 10 11 17 11 11 28 12 12 14 13 12 20 14 12 24 15 12 28 16 13 26 17 13 34 18 13 34 19 13 46 20 14 26 21 14 36 22 14 60 23 14 80 24 15 20 25 15 26 26 15 54 27 16 32 28 16 40 29 17 32 30 17 40 31 17 50 32 18 42 33 18 56 34 18 76 35 18 84 36 19 36 37 19 46 38 19 68 39 20 32 40 20 48 41 20 52 42 20 56 43 20 64 44 22 66 45 23 54 46 24 70 47 24 92 48 24 93 49 24 120 50 25 85 ``` ]] --- class: split-40 count: false .column[.content[ ```r cars %>% * ggplot() ``` ]] .column[.content[ ![](flipbookr_building_blocks_files/figure-html/cars_plot_auto_2_output-1.png)<!-- --> ]] --- class: split-40 count: false .column[.content[ ```r cars %>% ggplot() + * aes(x = speed) ``` ]] .column[.content[ ![](flipbookr_building_blocks_files/figure-html/cars_plot_auto_3_output-1.png)<!-- --> ]] --- class: split-40 count: false .column[.content[ ```r cars %>% ggplot() + aes(x = speed) + * aes(color = speed) ``` ]] .column[.content[ ![](flipbookr_building_blocks_files/figure-html/cars_plot_auto_4_output-1.png)<!-- --> ]] --- class: split-40 count: false .column[.content[ ```r cars %>% ggplot() + aes(x = speed) + aes(color = speed) + * geom_rug() ``` ]] .column[.content[ ![](flipbookr_building_blocks_files/figure-html/cars_plot_auto_5_output-1.png)<!-- --> ]] --- # Move to automate ## 1. *Automatically* parsing a block of input code -- ## 2. *Automatically* reconstructing code into partial builds -- ## 3. *Automatically* delivered on a presentation platform (here Xaringan) --- Now, lets look at some of the functions working in the background. --- ## 1. *Automatically* parsing a block of input code --- class: split-40 count: false .column[.content[ ```r * # The name of the code chunk containing code to be flipped *flipbookr:::create_code() ``` ]] .column[.content[ ``` [1] "cars %>% # the data #REVEAL\n filter(speed > 4) %>% # subset\n ggplot() + # pipe to ggplot\n aes(x = speed) +\n aes(y = dist) +\n # Describing what follows\n geom_point(alpha = .3) + #REVEAL\n geom_point(alpha = 1) + #BREAK2\n geom_jitter(alpha = .5) + #BREAK3\n aes(color =\n speed > 14\n ) %+%\n cars ->\n my_plot #BREAK" ``` ]] --- class: split-40 count: false .column[.content[ ```r # The name of the code chunk containing code to be flipped flipbookr:::create_code() %>% * # parse that code into code, pipes, comments, etc * flipbookr:::code_parse() ``` ]] .column[.content[ ``` # A tibble: 14 x 8 line raw_code code connector comment auto user non_seq <int> <chr> <chr> <chr> <chr> <lgl> <lgl> <dbl> 1 1 "cars %>% … "cars " "%>%" "# the dat… TRUE FALSE 1 2 2 " filter(speed … " filter(… "%>%" "# subset" TRUE FALSE 1 3 3 " ggplot() + … " ggplot(… "+" "# pipe to… TRUE FALSE 1 4 4 " aes(x = speed… " aes(x =… "+" "" TRUE FALSE 1 5 5 " aes(y = dist)… " aes(y =… "+" "" TRUE FALSE 1 6 6 " # Describing … "" "" "# Describ… FALSE FALSE 1 7 7 " geom_point(al… " geom_po… "+" "#REVEAL" TRUE FALSE 1 8 8 " geom_point(al… " geom_po… "+" "" TRUE FALSE 2 9 9 " geom_jitter(a… " geom_ji… "+" "" TRUE FALSE 3 10 10 " aes(color =" " aes(col… "" "" FALSE FALSE 1 11 11 " speed > 14" " speed >… "" "" FALSE FALSE 1 12 12 " ) %+%" " ) " "%+%" "" TRUE FALSE 1 13 13 " cars ->" " cars " "->" "" TRUE FALSE 1 14 14 " my_plot #BRE… " my_plot" "" "" TRUE TRUE 1 ``` ]] --- ## 2. *Automatically* reconstructing code into partial builds --- ## First calculate which lines need to be shown and highlighted in each frame of the mini flipbook. -- A list is returned where each frame corresponds to the element in the list. -- Within the list numbers of the lines of code for each frame is returned. -- This is dependent on what the user has set for the break_type. -- Appropriate highlighting is calculated based on what appears in previous frame and what is new in current frame. --- class: split-40 count: false .column[.content[ ```r * # The name of the code chunk containing code to be flipped *flipbookr:::create_code() ``` ]] .column[.content[ ``` [1] "cars %>% # the data #REVEAL\n filter(speed > 4) %>% # subset\n ggplot() + # pipe to ggplot\n aes(x = speed) +\n aes(y = dist) +\n # Describing what follows\n geom_point(alpha = .3) + #REVEAL\n geom_point(alpha = 1) + #BREAK2\n geom_jitter(alpha = .5) + #BREAK3\n aes(color =\n speed > 14\n ) %+%\n cars ->\n my_plot #BREAK" ``` ]] --- class: split-40 count: false .column[.content[ ```r # The name of the code chunk containing code to be flipped flipbookr:::create_code() %>% * # parse that code into code, pipes, comments, etc * flipbookr:::code_parse() ``` ]] .column[.content[ ``` # A tibble: 14 x 8 line raw_code code connector comment auto user non_seq <int> <chr> <chr> <chr> <chr> <lgl> <lgl> <dbl> 1 1 "cars %>% … "cars " "%>%" "# the dat… TRUE FALSE 1 2 2 " filter(speed … " filter(… "%>%" "# subset" TRUE FALSE 1 3 3 " ggplot() + … " ggplot(… "+" "# pipe to… TRUE FALSE 1 4 4 " aes(x = speed… " aes(x =… "+" "" TRUE FALSE 1 5 5 " aes(y = dist)… " aes(y =… "+" "" TRUE FALSE 1 6 6 " # Describing … "" "" "# Describ… FALSE FALSE 1 7 7 " geom_point(al… " geom_po… "+" "#REVEAL" TRUE FALSE 1 8 8 " geom_point(al… " geom_po… "+" "" TRUE FALSE 2 9 9 " geom_jitter(a… " geom_ji… "+" "" TRUE FALSE 3 10 10 " aes(color =" " aes(col… "" "" FALSE FALSE 1 11 11 " speed > 14" " speed >… "" "" FALSE FALSE 1 12 12 " ) %+%" " ) " "%+%" "" TRUE FALSE 1 13 13 " cars ->" " cars " "->" "" TRUE FALSE 1 14 14 " my_plot #BRE… " my_plot" "" "" TRUE TRUE 1 ``` ]] --- class: split-40 count: false .column[.content[ ```r # The name of the code chunk containing code to be flipped flipbookr:::create_code() %>% # parse that code into code, pipes, comments, etc flipbookr:::code_parse() %>% * # calculate which lines to show for each frame * flipbookr:::parsed_calc_show(break_type = "non_seq") ``` ]] .column[.content[ ``` [[1]] [1] 1 2 3 4 5 6 7 10 11 12 13 14 [[2]] [1] 1 2 3 4 5 6 7 8 10 11 12 13 14 [[3]] [1] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 ``` ]] --- class: split-40 count: false .column[.content[ ```r # The name of the code chunk containing code to be flipped flipbookr:::create_code() %>% # parse that code into code, pipes, comments, etc flipbookr:::code_parse() %>% # calculate which lines to show for each frame flipbookr:::parsed_calc_show(break_type = "non_seq") %>% * # calculate highlighting that is needed for each frame * flipbookr:::shown_lines_calc_highlight(break_type = "non_seq") ``` ]] .column[.content[ ``` [[1]] integer(0) [[2]] [1] 8 [[3]] [1] 9 ``` ]] --- ## Based on the calculation, return partial code builds for each frame of the flipbook. -- We create a list of strings of the partial builds, which can be saved as an object `code_seq`. --- class: split-40 count: false .column[.content[ ```r * # from the parsed code * # and using calc for which lines to display ... *flipbookr:::create_code() ``` ]] .column[.content[ ``` [1] "cars %>% # the data #REVEAL\n filter(speed > 4) %>% # subset\n ggplot() + # pipe to ggplot\n aes(x = speed) +\n aes(y = dist) +\n # Describing what follows\n geom_point(alpha = .3) + #REVEAL\n geom_point(alpha = 1) + #BREAK2\n geom_jitter(alpha = .5) + #BREAK3\n aes(color =\n speed > 14\n ) %+%\n cars ->\n my_plot #BREAK" ``` ]] --- class: split-40 count: false .column[.content[ ```r # from the parsed code # and using calc for which lines to display ... flipbookr:::create_code() %>% * flipbookr:::code_parse() ``` ]] .column[.content[ ``` # A tibble: 14 x 8 line raw_code code connector comment auto user non_seq <int> <chr> <chr> <chr> <chr> <lgl> <lgl> <dbl> 1 1 "cars %>% … "cars " "%>%" "# the dat… TRUE FALSE 1 2 2 " filter(speed … " filter(… "%>%" "# subset" TRUE FALSE 1 3 3 " ggplot() + … " ggplot(… "+" "# pipe to… TRUE FALSE 1 4 4 " aes(x = speed… " aes(x =… "+" "" TRUE FALSE 1 5 5 " aes(y = dist)… " aes(y =… "+" "" TRUE FALSE 1 6 6 " # Describing … "" "" "# Describ… FALSE FALSE 1 7 7 " geom_point(al… " geom_po… "+" "#REVEAL" TRUE FALSE 1 8 8 " geom_point(al… " geom_po… "+" "" TRUE FALSE 2 9 9 " geom_jitter(a… " geom_ji… "+" "" TRUE FALSE 3 10 10 " aes(color =" " aes(col… "" "" FALSE FALSE 1 11 11 " speed > 14" " speed >… "" "" FALSE FALSE 1 12 12 " ) %+%" " ) " "%+%" "" TRUE FALSE 1 13 13 " cars ->" " cars " "->" "" TRUE FALSE 1 14 14 " my_plot #BRE… " my_plot" "" "" TRUE TRUE 1 ``` ]] --- class: split-40 count: false .column[.content[ ```r # from the parsed code # and using calc for which lines to display ... flipbookr:::create_code() %>% flipbookr:::code_parse() %>% * # ... return the code needed for each slide ... * flipbookr:::parsed_return_partial_code_sequence() ``` ]] .column[.content[ ``` [[1]] [1] "cars # the data #REVEAL#<<" [[2]] [1] "cars %>% # the data #REVEAL" " filter(speed > 4) # subset#<<" [[3]] [1] "cars %>% # the data #REVEAL" " filter(speed > 4) %>% # subset" [3] " ggplot() # pipe to ggplot#<<" [[4]] [1] "cars %>% # the data #REVEAL" " filter(speed > 4) %>% # subset" [3] " ggplot() + # pipe to ggplot" " aes(x = speed) #<<" [[5]] [1] "cars %>% # the data #REVEAL" " filter(speed > 4) %>% # subset" [3] " ggplot() + # pipe to ggplot" " aes(x = speed) + " [5] " aes(y = dist) #<<" [[6]] [1] "cars %>% # the data #REVEAL" [2] " filter(speed > 4) %>% # subset" [3] " ggplot() + # pipe to ggplot" [4] " aes(x = speed) + " [5] " aes(y = dist) + " [6] " # Describing what follows#<<" [7] " geom_point(alpha = .3) #REVEAL#<<" [[7]] [1] "cars %>% # the data #REVEAL" " filter(speed > 4) %>% # subset" [3] " ggplot() + # pipe to ggplot" " aes(x = speed) + " [5] " aes(y = dist) + " " # Describing what follows" [7] " geom_point(alpha = .3) + #REVEAL" " geom_point(alpha = 1) #<<" [[8]] [1] "cars %>% # the data #REVEAL" " filter(speed > 4) %>% # subset" [3] " ggplot() + # pipe to ggplot" " aes(x = speed) + " [5] " aes(y = dist) + " " # Describing what follows" [7] " geom_point(alpha = .3) + #REVEAL" " geom_point(alpha = 1) + " [9] " geom_jitter(alpha = .5) #<<" [[9]] [1] "cars %>% # the data #REVEAL" " filter(speed > 4) %>% # subset" [3] " ggplot() + # pipe to ggplot" " aes(x = speed) + " [5] " aes(y = dist) + " " # Describing what follows" [7] " geom_point(alpha = .3) + #REVEAL" " geom_point(alpha = 1) + " [9] " geom_jitter(alpha = .5) + " " aes(color = #<<" [11] " speed > 14 #<<" " ) #<<" [[10]] [1] "cars %>% # the data #REVEAL" " filter(speed > 4) %>% # subset" [3] " ggplot() + # pipe to ggplot" " aes(x = speed) + " [5] " aes(y = dist) + " " # Describing what follows" [7] " geom_point(alpha = .3) + #REVEAL" " geom_point(alpha = 1) + " [9] " geom_jitter(alpha = .5) + " " aes(color = " [11] " speed > 14 " " ) %+% " [13] " cars #<<" [[11]] [1] "cars %>% # the data #REVEAL" " filter(speed > 4) %>% # subset" [3] " ggplot() + # pipe to ggplot" " aes(x = speed) + " [5] " aes(y = dist) + " " # Describing what follows" [7] " geom_point(alpha = .3) + #REVEAL" " geom_point(alpha = 1) + " [9] " geom_jitter(alpha = .5) + " " aes(color = " [11] " speed > 14 " " ) %+% " [13] " cars -> " " my_plot #<<" ``` ]] --- class: split-40 count: false .column[.content[ ```r # from the parsed code # and using calc for which lines to display ... flipbookr:::create_code() %>% flipbookr:::code_parse() %>% # ... return the code needed for each slide ... flipbookr:::parsed_return_partial_code_sequence() -> * # ... save this list *code_seq ``` ]] .column[.content[ ]] --- ## 3. *Automatically* delivered on a presentation platform (here Xaringan) ```r return_partial_chunks_template_code() ``` ``` [1] "```{r {{{chunk_name}}}_{{{break_type}}}_{{{breaks}}}_code, eval = FALSE, echo = TRUE, code = code_seq[[{{{breaks}}}]]}\n ```" ``` ```r return_partial_chunks_template_output() ``` ``` [1] "```{r {{{chunk_name}}}_{{{break_type}}}_{{{breaks}}}_output, eval = TRUE, echo = FALSE, code = code_seq[[{{{breaks}}}]]}\n```" ``` --- # The generic templates above... --- ## ... are used to create chunks of code and output displayed side-by-side ```r chunk_expand(chunk_name = "cars_plot") ``` ```` class: split-40 count: false .column[.content[ ```{r cars_plot_auto_1_code, eval = FALSE, echo = TRUE, code = code_seq[[1]]} ``` ]] .column[.content[ ```{r cars_plot_auto_1_output, eval = TRUE, echo = FALSE, code = code_seq[[1]]} ``` ]] --- class: split-40 count: false .column[.content[ ```{r cars_plot_auto_2_code, eval = FALSE, echo = TRUE, code = code_seq[[2]]} ``` ]] .column[.content[ ```{r cars_plot_auto_2_output, eval = TRUE, echo = FALSE, code = code_seq[[2]]} ``` ]] ```` --- ## ... or a series of slides that *just* display output or *just* display code ```r chunk_expand(num_breaks = 2, display_type = "code") ``` ```` count: false ```{r example_auto_1_code, eval = FALSE, echo = TRUE, code = code_seq[[1]]} ``` --- count: false ```{r example_auto_2_code, eval = FALSE, echo = TRUE, code = code_seq[[2]]} ``` ```` ```r chunk_expand(num_breaks = 2, display_type = "output") ``` ```` count: false ```{r example_auto_1_output, eval = TRUE, echo = FALSE, code = code_seq[[1]]} ``` --- count: false ```{r example_auto_2_output, eval = TRUE, echo = FALSE, code = code_seq[[2]]} ``` ```` --- # The reveal asks for the chunks we've created to be evaluated, by using the knitr::knit() function. We've applied it above. Let's just look at the function itself. ```r chunk_reveal ``` ``` function (chunk_name = "example_name", break_type = "auto", left_assign = F, code_seq = chunk_name_return_code_sequence(chunk_name, break_type, left_assign), num_breaks = length(code_seq), display_type = "both", split = 40) { text <- chunk_expand(chunk_name = chunk_name, break_type = break_type, num_breaks = num_breaks, display_type = display_type, split = split) paste(knitr::knit(text = text), collapse = "\n") } <bytecode: 0x7fdebb5b06d0> <environment: namespace:flipbookr> ``` --- # Notes (points of vacillation) I'm vacillating on the public function name still. I thought about chunk_reveal, reveal, flipbookify, flipify. Most recently, I was most excited about chunk_reveal, but the function will also work with the parsed and expanded code as an input, which I think is really cool, and makes the code nice and flexible a la Yihui here: https://yihui.org/en/2018/11/two-roads/ Related, I'm a little nervous about code_seq argument. What if code_seq is defined in the global environment? There is this interesting warning about this formulation: https://adv-r.hadley.nz/functions.html#default-arguments Probably the right (worry free) way is to set the default to NULL, and then do an if.null() inside the function where code_seq is defined. The one downside is that this is a little verbose, right? num_breaks would also need to be adjusted in this way. Another point of waffling was the argument order. What's really the main input, is it the expanded code, or is it the chunk name? Which ever (I'm not sure) should be the first thing. It probably must be the chunk name, as this is the way people will most likely use the code, else there would be a lot of confusion for the user. Still, I found it unsatisfying that you, can't then somehow pipe code into this final function. --- # Go to code [**here**](https://github.com/EvaMaeRey/flipbookr/blob/master/R/base_parse_reveal_xaringan.R) https://github.com/EvaMaeRey/flipbookr/blob/master/R/base_parse_reveal_xaringan.R <style type="text/css"> .remark-code{line-height: 1.5; font-size: 60%} </style>