R语言 如何计算在特定时间内“活动”的项目数量?

xkrw2x1b  于 2023-10-13  发布在  其他
关注(0)|答案(5)|浏览(114)

后台

我有以下项目的数据框架:
| ID|开始日期|结束日期|
| --|--|--|
| 01 |2019年11月6日|2021年4月7日|
| 02 |2021年12月8日|2022年1月18日|
| 03 |2019年10月24日|2019年12月3日|
| 04 |2021年9月20日|2021年11月11日|
| 05 |2021年11月11日|2021年11月11日|
我也有一个日期列表:
date_list <- c(1/1/2020, 1/1/2021, 1/1,2022)
我想数一下在这些特定日期有多少项目是“活跃”的。
举例来说:在2020年1月1日,只有1个项目处于活动状态,即项目1,因为它在2020年1月1日之前开始,在2020年1月1日之后结束。换句话说,这是一个当时正在进行的项目。

当前方法

for (date in date-list) {
  projects %>% filter(start_date <= date & end_date > date) %>% count()
}

这是太长了,因为我的工作与成千上万的日期和成千上万的项目列表。

euoag5mw

euoag5mw1#

您可以首先构建一个包含给定日期所有打开项目的表。这里有一个方法

library(dplyr)
number_open <- rbind(
  data.frame(date=dd$end_date+1, open=-1),
  data.frame(date=dd$start_date, open=1)
) %>%
  arrange(date) %>% 
  summarize(open=sum(open), .by=date) %>% 
  mutate(open=cumsum(open))

返回

date open
1 2019-06-11    1
2 2019-10-24    2
3 2019-12-04    1
4 2021-04-08    0
5 2021-08-12    1
6 2021-09-20    2
7 2021-11-11    3
8 2021-11-12    1
9 2022-01-19    0

所以对于每个日期,我们可以看到项目数量的变化。我们可以使用findInterval来查找给定日期的开放事件的数量

date_list <- as.Date(c("1/1/2020", "1/1/2021", "1/1/2022"), "%m/%d/%Y")
number_open$open[findInterval(date_list, number_open$date)]
# [1] 1 1 1

因此,在这些日期中的每个日期都有一个项目开放。测试与

dd <- read.table(text="
id  start_date  end_date
01  6/11/2019   4/7/2021
02  8/12/2021   1/18/2022
03  10/24/2019  12/3/2019
04  9/20/2021   11/11/2021
05  11/11/2021  11/11/2021", header=TRUE)
dd$start_date <- as.Date(dd$start_date, "%m/%d/%Y")
dd$end_date <- as.Date(dd$end_date, "%m/%d/%Y")
ws51t4hk

ws51t4hk2#

一个有效的方法是使用data.table::foverlaps(),一个 * 基于快速二进制搜索的两个数据表的重叠连接 *。
首先为连接创建两个data.tabledate_listdata.table将有一个dummy_date字段,因为foverlaps()可以找到重叠的范围。但在这种情况下,范围的开始和结束将是相同的。

library(data.table)

setDT(projects)
# Create a data.table to join against
date_dt <- data.table(date = date_list, dummy_date = date_list)

# Set keys for join
setkey(projects, start_date, end_date)
setkey(date_dt, date, dummy_date)

然后我们可以进行join,按date分组,并计算每个日期有多少个项目打开:

# Do the join
project_dates <- foverlaps(projects, date_dt, type = "any")

# Summarise by date
project_dates[!is.na(date), .N, date]
#          date     N
#        <IDat> <int>
# 1: 2020-01-01     1
# 2: 2021-01-01     1
# 3: 2022-01-01     1

输入数据

projects  <- structure(list(id = 1:5, start_date = structure(c(18058L, 18851L, 
18193L, 18890L, 18942L), class = c("IDate", "Date")), end_date = structure(c(18724L, 
19010L, 18233L, 18942L, 18942L), class = c("IDate", "Date"))), class = "data.frame", row.names = c(NA, -5L))

date_list <- structure(c(18262L, 18628L, 18993L), class = c("IDate", "Date"))
snvhrwxg

snvhrwxg3#

1.将1分配给开始日期,将-1分配给结束日期,将0分配给查找日期
1.将所有日期绑定到一个表中,并为其分配值
1.按日期排序
1.取指定数字的累计和
1.查找日期的子集
data.table

library(data.table)

date_list <- as.Date(c("1/1/2020", "1/1/2021", "1/1/2022"), "%m/%d/%Y")

setorder(
  rbindlist(
    list(
      projects[,.(date = c(start_date, end_date), inc = rep(c(1L, -1L), each = .N))],
      data.table(date = date_list, inc = 0L)
    )
  ), date
)[,n := cumsum(inc)][inc == 0L][,inc := NULL][]
#>          date n
#> 1: 2020-01-01 1
#> 2: 2021-01-01 1
#> 3: 2022-01-01 1

数据类型:

projects <- structure(list(id = 1:5, start_date = structure(c(18058, 18851, 
18193, 18890, 18942), class = "Date"), end_date = structure(c(18724, 
19010, 18233, 18942, 18942), class = "Date")), row.names = c(NA, 
-5L), class = c("data.table", "data.frame"))
vsmadaxz

vsmadaxz4#

使用tidyr::crossing的另一个选项

library(dplyr)
library(tidyr)

crossing(date_list, projects) %>%
  filter(between(date_list, start_date, end_date)) %>%
  count(date_list)

#----
# A tibble: 3 x 2
  date_list      n
  <date>     <int>
1 2020-01-01     1
2 2021-01-01     1
3 2022-01-01     1

示例数据

projects <- structure(list(id = 1:5, start_date = structure(c(18058, 18851, 
                                                              18193, 18890, 18942), class = "Date"), end_date = structure(c(18724, 
                                                                                                                            19010, 18233, 18942, 18942), class = "Date")), row.names = c(NA, 
                                                                                                                                                                                         -5L), class = c("data.table", "data.frame"))
date_list <- as.Date(c("1/1/2020", "1/1/2021", "1/1/2022"), "%m/%d/%Y")

由于所有组合都是创建的,因此这可能不是最有效的解决方案。但对1,800个约会对象的快速测试只需要一两秒钟

dates_tst <- seq(as.Date("2018-1-1"), as.Date("2023-1-1"), by = '1 day')

projects_tst <- data.frame(projects = 1:length(dates_tst),
                           start_date = dates_tst - 100,
                           end_date = dates_tst + 100)

crossing(dates_tst, projects_tst) %>% # 3.3M combinations
  filter(between(dates_tst, start_date, end_date)) %>%
  count(dates_tst)

#----
# A tibble: 1,827 x 2
   dates_tst      n
   <date>     <int>
 1 2018-01-01   101
 2 2018-01-02   102
 3 2018-01-03   103
 4 2018-01-04   104
 5 2018-01-05   105
 6 2018-01-06   106
 7 2018-01-07   107
 8 2018-01-08   108
 9 2018-01-09   109
10 2018-01-10   110
# i 1,817 more rows
vptzau2j

vptzau2j5#

不完全确定预期的结果,但这增加了每个 project 行有多少 date_list 条目是活动的。
注意:使用projects$start_date <- as.Date(projects$start_date, "%m/%d/%Y")projects$end_date <- as.Date(projects$end_date, "%m/%d/%Y")将日期转换为Date对象
制备

library(data.table)

setDT(projects)

date_list <- c("1/1/2020", "1/1/2021", "1/1/2022")
projects$active <- rowSums(sapply(as.Date(date_list, "%m/%d/%Y"), \(x) 
  data.table::between(x, projects$start_date, projects$end_date)))

结果

projects
   id start_date   end_date active
1:  1 2019-06-11 2021-04-07      2
2:  2 2021-08-12 2022-01-18      1
3:  3 2019-10-24 2019-12-03      0
4:  4 2021-09-20 2021-11-11      0
5:  5 2021-11-11 2021-11-11      0

数据

projects <- structure(list(id = 1:5, start_date = structure(c(18058, 18851, 
18193, 18890, 18942), class = "Date"), end_date = structure(c(18724, 
19010, 18233, 18942, 18942), class = "Date")), row.names = c(NA, 
-5L), class = "data.frame")

相关问题