library(learnr) library(tidyverse) library(knitr) knitr::opts_chunk$set(echo = FALSE,fig.align = 'center') source('./www/helper.R') imageDir <- paste0(dirname(rstudioapi::getSourceEditorContext()$path), '/images/') #--------------------------------------------- # These lines should be uncommented for development createImages = FALSE # This should never be true, build the images separately # However - if you want to create them all at once then change imageDir to: # imageDir = 'images/' and recognise that all images will appear twice imageDir = 'inst/tutorialsLecturingWithlearnr/images/' devtools::load_all() #--------------------------------------------- tutorial_options(exercise.eval = FALSE) #--------------------------------------------- # Instructions for storing answers to the tutorials, not particularly useful for packaged tutorials record_answers <- function(tutorial_id, tutorial_version, user_id, event, data) { cat(file=paste0(getwd(),'/answers.txt'),append=T, as.numeric(Sys.time()), ".", tutorial_id, ".", tutorial_version, ": ", user_id, ", ", event, ", ", data$label, ", ", data$answer, ", ", data$correct, "\n", sep = "") } options(tutorial.event_recorder = record_answers) #---------------------------------------------
```{css print-tutorial-functionality} @media print { @page {size: landscape} h2 { page-break-before: always; } .topicsContainer, .topicActions, .exerciseActions .skip { display: none; } .topics .tutorialTitle, .topics .section.level2, .topics .section.level3:not(.hide) { display: block; } .topics { width: 100%; } .tutorial-exercise, .tutorial-question { page-break-inside: avoid; } .section.level3.done h3 { padding-left: 0; background-image: none; } .topics .showSkip .exerciseActions::before { content: "Topic not yet completed..."; font-style: italic; } }
<!-- This chunk increases the text in the ace editor to make the code larger It is especially useful for demonstrating code in lectures. --> ```{js} $(function() { var editor; $('.ace_editor').each(function( index ) { editor = ace.edit(this); editor.setFontSize("20px"); }); })
This talk is about how to extend your use of R & Rmarkdown {width=10%}
Shiny Apps are interactive applications written in R and output to HTML
learnR Tutorials can combine a variety of media (including Shiny Apps) within a navigable web-page
This tutorial will give an overview of:
r emo::ji('question')
There should be plenty of` time for questions - please interrupt!
Problems
Solution
Bells!! Whistles!! Interactive Elements!!
{width="25%"}
The Shiny package takes care of all the fussy bits of web-based presentation so for R-users the learning curve is quite short.
# Application title titlePanel("Sample Application: Old Faithful Geyser Data") sliderInput("bins", "Number of bins:", min = 1, max = 50, value = 30) plotOutput("distPlot")
output$distPlot <- renderPlot({ # generate bins based on input$bins from ui.R x <- faithful[, 2] bins <- seq(min(x), max(x), length.out = input$bins + 1) # draw the histogram with the specified number of bins hist(x, breaks = bins, col = 'darkgray', border = 'white', main='Old Faithful Eruptions', xlab = 'Waiting time to next eruption (minutes)', xlim=c(0,100)) })
:::: {style="display: flex;"}
::: {style="width: 50%"}
titlePanel("Sample Shiny App: Geyser Data") sliderInput("bins2", "Number of bins:", min = 1, max = 50, value = 30) plotOutput("distPlot2")
output$distPlot2 <- renderPlot({ # note the use of input$ x <- faithful[, 2] bins <- seq(min(x), max(x),length.out = input$bins2 + 1) # draw the histogram hist(x, breaks = bins, col = 'darkgray', border = 'white', main='Old Faithful Eruptions', xlab = 'Waiting time to next eruption (minutes)', xlim=c(0,100)) })
:::
::: {}
# Add a little space tags$p(HTML(' '))
:::
::: {}
titlePanel("Sample Shiny App: Geyser Data") sliderInput("bins2", "Number of bins:", min = 1, max = 50, value = 30) plotOutput("distPlot2")
output$distPlot2 <- renderPlot({ # note the use of input$ x <- faithful[, 2] bins <- seq(min(x), max(x), length.out = input$bins2 + 1) # draw the histogram hist(x, breaks = bins, col = 'darkgray', border = 'white') })
:::
::::
The beauty of R is that there is no need to re-invent the wheel
There are plenty of existing applications to choose from or to provide inspiration
The RStudio Gallery
Tools for Teaching Quantitative Thinking
Shiny Applications are interactive, and can incorporate varying levels of navigation (panels, tabs etc).
{width="10%"} Shiny apps are run on a Shiny Server. If you have the Shiny package installed then you can serve your apps on your laptop and easily incorporate them into lectures.
But this doesn't allow students to interact with your app!
There are three options for running Shiny Apps:
...or you can encapsulate your apps into tutorials with learnr {width="10%"}
The learnr package encapsulates a variety of content into independent, interactive tutorials.
{width="20%"}
{width="25%"} +
{width="25%"} =
{width="25%"}
--- title: "A Basic RMarkdown" output: html_document ---
library(shiny) # Define UI for application that draws a histogram ui <- fluidPage( # UI Elements: Title, Input etc ) ) # Define server logic required to draw a histogram server <- function(input, output) { # Output functions: plots, tables, statistics } # Runs with the shinyApp command shinyApp(ui = ui, server = server)
--- title: "A learnr Tutorial" output: learnr::tutorial: css: css/style.css runtime: shiny_prerendered --- Text Interspersed with code chunks ---{r} # User Interface Code titlePanel("Sample Application: Old Faithful Geyser Data") sliderInput("bins", "Number of bins:", min = 1, max = 50, value = 30) plotOutput("distPlot") --- ---{r named-chunk, context="server"} # Server Code output$distPlot <- renderPlot({ # generate bins based on input$bins from ui.R x <- faithful[, 2] bins <- seq(min(x), max(x), length.out = input$bins + 1) # draw the histogram with the specified number of bins hist(x, breaks = bins, col = 'darkgray', border = 'white', main='Old Faithful Eruptions', xlab = 'Waiting time to next eruption (minutes)', xlim=c(0,100)) }) ---
Static images can be used as with any Rmarkdown
library(tidyverse) my_theme <- ggplot2::theme_classic() + theme(axis.line.y = element_blank(), axis.ticks.y = element_blank(), axis.text.y = element_blank(), axis.title = element_blank()) mean_x=100 sd_x=10 range_x=4*sd_x plotdata=tibble(x=seq(mean_x-range_x,mean_x+range_x,length.out = 1000), y=dnorm(x,mean_x,sd_x)) ggplot(plotdata, aes(x = x,y=y)) + geom_line(col=style.env$plot_colour[1]) + geom_ribbon(aes(ymin=0,ymax=y,x=x),fill=style.env$plot_colour[1],alpha=.2)+ scale_x_continuous(breaks= seq(mean_x-range_x,mean_x+range_x,sd_x),limits=c(mean_x-range_x,mean_x+range_x)) + annotate('segment', x=mean_x,xend = mean_x,y=0,yend=max(plotdata$y),colour=style.env$plot_colour[2])+ annotate(geom = "curve", x = mean_x+2*sd_x, y = .8*max(plotdata$y), xend = mean_x, yend = .5*max(plotdata$y), curvature = .3, arrow = arrow(length = unit(2, "mm")),colour=style.env$plot_colour[2]) + annotate(geom = "text", x =mean_x+2*sd_x, y =.8*max(plotdata$y), label = deparse(bquote('Mean,'~mu~'=100')), hjust = "left",colour=style.env$plot_colour[2],size=4,parse=T) + annotate('segment', x=mean_x+sd_x,xend = mean_x+sd_x,y=0,yend=dnorm(mean_x+sd_x,mean_x,sd_x), colour=style.env$plot_colour[2],lty=2) + annotate('segment', x=mean_x-sd_x,xend = mean_x-sd_x,y=0,yend=dnorm(mean_x+sd_x,mean_x,sd_x), colour=style.env$plot_colour[2],lty=2) + annotate('segment', x=mean_x+2*sd_x,xend = mean_x+2*sd_x,y=0,yend=dnorm(mean_x+2*sd_x,mean_x,sd_x), colour=style.env$plot_colour[2],lty=2) + annotate('segment', x=mean_x-2*sd_x,xend = mean_x-2*sd_x,y=0,yend=dnorm(mean_x+2*sd_x,mean_x,sd_x), colour=style.env$plot_colour[2],lty=2) + annotate('segment', x=mean_x,xend = mean_x+sd_x,y=.4*max(plotdata$y),yend=.4*max(plotdata$y), colour=style.env$plot_colour[2]) + annotate(geom = "curve", x = mean_x+1.5*sd_x, y = .65*max(plotdata$y), xend = mean_x+.5*sd_x, yend = .4*max(plotdata$y), curvature = .3, arrow = arrow(length = unit(2, "mm")),colour=style.env$plot_colour[2]) + annotate(geom = "text", x =mean_x+1.5*sd_x, y =.65*max(plotdata$y), label = deparse(bquote('Standard Deviation,'~sigma~'=10')), hjust = "left",colour=style.env$plot_colour[2],size=4,parse=T) + annotate(geom='text', x =mean_x-.65*sd_x, y =plotdata$y[which(round(plotdata$x)==round(mean_x-.5*sd_x))[1]], label = 'Symmetric around the mean',hjust='right') + my_theme + ggtitle('The Normal Distribution') ggsave(filename = paste0(imageDir,'normal_example.png'),height=90,width=160,units='mm')
{width="95%"}
In fact, existing Rmarkdown documents can easily be converted to learnr tutorials by simply updating the yaml header to change the output. Xaringan slides take a bit more effort...
Or shiny elements can be embedded to allow manipulation of graphs during the lecture.
You can explore the properties of the normal distribution by changing the mean ($\mu$) and standard deviation ($\sigma$). Note that most of the area under the curve is within 3 standard deviations of the mean.
# table tags$table(width = "100%", # row tags$tr(width = "100%", # Left cell: plot tags$td(width='75%',align="left", plotOutput("normalCurve",height = 300), # Right cell: input ! Imporant! This should be given class 'well' tags$td(width='25%',align="left",class="well", # New table to contain the input tags$table(width = "100%", # row 1 tags$tr(width = "100%", tags$td(align="left", numericInput("m", 'Mean:', min = 10, max = 90, value = 50))), # row 2 tags$tr(width = "100%", tags$td(align="left", numericInput("s", "Standard Deviation:", min = 1, max = 30, value = 10))), # row 3 tags$tr(width = "100%", tags$td(align="left", checkboxInput("showSD", "Show Standard Deviations" )))))))) # Or this is tidier code, but uglier output # numericInput("m", # 'Mean:', # min = 10, # max = 90, # value = 50) # numericInput("s", # "Standard Deviation:", # min = 1, # max = 30, # value = 10) # plotOutput("normalCurve",height = 300)
output$normalCurve <- renderPlot({ # reduce base graphic margins to (roughly) match ggplot par(oma=c(1,0,0,0),mar=c(2.1,0,plot_mtext_cex,0)) plot_mean=input$m plot_sd=input$s x <- seq(0,100,length=1000) y <- dnorm(x,mean=plot_mean, sd=plot_sd) plot(x,y, type="l", lwd=1,axes=F,ylab="",xlab="") axis(1,at=seq(0,100,10), labels=seq(0,100,10)) polygon(c(x, max(x),min(x)), c(y,0,0), col=style.env$plot_colour[1]) if (input$showSD) { for (i in seq(-3,3,1)){ xLine=i*plot_sd+plot_mean segments(xLine,0,xLine,dnorm(xLine,mean=plot_mean, sd=plot_sd),lty=2,lwd=2, col=style.env$contrast_plot_colour) } # This draws the mean in a solid line segments(plot_mean,0,plot_mean,dnorm(plot_mean,mean=plot_mean, sd=plot_sd),lwd=2, col=style.env$contrast_plot_colour) } })
withMathJax() # Allows latex # table tags$table(width = "100%", # Row 1 plot & user input tags$tr(width = "100%", # Left cell: plot tags$td(width='75%',align="left", plotOutput("Xplot",height = 200)), # Right cell: input ! Imporant! This should be given class 'well' tags$td(width='25%',align="left",class="well", tagList(numericInput("m2",'Mean:', min = 10, max = 90,value = 50), numericInput("s2", "Standard Deviation:",min = 1,max = 30,value = 10) ))), # Row 2 - Slider Input tags$tr(width = "100%", # Left cell: slider input tags$td(width='75%',align="left", sliderInput('x_val', 'Choose a value x, to compute a z-score for:', 0, 100, 50, step = 1) )), # Row 3 - Z plot & Z score tags$tr(width = "100%", # Left cell: Z plot tags$td(width='75%',align="left", plotOutput("Zplot",height = 200)), # Right cell: Z score (embedded in another table) tags$td(width='25%',align="center", tagList(tags$p('$$Z=\\frac{x-\\mu}{\\sigma}$$')), uiOutput('z_formula'), htmlOutput('z_val') ) ) )
# plots output$Xplot <- renderPlot({ # reduce base graphic margins to (roughly) match ggplot par(oma=c(1,0,0,0),mar=c(2.1,0,plot_mtext_cex,0)) x <- seq(0,100,length.out = 1000) y <- dnorm(x,mean=input$m2, sd=input$s2) plot(x,y, type="l", lwd=1,axes=F,ylab="",xlab="") polygon(c(min(x),x, max(x),min(x)), c(0,y,0,0), col=style.env$plot_colour[1]) points(input$x_val,y=max(y)/25,bg=style.env$plot_colour[2],col=style.env$contrast_plot_colour,pch=21,cex=3) axis(1,at=seq(0,100,10),labels=seq(0,100,10)) mtext('Random Variable X',col=style.env$plot_colour[1],cex=plot_mtext_cex,adj = 0) mtext(bquote(mu == .(input$m2) *" , "~ sigma == .(input$s2)),col=style.env$plot_colour[1],cex=plot_mtext_cex) }) # end X Plot output$Zplot <- renderPlot({ # reduce base graphic margins to (roughly) match ggplot par(oma=c(1,0,0,0),mar=c(2.1,0,plot_mtext_cex,0)) lower_Z = (0-input$m2)/input$s2 upper_Z =(100-input$m2)/input$s2 xZ = seq(lower_Z,upper_Z,length=1000) z <- dnorm(xZ,mean=0, sd=1) plot(xZ,z, type="l", lwd=1,axes=F,ylab="",xlab='') polygon(c(min(xZ),xZ, max(xZ),min(xZ)), c(0,z,0,0), col=style.env$plot_colour[2]) points(x=(input$x_val-input$m2)/input$s2,y=max(z)/25,bg=style.env$plot_colour[1],col=style.env$contrast_plot_colour,pch=21,cex=3) axis(1,at=seq(round(lower_Z),round(upper_Z),length.out = 11),labels=seq(round(lower_Z),round(upper_Z),length.out = 11)) mtext("Standard Normal Z ",col=style.env$plot_colour[2],cex=plot_mtext_cex, adj=0) mtext(bquote(mu == 1~" , "~ sigma == 0),col=style.env$plot_colour[2],cex=plot_mtext_cex) }) # end Z plot # This output is not yet working x = reactive(input$xval) m = reactive(input$m2) s = reactive(input$s2) output$z_formula <-renderUI({ formula <- "$$Z-score = \\frac{%.0f - %.0f }{%.0f }$$" text <-sprintf(formula,x(),m(),s()) withMathJax( tags$p(text) ) }) # End Z formula () output$z_val <-renderText({ paste("<font color='",style.env$plot_colour[2],"'><p><b>", 'Z-score = ',format(round((input$x_val-input$m2)/input$s2,2),nsmall = 2), "</b></p></font>") }) # End Z value ()
R functions can be used to automate repetitive tasks
calc_z_IQ <- function(x){ m=100 s=15 z = (x-m)/s return(z) } calc_IQ_centile <- function(IQ){ zscore <- calc_z_IQ(IQ) pnorm(zscore,lower.tail = T) } # Calculate Z score then probability calc_z_IQ(110) pnorm(.67,lower.tail = T) # Calculate Probability Directly calc_IQ_centile(110)
Drag the selector to find the probability on the right.
sliderInput('zVal', '', -4, 4, 0, step = .1) plotOutput("Zprob",height = 300)
style.env$plot_colour[1]='#00508F' plot_mtext_cex=1.2 output$Zprob <- renderPlot({ zVal= input$zVal x <- seq(-4,4,length=1000) y <- dnorm(x) par(mar = c(2.1,0,1.1,0)) # removed left/right margins plot(x,y, type="l", lwd=1,axes=F,ylab="",xlab="") polygon(c(zVal,x[x>zVal],4, zVal), c(0,y[x>zVal],0,0), col=style.env$plot_colour[2]) axis(1,at=seq(-4,4,length.out=9), labels=seq(-4,4,length.out=9)) mtext(paste('Probability Z >',zVal,'=',round(pnorm(zVal,lower.tail = F),3)),adj=1,col=style.env$plot_colour[2],cex=plot_mtext_cex) mtext(paste('Probability Z <',zVal,'=',round(pnorm(zVal),3)),cex=plot_mtext_cex,col=style.env$plot_colour[1],adj=0) polygon(c(zVal,-4,x[x<zVal], zVal), c(0,y[x<zVal],0,0), col=style.env$plot_colour[1]) text(x=2.8,y=.5*dnorm(0),labels=paste0('R code:\n pnorm(',zVal,',lower.tail=FALSE)'),adj=c(0.5,0),family='Courier',cex=1.5) # place the end of the right arrow halfway through the 'red' probability x_end = mean(c(3,zVal)) arrows(x0=3,y0=.48*dnorm(0),x1=x_end,y1=.5*dnorm(x_end),lwd=2) text(x=-3,y=.5*dnorm(0),labels=paste0('R code:\n pnorm(',zVal,')'),adj=c(0.5,0),family='Courier',cex=1.5) # place the end of the left arrow halfway through the 'white' probability x_end = mean(c(-3,zVal)) arrows(x0=-3,y0=.48*dnorm(0),x1=x_end,y1=.5*dnorm(x_end),lwd=2) })
You may want to store the solution to your code, just in case of time constraints or terribly typing ability
IQ is approximately normally distributed with mean 100 and standard deviation 15. Jack's IQ is 104 and Jill's IQ is 129. Write a function to calculate a z-score for any IQ score and then use it to calculate Z-scores for Jack's and Jill's IQ.
get_z_score <- function(iq) { z = (iq-100)/15 return(z) } # Jill get_z_score(129) # Jack get_z_score(104)
Learn R: Now update your function to allow a z-score to be calculated for any normal distribution (hint: it should have three arguments)
get_z_score <- function(x,mean,sd) { z = (x-mean)/sd return(z) }
Seemlessly embed videos (or other web-based content) into lectures
Rmarkdown handles the layout so your can dedicate your energy to content.
All the capabilities of Rmarkdown plus:
= Engaged Students?
As well as being a handy tool for lectures and presentations, learnr documents can be distributed to students as stand-alone tutorials
Distributing tutorials to students allows them to interact with your content.
These tutorials can be used for:
Learn R: Use R to calculate the Z score for $x=10$, where $X$ is normally distributed random variable with $\mu=20$ and $\sigma=4$:
(10-20)/4
quiz(caption='Quick Quiz', question("What is the Z-score for a score of 70 on a normally distributed variable with mean of 100 and standard deviation of 10? (Use the code box above for the calculation)", answer("3",message='All score below the mean have negative Z scores'), answer("30", message='Check the formula on the previous page'), answer("-3", correct = TRUE), answer("70",message='Check the formula on the previous page') ) )
{width="50%"}
library(ggpubr) # Defining a custom ggplot theme my_theme <- ggplot2::theme_classic(base_size = 2) + theme(axis.line.y = element_blank(), axis.ticks.x = element_blank(), axis.ticks.y = element_blank(), axis.text.y = element_blank(), axis.text.x = element_blank(), axis.title = element_blank()) n=1000 plotdata=tibble( A = rlnorm(n,mean=3,.5), B= rnorm(n,mean=30,sd=5), C = rnorm(n,mean=60,sd=15), D = 100-rlnorm(n,mean=3,.5), ) plots<-NULL for (var in LETTERS[1:4]){ plots[[var]] <- plotdata %>% ggplot(aes_string(x=var)) + geom_histogram(bins = 40) + my_theme } ggarrange(plotlist=plots,labels = LETTERS,font.label = list(size = 8)) ggsave(filename=paste0(imageDir,'normal_plot_quiz.png'),width=8,height=4,units='cm')
# NOTE: quiz chunks MUST be names #-------------------------------------------------------- # Here is a useful link to example questions: # https://pkgs.rstudio.com/learnr/articles/questions.html # The first question allows a retry # The second question will appear in a random order quiz(caption='', question("Which of the above plots are Normally distributed?", answer("A",message='A is right skewed'), answer("B", correct = TRUE), answer("C", correct = TRUE), answer("D",message='D is left skewed'), allow_retry = TRUE ), question("What is the mean of the standard normal distribution? (Will appear in random order)", answer("-100"), answer("100"), answer("0", correct = TRUE), answer("-50"), answer("50"), random_answer_order = TRUE, allow_retry = TRUE ) )
Random values can be incorporated into the questions and answers, to make quizzes different for each student.
In addition to random values, the answers can appear in a random order (this is a argument that you set in the question function).
# This provides one example of how to create questions with a random element #--------------------------------------------- # heart rate question variables m=floor(runif(1,60,75)) s=floor(runif(1,7,14)) bpm=floor(rnorm(1)*s+m) qHeartRate = paste0("If heart rate in a special patient population is Normally distributed with a mean of ",m, " and a standard deviation of ",s, ", what is the probability that a person in this group will have a heart rate greater than ", bpm,"?") a1 = rnd(pnorm(bpm,m,s,lower.tail = F),3) a2 = rnd(pnorm(bpm,m,s,lower.tail = T),3) a3 = rnd(abs(bpm-m)/s,3) a4 = paste0('-',a3) a5 = rnd(pt((bpm-m)/s,df=10,lower.tail = F),3) #--------------------------------------------- quiz(caption='Quiz on the Normal distribution', question(qHeartRate, answer(a1, correct=TRUE ), answer(a2), answer(a3), answer(a4), answer(a5), random_answer_order=T, allow_retry = TRUE ) )
This quiz also uses random values. You could write an item bank to distribute to provide additional practice.
hr_m <- as.integer(rnorm(1,70,4)) hr_s <- as.integer(rnorm(1,10,2)) x_val <- as.integer(rnorm(1,hr_m,hr_s)) z_val = (x_val-hr_m)/hr_s p_above = pnorm(z_val,lower.tail = F) p_below = pnorm(z_val,lower.tail = T) lb <- round(qnorm(.25,hr_m,hr_s),1) ub <- round(qnorm(.75,hr_m,hr_s),1) quiz(caption = sprintf('Assume that the distribution of heart rate (bpm) for healthy adults at rest follows a Normal distribution with mean ($\\mu$) of %1.0f and standard deviation ($\\sigma$) of %1.0f.',hr_m,hr_s), question(sprintf('What percentage of healthy adults at rest have heart rate (bpm) values above %1.0f?',x_val), answer(sprintf('%1.0f%%' ,100*p_above), correct=TRUE), answer(sprintf('%1.0f%%' ,100*p_below), message='This is the proportion with values below the value of interest.'), answer(sprintf('%1.2f' ,z_val), message='That is the z-score, not the probability'), answer(sprintf('%1.2f' ,-z_val)), answer(sprintf('%1.0f%%' ,100*ifelse(p_below<.5,.5-p_below,p_below-.5))), answer(sprintf('%1.0f%%' ,100*p_above/2)), allow_retry = TRUE, random_answer_order = T), question('Between what two values do the middle 50% of heart rate (bpm) values lie?', answer(sprintf('%1.0f and %1.0f',lb,ub), correct=TRUE), answer('-0.67 and 0.67', message='These are the z-scores that contain the middle values.'), answer(sprintf('%1.0f and %1.0f',x_val-hr_s,x_val+hr_s)), answer(sprintf('%1.0f and %1.0f',x_val-.5*hr_s,x_val+-.5*hr_s)), answer(sprintf('%1.0f and %1.0f',x_val-2*hr_s,x_val+2*hr_s)), allow_retry = TRUE, random_answer_order = T) )
A function can be defined to record submitted answers. This should be included in the setup
code chunk.
# Instructions for storing answers to the tutorials # (not particularly useful for packaged tutorials) record_answers <- function(tutorial_id, tutorial_version, user_id, event, data) { cat(file='answers.txt'),append=T, as.numeric(Sys.time()), tutorial_id, " (", tutorial_version, "): ", user_id, ", ", event, ", ", data$label, ", ", data$answers, ", ", data$correct, "\n", sep = "") } options(tutorial.event_recorder = record_answers)
Answers.txt content:
1646686868.lRt.2022.lwlr.1: lisa, session_start, , , 1646686874.lRt.2022.lwlr.1: lisa, question_submission, quiz-1-1, The mean and the standard deviation, TRUE 1646686880.lRt.2022.lwlr.1: lisa, question_submission, quiz-1-2, The area under the curve is the total amount of variation, FALSE 1646686928.lRt.2022.lwlr.1: lisa, question_submission, quiz-1-3, 0.159, TRUE 1646686930.lRt.2022.lwlr.1: lisa, session_stop, , ,
To print the tutorial to pdf requires an @media rule. This is included as a css chunk
within the markdown document.
This allows you to:
r emo::ji('exclamation')
Only tutorial pages that have been visited will be printed.r emo::ji('exclamation')
Click here for css print code
@media print { @page {size: landscape} h2 { page-break-before: always; } .topicsContainer, .topicActions, .exerciseActions .skip { display: none; } .topics .tutorialTitle, .topics .section.level2, .topics .section.level3:not(.hide) { display: block; } .topics { width: 100%; } .tutorial-exercise, .tutorial-question { page-break-inside: avoid; } .section.level3.done h3 { padding-left: 0; background-image: none; } .topics .showSkip .exerciseActions::before { content: "Topic not yet completed..."; font-style: italic; } }
How can learnr be used in teaching?
Tutorials as dynamic lecture slides
interactive elements
including multimedia
Development of tutorial packages for asynchronous learning
allows for student interaction
These different end goals require slightly different development workflows.
It is easiest to begin with tutorials intended for use as presentations or lecture slides.
One lecture will be a single tutorial document, with a topic for each slide
Similar structure to slide creation with Beamer or Xaringan, but elements can be dynamic
All lectures can be stored in a single folder with shared dependencies
All tutorials are contained in an R package
Each tutorial has its own set of dependencies, tutorials are sandboxed
However during development, all tutorials can access the shared resources, which can then be copied across to the tutorial folders.
Package can easily be updated and distributed to students via github
This tutorial is in my ExampleTutorial package, available on my gitHub page
You can run the tutorial by installing the package & running the tutorial (instructions on git)
...but better still, download the folder to start quickly developing your own tutorials
Enjoy r emo::ji('smile')
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.