115
votes

I have a yearmon object:

require(zoo)
date1 <- as.yearmon("Mar 2012", "%b %Y")
class(date1)
# [1] "yearmon"

How can I extract the month and year from this?

month1 <- fn(date1)
year1 <- fn(date1)

What function should I use in place of fn()

7

7 Answers

147
votes

Use the format() method for objects of class "yearmon". Here is your example date (properly created!)

date1 <- as.yearmon("Mar 2012", "%b %Y")

Then we can extract the date parts as required:

> format(date1, "%b") ## Month, char, abbreviated
[1] "Mar"
> format(date1, "%Y") ## Year with century
[1] "2012"
> format(date1, "%m") ## numeric month
[1] "03"

These are returned as characters. Where appropriate, wrap in as.numeric() if you want the year or numeric month as a numeric variable, e.g.

> as.numeric(format(date1, "%m"))
[1] 3
> as.numeric(format(date1, "%Y"))
[1] 2012

See ?yearmon and ?strftime for details - the latter explains the placeholder characters you can use.

105
votes

The lubridate package is amazing for this kind of thing:

> require(lubridate)
> month(date1)
[1] 3
> year(date1)
[1] 2012
15
votes

I know the OP is using zoo here, but I found this thread googling for a standard ts solution for the same problem. So I thought I'd add a zoo-free answer for ts as well.

# create an example Date 
date_1 <- as.Date("1990-01-01")
# extract year
as.numeric(format(date_1, "%Y"))
# extract month
as.numeric(format(date_1, "%m"))
12
votes

You can use format:

library(zoo)
x <- as.yearmon(Sys.time())
format(x,"%b")
[1] "Mar"
format(x,"%Y")
[1] "2012"
5
votes

For large vectors:

y = as.POSIXlt(date1)$year + 1900    # x$year : years since 1900
m = as.POSIXlt(date1)$mon + 1        # x$mon : 0–11
1
votes

Based on comments the result should be the month number (January = 1) and the 4 digit year so assuming that we have just run the code in the question we have the following. This uses no extra packages other than what is already used in the question, is very short and is much faster than any of the other solutions (see Benchmark section below).

cycle(date1)
## [1] 3
as.integer(date1)
## [1] 2012

Benchmark

On a yearmon object of length 1000 the solution above is about 1000x faster than any of the others for year and 200x faster for month.

library(zoo)
library(microbenchmark)
library(lubridate)

ym <- as.yearmon(rep(2000, 1000))

microbenchmark(
  as.integer(ym),
  as.numeric(format(ym, "%y")),
  as.POSIXlt(ym)$year + 1900,
  year(ym)
)

Unit: microseconds
                         expr     min       lq     mean   median       uq     max neval cld
               as.integer(ym)    18.2    27.90    28.93    29.15    31.15    51.2   100 a  
 as.numeric(format(ym, "%y")) 46515.8 47090.05 48122.28 47525.00 48080.25 69967.6   100   c
   as.POSIXlt(ym)$year + 1900 40874.4 41223.65 41798.60 41747.30 42171.25 44381.2   100  b 
                     year(ym) 40793.2 41167.70 42003.07 41742.40 42140.30 65203.3   100  b 
 
microbenchmark(
  cycle(ym),
  as.numeric(format(ym, "%m")),
  as.POSIXlt(ym)$mon + 1,
  month(ym)
)

Unit: microseconds
                         expr     min      lq      mean   median       uq     max neval cld
                    cycle(ym)   138.1   166.0   173.893   172.95   181.45   344.0   100 a  
 as.numeric(format(ym, "%m")) 46637.1 46954.8 47632.307 47325.90 47672.40 67690.1   100   c
       as.POSIXlt(ym)$mon + 1 40923.3 41339.1 41976.836 41689.95 42078.15 65786.4   100  b 
                    month(ym) 41056.4 41408.9 42082.975 41743.35 42164.95 66651.0   100  b 
0
votes

Having had a similar problem with data from 1800 to now, this worked for me:

data2$date=as.character(data2$date) 
lct <- Sys.getlocale("LC_TIME"); 
Sys.setlocale("LC_TIME","C")
data2$date<- as.Date(data2$date, format = "%Y %m %d") # and it works