Find which season a particular date belongs to

2019-01-03 10:16发布

I have a vector of dates and for each entry, I would like to assign a season. So for example, if a date is between 21.12. and 21.3., I would says that's winter. So far I have tried the following code but I couldn't make it more generic, irrespective of the year.

my.dates <- as.Date("2011-12-01", format = "%Y-%m-%d") + 0:60
low.date <- as.Date("2011-12-15", format = "%Y-%m-%d")
high.date <- as.Date("2012-01-15", format = "%Y-%m-%d")

my.dates[my.dates <= high.date & my.dates >= low.date] 
 [1] "2011-12-15" "2011-12-16" "2011-12-17" "2011-12-18" "2011-12-19" "2011-12-20" "2011-12-21" "2011-12-22" "2011-12-23" "2011-12-24" "2011-12-25"
[12] "2011-12-26" "2011-12-27" "2011-12-28" "2011-12-29" "2011-12-30" "2011-12-31" "2012-01-01" "2012-01-02" "2012-01-03" "2012-01-04" "2012-01-05"
[23] "2012-01-06" "2012-01-07" "2012-01-08" "2012-01-09" "2012-01-10" "2012-01-11" "2012-01-12" "2012-01-13" "2012-01-14" "2012-01-15"

I have tried formatting the dates without the year, but it isn't working.

ld <- as.Date("12-15", format = "%m-%d")
hd <- as.Date("01-15", format = "%m-%d")
my.dates[my.dates <= hd & my.dates >= ld] 

标签: r date
8条回答
小情绪 Triste *
2楼-- · 2019-01-03 10:28

Here a more general solution, that nevertheless needs 3 libraries... It considers all years and the hemisphere:

library(data.table)
library(zoo)
library(dplyr)

get.seasons <- function(dates, hemisphere = "N"){
  years <- unique(year(dates))
  years <- c(min(years - 1), max(years + 1), years) %>% sort

  if(hemisphere == "N"){
    seasons <- c("winter", "spring", "summer", "fall")}else{
      seasons <- c("summer", "fall", "winter", "spring")}

  dt.dates <- bind_rows(
    data.table(date = as.Date(paste0(years, "-12-21")), init = seasons[1], type = "B"),# Summer in south hemisphere
    data.table(date = as.Date(paste0(years, "-3-21")), init = seasons[2], type = "B"), # Fall in south hemisphere
    data.table(date = as.Date(paste0(years, "-6-21")), init = seasons[3], type = "B"), # Winter in south hemisphere
    data.table(date = as.Date(paste0(years, "-9-23")), init = seasons[4], type = "B"), # Winter in south hemisphere
    data.table(date = dates, i = 1:(length(dates)), type = "A") # dates to compute
  )[order(date)] 

  dt.dates[, init := zoo::na.locf(init)] 

  return(dt.dates[type == "A"][order(i)]$init)
}
查看更多
我欲成王,谁敢阻挡
3楼-- · 2019-01-03 10:31

I would create a lookup table, and go from there. An example (note the code obfuscation using the d() function and the pragmatic way of filling the lut):

# Making lookup table (lut), only needed once. You can save
# it using save() for later use. Note I take a leap year.
d = function(month_day) which(lut$month_day == month_day)
lut = data.frame(all_dates = as.POSIXct("2012-1-1") + ((0:365) * 3600 * 24),
                 season = NA)
lut = within(lut, { month_day = strftime(all_dates, "%b-%d") })
lut[c(d("Jan-01"):d("Mar-20"), d("Dec-21"):d("Dec-31")), "season"] = "winter"
lut[c(d("Mar-21"):d("Jun-20")), "season"] = "spring"
lut[c(d("Jun-21"):d("Sep-20")), "season"] = "summer"
lut[c(d("Sep-21"):d("Dec-20")), "season"] = "autumn"
rownames(lut) = lut$month_day

After creating the lookup table, you can extract quite easily from it to what season a month/day combination belongs to:

dat = data.frame(dates = Sys.Date() + (0:11)*30)
dat = within(dat, { 
  season =  lut[strftime(dates, "%b-%d"), "season"] 
 })
> dat
        dates season
1  2012-02-29 winter
2  2012-03-30 spring
3  2012-04-29 spring
4  2012-05-29 spring
5  2012-06-28 summer
6  2012-07-28 summer
7  2012-08-27 summer
8  2012-09-26 autumn
9  2012-10-26 autumn
10 2012-11-25 autumn
11 2012-12-25 winter
12 2013-01-24 winter

All nice and vectorized :). I think once the table is created, this is very quick.

查看更多
走好不送
4楼-- · 2019-01-03 10:35

I think this would do it, but it's an ugly solution:

    my.dates <- as.Date("2011-12-01", format = "%Y-%m-%d") + 0:60
    ld <- as.Date("12-15", format = "%m-%d")
    hd <- as.Date("01-15", format = "%m-%d")
    my.dates2 <- as.Date(unlist(lapply(strsplit(as.character(my.dates),split=""),function(x)   paste(x[6:10],collapse=""))),format="%m-%d")
    my.dates[my.dates2 <= hd | my.dates2 >= ld] 
    [1] "2011-12-15" "2011-12-16" "2011-12-17" "2011-12-18" "2011-12-19"
    [6] "2011-12-20" "2011-12-21" "2011-12-22" "2011-12-23" "2011-12-24"
    [11] "2011-12-25" "2011-12-26" "2011-12-27" "2011-12-28" "2011-12-29"
    [16] "2011-12-30" "2011-12-31" "2012-01-01" "2012-01-02" "2012-01-03"
    [21] "2012-01-04" "2012-01-05" "2012-01-06" "2012-01-07" "2012-01-08"
    [26] "2012-01-09" "2012-01-10" "2012-01-11" "2012-01-12" "2012-01-13"
    [31] "2012-01-14" "2012-01-15"
查看更多
We Are One
5楼-- · 2019-01-03 10:35

My solution is not fast but is flexible about the starts of the seasons as long as they are defined in a dataframe first for the function assignSeason. It requires magrittr for the piping functions, lubridate for the year function, and dplyr for mutate.

seasons <- data.frame(
   SE = as.POSIXct(c("2009-3-20", "2010-3-20", "2011-3-20", "2012-3-20", 
        "2013-3-20", "2014-3-20"), format="%Y-%m-%d"),
   SS = as.POSIXct(c("2009-6-21", "2010-6-21", "2011-6-21", "2012-6-20",
        "2013-6-21", "2014-6-21"), format="%Y-%m-%d"),
   FE = as.POSIXct(c("2009-9-22", "2010-9-23", "2011-9-23", "2012-9-22",
        "2013-9-22", "2014-9-23"), format="%Y-%m-%d"),
   WS = as.POSIXct(c("2009-12-21", "2010-12-21", "2011-12-22", "2012-12-21", 
        "2013-12-21", "2014-12-21"), format="%Y-%m-%d")
)

assignSeason <- function(dat, SeasonStarts=seasons) {
    dat %<>% mutate(
        Season = lapply(Date,
            function(x) {
                findInterval(
                    x, 
                    SeasonStarts[which(year(x)==year(SeasonStarts$WS)), ]
                )
            }
        ) %>% unlist    
    )
    dat[which(dat$Season==0 | dat$Season==4), ]$Season   <- "Winter"
    dat[which(dat$Season==1), ]$Season                  <- "Spring"
    dat[which(dat$Season==2), ]$Season                  <- "Summer"
    dat[which(dat$Season==3), ]$Season                  <- "Fall"
    return(dat)
}

Example data:

dat = data.frame(
    Date = as.POSIXct(strptime(as.Date("2011-12-01", format = "%Y-%m-%d") + 
        (0:10)*30, format="%Y-%m-%d"))
)
dat %>% assignSeason

Result:

         Date Season
1  2011-12-01   Fall
2  2011-12-31 Winter
3  2012-01-30 Winter
4  2012-02-29 Winter
5  2012-03-30 Spring
6  2012-04-29 Spring
7  2012-05-29 Spring
8  2012-06-28 Summer
9  2012-07-28 Summer
10 2012-08-27 Summer
11 2012-09-26   Fall
查看更多
放我归山
6楼-- · 2019-01-03 10:38

Simply use time2season function. It gets date and generates season:

time2season(x, out.fmt = "months", type="default")

You can find more infromation here.

查看更多
We Are One
7楼-- · 2019-01-03 10:45

I have something similarly ugly as Tim:

R> toSeason <- function(dat) {
+ 
+     stopifnot(class(dat) == "Date")
+ 
+     scalarCheck <- function(dat) {
+         m <- as.POSIXlt(dat)$mon + 1        # correct for 0:11 range
+         d <- as.POSIXlt(dat)$mday           # correct for 0:11 range
+         if ((m == 3 & d >= 21) | (m == 4) | (m == 5) | (m == 6 & d < 21)) {
+             r <- 1
+         } else if ((m == 6 & d >= 21) | (m == 7) | (m == 8) | (m == 9 & d < 21)) {
+             r <- 2
+         } else if ((m == 9 & d >= 21) | (m == 10) | (m == 11) | (m == 12 & d < 21)) {
+             r <- 3
+         } else {
+             r <- 4
+         }
+         r
+     }
+ 
+     res <- sapply(dat, scalarCheck)
+     res <- ordered(res, labels=c("Spring", "Summer", "Fall", "Winter"))
+     invisible(res)
+ }
R> 

And here is a test:

R> date <- Sys.Date() + (0:11)*30
R> DF <- data.frame(Date=date, Season=toSeason(date))
R> DF
         Date Season
1  2012-02-29 Winter
2  2012-03-30 Spring
3  2012-04-29 Spring
4  2012-05-29 Spring
5  2012-06-28 Summer
6  2012-07-28 Summer
7  2012-08-27 Summer
8  2012-09-26   Fall
9  2012-10-26   Fall
10 2012-11-25   Fall
11 2012-12-25 Winter
12 2013-01-24 Winter
R> summary(DF)
      Date               Season 
 Min.   :2012-02-29   Spring:3  
 1st Qu.:2012-05-21   Summer:3  
 Median :2012-08-12   Fall  :3  
 Mean   :2012-08-12   Winter:3  
 3rd Qu.:2012-11-02             
 Max.   :2013-01-24             
R> 
查看更多
登录 后发表回答