0

I'm plotting some data with a time-duration in seconds on the x-axis using ggplot2. The data is the result of either a simulation or is computed on a monitored trace.

I'm using scale_x_time to get proper labels. However, as the data spans a significant time frame, I would like to include the number of days and weeks in the labels. Minimal example:

library(ggplot2)
n = 100
d <- data.frame(
    t = seq(from=0, to=100*24*60*60, length.out=n),
    v = runif(n, min=10, max=20)
)
ggplot(d) + geom_point(aes(x=t, y=v)) + scale_x_time()

ggplot output

Is there a way to achieve just that using the hms package and base R? Optimally, I would like to just give a format string, e.g. "%Ww %dd %H:%M:%S".

The hms package contains plenty of helper functions to extract information from hms objects and format the objects. These functions are not exported. I had hoped to extract the number of days, format the number of days and weeks myself and then construct a new hms object and use the shipped format function. But I don't want to re-implement all the hms package's logic in my own code.

My workaround thus far has been to convert hms objects to lubridate period objects. lubridate has plenty of functions to extract information from periods. But this seems way too complicated to me. Also, periods express "human" time spans which does not match the use-case.

library(lubridate)

format_dhms <- function(x) {
    p <- as.period(x)

    return(
        format(p)
    )
}

format_wdhms <- function(x) {
    p <- as.period(x)

    w <- floor(day(p) / 7)

    return(
        paste(
            ifelse(w >= 1, paste(w, "w", sep=""), ""),
            ifelse(w >= 1, format(p - weeks(w)), format(p))
        )
    )
}

ggplot(d) + geom_point(aes(x=t, y=v)) + scale_x_time(labels=format_wdhms)
Seoester
  • 1,116
  • 14
  • 18

1 Answers1

1

Using this question/answer as a starting point "Convert seconds to days: hours:minutes:seconds" and extending to add weeks:

library(ggplot2)
n = 100
d <- data.frame(
  t = seq(from=0, to=100*24*60*60, length.out=n),
  v = runif(n, min=10, max=20)
)

wdhms <- function(t){
  t<-as.numeric(t)
  paste(t %/% (60*60*24*7), formatC(t %/% (60*60*24) %% 7, width = 2, format = "d", flag = "0") 
    ,paste(formatC(t %/% (60*60) %% 24, width = 2, format = "d", flag = "0")
           ,formatC(t %/% 60 %% 60, width = 2, format = "d", flag = "0")
           ,formatC(t %% 60, width = 2, format = "d", flag = "0")
           ,sep = ":")
  )
}
ggplot(d) + geom_point(aes(x=t, y=v)) + scale_x_time(labels=wdhms)
Dave2e
  • 22,192
  • 18
  • 42
  • 50
  • That works, formats nicely and uses only base R! But all the no of weeks / days / hours calculations have to be re-implemented. I'm leaving this question open for now in case somebody comes up with a cleaner solution. – Seoester Oct 16 '19 at 10:20
  • `as.numeric()` leads to rounding issues. The nested paste could be replaced by `format(hms::as_hms(t %% 24))` (but yields a decimal point for many values). – Seoester Oct 16 '19 at 10:32