我有一个数据集,我想按年份分组(总和超过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)
答案 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))