119

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()

zx8754
  • 52,746
  • 12
  • 114
  • 209
adam.888
  • 7,686
  • 17
  • 70
  • 105

7 Answers7

150

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.

Gavin Simpson
  • 170,508
  • 25
  • 396
  • 453
106

The lubridate package is amazing for this kind of thing:

> require(lubridate)
> month(date1)
[1] 3
> year(date1)
[1] 2012
Ari B. Friedman
  • 71,271
  • 35
  • 175
  • 235
  • 2
    Ha thank you for this answer. It especially beats the other solutions when you want to do something like if(year(date1) > 2014){year(date1) <- year(date1) - 100} – Vincent Feb 10 '14 at 09:34
  • 1
    this was definitely the best answer for my requirements of taking the year piece out of 4000 contracts' start dates. – d8aninja Mar 07 '15 at 18:06
  • @Ari B. Friedman i am currently using R 3.1.0 while this doesn'tsupport `lubridate` package and tried installing this and used year(date) but it gives the day instead of year does this only work on dates whose format is "2015-05-06" ? – KRU May 06 '15 at 03:34
  • 1
    @KRU New versions of R sometimes take a few weeks for the repositories to update all packages. It should work on all date formats as long as it's a true date format, not a character vector. Please post a new q if that still doesn't solve your problem and you can't search SO for either component of your question. – Ari B. Friedman May 08 '15 at 06:43
14

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"))
Matt Bannert
  • 27,631
  • 38
  • 141
  • 207
12

You can use format:

library(zoo)
x <- as.yearmon(Sys.time())
format(x,"%b")
[1] "Mar"
format(x,"%Y")
[1] "2012"
James
  • 65,548
  • 14
  • 155
  • 193
5

For large vectors:

y = as.POSIXlt(date1)$year + 1900    # x$year : years since 1900
m = as.POSIXlt(date1)$mon + 1        # x$mon : 0–11
user3226167
  • 3,131
  • 2
  • 30
  • 34
  • 2
    This is the best answer, since R already provides the handy `POSIXlt` object that makes zoo package unnecessary – Marco Demaio Aug 11 '15 at 15:45
  • @Marco, That is not true. It is using as.POSIXlt.yearmon from zoo. – G. Grothendieck Jul 02 '21 at 03:17
  • @G. Grothendieck, I beg to differ. as.POSIXlt is defined in base R v 4.2.3, whereas loading the package lubridate warns me that date, intersect, setdiff, union from package:base have been masked. So a zoo-free solution is now easy-as in base R... but the OP is clearly using zoo so they might as well learn how to use lubridate! – Clark Thomborson Jun 21 '23 at 08:18
  • @Clark Thomborson, Irrelevant. This question is about extracting "month and year from a zoo::yearmon object". – G. Grothendieck Jun 22 '23 at 13:01
1

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 
G. Grothendieck
  • 254,981
  • 17
  • 203
  • 341
0

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
David Buck
  • 3,752
  • 35
  • 31
  • 35