0

I have tried all solutions recommended in How to display only integer values on an axis using ggplot2. Unfortunately, I could not solve the issue with any of them.

I have created a Shiny app that produces line graphs of annual data on a variety of variables. This works out nicely for most parameterizations:

enter image description here

No non-integer breaks

However, if I choose certain time spans on the slider, it produces graphs that have non-integer breaks on the x-axis, which makes no sense for a yearly data.

enter image description here

With non-integer breaks

Edit: Here a minimal reproducible version of the application

library(tidyverse)
library(shiny)

options(scipen = 999)

# Data
data1<-data.frame(values = c(15500, 16300, 18200, 28300, 23500, 23700,
                             31500, 35800, 34700, 36900, 40000, 44700,
                             53300, 55800, 69800, 89500, 1.13E+5,
                             1.53E+5, 1.77E+5, 1.83E+5, 1.99E+5), 
                  year = seq(1990, 2010, 1))

#Shiny app

ui <- fluidPage(
  sidebarLayout(
    sidebarPanel(
    sliderInput("period", "Year:", min = 1990, max = 2010, value = c(1990, 2010), sep = "")),
mainPanel(plotOutput("ggplot2"))))

server <- function(input, output) {

  data1_subset <- reactive({
      filter(data1, year >= input$period[1] & year <= input$period[2])
  })


  output$ggplot2 <- renderPlot({
        ggplot(data = data1_subset(), aes(x = year, y = values)) +
          geom_line(aes(color = "red")) +
          scale_x_continuous(name = "Year") +
          scale_color_discrete(guide=FALSE)+
          theme_minimal()

  })
}

shinyApp(ui = ui, server = server)

To see the problem, select e.g. time span 2000-2010

Is there any way to suppress non-integer breaks as there are clearly nonsensical with annual data?

Thanks a lot in advance for your help!

Frank
  • 2,386
  • 17
  • 26
miwin
  • 61
  • 9
  • 1) It is probably just a `ggplot2` issue, so the `shiny` part will likely not matter. 2) How come all of the answers in that linked question don't work? Please show exactly what you have tried. Adding `breaks = function(x) unique(floor(pretty(seq(0, (max(x) + 1) * 1.1))))` to your `scale_x_continuous` should work... – Axeman Aug 01 '18 at 13:26
  • Thank you very much for your answer. If I simply add `breaks = function(x) unique(floor(pretty(seq(0, (max(x) + 1) * 1.1))))` to the `scale_x_continuous`, as you suggest, the x-axis shows only a break at the year 2000 (or no break if 2000 is not in the range) (https://imgur.com/a/CpPnH0x). Maybe I did not fully understand what the function does and one can adapt it accordingly. – miwin Aug 01 '18 at 13:46
  • Ok, I now see that function isn't all that great. How about `breaks = function(x) unique(floor(pretty(seq(min(x - 1), (max(x) + 1) * 1.1))))`? I can't test, because there is no data in your question. Or try one of the other answers there. – Axeman Aug 01 '18 at 13:48
  • Unfortunately, this produces exactly the same results as the previous function: only one break (at year 2000) or none, depending on the range. – miwin Aug 01 '18 at 13:53
  • Well, as it stands now, there is nothing specific in this question that makes it different from your linked question. We can't actually reproduce the problem you are having, since there is no minimal reproducible code here. – Axeman Aug 01 '18 at 13:54
  • I have now created a minimal reproducible version of the application. – miwin Aug 01 '18 at 15:07

2 Answers2

2

Thanks for your help! It seems that the answer was much simpler as I thought. Putting breaks = function(x) unique(floor(pretty(x))) in my scale_x_continuous() function produced integer-only breaks, even without transforming the data into Date format. Removing the unique() does not change the behavior in my case, but it might do in other cases.

miwin
  • 61
  • 9
0

You should take care to use the correct class for your date column year (something like Date or POSIXct). As the comment suggests, you need to pick a particular date, like Jan 1st. This will inform ggplot2 about what's probably your intention and it will make it easier for you to steer the display format to something meaningful.

It will work out of the box, you can tune it more with ggplot's date/time scales.

The reprex with the presented idea implemented (and one more glitch regarding the line colour fixed):

library(tidyverse)
library(shiny)

# Data
tibble(values = c(15500,16300,18200,28300,23500,23700,
                  31500,35800,34700,36900,40000,44700,
                  53300,55800,69800,89500,1.13E+5,
                  1.53E+5,1.77E+5,1.83E+5,1.99E+5), 
                  year = seq(1990, 2010, 1)) %>% 
  mutate(year = lubridate::ymd(year, truncated = 2L)) ->
  data1

# Shiny app
ui <- fluidPage(
  sidebarLayout(
    sidebarPanel(
    sliderInput("period", "Year:", 
                min = 1990, max = 2010, 
                value = c(1990, 2010), sep = "")),
mainPanel(plotOutput("ggplot2"))))

server <- function(input, output) {

  data1_subset <- reactive({
      filter(data1,
             year >= lubridate::ymd(input$period[1], truncated = 2L),
             year <= lubridate::ymd(input$period[2], truncated = 2L))
  })


  output$ggplot2 <- renderPlot({
        ggplot(data = data1_subset(), aes(x = year, y = values)) +
          geom_line(color = "red") +
          xlab("Year") +
          theme_minimal()

  })
}

shinyApp(ui = ui, server = server)
liborm
  • 2,634
  • 20
  • 32
  • Is it sensible to convert an ordinary 4-digit year variable in `Date` format? The thread here seems to suggest it is not: https://stackoverflow.com/questions/30255833/convert-four-digit-year-values-to-a-date-type – miwin Aug 01 '18 at 14:15
  • 1
    I would say the reasoning is the other way around - you want a date-time axis, which formats itself nicely as humans expect a date-time axis to work, without much work on your side. `ggplot` can do this, if it knows that you want it. Then what is left is 'how do I tell `ggplot` I want it' - and here you need to pick a particular date so you conform to the 'protcol'. – liborm Aug 01 '18 at 14:38
  • Thanks, this works fine for all time spans except for 2008-2010 and 2009-2010, where breaks for points in time appear, where no data exists. – miwin Aug 01 '18 at 17:14
  • Because axis labels are not data points. That's completely correct IMHO. If you want to stress presence/abscence of a datapoint, add one more layer with `geom_point(size=4)`. – liborm Aug 01 '18 at 17:52
  • To be more clear, it did not completely suppress non-integer axis breaks as mentioned in the question. But your point is well taken. – miwin Aug 01 '18 at 18:27
  • I tried to provide a reasonable -and- simple solution. Now you can delve into making a custom `breaks` function, which calls the original one (because it's really smart eg in not producing too much breaks), and then filter out those you don't like...;) – liborm Aug 01 '18 at 18:45