Search code examples
rplotlydstr-plotly

Plotly barcharts with POSIXct and daylight-savings-time


I want to plot a barchart timeseries with timestamps on the x-axis. My problem is the daylight-saving in Octobre, as there are 2 timestamps at 02:00, one with CEST timezone and one with CET.

The plotly-barchart superimposes these values, which is hard to spot and looks like an error when you hover over the barchart as you will find that the value does not correspond to the y-axis.

How can I display these 2 bars side by side, without changing the tickmode to "array" and defining tickvals/ticktext?

library(plotly)

df <- structure(list(value = round(runif(46, 20, 26), 3), 
                     timestamp = structure(c(1603576800, 1603584000, 1603587600, 1603591200, 1603594800, 1603598400, 
                                             1603602000, 1603605600, 1603609200, 1603612800, 1603616400, 1603620000,
                                             1603623600, 1603627200, 1603630800, 1603634400, 1603638000, 1603641600,
                                             1603645200, 1603648800, 1603652400, 1603656000, 1603659600, 1603576800,
                                             1603584000, 1603587600, 1603591200, 1603594800, 1603598400, 1603602000, 
                                             1603605600, 1603609200, 1603612800, 1603616400, 1603620000, 1603623600,
                                             1603627200, 1603630800, 1603634400, 1603638000, 1603641600, 1603645200,
                                             1603648800, 1603652400, 1603656000, 1603659600), 
                                           class = c("POSIXct", "POSIXt"), tzone = "Europe/Berlin"), 
                     col = c(rep("#FFBA00", 23), rep("#B9CC2E", 23)), 
                     name = c(rep("Grp1", 23), rep("Grp2", 23))), 
                row.names = 1:46, class = "data.frame")

plot_ly(data = df, text = "text") %>%
  add_trace(x = ~timestamp, y = ~value, type = "bar",
            marker = list(color = ~col),
            text = ~sprintf("Time: %s<br>Value: %s", timestamp, value),
            hoverinfo = "text",
            name = ~name) %>% 
  plotly::layout(xaxis = list(title = 'Time', type = "date"),
                 barmode = 'group')

Solution

  • Wraping up my comments to an answer for future readers:

    plotly currently doesn't support displaying daylight saving time natively:

    Changes our internal date linearization to use UTC rather than local milliseconds. Every day on a Plotly graph will now be 24 hours long, with no daylight shifts.

    source: https://github.com/plotly/plotly.js/pull/1194#issue-95087563

    Accordingly, the only possibility I'm aware of, to work around this issue is using the tickvals and ticktext arguments in plotly's layout function (not desired by @SeGa and this is reasonable as we'll have duplicated tick labels. Nevertheless, maybe the following helps others).

    library(plotly)
    
    DF <- structure(list(value = round(runif(46, 20, 26), 3), 
                         timestamp = structure(c(1603576800, 1603584000, 1603587600, 1603591200, 1603594800, 1603598400, 
                                                 1603602000, 1603605600, 1603609200, 1603612800, 1603616400, 1603620000,
                                                 1603623600, 1603627200, 1603630800, 1603634400, 1603638000, 1603641600,
                                                 1603645200, 1603648800, 1603652400, 1603656000, 1603659600, 1603576800,
                                                 1603584000, 1603587600, 1603591200, 1603594800, 1603598400, 1603602000, 
                                                 1603605600, 1603609200, 1603612800, 1603616400, 1603620000, 1603623600,
                                                 1603627200, 1603630800, 1603634400, 1603638000, 1603641600, 1603645200,
                                                 1603648800, 1603652400, 1603656000, 1603659600), 
                                               class = c("POSIXct", "POSIXt"), tzone = "Europe/Berlin"), 
                         col = c(rep("#FFBA00", 23), rep("#B9CC2E", 23)), 
                         name = c(rep("Grp1", 23), rep("Grp2", 23))), 
                    row.names = 1:46, class = "data.frame")
    
    # DF$timestamp_utc <- DF$timestamp
    # attr(DF$timestamp_utc, "tzone") <- "UTC" 
    
    plot_ly(data = DF, text = "text") %>%
      add_trace(x = ~as.numeric(timestamp), y = ~value, type = "bar",
                marker = list(color = ~col),
                text = ~sprintf("Time: %s<br>Value: %s", timestamp, value),
                hoverinfo = "text",
                name = ~name) %>% 
      plotly::layout(xaxis = list(title = 'Time',
                                  ticktext = ~timestamp, 
                                  tickvals = ~as.numeric(timestamp),
                                  tickmode = "array"),
                     barmode = 'group')
    

    result


    Minor edit: If we want to reduce the amount of ticklabels without a lot of effort we can use pretty in ticktext and tickvals:

    ticktext = ~pretty(timestamp), 
    tickvals = ~as.numeric(pretty(timestamp))
    

    pretty

    Related docs: Formatting Ticks in R