R:按日期和月份将季节分配给日期

时间:2020-11-03 15:54:47

标签: r

我有一个数据框,其中包含很多年的日期。看起来像这样(而不是三年,我有40年):

DATES<-c(seq(as.Date('2017-01-01'), as.Date('2019-12-31'), by = 'days'))
df<-data.frame(DATES)

我想添加季节的每一天。因此,春季应从3月20日开始,夏季应于6月21日开始,秋天应于9月23日开始,冬季应于12月21日开始。这些日期这些年来保持不变。

我想出了下面的代码,它可以工作(至少,我认为是这样)。但是,我想知道,如果没有更优雅的方式来获得我想要的东西。

df$MONTH<-month(df$DATES)
df$DAY<-mday(df$DATES)
df$DAY_PLUS_MONTH<-df$DAY+df$MONTH*100

df <- df %>%
  mutate(SEASON = case_when(
    DAY_PLUS_MONTH %in% 320:620 ~ 'SPRING',
    DAY_PLUS_MONTH %in% 621:922 ~ 'SUMMER',
    DAY_PLUS_MONTH %in% 923:1221 ~ 'AUTUMN',
    TRUE ~ 'WINTER'))

2 个答案:

答案 0 :(得分:2)

我认为这应该对您有用:

cut(lubridate::yday(df$DATES - lubridate::days(79)), 
    breaks = c(0, 93, 187, 276, Inf), 
    labels = c("Spring", "Summer", "Autumn", "Winter"))

答案 1 :(得分:1)

使用$yday(无论是lubridate还是as.POSIXlt)可能会导致leap年的错误结果。我认为一种比较安全的方法是为这些年份中的每个日期创建一个向量,并在每个方向(之前/之后)添加一年。

我正在使用findInterval,但与cut差不多,您可以在此处使用相同的变量来使用该方法。

season_dates <- as.Date(sort(c(outer(
  do.call(seq.int, as.list(1900 + as.POSIXlt(range(df$DATES) + c(-365, 365))$year)), 
  c("-03-20", "-06-21", "-09-23", "-12-21"),
  paste0))))
season_dates
#  [1] "2016-03-20" "2016-06-21" "2016-09-23" "2016-12-21" "2017-03-20" "2017-06-21" "2017-09-23" "2017-12-21" "2018-03-20"
# [10] "2018-06-21" "2018-09-23" "2018-12-21" "2019-03-20" "2019-06-21" "2019-09-23" "2019-12-21" "2020-03-20" "2020-06-21"
# [19] "2020-09-23" "2020-12-21"
season_names <- rep(c("Spring", "Summer", "Autumn", "Winter"), length.out = length(season_dates))
season_names
#  [1] "Spring" "Summer" "Autumn" "Winter" "Spring" "Summer" "Autumn" "Winter" "Spring" "Summer" "Autumn" "Winter" "Spring"
# [14] "Summer" "Autumn" "Winter" "Spring" "Summer" "Autumn" "Winter"

set.seed(42)
as_tibble(df) %>%
  mutate(SEASON = season_names[ findInterval(DATES, season_dates) ]) %>%
  sample_n(10) %>%
  arrange(DATES)
# # A tibble: 10 x 2
#    DATES      SEASON
#    <date>     <chr> 
#  1 2017-01-24 Winter
#  2 2017-02-18 Winter
#  3 2017-06-14 Spring
#  4 2017-11-17 Autumn
#  5 2017-12-22 Winter
#  6 2018-02-14 Winter
#  7 2018-07-15 Summer
#  8 2018-09-14 Summer
#  9 2018-09-26 Autumn
# 10 2019-06-18 Spring

我对输出进行采样只是为了显示出一些差异,否则前10个结果都是冬天。另外,由于它基于1900,因此我使用了as.POSIXlt(.)$year,然后不得不对其进行调整。 lubridate::year也可以在这里工作。