Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

R Plot Specify number of time tickmarks - time/date equivalent to pretty

Tags:

plot

r

I was wondering how I could plot more tick marks when plotting time on the x-axis.

Basically, a time equivalent to pretty. Pretty obviously doesn't work so well with times, as it uses factors of 1,2,5 and 10. For time one probably wants e.g. hours, half hours, ...

plot(as.POSIXct(x,origin="1960-01-01"),y,type="l",xlab="Time")

gives really too few and widely spaced tickmarks.

zoox<-zoo(y,as.POSIXct(stats$Time,origin="1960-01-01"))
plot(zoox)

gives the same.

Thanks

EDIT:

Just to clarify (so far answers don't address my issue): What I am looking for is a function like pretty for dates, e.g. a function, that takes a start date, an end date, a number of ticks, and outputs the location of the ticks. That is, I am well aware it is possible to plot hours, to plot minutes, and what else, but pretty automates the tick distance for numbers, and a resulting function for dates should decide by itself whether to use days, hours, minutes, second, milliseconds, microseconds, 30 minutes, 500 micros, 5 seconds, etc. intervals. That is what pretty does for numbers, anyway.

EDIT2:

This is the function I currently use to decide the format for the time axis (note that this doesn't work for dates):

mydiff <- end-start
if(mydiff>1800) {
    axis.POSIXct(1,xrange,format="%H:%M")
} else if(mydiff>30) {
    axis.POSIXct(1,xrange,format="%H:%M:%S")
} else if(mydiff>0.5) {
    axis.POSIXct(1,xrange,format="%H:%M:%OS3")
} else
    axis.POSIXct(1,xrange,format="%H:%M:%OS6")
}

I don't have a function that increase tick marks, so I use the default number of tick marks

like image 873
Cookie Avatar asked Jul 06 '11 06:07

Cookie


2 Answers

axis.POSIXct() already works quite hard to guess suitable pretty values for the axis, so I would start by hacking that. At the moment, it relies internally on using pretty() applied to the some function of the datetimes. It uses the defaults for pretty() so you could hack the function to add an n or min.n argument which would increase the number of pretty marks selected.

Copy axis.POSIXct() to your own function/file (give it a new name). Add a n or min.n argument to the definition, probably with larger values as defaults than those used by the pretty() function. And pass that to each of the pretty() calls that is made.

Try it out. If it works reasonably well, then you can do fixInNamespace(axis.POSIXct) to make the same changes to the actual function so it gets used on all plots for which it is called.

P.S. Here is a possible hack

function (side, x, at, format, labels = TRUE, n = 5, ...) {
  mat <- missing(at) || is.null(at)
  if (!mat) 
    x <- as.POSIXct(at)
  else x <- as.POSIXct(x)
  range <- par("usr")[if (side%%2) 
    1L:2L
    else 3L:4L]
  d <- range[2L] - range[1L]
  z <- c(range, x[is.finite(x)])
  attr(z, "tzone") <- attr(x, "tzone")
  if (d < 1.1 ) {
    sc <- 0.001
    if (missing(format)) 
      format <- "%H:%M:%OS6"
  }
  else if (d < 1.1 * 30) {
    sc <- 1
    if (missing(format)) 
      format <- "%H:%M:%OS3"
  }
  else if (d < 1.1 * 60) {
    sc <- 1
    if (missing(format)) 
      format <- "%H:%M:%S"
  }
  else if (d < 1.1 * 30 * 60) {
    sc <- 60
    if (missing(format)) 
      format <- "%H:%M:%S"
  }
  else if (d < 1.1 * 60 * 60) {
    sc <- 60
    if (missing(format)) 
      format <- "%H:%M"
  }
  else if (d < 1.3 * 60 * 60 * 24) {
    sc <- 60 * 60
    if (missing(format)) 
      format <- "%H:%M"
  }
  else if (d < 2 * 60 * 60 * 24) {
    sc <- 60 * 60
    if (missing(format)) 
      format <- "%a %H:%M"
  }
  else if (d < 7 * 60 * 60 * 24) {
    sc <- 60 * 60 * 24
    if (missing(format)) 
      format <- "%a"
  }
  else {
    sc <- 60 * 60 * 24
  }
  if (d < 60 * 60 * 24 * 50) {
    zz <- pretty(z/sc,n=n)
    z <- zz * sc
    z <- .POSIXct(z, attr(x, "tzone"))
    if (sc == 60 * 60 * 24) 
      z <- as.POSIXct(round(z, "days"))
    if (missing(format)) 
      format <- "%b %d"
  }
  else if (d < 1.1 * 60 * 60 * 24 * 365) {
    z <- .POSIXct(z, attr(x, "tzone"))
    zz <- as.POSIXlt(z)
    zz$mday <- zz$wday <- zz$yday <- 1
    zz$isdst <- -1
    zz$hour <- zz$min <- zz$sec <- 0
    zz$mon <- pretty(zz$mon,n=n)
    m <- length(zz$mon)
    M <- 2 * m
    m <- rep.int(zz$year[1L], m)
    zz$year <- c(m, m + 1)
    zz <- lapply(zz, function(x) rep(x, length.out = M))
    zz <- .POSIXlt(zz, attr(x, "tzone"))
    z <- as.POSIXct(zz)
    if (missing(format)) 
      format <- "%b"
  }
  else {
    z <- .POSIXct(z, attr(x, "tzone"))
    zz <- as.POSIXlt(z)
    zz$mday <- zz$wday <- zz$yday <- 1
    zz$isdst <- -1
    zz$mon <- zz$hour <- zz$min <- zz$sec <- 0
    zz$year <- pretty(zz$year,n=n)
    M <- length(zz$year)
    zz <- lapply(zz, function(x) rep(x, length.out = M))
    z <- as.POSIXct(.POSIXlt(zz))
    if (missing(format)) 
      format <- "%Y"
  }
  if (!mat) 
    z <- x[is.finite(x)]
  keep <- z >= range[1L] & z <= range[2L]
  z <- z[keep]
  if (!is.logical(labels)) 
    labels <- labels[keep]
  else if (identical(labels, TRUE)) 
    labels <- format(z, format = format)
  else if (identical(labels, FALSE)) 
    labels <- rep("", length(z))
  axis(side, at = z, labels = labels, ...)
}

Differences to the original function can be seen here

like image 146
Gavin Simpson Avatar answered Oct 12 '22 22:10

Gavin Simpson


I tend to use function axis.POSIXct and/or function cut.POSIXt. Let's say your vector of date is time and your vector of value associated x:

plot(time,x,xaxt="n")
axis.POSIXct(side=1,at=seq(min(time),max(time),by="week"),format="%d-%m") #For instance

And, going further, with cut.POSIXt:

plot(time,x,xaxt="n")
axis.POSIXct(side=1,at=cut(time, breaks="week"),format="%d-%m")
like image 38
plannapus Avatar answered Oct 12 '22 21:10

plannapus