[英]How to calculate end date R based on next start date, and reshaping the data into date count / time series?
[英]How to count records with start date end date interval in R?
我有一個包含項目的數據框,並且每個項目都有一個開始日期和結束日期。 我想知道在一段時間內每天有多少項是活躍的。
示例數據集:
ItemId <- c(1,2,3)
StartDate <- c(ymd("2014-01-01"),ymd("2014-02-01"),ymd("2014-03-01"))
EndDate <- c(ymd("2014-02-15"),ymd("2014-02-07"),ymd("2014-03-03"))
data.frame(ItemId,StartDate,EndDate)
ItemId StartDate EndDate
1 1 2014-01-01 01:00:00 2014-02-15 01:00:00
2 2 2014-02-01 01:00:00 2014-02-07 01:00:00
3 3 2014-03-01 01:00:00 2014-03-03 01:00:00
結果看起來應該是這樣的(每天一個條目):
Date ActiveCount
2014-01-01 1
2014-01-02 1
...
2014-02-01 2
...
我有一個使用sqldf的解決方案,但不知道如何在R中執行此操作
select d.date
, ( select count(ItemID)
from items
where startdate <= d.date
and enddate >= d.date
) activecount
from (select distinct startdate from items
union
select distinct enddate from items
) d
order by 1
(我的每天包含多個條目,因此對於R中的sqlite,這是有效的。在postgresql上,我可以生成一系列更好的日期。)
謝謝。
調用你的數據df
:
dates = seq(min(df$StartDate), max(df$EndDate), by = "day")
counts = data.frame(date = dates,
count = sapply(dates, function(x) sum(x <= df$EndDate & x >= df$StartDate)))
每當R任務類似於SQL任務時,可能需要將dplyr
帶出櫥櫃:
library(dplyr)
ItemId <- c(1,2,3)
StartDate <- c(ymd("2014-01-01"),ymd("2014-02-01"),ymd("2014-03-01"))
EndDate <- c(ymd("2014-02-15"),ymd("2014-02-07"),ymd("2014-03-03"))
jim <- data.frame(ItemId,StartDate,EndDate)
# One technique that's often useful especially in R, is to take your
# iterator, and define it as a variable. You can use that to implement
# a vectorised version of whatever you were thinking of doing.*/
ed <- data.frame(rng = seq(min(jim$StartDate), max(jim$EndDate), by = 'day'))
merge(jim, ed, all=TRUE) %>%
filter(rng >= StartDate, rng <= EndDate) %>%
group_by(rng) %>%
summarise(n())
這會給你:
rng n()
1 2014-01-01 1
2 2014-01-02 1
3 2014-01-03 1
...
我已經多次回到這個問題,我一直在尋找最有效的方法。
我之前使用過map-reduce方法,但發現它不能很好地擴展到具有寬日期間隔的大型數據幀。 我只是嘗試使用lubridate
包中的interval
類,並發現它是目前為止最快的實現。
這是最終的代碼:
library(tidyverse)
library(lubridate)
# Initialize a dataframe with start and end "active" dates per object
N = 1000
id_dates = tibble(id = 1 : N) %>%
mutate(
start = sample(seq(as.Date('2018-1-1'), as.Date('2019-1-1'), by = "day"), size = N, replace = TRUE),
end = start + sample(7 : 100, size = N, replace = TRUE),
interval = interval(start, end))
# Use the %within% command to calculate the number of active items per date
queue_history = tibble(Date = seq(min(id_dates$start), max(id_dates$end), by = "1 day")) %>%
rowwise() %>%
mutate(numInWIP = sum(Date %within% id_dates$interval)) %>%
ungroup()
以下是一些基准測試,表明潤滑劑解決方案比目前的答案和地圖減少方法快得多
library(tidyverse)
library(lubridate)
# Initialize a dataframe with start and end "active" dates per object
N = 1000
id_dates = tibble(id = 1 : N) %>%
mutate(
start = sample(seq(as.Date('2018-1-1'), as.Date('2019-1-1'), by = "day"), size = N, replace = TRUE),
end = start + sample(7 : 100, size = N, replace = TRUE),
interval = interval(start, end))
# a map-reduce solution
method_mapreduce = function() {
queue_history = as.tibble(table(reduce(map2(id_dates$start, id_dates$end, seq, by = 1), c)))
queue_history = queue_history %>%
rename(Date = Var1, numInWIP = Freq) %>%
mutate(Date = as_date(Date))
return (queue_history)
}
# a lubridate interval solution
method_intervals = function() {
date_df = tibble(Date = seq(min(id_dates$start), max(id_dates$end), by = "1 day"))
queue_history = date_df %>%
rowwise() %>%
mutate(numInWIP = sum(Date %within% id_dates$interval))
return (queue_history)
}
# current best answer
method_currentsolution = function() {
date_df = tibble(Date = seq(min(id_dates$start), max(id_dates$end), by = "1 day"))
queue_history = merge(id_dates, date_df, all=TRUE) %>%
filter(Date >= start, Date <= end) %>%
group_by(Date) %>%
summarise(n())
}
# Compare with benchmarks
tst = microbenchmark::microbenchmark(
method_mapreduce(),
method_intervals(),
method_currentsolution(),
times = 5)
microbenchmark::autoplot.microbenchmark(tst) +
scale_y_log10(
name = sprintf("Time [%s]", attr(summary(tst), "unit")),
breaks = scales::trans_breaks("log10", function(x) round(10^x)))
您首先要獲得至少有一個活動項目的所有日期,然后您想要計算每天活動項目的數量。 如果我們將數據存儲在itemDates
那么這應該照顧它:
dates <- min(itemDates$StartDate) + days(0:as.numeric(max(itemDates$EndDate) - min(itemDates$StartDate)))
dateCounts <- data.frame(
row.names=dates,
counts=sapply(dates, function(date)
sum(date >= itemDates$StartDate & date <= itemDates$EndDate)))
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.