Putting times with MM:SS axis labels?

104 views
Skip to first unread message

Winston Chang

unread,
Jun 7, 2009, 10:12:14 PM6/7/09
to ggplot2
I've been plotting some data with times, using the chron package. These times represent durations rather than absolute times. When I plot the data, the time axis is labeled with the numerical values of the "times" class, which is in days (1 = one day, 0.0416 = one hour, and so on). Is there a simple way to make axis labels show up in HH:MM:SS format instead?

library(chron)
library(ggplot2)

duration <- times(c("00:34:10", "00:35:10", "00:32:49", "00:34:34", "00:43:24",
"00:28:16", "00:34:41", "00:20:53", "00:31:34", "00:35:55", "00:30:50",
"00:31:36", "00:30:43", "00:29:55", "00:34:48", "00:32:18", "00:30:47",
"00:27:43", "00:28:07", "00:34:54", "00:28:18", "00:24:44", "00:28:13",
"00:27:08", "00:34:40", "00:35:06", "00:27:29", "00:34:05", "00:34:22",
"00:31:18"))

df <- data.frame(duration)

# Make a histogram (x-axis label is in days)
ggplot(df, aes(x=duration)) + geom_histogram()


# This shows the numeric values (in days)
as.numeric(df$duration)

# This shows the conversion to character, which is in HH:MM:SS format
as.character(df$duration)


I suppose it would be possible to use scale_x_continuous and set the breaks and values, using as.character() to generate the labels from the numeric values of the breaks. But this would require setting the breaks manually. Is it possible to use the automatically-calculated breaks and somehow use as.character() to convert the labels to HH:MM:SS format?

-Winston

learnr

unread,
Jun 8, 2009, 7:12:32 AM6/8/09
to ggplot2
Is this presentation what you had in mind?

lbl_formatter <- function(x) {as.character(times(x))}

ggplot(df, aes(x=duration)) +
geom_histogram() +
scale_x_continuous(formatter = lbl_formatter)

--
http://learnr.wordpress.com/

Winston Chang

unread,
Jun 8, 2009, 12:35:39 PM6/8/09
to learnr, ggplot2
Thanks - that's very helpful. It'll still be necessary to define the breaks manually, if I want them to fall on nice round numbers. This is because the time format is in days. The automatically-calculated breaks for raw numeric values might be .015, .020, .035, but when translated to HH:MM:SS, that's equivalent to 00:21:36, 00:28:48, and 00:36:00.


I found an alternative solution which might work better for my purposes: convert the vector from "times" objects (encoded in days) to number of minutes, and write a formatter that prints that in a nice format. This way, the automatic breaks will fall at convenient locations.

In this version, the times() function is used only to parse the time strings. Then the times vector is converted to minutes.


library(chron)
library(ggplot2)

duration <- times(c("00:34:10", "00:35:10", "00:32:49", "00:34:34", "00:43:24",
"00:28:16", "00:34:41", "00:20:53", "00:31:34", "00:35:55", "00:30:50",
"00:31:36", "00:30:43", "00:29:55", "00:34:48", "00:32:18", "00:30:47",
"00:27:43", "00:28:07", "00:34:54", "00:28:18", "00:24:44", "00:28:13",
"00:27:08", "00:34:40", "00:35:06", "00:27:29", "00:34:05", "00:34:22",
"00:31:18"))

df <- data.frame(duration)

# Convert from number of days to number of minutes
df$duration_min <- as.numeric(df$duration) * 24 * 60

# Print number of minutes in HH:MM:SS format
lbl_formatter <- function(x) {
    h <- floor(x/60)
    m <- floor(x %% 60)
    s <- round(60*(x %% 1))  # Round to nearest second
    sprintf('%02d:%02d:%02d', h, m, s)
}

# Make a histogram
ggplot(df, aes(x=duration_min)) +

 geom_histogram() +
 scale_x_continuous(formatter = lbl_formatter)

-Winston

JiHO

unread,
Jun 8, 2009, 4:47:22 PM6/8/09
to Winston Chang, ggplot2
I am replaying a conversation we got off list with winston (I forgot
to reply-all).

Basically, the remaining issue is that ggplot seems to be using .
instead of : to separate hours and minutes in the labels. I remember
seeing : not too long ago so this is likely the consequence of a
recent change and looks like a bug.

On 2009-June-08 , at 12:35 , Winston Chang wrote:

>>>> I found an alternative solution which might work better for my
>>>> purposes: convert the vector from “times” objects (encoded in
>>>> days) to number of minutes, and write a formatter that prints
>>>> that in a nice format. This way, the automatic breaks will fall
>>>> at convenient locations.
>>>

>>> if you can somehow convert it to POSIXct ggplot deals with the
>>> dates correctly : displays labels at the correct locations and
>>> formats them to show only the part of the date that matters (i.e
>>> only hours, or only days, or only years, etc. depending on the
>>> range of variation on the scale)
>>
>> That seems to work well for the graphs, although it seems to use a
>> "." instead of ":" to separate the numbers.
>> duration <- c("00:34:10", "00:35:10", "00:32:49", "00:34:34",

>> "00:43:24",
>> "00:28:16", "00:34:41", "00:20:53", "00:31:34", "00:35:55",
>> "00:30:50",
>> "00:31:36", "00:30:43", "00:29:55", "00:34:48", "00:32:18",
>> "00:30:47",
>> "00:27:43", "00:28:07", "00:34:54", "00:28:18", "00:24:44",
>> "00:28:13",
>> "00:27:08", "00:34:40", "00:35:06", "00:27:29", "00:34:05",
>> "00:34:22",
>> "00:31:18")
>>

>> dct <- as.POSIXct( strptime(duration, format="%H:%M:%S"))
>>
>> df <- data.frame(dct)
>>
>> df$dct <- as.POSIXct( strptime(duration, format="%H:%M:%S"))
>>
>> # Separator is period instead of colon
>> ggplot(df, aes(x=dct)) + geom_histogram()
>>
>>
>> df$dct
>> # Text output looks like this:
>> #[1] "2009-06-08 00:34:10 CDT" "2009-06-08 00:35:10 CDT"
>> #[3] "2009-06-08 00:32:49 CDT" "2009-06-08 00:34:34 CDT"
>> # ...
>
> that's strange, I remember it being a colon. I guess Hadley changed
> the formatter. You should bring that back to the list (it was a
> mistake on my part to reply to you only in the first place. I meant
> to reply to the list).
>
>> Also, the text display format is a little inconvenient. If it were
>> possible to change the text output as well, that would be ideal.
>
> you can probably get something more convenient with format() for
> some specific output.
>
> If you cant to modify it globally for R, you need redefine a "print"
> method for object of class POSIXt but beware that this can have
> unexpected consequences = it will *show* you the data differently
> but internally, the representation will still include dates etc.
> That's a recipe for headaches down the road, once you forget what
> you did initially. I would advise to define and use this print
> method only locally, for the particular project you are currently
> dealing with.
>
> duration <- c("00:34:10", "00:35:10", "00:32:49", "00:34:34",

> "00:43:24",
> "00:28:16", "00:34:41", "00:20:53", "00:31:34", "00:35:55",
> "00:30:50",
> "00:31:36", "00:30:43", "00:29:55", "00:34:48", "00:32:18",
> "00:30:47",
> "00:27:43", "00:28:07", "00:34:54", "00:28:18", "00:24:44",
> "00:28:13",
> "00:27:08", "00:34:40", "00:35:06", "00:27:29", "00:34:05",
> "00:34:22",
> "00:31:18")
>

> dct <- as.POSIXct( strptime(duration, format="%H:%M:%S"))
>
> print.POSIXt <- function(x) {
> print(format(x, "%H:%M"))
> }
>
> dct
>
> # unload it
> rm("print.POSIXt")


JiHO
---
http://jo.irisson.free.fr/

hadley wickham

unread,
Jun 11, 2009, 9:23:28 PM6/11/09
to JiHO, Winston Chang, ggplot2
Is it possible that you're seeing . used to separate minutes and
seconds? That's what the code is telling me.

Hadley
--
http://had.co.nz/

JiHO

unread,
Jun 11, 2009, 11:36:15 PM6/11/09
to hadley wickham, Winston Chang, ggplot2
On 2009-June-11 , at 21:23 , hadley wickham wrote:

> Is it possible that you're seeing . used to separate minutes and
> seconds? That's what the code is telling me.

That's indeed the case. It used ":", as expected, before IIRC.

JiHO
---
http://jo.irisson.free.fr/


hadley wickham

unread,
Jun 15, 2009, 10:03:14 AM6/15/09
to JiHO, Winston Chang, ggplot2
>> Is it possible that you're seeing . used to separate minutes and
>> seconds?  That's what the code is telling me.
>
> That's indeed the case. It used ":", as expected, before IIRC.

If it's both H:M and M:S, how do you know which is which?

Hadley

--
http://had.co.nz/

JiHO

unread,
Jun 15, 2009, 12:14:30 PM6/15/09
to hadley wickham, Winston Chang, ggplot2
On 2009-June-15 , at 10:03 , hadley wickham wrote:

>>> Is it possible that you're seeing . used to separate minutes and
>>> seconds? That's what the code is telling me.
>>
>> That's indeed the case. It used ":", as expected, before IIRC.
>
> If it's both H:M and M:S, how do you know which is which?

Oh right. I did not realize it was MM.SS. I thought it was HH:MM. The
"." as delimiter definitely makes sense in the general case then. Now,
Winston if you absolutely need : instead of . you might need to alter
the code (i.e. redefine the function involved). What would this
function be Hadley? format.POSIXct? I see that scale-datetime.r uses:
labels <- function(.) {
breaks <- .$.tr$inverse(.$input_breaks())
attr(breaks, "tzone") <- .$tz
format(breaks, .$break_points()[3])
}

Hope that helps.

JiHO
---
http://jo.irisson.free.fr/

hadley wickham

unread,
Jun 15, 2009, 12:16:54 PM6/15/09
to JiHO, Winston Chang, ggplot2
> Oh right. I did not realize it was MM.SS. I thought it was HH:MM. The "." as
> delimiter definitely makes sense in the general case then. Now, Winston if
> you absolutely need : instead of . you might need to alter the code (i.e.
> redefine the function involved). What would this function be Hadley?
> format.POSIXct? I see that scale-datetime.r uses:
>        labels <- function(.) {
>          breaks <- .$.tr$inverse(.$input_breaks())
>          attr(breaks, "tzone") <- .$tz
>          format(breaks, .$break_points()[3])
>        }


Nothing that complicated! Just use the format argument to scale_datetime.

Hadley

--
http://had.co.nz/

Reply all
Reply to author
Forward
0 new messages