Format Date-Time as Seasons in R

Format date-time as seasons in R?

as.POSIXlt returns a named list (which makes it unsuitable for data.frame columns). The list columns can be individually accessed and include "year" (1900-based, unlike 1970 used for default) and "mon" (0-based). Best place to see this list in hte help system is ?DateTimeClasses:

First just a Seasons calculation, then a Year-Seasons calculation

 c('DJF', 'MAM', 'JJA', 'SON')[ # select from character vector with numeric vector
1+((as.POSIXlt(dates)$mon+1) %/% 3)%%4]

[1] "JJA" "JJA" "SON" "SON" "DJF" "DJF" "DJF" "MAM" "MAM" "JJA"
[11] "JJA"

paste( 1900 + # this is the base year for POSIXlt year numbering
as.POSIXlt( dates )$year +
1*(as.POSIXlt( dates )$year==12) , # offset needed for December
c('DJF', 'MAM', 'JJA', 'SON')[ # indexing from 0-based-mon
1+((as.POSIXlt(dates)$mon+1) %/% 3)%%4]
, sep="-")
[1] "2014-JJA" "2014-JJA" "2014-SON" "2014-SON" "2014-DJF"
[6] "2015-DJF" "2015-DJF" "2015-MAM" "2015-MAM" "2015-JJA"
[11] "2015-JJA"

Shouldn't be that difficult to make a function that constructs the formatting you expect. This is just modulo arithmetic on the POSIXlt values for month and year.

Find which season a particular date belongs to

How about using something like this:

getSeason <- function(DATES) {
WS <- as.Date("2012-12-15", format = "%Y-%m-%d") # Winter Solstice
SE <- as.Date("2012-3-15", format = "%Y-%m-%d") # Spring Equinox
SS <- as.Date("2012-6-15", format = "%Y-%m-%d") # Summer Solstice
FE <- as.Date("2012-9-15", format = "%Y-%m-%d") # Fall Equinox

# Convert dates from any year to 2012 dates
d <- as.Date(strftime(DATES, format="2012-%m-%d"))

ifelse (d >= WS | d < SE, "Winter",
ifelse (d >= SE & d < SS, "Spring",
ifelse (d >= SS & d < FE, "Summer", "Fall")))
}

my.dates <- as.Date("2011-12-01", format = "%Y-%m-%d") + 0:60
head(getSeason(my.dates), 24)
# [1] "Fall" "Fall" "Fall" "Fall" "Fall" "Fall" "Fall"
# [8] "Fall" "Fall" "Fall" "Fall" "Fall" "Fall" "Fall"
# [15] "Winter" "Winter" "Winter" "Winter" "Winter" "Winter"

One note: 2012 is a good year to which to convert all of the dates; since it is a leap year, any February 29ths in your data set will be handled smoothly.

R Need to extract month and assign season

Regarding the subject/title of the question, its actually possible to do this without extracting the month. The first two solutions below do not extract the month. There is also a third solution which does extract the month but only to increment it.

1) as.yearqtr/as.yearmon Convert the dates to year/month and add one month (1/12). Then the calendar quarters correspond to the seasons so convert to year/quarter, yq, and label the quarters as shown:

library(zoo)
yq <- as.yearqtr(as.yearmon(DF$dates, "%m/%d/%Y") + 1/12)
DF$Season <- factor(format(yq, "%q"), levels = 1:4,
labels = c("winter", "spring", "summer", "fall"))

giving:

       dates Season
1 7/28/2010 summer
2 4/21/2011 spring
3 7/23/2010 summer
4 6/14/2011 summer
5 12/3/2010 winter
6 11/18/2010 fall
7 11/6/2010 fall
8 7/23/2010 summer
9 6/14/2011 summer

1a) A variation of this is to use chron's quarters which produces a factor so that levels=1:4 does not have to be specified. To use chron replace the last line in (1) with:

library(chron)
DF$Season <- factor(quarters(as.chron(yq)),
labels = c("winter", "spring", "summer", "fall"))

chron could also be used in conjunction with the remaining solutions.

2) cut. This solution only uses the base of R. First convert the dates to the first of the month using cut and add 32 to get a date in the next month, d. The quarters corresponding to d are the seasons so compute the quarters using quarters and construct the labels in the same fashion as the first answser:

d <- as.Date(cut(as.Date(DF$dates, "%m/%d/%Y"), "month")) + 32
DF$Season <- factor(quarters(d), levels = c("Q1", "Q2", "Q3", "Q4"),
labels = c("winter", "spring", "summer", "fall"))

giving the same answer.

3) POSIXlt This solution also uses only the base of R:

p <- as.POSIXlt(as.Date(DF$dates, "%m/%d/%Y"))
p$day <- 1
p$mo <- p$mo+1
DF$Season <- factor(quarters(p), levels = c("Q1", "Q2", "Q3", "Q4"),
labels = c("winter", "spring", "summer", "fall"))

Note 1: We could optionally omit levels= in all these solutions if we knew that every season appears.

Note 2: We used this data frame:

DF <- data.frame(dates = c('7/28/2010', '4/21/2011', '7/23/2010', 
'6/14/2011', '12/3/2010', '11/18/2010', '11/6/2010', '7/23/2010',
'6/14/2011'))

Convert dates to season year

lubridate package is your friend:

library(lubridate)
date <- c("08/08/15", "08/09/16")

# extract year information
year <- year(mdy(date))
# paste season
season <- paste0(year, "/", year + 1)
season

How to classify a given date/time by the season (e.g. summer, autumn)

Here are three possibilities, one using cut, as suggested by @Spacedman in the comments, a second using findInterval, and a third which uses a rolling join in data.table. As discussed in the comments above, solstice and equinox dates are defined as onset of a Season. This may explain the difference between my output and OP:s (not yet edited) desired result. I leave the mapping of 'seas' to more sensible season names to you.

1. cut

Date.time$seastime <- as.POSIXct(cut(Date.time$datetime, seasonality$seastime))
Date.time <- merge(Date.time, seasonality)
Date.time[ , c("datetime", "seas")]
# datetime seas
# 1 2012-12-20 19:28:00 September Equinox
# 2 2012-12-25 21:08:00 December Solstice
# 3 2012-12-29 02:50:00 December Solstice
# snip
# 12 2013-03-12 02:24:00 December Solstice
# 13 2013-03-18 21:54:00 December Solstice
# 14 2013-03-24 04:50:00 March Equinox
# 15 2013-03-31 05:54:06 March Equinox
# 16 2013-04-01 03:52:00 March Equinox
# 17 2013-04-04 12:34:00 March Equinox

2. findInterval

seasonality <- seasonality[order(seasonality$seastime), ]
Date.time$seas <- seasonality$seas[findInterval(x = Date.time$datetime, vec = seasonality$seastime)]
Date.time
# datetime seas
# 1 2012-12-20 19:28:00 September Equinox
# 2 2012-12-25 21:08:00 December Solstice
# 3 2012-12-29 02:50:00 December Solstice
# snip
# 12 2013-03-12 02:24:00 December Solstice
# 13 2013-03-18 21:54:00 December Solstice
# 14 2013-03-24 04:50:00 March Equinox
# 15 2013-03-31 05:54:06 March Equinox
# 16 2013-04-01 03:52:00 March Equinox
# 17 2013-04-04 12:34:00 March Equinox

3. data.table rolling join

library(data.table)
setDT(Date.time)
setDT(seasonality)

setkey(Date.time, datetime)
setkey(seasonality, seastime)

seasonality[Date.time, roll = Inf]
# seastime seas
# 1: 2012-12-20 19:28:00 September Equinox
# 2: 2012-12-25 21:08:00 December Solstice
# 3: 2012-12-29 02:50:00 December Solstice
# snip
# 12: 2013-03-12 02:24:00 December Solstice
# 13: 2013-03-18 21:54:00 December Solstice
# 14: 2013-03-24 04:50:00 March Equinox
# 15: 2013-03-31 05:54:06 March Equinox
# 16: 2013-04-01 03:52:00 March Equinox
# 17: 2013-04-04 12:34:00 March Equinox

# possibly rename 'seastime' here.

'Date.time' used here:

structure(list(datetime = structure(c(1356028080, 1356466080, 
1356745800, 1357039920, 1357878240, 1360560720, 1360703160, 1361011320,
1361268360, 1361535000, 1361853000, 1363051440, 1363640040, 1364097000,
1364702046, 1364781120, 1365071640), class = c("POSIXct", "POSIXt"
), tzone = "")), .Names = "datetime", class = "data.frame", row.names = c(NA,
-17L))

Changing months to seasons

Here is an option where we create a named vector and use that to match and replace the 'Month' to create new column

library(dplyr)
nm1 <- setNames(rep(c("Winter", "Spring", "Summer", "Fall"),
each = 3), month.name)
df1 %>%
mutate(Season = nm1[Month])

-output

#         Date     Month Temperature Season
#1 2016-07-01 July 13 Summer
#2 2017-01-08 January 5 Winter
#3 2018-09-19 September 11 Summer
#4 2019-10-24 October 9 Fall

Or it can be done in base R

df1$Season = nm1[df1$Month]

data

df1 <- structure(list(Date = c("2016-07-01", "2017-01-08", "2018-09-19", 
"2019-10-24"), Month = c("July", "January", "September", "October"
), Temperature = c(13L, 5L, 11L, 9L)), class = "data.frame",
row.names = c(NA,
-4L))

Convert Date String in Format Mon Day, Year Time am/pm to POSIXlt format in R?

You will want to use the format "%b %d, %Y %I:%M %p" in as.POSIXlt()

  • %b for the abbreviated month (in the current locale)
  • %d for the day
  • %Y for the full four-digit year
  • %I for the hour (when using %p a.k.a am/pm)
  • %M for the minutes
  • %p for am/pm

So for a POSIXlt date-time, you can do

(x <- as.POSIXlt("Aug 19, 2015 07:09 am", format = "%b %d, %Y %I:%M %p"))
# [1] "2015-08-19 07:09:00 PDT"

As mentioned in the comments, you must have created the entire POSIX object in order to extract its parts (well, to formally extract them anyway).

months(x)
# [1] "August"
months(x, abbreviate = TRUE)
# [1] "Aug"

Additionally, in order to use strptime(), you must plan on creating the entire date-time object as you cannot just create a month and have it classed as such. See help(strptime) and help(as.POSIXlt) for more.

Determine season from Date using lubridate in R

I packaged @Lars Arne Jordanger's much more elegant approach into a function:

getTwoSeasons <- function(input.date){
numeric.date <- 100*month(input.date)+day(input.date)
## input Seasons upper limits in the form MMDD in the "break =" option:
cuts <- base::cut(numeric.date, breaks = c(0,415,1015,1231))
# rename the resulting groups (could've been done within cut(...levels=) if "Winter" wasn't double
levels(cuts) <- c("Winter", "Summer","Winter")
return(cuts)
}

Testing it on some sample data seems to work fine:

getTwoSeasons(as.POSIXct("2016-01-01 12:00:00")+(0:365)*(60*60*24))


Related Topics



Leave a reply



Submit