R에서 날짜를 분할 (분할)하는 방법은 무엇입니까?
등록 데이터를 사용하여 병가에 대한 연구를하고 있습니다. 기록부에서 각 개인의 병가 시작일과 종료일 만 얻었습니다. 그러나 날짜는 해마다 분류되지 않습니다. 예를 들어 사람 A의 경우 시작일 (2016 년 5 월 1 일) 및 종료일 (2018 년 2 월 14 일)에 대한 데이터 만 있습니다.
따라서 R에서 연도별로 날짜를 분할하는 방법을 알고 싶습니다 (예 : 01/05/16 ~ 14/02/18은 01 / 5 / 16-31 / 12 / 16, 01 / 01 / 2017-31 / 12 / 17, 01 / 01 / 18-14 / 02 / 18)을 사용하여 매년 총 병가 수를 계산합니다.
질문에 대해 생성 된 예제 데이터는 다음과 같습니다.
sick_leave <- tribble(
~id, ~from, ~to,
1, "01/01/2018", "03/10/2020",
2, "01/01/2016", "01/01/2021",
3, "02/01/2018", "02/06/2018",
3, "02/07/2018", "31/12/2018",
4, "02/10/2018", "02/02/2019",
4, "31/12/2019", "01/01/2021",
5, "02/10/2017", "20/05/2018",
6, "02/03/2021", "31/12/2021",
7, "01/01/2016", "05/06/2016"
) %>% mutate(from = dmy(from),to = dmy(to))
원하는 출력은 다음과 같습니다.
id year from to wanted
1 2018 2018-01-01 2018-12-31 365
1 2019 2019-01-01 2019-12-31 365
1 2020 2020-01-01 2020-10-03 277
2 2016 2016-01-01 2016-12-31 366
2 2017 2017-01-01 2017-12-31 365
2 2018 2018-01-01 2018-12-31 365
2 2019 2019-01-01 2019-12-31 365
2 2020 2020-01-01 2020-12-31 366
2 2021 2021-01-01 2021-01-01 1
3 2018 2018-01-02 2018-06-02 152
3 2018 2018-07-02 2018-12-31 183
4 2018 2018-10-02 2018-12-31 91
4 2019 2019-01-01 2019-02-02 33
4 2019 2019-12-31 2019-12-31 1
4 2020 2020-01-01 2020-12-31 366
4 2021 2021-01-01 2021-01-01 1
5 2017 2017-10-02 2017-12-31 91
5 2018 2018-01-01 2018-05-20 140
6 2021 2021-03-02 2021-12-31 305
7 2016 2016-01-01 2016-06-05 157
답변
1 Edo
이 솔루션을 사용하면 요청에 따라 새 행을 만들어 날짜를 분할 할 수 있습니다.
이 기능 split_by_year
은 행 단위로 수행됩니다.
코드에 몇 가지 의견을 남길 것입니다.
# necessary libraries
library(dplyr)
library(lubridate)
split_by_year <- function(from, to){
year_from <- year(from)
year_to <- year(to)
# get sequence of years
years <- seq(year_from, year_to)
# create start and end date for each year
starts <- make_date(years)
ends <- make_date(years, 12, 31)
# set starts and ends together, replace limits with from and end
dates <- sort(c(starts, ends))
dates[c(1, length(dates))] <- c(from, to)
# recreate dataframe with columns from and to
m <- matrix(dates, ncol = 2, byrow = TRUE)
colnames(m) <- c("from", "to")
mutate_all(as_tibble(m), as_date)
}
sick_leave %>%
rowwise() %>% # next line will be performed row by row
summarise(id = id, split_by_year(from, to)) %>%
mutate(sick_days = as.numeric(to - from + 1))
산출:
# A tibble: 20 x 4
id from to sick_days
<dbl> <date> <date> <dbl>
1 1 2018-01-01 2018-12-31 365
2 1 2019-01-01 2019-12-31 365
3 1 2020-01-01 2020-10-03 277
4 2 2016-01-01 2016-12-31 366
5 2 2017-01-01 2017-12-31 365
6 2 2018-01-01 2018-12-31 365
7 2 2019-01-01 2019-12-31 365
8 2 2020-01-01 2020-12-31 366
9 2 2021-01-01 2021-01-01 1
10 3 2018-01-02 2018-06-02 152
11 3 2018-07-02 2018-12-31 183
12 4 2018-10-02 2018-12-31 91
13 4 2019-01-01 2019-02-02 33
14 4 2019-12-31 2019-12-31 1
15 4 2020-01-01 2020-12-31 366
16 4 2021-01-01 2021-01-01 1
17 5 2017-10-02 2017-12-31 91
18 5 2018-01-01 2018-05-20 140
19 6 2021-03-02 2021-12-31 305
20 7 2016-01-01 2016-06-05 157
1 Wimpel
귀하의 질문은 XY 문제 처럼 들립니다 .
그래서 연도 별 간격 생성을 건너 뛰고 원하는 답인 연간 ID 당 병가 계산 ..
원하는 출력을 위해 업데이트 됨 .. 하단에 추가 된 코드 참조
샘플 데이터
#create sample data
library( data.table)
library( lubridate )
sick_leave <- data.table::fread('
id, from, to
1, "1/1/2018", "3/10/2020"
2, "1/1/2016", "1/1/2021"
3, "2/1/2018", "2/6/2018"
3, "2/7/2018", "31/12/2018"
4, "2/10/2018", "2/2/2019"
4, "31/12/2019", "1/1/2021"
5, "2/10/2017", "20/5/2018"
6, "2/3/2021", "31/12/2021"
7, "1/1/2016", "5/6/2016"')
#set dates as real dates
cols = c("from", "to")
sick_leave[, (cols) := lapply( .SD, as.Date, format = "%d/%m/%Y"), .SDcols = cols ]
암호
#if your data is in data.frame / tibble format, use
data.table::setDT( sick_leave )
#to make it a data.table
#create table from min-date to max_date
DT <- data.table( from = seq( min( sick_leave$from, na.rm = TRUE ), max( sick_leave$to, na.rm = TRUE ),
by = "1 days") )
DT[, to := from + lubridate::days(1) ]
#set keys
setkey( sick_leave, from, to )
setkey( DT, from, to )
#perform overlap join
ans <- foverlaps( sick_leave, DT )
#summarise
ans <- ans[, .(days_sick = .N), by = .(id, year = lubridate::year(from) )]
#cast to wide
dcast( ans, id ~ year, value.var = "days_sick", fill = 0 )
산출
# id 2016 2017 2018 2019 2020 2021
# 1: 1 0 1 365 365 277 0
# 2: 2 366 365 365 365 366 1
# 3: 3 0 0 337 0 0 0
# 4: 4 0 0 92 35 366 1
# 5: 5 0 92 140 0 0 0
# 6: 6 0 0 0 0 0 306
# 7: 7 157 0 0 0 0 0
원하는 출력과 일치하는 업데이트
암호
#if your data is in data.frame / tibble format, use
data.table::setDT( sick_leave )
#to make it a data.table
#make data-table with years
DT <- data.table( from = seq( as.Date("2000-01-01"), length.out = 30, by = "1 year"),
to = seq( as.Date("2000-12-31"), length.out = 30, by = "1 year") )
#set keys
setkey( sick_leave, from, to ); setkey( DT, from, to )
#perform overlap join
ans <- foverlaps( sick_leave, DT )
#choose keep the right columns (start/end)
ans[ from < i.from, from := i.from ]
ans[ to > i.to, to := i.to ]
#cleaning
ans[, `:=`(i.from = NULL, i.to = NULL)][]
#order
setorder( ans, id, from )
#calculate duration
ans[, `:=`( year = lubridate::year( from ),
wanted = to - from + 1) ]
산출
# from to id year wanted
# 1: 2018-01-01 2018-12-31 1 2018 365 days
# 2: 2019-01-01 2019-12-31 1 2019 365 days
# 3: 2020-01-01 2020-10-03 1 2020 277 days
# 4: 2016-01-01 2016-12-31 2 2016 366 days
# 5: 2017-01-01 2017-12-31 2 2017 365 days
# 6: 2018-01-01 2018-12-31 2 2018 365 days
# 7: 2019-01-01 2019-12-31 2 2019 365 days
# 8: 2020-01-01 2020-12-31 2 2020 366 days
# 9: 2021-01-01 2021-01-01 2 2021 1 days
# 10: 2018-01-02 2018-06-02 3 2018 152 days
# 11: 2018-07-02 2018-12-31 3 2018 183 days
# 12: 2018-10-02 2018-12-31 4 2018 91 days
# 13: 2019-01-01 2019-02-02 4 2019 33 days
# 14: 2019-12-31 2019-12-31 4 2019 1 days
# 15: 2020-01-01 2020-12-31 4 2020 366 days
# 16: 2021-01-01 2021-01-01 4 2021 1 days
# 17: 2017-10-02 2017-12-31 5 2017 91 days
# 18: 2018-01-01 2018-05-20 5 2018 140 days
# 19: 2021-03-02 2021-12-31 6 2021 305 days
# 20: 2016-01-01 2016-06-05 7 2016 157 days