Peter Haschke



Back to the Index

Political Terror Scale ShinyApp

I have been following RStudio’s Shiny project for a while. I think Shiny applications are a pretty neat way to explore data interactively. As the Political Terror Scale website currently has very little to offer in terms of an interactive way to visualize and explore the data, I thought I mess around with Shiny and see if I could make sense of it and produce anything even remotely reasonable.

It turns out that RStudio provides an incredibly useful tutorial for getting started with Shiny. All one has to do is to install the shiny package in R and follow the instructions in the tutorial. From then on out little else is required beyond basic R knowledge.

Below I reproduce the code for the little test application I wrote. The application itself is hosted on shinyapps.io and can be accessed there: PTS Shiny App.1 Please note that my code is terribly cumbersome, inefficient, and inelegant. The ShinyApp servers are also pretty slow, I think. In any case the app is probably not going to be very smooth and/or responsive. Any suggestions will be appreciated.

ui.R

library(shiny)

# Loading Data


pts <- read.csv("http://www.peterhaschke.com/files/PTS.csv") 


shinyUI(pageWithSidebar(

  # Application title


  headerPanel("The Political Terror Scale (PTS) App"),

  # Sidebar with controls 


  sidebarPanel(

	# Changing some html settings of the user interface


	tags$style(type='text/css', "h1 { color: #586e75;}"),
	tags$style(type='text/css', ".well { background-color: #eee8d5; }"),
	tags$style(type='text/css', ".span4 { max-width: 300px }"),
	tags$style(type="text/css", "textarea { max-width: 220px }"),
	tags$style(type="text/css", ".jslider { max-width: 220px; }"),


	# Input selector for PTS Scales


	selectInput("scale", "Choose Scale:", 
                choices = c("Amnesty International" = "Amnesty",
                "State Department" = "StateDept")),
	br(),
	br(),

	# Input selector for Country


	selectInput("country", "Choose Country:",
                unique(pts$Country)),

	br(),
	br(),

	# Input checkboxes


	checkboxInput("trend", "Show Country Trend", FALSE),

	checkboxInput("world", "Show World Trend", FALSE),

	br(),
	br(),

	# Input Slider


  	sliderInput("span", 
                "Smoothness of Fit:", 
                min = 0.25,
                max = 1, 
                value = 0.75,
                step= 0.05),

	br(),
	br(),

	# Text area


	helpText("Source:", a("The Political Terror Scale",
		href="http://www.politicalterrorscale.org/"))
  ),

  # Output Panel


  mainPanel(

    tags$style(type='text/css', "body { background-color: #fdf6e3;}"),

    # separate tabs for various ouputs


	tabsetPanel(
		tabPanel("Plot", plotOutput("plot", height = "600px")), 
		tabPanel("Table", tableOutput("table")),
		tabPanel("Data", tableOutput("table2"), 
			downloadButton('downloadData', "Download this Data"), br(), br())
	)

  )

… and …

server.R

library(shiny)
library(ggplot2)
library(ggthemes)

theme_set(theme_solarized())

pts <- read.csv("http://www.peterhaschke.com/files/PTS.csv") # Web



shinyServer(function(input, output) {

  output$plot <- renderPlot({

  if (input$scale == "Amnesty") {

    temp <- data.frame(Year = pts$Year, Scale = pts$Amnesty, Country = pts$Country)
    world <- data.frame(Year = pts$Year, Scale = pts$Amnesty)

  } else {

    temp <- data.frame(Year = pts$Year, Scale = pts$StateDept, Country = pts$Country)
    world <- data.frame(Year = pts$Year, Scale = pts$StateDept)

  }

  temp <- subset(temp, temp$Country == input$country)

  if (length(temp$Scale) - sum(is.na(temp$Scale)) > 5) {

    p1 <- ggplot(temp, aes(x=Year, y=Scale)) +
      ggtitle(input$country) +
      geom_point(size = 3, col = "black") +
      labs(y = paste("PTS - ", input$scale)) +
      scale_x_continuous(breaks = seq(1975, 2015, by = 5)) +
      coord_cartesian(ylim = c(0.25 ,5.75), xlim = c(1974.5, 2015.5)) 

    if(input$trend) {

      p1 <- p1 +
        geom_smooth(aes(col = paste(" ",Country, "Trend  ")), method = "loess",
          span = input$span, alpha = 0.3, size = 0.7) +
        theme(legend.direction = "horizontal", legend.position = "bottom") +
        guides(color = guide_legend(title = "", title.position = "top",
      	  title.hjust=.5, keywidth = 2, keyheight = 1, , override.aes = list(alpha=0.1))) +
        scale_color_manual("", values = c("#268bd2", "#d33682"))
    }

  } else {

    p1 <- ggplot(temp, aes(x=Year, y=Scale)) +
      ggtitle(input$country) +
      geom_point(size = 3, col = "black") +
      labs(y = paste("PTS - ", input$scale)) +
      scale_x_continuous(breaks = seq(1975, 2015, by = 5)) +
      coord_cartesian(ylim = c(0.25 ,5.75), xlim = c(1974.5, 2015.5))  
    }

    if(input$world) {

      p1 <- p1 + 
      geom_smooth(data = world, aes(x= Year, y= Scale, color = "World Trend"), alpha = 0.3) + 
      theme(legend.direction = "horizontal", legend.position = "bottom",
        legend.background = element_rect(fill="transparent")) +
      guides(color = guide_legend(title = "", title.position = "top", title.hjust=.5,
    	keywidth = 2, keyheight = 1, override.aes = list(alpha=0.1)))

    }

  	print(p1)

  }, bg="transparent")

  # Summary Stats


  output$table <- renderTable({

    if (input$scale == "Amnesty") {

      temp <- data.frame(Year = pts$Year, Scale = pts$Amnesty, Country = pts$Country)
      world <- data.frame(Year = pts$Year, Scale = pts$Amnesty)

    } else {

      temp <- data.frame(Year = pts$Year, Scale = pts$StateDept, Country = pts$Country)
      world <- data.frame(Year = pts$Year, Scale = pts$StateDept)

    }

    temp <- subset(temp, temp$Country == input$country)

    m  <- round(mean(temp$Scale, na.rm = TRUE), digits = 2)
    if (is.nan(m) == TRUE) {m <- "NA"}
    m2 <- round(median(temp$Scale, na.rm = TRUE), digits = 2)
    if (is.na(m2) == TRUE) {m2 <- "NA"}
    s  <- round(sd(temp$Scale, na.rm = TRUE), digits = 2)
    if (is.na(s) == TRUE) {s <- "NA"}
    mi <- round(min(temp$Scale, na.rm = TRUE), digits = 0)
    if (is.infinite(mi) == TRUE) {mi <- "NA"}
    ma <- round(max(temp$Scale, na.rm = TRUE), digits = 0)
    if (is.infinite(ma) == TRUE) {ma <- "NA"}
    
    ym <- sum(is.na(temp$Scale))

    foo <- c(input$scale, m, m2, s, mi, ma, ym)
    word <- c("Scale", "Mean", "Median", "Standard Deviation", "Min", "Max", "Obs. Missing")

    put <- data.frame(word, foo)
    names(put) <- c("Country", input$country)
    put

  })

  # Table displaying the data


  output$table2 <- renderTable({

    temp <- subset(pts, pts$Country == input$country)

    foo <- data.frame("Year" = temp$Year, "Amnesty" = temp$Amnesty, "StateDept" = temp$StateDept)
    names(foo) <- c("Year", "PTS -\nAmnesty", "PTS -\nState Dept.")
    foo  

  })

  # Download Data


  output$downloadData <- downloadHandler(
    filename = function() { paste(input$country, '.csv', sep='') },
    content = function(file) {
      write.csv(subset(pts, pts$Country == input$country)[,5:7], file)
    }
  )

})
  1. It took me a while to figure out how to deploy the shiny application I wrote. It is important that your R packages are up-to-date, that you specify an app name that is not too short and not too long (and doesn’t contain any funky characters). You also need the devtools and shinyapps R packages. They are available on GitHub. For some reason the documentation for ShinyApps and instructions for how to deploy your app to the ShinyApps servers is kind of hidden. Here’s the link: ShinyApps

This post is filed under category R, This post is filed under category Human Rights, and contains the following tags: R, ggplot2, plots, Shiny, ShinyApps, Human Rights, Repression, Political Terror Scale, PTS.

Back to the Blog-Index