Chapter 13 Presæntering af datasæt over for andre

13.1 Introduktion til chapter og læringsmålene

I dette chapter beskæftiger vi os med at lave interaktive visualiseringer, som vi kan vise overfor andre, fk. som en del af en presæntation. Til sidste vil der være generalt råd til at lave plots fk. i en powerpoint præsentation.

13.1.1 Læringsmålene

Du skal være i stand til at

  • lave interactive plotter med pakken plot_ly.
  • anvende R pakken Shiny for at lave en simpel app og gør den interactiv
  • bygge på appen i Shiny - tilføj forskellige inputs, plot typer og panels.
  • kende generelle råde om presentering af data gennem visualiseringer

Husk evaluering i kurset!

13.2 Video ressourcer

  • Video 1 - Introduktion til Shiny

Link her hvis det ikke virker nedenunder: https://player.vimeo.com/video/559363250

  • Video 2 - Introduktion til Shiny - interaktiv plots

Link her hvis det ikke virker nedenunder: https://player.vimeo.com/video/559558820

  • Video 3 - Introduktion til Shiny - ui layout

Link her hvis det ikke virker nedenunder: https://player.vimeo.com/video/559562153

13.3 Interactiv plots

Der er en nyttig pakke, der hedder plot_ly, som man kan anvende til at lave interactive plotter. Kig for eksempel på følgende plot og afprøve de forskellige interaktiv muligheder.

library(plotly)
data(diamonds)
plot_ly(diamonds, 
        x = ~cut)

Her er et scatter plot lavet med plot_ly:

diamonds %>% slice_sample(n = 1000) %>%
  plot_ly(x = ~ carat, 
          y = ~ price) %>%
  add_markers(color = ~ color)

Bemærk at vi har specificeret slice_sample her, som vælger 1000 tilfældige rækker fra datasættet. Det er fordi diamonds er en meget stor datasæt og hvis man forsøger at plotte alle observeationer på en gang med plot_ly kan det være at den crasher eller kører for langsom.

Der er faktisk en nyttig funktion indenfor plot_ly som hedder ggplotly- man kan tage et plot som var lavet i ggplot2 og så anvende ggplotly til at gøre den interaktiv. Her er samme plot:

my_plot <- diamonds %>% slice_sample(n = 1000) %>%
  ggplot(aes(x=carat,y=price,colour=color)) + 
  geom_point() + 
  theme_minimal()

ggplotly(my_plot)

13.4 Shiny

Man kan anvende Shiny til at lave en interaktiv program. Det er nemt at få din første app op at køre. Her laver jeg en simpel app, som inddrager et plot af noget af data, vi har arbejdet med i kurset.

13.4.1 Eksempeler som viser Shiny

Indenfor pakken shiny er der nogle eksempler som viser nogle forskellige Shiny apps. Prøve at køre nogle af følgende kode linjer.

library(shiny)
runExample("01_hello")      # a histogram
runExample("02_text")       # tables and data frames
runExample("03_reactivity") # a reactive expression
runExample("04_mpg")        # global variables
runExample("05_sliders")    # slider bars
runExample("06_tabsets")    # tabbed panels
runExample("07_widgets")    # help text and submit buttons
runExample("08_html")       # Shiny app built from HTML
runExample("09_upload")     # file upload wizard
runExample("10_download")   # file download wizard
runExample("11_timer")      # an automated timer

I eksempel 1 er der en histogram som viser nogle waiting times, med en slider hvor man kan specificere antallet af bins i det histogram. Man kan således ændre nogle parametre, og så plottet bliver ændret automatiske. Eksempel 2 har en table hvor man kan specificere hvor mange række man gerne vil have, og så kan man også vælger imellem forskellige datasæt.

Disse eksempeler har fordelen af, at man bare kan kopiere koden og så lave egen app og ændre den efter sin egne data.

13.4.2 Create new app

  • Man vælger en mappe, og “Single File”. Indenfor mappen skal ligge en skript der hedder “app.R” (det er vigtigt at man ikke ændre navnet af “app.R”).

  • Man kan trykke på “Run App” indenfor R Studio for at få den at køre

13.4.3 Struktur af en Shiny App

Her er den grundlæggende struktur af en Shiny app:

Indenfor “app.R” er der tre komponenter som kan skal være opmærksom på. Vi ændrer kun to af de tre komponenter.

Dele Beskrivelse
ui <- fluidPage() “user interface” object: det fortæller Shiny hvordan appen ser ud.
server <- function(input,output){} her skrives R kode, fk til et plot, som skal være en del af den R objekt som hedder output. input er for eksempel antallet af bins som man specificerer med en slider som i det histogram eksempel.
shinyApp(ui = ui, server = server) er altid den sidste linje - den ændre vi ikke. Den få appen til at køre.

13.4.4 Minimal example (men ikke interaktiv)

Jeg starter med at lave et meget simpelt “minimal” eksempel af hvordan man kan lave et program med Shiny:

  • ui komponent: Vi vil gerne viser et plot som hedder “my_plot” - så skriver jeg plotOutput("my_plot")
ui <- fluidPage(
    plotOutput("my_plot") #fortælle, at vi vil fremvise "my_plot".
)
  • server komponent: angiver vi koden til my_plot.
    • Plottet skal være en del af vores output, så vi skriver output$my_plot <-.
    • renderPlot({ #plot kode }) fortæl den at det vi lave er et plot og vi skriver koden indenfor.
server <- function(input, output) {
    #definere "my_plot"
    output$my_plot <- renderPlot({
        #Skriv plot kode her
  })
}

Her er appen med ovenstående kode sæt ind: man godt kan køre den men når vi ikke har nogle data eller plot kode er den meget kedelig.

library(shiny)
library(tidyverse)

ui <- fluidPage(
    plotOutput("my_plot") #fortælle, at vi vil fremvise "my_plot".
)

server <- function(input, output) {
    #definere "my_plot"
    output$my_plot <- renderPlot({
        #Skriv plot kode her
  })
}
# Run the application 
shinyApp(ui = ui, server = server)

Tilføje dataseættet iris og lave et plot

Lad os tilføje nogle data til vores plot i formen af iris, og skriv nogle kode til et scatter plot:

library(shiny)
library(tidyverse)
data(iris)

#ui: output plottet 'my_plot'
ui <- fluidPage(
    plotOutput("my_plot")
)

#server: lav plot og kalde den for output$my_plot
server <- function(input, output) {
    output$my_plot <- renderPlot({
        iris %>%
            ggplot(aes(x=Sepal.Width,y=Petal.Length)) + 
            geom_point() +
            theme_classic()
  })
}

# køre appen
shinyApp(ui = ui, server = server)

Hvis vi køre koden kan man se at vi har et plot frem. Appen er dog ikke interaktiv.

13.4.5 Minimal example - interactiv.

Vi vil gerne gøre vores app mere fleksibelt - lad os gøre det interaktiv ved at plotte et subset af Iris for en af de tre species, som vælges fra en “drop-down” box - vi kan anvende en funktion som hedder selectInput:

Her er koden for vores selectInput, som skal tilføjes indenfor fluidPage.

    selectInput(inputId = "Species", #giv en id
                choices = iris %>% distinct(Species) %>% pull(Species), #setosa, virginica eller versicola
                selected = "setosa", #default species
                label = "Choose species") #label på plottet

Vi skal også ændre koden på plottet så at det reagerer på vores selectInput:

  • Vi laver en subset iris_subset af de data efter “Species” vi valgt i vores “drop-down box” ved at angiv Species==input$Species indenfor funktionen filter.
  • Vi laver et scatter plot med iris_subset:
iris_subset <- iris %>% 
            filter(Species==input$Species)
        
        iris_subset %>%
            ggplot(aes(x=Sepal.Width,y=Petal.Length)) + 
            geom_point() +
            theme_classic()

Her tilføjer jeg de kode chunks fra ovenpå til vores program:

library(shiny)
library(tidyverse)
data(iris)

ui <- fluidPage(
    selectInput(inputId = "Species",
                choices = iris %>% distinct(Species) %>% pull(Species),
                selected = "setosa",
                label = "Choose species"),
    
    plotOutput("my_plot")
)

server <- function(input, output) {
    output$my_plot <- renderPlot({
        
        iris_subset <- iris %>% 
            filter(Species==input$Species)
        
        iris_subset %>%
            ggplot(aes(x=Sepal.Width,y=Petal.Length)) + 
            geom_point() +
            theme_classic()
        
    })
}

# Run the application 
shinyApp(ui = ui, server = server)

Som man kan se i screenshot nedenunder, har vi en “drop-down box” hvor vi kan vælge imellem de tre Species.

13.4.6 Flere plots på samme Shiny app

  • Vi laver to plotter, et scatter plot og et histogram, som reagere på selectInput som fortæller de subset af de data efter Species som skal plottes.
  • Vi kalder dem for p1 og p2 og plotte dem ved siden af hinanden med grid.arrange:
grid.arrange(p1,p2,ncol=2)
library(shiny)
library(tidyverse)
library(gridExtra)
data(iris)

ui <- fluidPage(
    selectInput(inputId = "Species",
                choices = iris %>% distinct(Species) %>% pull(Species),
                selected = "setosa",
                label = "Choose species"),
    
    plotOutput("my_plot")
)

server <- function(input, output) {
  
    output$my_plot <- renderPlot({
        
        iris_subset <- iris %>% 
            filter(Species==input$Species)
        
     p1 <- iris_subset %>%
            ggplot(aes(x=Sepal.Width,y=Petal.Length)) + 
            geom_point(colour="steel blue") + 
            ggtitle(input$Species) +
            theme_classic()
        
        p2 <- iris_subset %>%
            ggplot(aes(x=Sepal.Width)) + 
            geom_density(colour="firebrick3") + 
            ggtitle(input$Species) +
            theme_classic()
        
        grid.arrange(p1,p2,ncol=2)
        
    })
}

# Run the application 
shinyApp(ui = ui, server = server)

13.4.7 Involvere en table

Vi vil gerne tilføj en table så vi kan se vores data i appen. Jeg vil gerne specificere hvor mange rækker jeg skal vise frem i appen - vi kan gøre den interaktiv ved at giv en numericInput funktion:

Her er koden for vores numericInput. Bemærk, at man skriver tableOutput("my_table") for at fortælle at vi vil viser “my_table” i appen.

numericInput(inputId = "num_rows",                           #giv id num_rows
                 label = "Number of observations to view:",  #label på selve plottet
                 value = 5),                                 #default værdi

tableOutput("my_table")                                      #fortæl, at vi vil output "my_table"

Her er koden for my_table. Bemærk at vi anvender funktion renderTable i stedet for renderPlot, og der er en indstilling indenfor head hvor vi kan specificere hvor mange række vi skal have (med n = input$num_rows - altså tallet som vi skriver i appen).

    output$my_table <- renderTable({
        iris_subset <- iris %>% 
            filter(Species==input$Species)
        head(iris_subset, n = input$num_rows)
    })

Jeg tilføjer de to kode chunks til vores app i følgende (som kan kopireres og blive kørte):

library(shiny)
library(tidyverse)
library(gridExtra)
data(iris)

ui <- fluidPage(
  selectInput(inputId = "Species",
              choices = iris %>% distinct(Species) %>% pull(Species),
              selected = "setosa",
              label = "Choose species"),
  
  numericInput(inputId = "num_rows",
               label = "Number of observations to view:",
               value = 5),
  
  plotOutput("my_plot"),
  tableOutput("my_table")
  
)

server <- function(input, output) {
  
  output$my_plot <- renderPlot({
    
    iris_subset <- iris %>% 
      filter(Species==input$Species)
    
    p1 <- iris_subset %>%
      ggplot(aes(x=Sepal.Width,y=Petal.Length)) + 
      geom_point(colour="steel blue") + 
      ggtitle(input$Species) +
      theme_classic()
    
    p2 <- iris_subset %>%
      ggplot(aes(x=Sepal.Width)) + 
      geom_density(colour="firebrick3") + 
      ggtitle(input$Species) +
      theme_classic()
    
    grid.arrange(p1,p2,ncol=2)
    
  })
  
  output$my_table <- renderTable({
    iris_subset <- iris %>% 
      filter(Species==input$Species)
    head(iris_subset, n = input$num_rows)
  })
}

# Run the application 
shinyApp(ui = ui, server = server)

13.4.8 Add a slider

  • Lad os tilføj en sliderInput for at styr punkt eller linje størrelse i plottet. Den ser sådan ud:

  • Vores kode ser sådan ud. Vi lave en slider af integer fra 1 til 10.
    sliderInput(inputId = "Point",    #giv den id Point
                label = "Point size", #label på selve plottet
                min = 1,              #min værdi
                max = 10,             #max værdi
                step = 1,             #step size
                value = 1),           #default værdi
  • Vi referer vores point størrelse ind i plottet med input$Point:
         ....+   geom_point(size = input$Point, colour="steel blue") + ....
         ....+   geom_density(colour="firebrick3",lwd=input$Point) + ......
  • Vi tilføj ovenstående kode til vores plot:
library(shiny)
library(tidyverse)
library(gridExtra)
data(iris)

ui <- fluidPage(
  selectInput(inputId = "Species",
              choices = iris %>% distinct(Species) %>% pull(Species),
              selected = "setosa",
              label = "Choose species"),
  
  numericInput(inputId = "num_rows",
               label = "Number of observations to view:",
               value = 5),
  
  sliderInput(inputId = "Point",
              label = "Point size",
              min = 1,
              max = 10,
              step = 1,
              value = 1),
  
  plotOutput("my_plot"),
  tableOutput("my_table")
  
)

server <- function(input, output) {
  
  output$my_plot <- renderPlot({
    
    iris_subset <- iris %>% 
      filter(Species==input$Species)
    
    p1 <- iris_subset %>%
      ggplot(aes(x=Sepal.Width,y=Petal.Length)) + 
      geom_point(size = input$Point, colour="steel blue") + 
      ggtitle(input$Species) +
      theme_classic()
    
    p2 <- iris_subset %>%
      ggplot(aes(x=Sepal.Width)) + 
      geom_density(size = input$Point,colour="firebrick3") + 
      ggtitle(input$Species) +
      theme_classic()
    
    grid.arrange(p1,p2,ncol=2)
    
  })
  
  output$my_table <- renderTable({
    iris_subset <- iris %>% 
      filter(Species==input$Species)
    head(iris_subset, n = input$num_rows)
  })
}

# Run the application 
shinyApp(ui = ui, server = server)

13.4.9 Ekstras som man kan tilføje til ui

Her er nogle ekstra tekst som vi kan tilføj - for eksempel en title. Vi kan også lave nogle forskellige panels for at fremvise de forskellige dele af vores app - for eksempel en side panel og en main panel.

Title

  titlePanel("Shiny Text")

Specificier sidebarLayout

  sidebarLayout(
    sidebarPanel(),
    mainPanel()
  )

Her er hvordan appen ser ud med titlen samt en sidebarPanel og mainPanel

Her er fuld kode som man kan copy/paste ind i app.R:

library(shiny)
library(tidyverse)
library(gridExtra)
data(iris)

ui <- fluidPage(

  titlePanel("My Iris app!"),
  
  sidebarPanel(
    selectInput(inputId = "Species",
                choices = iris %>% distinct(Species) %>% pull(Species),
                selected = "setosa",
                label = "Choose species"),
    
    numericInput(inputId = "num_rows",
                 label = "Number of observations to view:",
                 value = 5),
    
    sliderInput(inputId = "Point",
                label = "Point size",
                min = 1,
                max = 10,
                step = 1,
                value = 1)
  ),
  mainPanel(
    plotOutput("my_plot"),
    tableOutput("my_table")
  )
)

server <- function(input, output) {
  
  output$my_plot <- renderPlot({
    
    iris_subset <- iris %>% 
      filter(Species==input$Species)
    
    p1 <- iris_subset %>%
      ggplot(aes(x=Sepal.Width,y=Petal.Length)) + 
      geom_point(size = input$Point, colour="steel blue") + 
      ggtitle(input$Species) +
      theme_classic()
    
    p2 <- iris_subset %>%
      ggplot(aes(x=Sepal.Width)) + 
      geom_density(size = input$Point,colour="firebrick3") + 
      ggtitle(input$Species) +
      theme_classic()
    
    grid.arrange(p1,p2,ncol=2)
    
  })
  
  output$my_table <- renderTable({
    iris_subset <- iris %>% 
      filter(Species==input$Species)
    head(iris_subset, n = input$num_rows)
  })
}

# Run the application 
shinyApp(ui = ui, server = server)

13.5 Ekstra generelle råd

  • Tilføje hensigtsmæssige title/akse labels etc. og gøre dem til en størrelse som er nem at læse (tænk i PowerPoint - personer bagest i rummet skal kunne læse teksten).
  • Fjerne unødvendige legends.
  • Anvende colour palettes som er designet til at fungere godt for de fleste (for eksempel undgå grøn og rød ved siden af hinanden).
  • Viser kun hvad er nødvendige for at fortælle den historie, du gerne vil videregive til andre. For eksempel undgår ekstra tekst som ikke tilføjer noget til beskeden.
  • En historie på en slide hvis man laver en PowerPoint præsentation.
  • Andvende hensigtsmæssige akse transformeringer som giver mest mening til de data
  • Undgå piecharts, 3D plots osv. medmindre de absolut tilføj noget ekstra til visualisering (meget sjældent).
  • Hvis man tilføjer et plot til en PowerPoint, anbefaler jeg at bruge PDFs og ikke PNG/JPG, når en PDF har en højere kvalitet.
  • Interaktiv plots kan være sjovt og interessant (men igen sikre at de tilføj nogle ekstra til din præsentation/rapport).

13.7 Problemstillinger

Problem 1) Lav interactiv plots

Anvend funktionen ggplotly fra pakken plotly til at lave følgende interkative plots:

  • Et barplot som viser antallet af biler for de forskellige antal cylinders i variablen cyl i datasættet mtcars.
  • Et scatter plot som viser wt på x-aksen og qsec på y-aksen og med farver efter variablen gear.
    • tilføj lineær trend linjer til plottet.

Problem 2) Shiny

  • Indlæs pakken Shiny og se på de forskellige eksempler fk. runExample("05_sliders").
  • Lav et nyt app og kør den default app (dvs. uden at redigere på noget).

Problem 3) Shiny

  • Slet default tekst og kopi koden fra ovenpå (med slider og to plotter) - kør appen.
  • Tilpas koden, hvor du ændrer datasættet fra Iris til Penguins.

Problem 4) Shiny

  • Lav et app som viser de første n rækker i dataframen mtcars (numericInput)
  • Tilføj også en “drop-down box” med funktionen selectInput for at vise en subset af dataframen, efter de forskellige mulige værdier i variablen gear.

Problem 5) Shiny

  • Lav et app med en sliderInput, hvor du styrer antallet af clusters med funktionen kmeans i datasættet penguins.
  • Vis clusters som forskellige farver indenfor et scatter plot (det kan være fk. to variabler eller den første to principal components).
  • Tilføj også en selectInput for at visualisere dine clusterings, i et subsæt af datasættet efter variablen island.
  • Anvend sidebarPanel og mainPanel til at separere dine inputs fra plotterne.
  • Tilføj en app titel samt plot-title/akse labels osv. for at bedste viser din app over for andre.

Problem 6) Shiny

Lav egen app og inddrager forskellige datasæt og koncepter fra kurset!