Fisseha Berhane, PhD

Data Scientist

443-970-2353 fisseha@jhu.edu CV Resume Linkedin GitHub twitter twitter

Interactive visualization with R-Shiny versus with Tableau -Part 1

Among my plans for this year is creating interactive data visualizations with R-Shiny, Python-Bokeh and Tableau, by integrating some awesome JavaScript libraries. This post is on interactive treemap with Shiny and Tableau. Treemapping is a method for displaying hierarchical data by using nested rectangles. A rectangle's area in a treemap is proportional to the data it shows. Using colors and sizes that show the data variability helps to quickly see patterns that are difficult to observe from other types of visualizations. Further, treemaps help to show lots of information without cluttering our vizualization and hence they are a nice way of creating interactive graphs.

Data

The data I am using is from the world bank. You can download it from here.

Visualization

In both the Shiny and Tableau visualizations below, you can select one or more regions and get the respective population treemap for the selected region(s) for 2015. Further, when you click on a country, you get a time series plot of the poluation since 1960. Both the treemap and the bar graph are interactive.

R-Shiny

Tableau

The code for R shiny is shown below.

server.R

In [ ]:
require(treemap)
require(dplyr)
require(shiny)
require(gridBase)
require(RColorBrewer)
require(plotly)

pop_data=read.csv("data/Population_by_country_region_1960_2015.csv", stringsAsFactors =FALSE)

### Handle cliks on a treemap
tmLocate <-
  function(coor, tmSave) {
    tm <- tmSave$tm
    
    # retrieve selected rectangle
    rectInd <- which(tm$x0 < coor[1] &
                       (tm$x0 + tm$w) > coor[1] &
                       tm$y0 < coor[2] &
                       (tm$y0 + tm$h) > coor[2])
    
    return(tm[rectInd[1], ])
    
  }
#######

shinyServer(function(input, output) {
  
  data_selected_region<-reactive({
    pop_data[pop_data$Region%in%input$region,]
  })
  
  output$threemap_population_country <- renderPlot({ 
    
    par(mar=c(0,0,0,0), xaxs='i', yaxs='i') 
    plot(c(0,1), c(0,1),axes=F, col="white")
    vps <- baseViewports()
    
    temp=data_selected_region()
    temp=filter(temp, Year==2015)
    .tm <<- treemap(temp, 
                    index="Country", 
                    vSize="Population", 
                    vColor="Population",
                    type="value",
                    title = "",
                    palette="Blues",
                    border.col ="white",
                    position.legend="right",
                    fontsize.labels = 16,
                    title.legend="")
  })
  
  
  treemap_clicked_country <- reactiveValues(
    center = NULL,
    for_condition=NULL
  )
  
  # Handle clicks on treemap by country
  observeEvent(input$click_treemap_country, {
    x <- input$click_treemap_country$x
    y <- input$click_treemap_country$y
    treemap_clicked_country$center <- c(x,y)
    
    if(is.null(treemap_clicked_country$for_condition)){
      treemap_clicked_country$for_condition=c(x,y)
    }
    else{treemap_clicked_country$for_condition=NULL}
  })

getRecord_population_country <- reactive({
  x <- treemap_clicked_country$center[1]
  y <- treemap_clicked_country$center[2]
  
  x <- (x - .tm$vpCoorX[1]) / (.tm$vpCoorX[2] - .tm$vpCoorX[1])
  y <- (y - .tm$vpCoorY[1]) / (.tm$vpCoorY[2] - .tm$vpCoorY[1])
  
  
  l <- tmLocate(list(x=x, y=y), .tm)
  z=l[, 1:(ncol(l)-5)]
  
    
    if(is.na(z[,1]))
      return(NULL)
  
    col=as.character(z[,1])
    
    filter(pop_data,Country==col)
  })
  
condition1<-reactive({
  
  refresh=refresh()
  
  if(is.null(treemap_clicked_country$for_condition) & refresh==0){
    result=1}else if((refresh%%2==0) & !is.null(treemap_clicked_country$for_condition)){
      result =0
    }else if((refresh%%2!=0) & !is.null(treemap_clicked_country$for_condition)){
      result =1
    }else if((refresh%%2!=0) & is.null(treemap_clicked_country$for_condition)){
      result =0
    }else if((refresh%%2==0) & is.null(treemap_clicked_country$for_condition)){
      result =1
    }
})


output$condition1 <- renderText({
  condition1()
})

outputOptions(output, 'condition1', suspendWhenHidden=FALSE)
  

output$population_country_time_series<-renderPlotly({
    temp=getRecord_population_country()
    title=paste0("Population of ",unique(temp$Country))
    
    f <- list(
      family = "Courier New, monospace",
      size = 16,
      color = "#7f7f7f"
    )
    plot_ly(temp, x = ~Year,y = ~Population, type = 'bar', color = I("orange")) %>%
        layout(title = title,font=f,
               xaxis = list(title = ""),
               yaxis = list(title = "",range=c(min(temp$Population),max(temp$Population))))
})


output$zoomout = renderUI({
  actionButton("refresh", em("Go to the previous page",style="text-align:center;color:red;font-size:200%"))
})

refresh=reactive({
  input$refresh
})

})
ui.R
In [ ]:
library(shiny)
library(shinydashboard)
library(plotly)

dashboardPage(
  dashboardHeader(title="World Population Drill Down",titleWidth =400),
  
  dashboardSidebar(width = 240,
                   br(),
                   br(),
                   br(),
                   selectizeInput("region", 
                               label = em("Select Region",style="text-align:center;color:#FFA319;font-size:150%"),
                               c("Latin America & Caribbean","South Asia","Sub-Saharan Africa",       
                                 "Europe & Central Asia","Middle East & North Africa", "East Asia & Pacific",       
                                 "North America"),selected = 'Europe & Central Asia',multiple=TRUE)
  ),
  
  
  dashboardBody(   
     conditionalPanel(
       condition = "output.condition1 == 1",

      tags$h1("World Population by Region in 2015",style="text-align:center;color:blue;font-size:200%"),
      
      tags$p("Click On Any Region To Get The Treemap Of That Region",style="text-align:center;color:purple"),
      plotOutput("threemap_population_country",height="600px",
                 click="click_treemap_country")
     ),
    
    conditionalPanel(
      condition = "output.condition1 == 0",
            br(),
            br(),
         plotlyOutput("population_country_time_series"),
         uiOutput("zoomout")
        )
      )
    )

Summary

Tabelau is expensive but awsome at creating stunning visualizations quickly. R-Shiny, on the other hand, is free and it needs coding. R-shiny could be a better options if you are good at R, Javascript and CSS for flexibility to create custom visualizations from any kind of statistical analysis but Tableau is painless and a good option when cost is not a concern and you do not need advanced and complex analysis.

comments powered by Disqus