Группировка по годам в R - PullRequest
       6

Группировка по годам в R

3 голосов
/ 01 февраля 2020

У меня есть набор данных, по которому я хочу сгруппировать по годам (и суммой свыше days), но если число days для определенного date больше, чем число дней, произошедших в году пока дополнительные дни должны быть добавлены к предыдущему году. Например, ниже, из 153 дней, связанных с 2019-02-01, 31 день должен go к 2019 году, а 122 - go к 2018.

Данные

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

Ожидаемый результат

year   days
2018   334
2019   90

Как я могу сделать это в R? (в идеале использовать dplyr, но base-R подойдет, если это единственный способ)

Ответы [ 3 ]

4 голосов
/ 01 февраля 2020

Вот один из способов использования базы 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
2 голосов
/ 01 февраля 2020

Возможный 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
1 голос
/ 01 февраля 2020

В основном, используя tapply, получая год из первых четырех символов substr ing.

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))
...