Search code examples
rdateggplot2epochchron

Trouble with placing and formatting dates in ggplot2 graph using chron


I've been trying to add appropriate dates on the x-axis of my graph, but can't figure out how to do it in a sane way. What I want is pretty simple: a date at every January 1st in between the minimum and maximum of my data set.

I don't want to include the month - just '2008' or '2009' or whatever is fine. A great example would be this graph:

example graph

Except I want the date on every year, rather than every other year.

I can't seem to figure this out. My dates are defined as days since 1/1/1970, and I've included a method dateEPOCH_formatter which converts the epoch format to a format using the chron package. I've figured out how to make a tick mark and date at the origin of the graph and every 365 days thereafter, but that's not quite the same thing.

Another minor problem is that, mysteriously, the line chron(floor(y), out.format="mon year",origin.=epoch) outputs a graph with axis markers like 'Mar 2008', but changing the line to chron(floor(y), out.format="year",origin.=epoch) doesn't give me a result like '2008' - it just results in the error:

Error in parse.format(format[1]) : unrecognized format year
Calls: print ... as.character.times -> format -> format.dates -> parse.format
Execution halted

Here's my code - thanks for the help.

library(ggplot2)
library(chron)
argv <- commandArgs(trailingOnly = TRUE)
mydata = read.csv(argv[1])
png(argv[2], height=300, width=470)


timeHMS_formatter <- function(x) {                  # Takes time in seconds from midnight, converts to HH:MM:SS
h <- floor(x/3600)
m <- floor(x %% 60)
s <- round(60*(x %% 1))                         # Round to nearest second
lab <- sprintf('%02d:%02d', h, m, s)        # Format the strings as HH:MM:SS
lab <- gsub('^00:', '', lab)                    # Remove leading 00: if present
lab <- gsub('^0', '', lab)                      # Remove leading 0 if present
}

dateEPOCH_formatter <- function (y){
epoch <- c(month=1,day=1,year=1970)
    chron(floor(y), out.format="mon year",origin.=epoch)
}

p=  ggplot() + 
coord_cartesian(xlim=c(min(mydata$day),max(mydata$day)), ylim=c(0,86400)) +         # displays data from first email through present
scale_color_hue() +
xlab("Date") +
ylab("Time of Day") +
scale_y_continuous(label=timeHMS_formatter, breaks=seq(0, 86400, 14400)) +              # adds tick marks every 4 hours
scale_x_continuous(label=dateEPOCH_formatter, breaks=seq(min(mydata$day), max(mydata$day), 365) ) +
ggtitle("Email Sending Times") +                                                        # adds graph title
theme( legend.position = "none", axis.title.x = element_text(vjust=-0.3)) +
theme_bw() +
layer(
    data=mydata, 
    mapping=aes(x=mydata$day, y=mydata$seconds), 
    stat="identity", 
    stat_params=list(), 
    geom="point", 
    geom_params=list(alpha=5/8, size=2, color="#A9203E"),
    position=position_identity(),
)   

print(p)
dev.off()

Solution

  • I think it will be much easier to use the built in function scale_x_date with date_format and date_breaks from the scales package. These should work with most date classes in R, such as Date, chron etc

    for example

    library(ggplot2)
    library(chron)
    library(scales)
    
    # some example data 
    days <- seq(as.Date('01-01-2000', format = '%d-%m-%Y'),
                as.Date('01-01-2010', format = '%d-%m-%Y'), by = 1)
    
    days_chron <- as.chron(days)
    mydata <- data.frame(day = days_chron, y = rnorm(length(days)))
    
    # the plot
    ggplot(mydata, aes(x=days, y= y)) + geom_point() + 
     scale_x_date(breaks = date_breaks('year'), labels = date_format('%Y'))
    

    enter image description here

    To show how intuitive and easy these function are, if you wanted Montth-year labels every 6 months - note that this requires a very wide plot or very small axis labels

    ggplot(mydata, aes(x=days, y= y)) + geom_point() +
       scale_x_date(breaks = date_breaks('6 months'), labels = date_format('%b-%Y'))
    

    enter image description here