2

I have two map plots of 'Total Population' and 'Population Density' created using a shape file. Now, I'm trying to build a shiny app so that I can change from Total Population to Population Density and the plot should change accordingly. When I ran the code, i got following error code:

Warning: Error in : ggplot2 doesn't know how to deal with data of class matrix

Here's the code that i've been trying to use:

library(shiny)
library(ggplot2) #Loading necessary libraries


ui <- fluidPage(
  selectInput("mr",
              label="Type of Plot",
              choices=c("Total Population", "Density"),
              selected="Total Population"),
  plotOutput("curv") #Giving an input name and listing out types to choose in the Shiny app
)

server <- function(input, output){

  output$curv <- renderPlot({
    ggplot() +
      geom_polygon(data = final.plot==input$mr, 
                   aes(x = long, y = lat, group = group, fill = Population), 
                   color = "black", size = 0.20) + 
      coord_map()+
      scale_fill_distiller(name="Population", palette = "YlGn")+
      labs(title="Population in Australia")
  }) # Output with the data file and input string to change when input changes.
}

shinyApp(ui = ui, server = server)

Any help is greatly appreciated.

UPDATE:

My dataset looks like this:

     id     long       lat order  hole piece
1 Ashmore and Cartier Islands 123.1169 -12.25333     1 FALSE     1
2 Ashmore and Cartier Islands 123.1206 -12.25611     2 FALSE     1
3 Ashmore and Cartier Islands 123.1222 -12.25861     3 FALSE     1
4 Ashmore and Cartier Islands 123.1239 -12.25528     4 FALSE     1
5 Ashmore and Cartier Islands 123.1258 -12.25333     5 FALSE     1
6 Ashmore and Cartier Islands 123.1275 -12.25619     6 FALSE     1
                          group Population Density
1 Ashmore and Cartier Islands.1         NA      NA
2 Ashmore and Cartier Islands.1         NA      NA
3 Ashmore and Cartier Islands.1         NA      NA
4 Ashmore and Cartier Islands.1         NA      NA
5 Ashmore and Cartier Islands.1         NA      NA
6 Ashmore and Cartier Islands.1         NA      NA

This is stored in the DataFrame called "final.plot". There's values of Population and Density for other states. I was able to create a static visualisation of Population and it looks like this:

enter image description here

There's a similar one for Density and I'm trying to create Shiny app where i can switch between these two so that the plot changes accordingly. Right now I've tried the following code:

library(shiny)
library(ggplot2) #Loading necessary libraries


ui <- fluidPage(
  selectInput("pop",
              label="Type of Plot",
              choices=c("Population", "Density"),
              selected="Total Population"),
  plotOutput("curv") #Giving an input name and listing out types to choose in the Shiny app
)

server <- function(input, output){

  output$curv <- renderPlot({
    ggplot() +
      geom_polygon(data = final.plot, 
                   aes(x = long, y = lat, group = group, fill = input$pop), 
                   color = "black", size = 0.25) + 
      coord_map()+
      scale_fill_distiller(name="Density", palette = "Spectral")+
      labs(title="Population in Australia")
  })
}
shinyApp(ui = ui, server = server)

But I'm getting an error saying "Discrete value supplied to continuous scale".

UPDATE 2: Here's the link for the dataset i'm using: Dataset

  • You could try to make a reactive dataset first (depending on input$mr) and then pass it to ggplot. – Michal Majka Apr 16 '16 at 10:47
  • 2
    this `data = final.plot==input$mr,` can't probably work and this explains your error message. I suspect other problems (for instance what does `final.plot` is supposed to be, and what `mr` is supposed to do) but that's hard to help if your example is not [reproducible](http://stackoverflow.com/questions/5963269/how-to-make-a-great-r-reproducible-example/5963610#5963610). – Vincent Bonhomme Apr 16 '16 at 11:42
  • Hi, I've edited the post. Would be great of you could look at it and comment on it. Thanks! – Arvind Suryanarayana Apr 17 '16 at 08:10
  • Can you share the data you want to use to create the plots? You can use the `dput` function to convert your data to a copy paste friendly format – RmIu Apr 17 '16 at 11:25
  • Hi, i've included a link to my dataset as its too large for me to post it here. Thanks for looking into this. – Arvind Suryanarayana Apr 17 '16 at 12:48
  • 1
    You can use head() in conjunction with dput() - `dput(head(final.plot,50))` will provide the first 50 rows of your data frame. – Graeme Apr 17 '16 at 13:18

1 Answers1

1

I've had a quick look at your code and have a couple of suggestions.

1) When providing your data set you can use the function dput() - this writes a text representation of your data.frame which people answering your question can simply paste into R. For example:

dput(final.plot)

This will produce text output that I can assign to a dataframe by prefixing final.plot <- to the output. I have recreated your dataframe and used dput() to output it as text below. Now other users can quickly cut & paste your data:

Note this dataset is faulty - see below

final.plot <- structure(list(id = structure(c(1L, 1L, 1L, 1L, 1L, 1L), .Label = "Ashmore and Cartier Islands", class = "factor"), 
                             long = c(123.1169, 123.1206, 123.1222, 123.1239, 123.1258, 123.1275),
                             lat = c(-12.25333, -12.25611, -12.25861, -12.25528, -12.25333, -12.25619),
                             order = 1:6, hole = c(FALSE, FALSE, FALSE, FALSE, FALSE, FALSE),
                             piece = c(1L, 1L, 1L, 1L, 1L, 1L),
                             group = structure(c(1L, 1L, 1L, 1L, 1L, 1L),
                             .Label = "Ashmore and Cartier Islands.1", class = "factor"), 
                             Population = c(NA, NA, NA, NA, NA, NA),
                             Density = c(NA, NA, NA, NA, NA, NA)),
                             .Names = c("id", "long", "lat", "order", "hole", "piece", "group", "Population", "Density"),
                             class = "data.frame",
                             row.names = c(NA, -6L))

The error "Discrete value supplied to continuous scale" is caused by two issues.

i) You are passing NA in both your Population and Density columns. The dataframe below adds some (unrealistic) numbers to these columns and the error is removed when I run the plotting code in isolation.

Corrected Toy Dataset

final.plot <- structure(list(id = structure(c(1L, 1L, 1L, 1L, 1L, 1L), .Label = "Ashmore and Cartier Islands", class = "factor"), 
                             long = c(123.1169, 123.1206, 123.1222, 123.1239, 123.1258, 123.1275),
                             lat = c(-12.25333, -12.25611, -12.25861, -12.25528, -12.25333, -12.25619),
                             order = 1:6, hole = c(FALSE, FALSE, FALSE, FALSE, FALSE, FALSE),
                             piece = c(1L, 1L, 1L, 1L, 1L, 1L),
                             group = structure(c(1L, 1L, 1L, 1L, 1L, 1L),
                                               .Label = "Ashmore and Cartier Islands.1", class = "factor"), 
                             Population = c(1, 2, 3, 4, 5, 6),
                             Density = c(7, 3, 9, 1, 3, 6)),
                        .Names = c("id", "long", "lat", "order", "hole", "piece", "group", "Population", "Density"),
                        class = "data.frame",
                        row.names = c(NA, -6L))

ii) When run interactively the error is caused because you are not passing appropriate data to fill in fill = input$pop. You should be passing the values from final.plot$Population or final.plot$Density depending on what was selected. You are instead passing the output of the dropdown box - "Population" or "Density". This can be corrected using a switch statement within renderPlot:

# User input assigns appropriate data to selectedData which can be passed to other functions:
  selectedData <- switch(input$pop, 
                   "Population" = final.plot$Population,
                   "Density" = final.plot$Density)

2) It would be helpful if you could provide the code which produced the static map you show in your Update above. When debugging Shiny code I find it easiest to get the function working non-interactively first and then to incorporate it into Shiny. I tried to extract your plotting code below but it is not producing the expected results:

library(ggplot2) #Loading necessary libraries
library(mapproj)
library(maps)
ggplot() +
  geom_polygon(data = final.plot, 
               [![aes(x = long, y = lat, group = group, fill = Population), 
               color = "black", size = 0.25) + 
  coord_map()+
  scale_fill_distiller(name="Density", palette = "Spectral")+
  labs(title="Population in Australia")`

enter image description here

3) I am not familiar with plotting data onto maps in R but I believe your app will need to load in library(mapproj) and library(maps) to get the results you need. Here is all the above put together:

library(shiny)
library(ggplot2) #Loading necessary libraries
#I added the two lines below:
library(mapproj) 
library(map)

ui <- fluidPage(

  selectInput("pop",
              label="Type of Plot",
              choices=list("Population", "Density"),
              selected="Population"), #NOTE: Total Population changed to Population so that it selects correct default value

  plotOutput("curv") #Giving an input name and listing out types to choose in the Shiny app
)

server <- function(input, output){

#You will probably want to simply import your dataframe final.plot using read.table etc:      
final.plot <- structure(list(id = structure(c(1L, 1L, 1L, 1L, 1L, 1L), .Label = "Ashmore and Cartier Islands", class = "factor"), 
                                   long = c(123.1169, 123.1206, 123.1222, 123.1239, 123.1258, 123.1275),
                                   lat = c(-12.25333, -12.25611, -12.25861, -12.25528, -12.25333, -12.25619),
                                   order = 1:6, hole = c(FALSE, FALSE, FALSE, FALSE, FALSE, FALSE),
                                   piece = c(1L, 1L, 1L, 1L, 1L, 1L),
                                   group = structure(c(1L, 1L, 1L, 1L, 1L, 1L),
                                                     .Label = "Ashmore and Cartier Islands.1", class = "factor"), 
                                   Population = c(1, 2, 3, 4, 5, 6),
                                   Density = c(7, 3, 9, 1, 3, 6)),
                              .Names = c("id", "long", "lat", "order", "hole", "piece", "group", "Population", "Density"),
                              class = "data.frame",
                              row.names = c(NA, -6L))

  output$curv <- renderPlot({
    #Assign value of selectedData based upon user input:
    selectedData <- switch(input$pop, 
                   "Population" = final.plot$Population,
                   "Density" = final.plot$Density)
    ggplot() +
      geom_polygon(data = final.plot, 
                   aes(x = long, y = lat, group = group, fill =  selectedData), 
                   color = "black", size = 0.25) + 
      coord_map()+
      scale_fill_distiller(name="Density", palette = "Spectral")+
      labs(title="Population in Australia")
  })
}
shinyApp(ui = ui, server = server)

Now all you need to do is substitute your code which produced the static map shown in your update for the faulty code in renderPlot in your shiny app.

Graeme
  • 363
  • 1
  • 9
  • Wow! Thank you for the detailed explanation about the problem Graeme! It's working now. I had to change fill = selectedData and that did the trick! Thanks for taking the time to show this to me. – Arvind Suryanarayana Apr 17 '16 at 13:15
  • I did mark it as the answer. Also, was wondering if i could change the name of the legend according to the choice? – Arvind Suryanarayana Apr 17 '16 at 13:28
  • 1
    Thank you Arvind, change the line `scale_fill_distiller(name="Density", palette = "Spectral")+` to `scale_fill_distiller(name=input$pop, palette = "Spectral")+` this will automatically change the figure legend based upon user input. – Graeme Apr 17 '16 at 14:31
  • Thanks again Greame! Worked perfectly! Looks sweet now! – Arvind Suryanarayana Apr 17 '16 at 16:07