################################################################################ # # # Teaching with loon # # ################################################################################ # # # # There are several ways that the looniverse can help with teaching. # # E.g. loon.data has a few artificial data sets which illustrate # statistical concepts. # see http://great-northern-diver.github.io/loon.data/reference/ # Or # https://great-northern-diver.github.io/loon.data/reference/index.html#section-teaching # They include # - pandemic ... a cyclical preference of treatments # - medicalRecords ... double reversing Simpson's paradox # - lizards ... ecological fallacy like Simpson's # - blocks ... sampling methods # - judgment ... sampling methods # # # - produces high-quality presentation graphics # - uses a pipeline style to build visualizations # - based on a variation on the original grammar of graphics (hence the gg) # - part of the tidyverse (with magrittr, dplyr, purrr, ...) # # # There are also a few demos() whose code could be adapted. # library(loon) library(loon.data) # to see all of the demos available demo(package = "loon") # Box-Cox power transformations; # A must use for the data analysis classroom demo("l_power") # Add polynomial regressions to selected points demo("l_add_regressions") # Explore the effect of influential points seen by # actually moving them around in the scatterplot. # Great for illustrating breakdown demo("l_regression_influential") # Hans Rosling's famous gapminder demonstration. # Now the modern statistical equivalent of "Hello world" # statistical visualization demo("l_us_and_them_slider") # Some new kinds of linking # Many to one (polygons in map) demo("l_us_and_them_choropleth") # Comparing/using many dimension reduction methods # at once in exploration demo("l_ng_dimred") ######## # # The code from the above demos show you how to build them. # (the code prints out when you run the demo) # # But, it really isn't too hard to build something straightforward. # # Because `loon` is built on the base `R` `tcltk` package (which ships with `R`), # you can always build your own interactive displays # using a mix of `loon` and `tcltk` functionality. # # For example, a simple slider is built as slider_window <- tktoplevel() tktitle(slider_window) <- "slider" slider_val <- tclVar('1') # default value slider <- tkscale(parent = slider_window, orient = "horizontal", variable = slider_val, from = -5, to = 5, resolution = 0.1, command = function(...) print(paste0("Slider value = ", ...))) tkgrid(slider, row = 0, column = 0, sticky="we", padx = 50) # Now move the slider. # # # ######## # # An important concept is that of binding events # # In a loon plot, it is EASY to bind a function to fire # whenever named states are changed. # # The main function here is `l_bind_state(target, event, callback)` # # For example myPlot <- l_plot(iris) makeItHappen <- function() {print ("It happened!")} l_bind_state(myPlot, c("color", "xTemp", "yTemp"), makeItHappen) # Now change the colour of some points. # Move some points. # It is that simple to extend behaviour # # We could attach different functions to fire for different state changes # WhoIsSpecial <- function() { cat("Selected points were: \n\t", myPlot["itemLabel"][myPlot["selected"]], "\n" ) } # Loon now allow you to bind myPlot so that # when the value of any of its "selected" state # values change, the function WhoIsSpecial # is called (with no argumenta) l_bind_state(myPlot, c("selected"), WhoIsSpecial) # # Which opens up a whole range of possibilities!! # # # Note that having a global variable like myPlot # appear within a function (as above) is generally # a very bad idea. # This was to simplify the illustration. # Generally, more care needs to be taken to ensure # that myPlot can only be the plot you are interested in. # # # A more realistic (and careful) example follows. # # Suppose we fit a least-squares line to some data: x <- longley$GNP y <- longley$Unemployed data <- data.frame(x = x, y = y) p <- l_plot(data$x, data$y) lm_fit <- lm( y ~ x, data = data) x_line <- extendrange(x) y_line <- predict(lm_fit, newdata = data.frame(x = x_line)) fitted_line <- l_layer_line(p, x = x_line, y = y_line, color = "red") # # And now would like to have the line change whenever points were moved. # (This would change values of the states "xTemp" and/or "yTemp" # in the plot.) # # Updating the least-squares line would be effected by a function like updateLine <- function(myPlot, ls_line = NULL) { if (!is.null(ls_line) & ls_line %in% l_layer_ids(myPlot)) { # we'll update it. xnew <- myPlot['xTemp'] if (length(xnew) == 0) {xnew <- myPlot['x']} # For y ynew <- myPlot['yTemp'] if (length(ynew) == 0) {ynew <- myPlot['y']} # New fit new_fit <- lm( y ~ x, data = data.frame(x = xnew, y = ynew)) x_line <- extendrange(xnew) y_line <- predict(new_fit, newdata = data.frame(x = x_line)) # configure the least-squares line l_configure(ls_line, x = x_line, y = y_line) } # Update the tcl language's event handler tcl('update', 'idletasks') } # The final step is simply l_bind_state(p, c("xTemp", "yTemp"), function() {updateLine(p, fitted_line)}) # # Now go move some points! # # # There is no end to the possibilities!! # # ################################################################################