在R中按年份分组

时间:2020-02-01 16:59:06

标签: r group-by dplyr

我有一个数据集,我想按年份分组(总和超过days),但是如果某个days的{​​{1}}的数量大于天数到目前为止的年份中,应该将额外的天数添加到上一年中。例如,以下与date关联的153天中,有31天应指向2019年,而122天应指向2018年。

数据

2019-02-01

预期输出

dat <- data.frame(date = as.Date( c("2018-02-01", "2018-06-01", "2018-07-01", "2018-09-01", "2019-02-01", "2019-03-01", "2019-04-01") ),
                  days = c(0, 120, 30, 62, 153, 28, 31))

date         days
2018-02-01   0
2018-06-01   120
2018-07-01   30
2018-09-01   62
2019-02-01   153
2019-03-01   28
2019-04-01   31

如何在R中做到这一点? (理想情况下使用year days 2018 334 2019 90 ,但如果这是唯一的方法,可以使用base-R)

3 个答案:

答案 0 :(得分:4)

这是使用基数R的一种方法:

#Get day of the year
dat$day_in_year <- as.integer(format(dat$date, "%j"))
#Get year from date
dat$year <- as.integer(format(dat$date, "%Y"))
#Index where day in year is less than days
inds <- dat$day_in_year < dat$days
#Create a new dataframe with adjusted values
other_df <- data.frame(days = dat$days[inds] - dat$day_in_year[inds] + 1, 
                       year = dat$year[inds] - 1)
#Update the original data
dat$days[inds] <- dat$day_in_year[inds] - 1

#Combine the two dataframe then aggregate
aggregate(days~year, rbind(dat[c('days', 'year')], other_df), sum)

#  year days
#1 2018  334
#2 2019   90

答案 1 :(得分:2)

一种可能的tidyverse方式:

library(tidyverse)

dat %>% group_by(year = as.integer(format(date, '%Y'))) %>%
  mutate(excess = days - (date - as.Date(paste0(year, '-01-01'))),
    days = ifelse(excess > 0, days - excess, days)) %>%
  summarise(days = sum(days), excess = as.integer(sum(excess[excess > 0]))) %>%
  ungroup %>%
  complete(year = seq(min(year), max(year)), fill = list(excess = 0)) %>%
  mutate(days = days + lead(excess, default = 0), excess = NULL)

输出:

# A tibble: 2 x 2
  year   days
  <chr> <dbl>
1 2018    334
2 2019     90

答案 2 :(得分:1)

基本上使用tapply,从前四个字符substr开始获取年份。

data.frame(days=with(dat, tapply(days, substr(date, 1, 4), sum)))
#      days
# 2018  212
# 2019  212

如果需要将年份作为列,最好使用aggregate

with(dat, aggregate(list(days=days), list(date=substr(date, 1, 4)), sum))
#   date days
# 1 2018  212
# 2 2019  212

要获得一年前的转账,我们可以编写一个函数fun减去,以获得转账tr

fun <- function(d) d - as.Date(paste0(substr(d, 1, 4), "-01-01"))
tr <- with(dat, as.numeric(days - fun(date)))

tapply解决方案:

res <- data.frame(days=with(dat, tapply(days, substr(date, 1, 4), sum)))
transform(res, days=days + tr[tr > 0] * c(1, -1))

#      days
# 2018  334
# 2019   90

类似地使用aggregate

res2 <- with(dat, aggregate(list(days=days), 
                            list(date=substr(date, 1, 4)), sum))
transform(res2, days=days + tr[tr > 0] * c(1, -1))
#   date days
# 1 2018  334
# 2 2019   90

数据:

dat <- structure(list(date = structure(c(17563, 17683, 17713, 17775, 
17928, 17956, 17987), class = "Date"), days = c(0, 120, 30, 62, 
153, 28, 31)), class = "data.frame", row.names = c(NA, -7L))