【问题标题】:Count the number of rows within a certain time range based on each row in R (tidyverse)根据R中的每一行统计一定时间范围内的行数(tidyverse)
【发布时间】:2021-04-22 16:31:49
【问题描述】:

我想根据id分组后的每一行统计一定时间范围内的行数。例如,让我们在“cleaned_date”列中的每个日期时间条目周围假设一个 1 个月的窗口。

head(data$cleaned_date)

[1] "2004-10-11 CDT" "2008-09-10 CDT" "2011-10-25 CDT" "2011-12-31 CST"

日期采用 POSIXct 格式。

对于第一个条目,我需要统计从 2004-09-11 到 2004-11-11 这段时间内的行数,对于第二个条目,我需要统计从 2008-08- 这段时间内的行数10 到 2008-10-10,依此类推。

我大致使用了以下代码

data %>% group_by(id) %>% filter(cleaned_date %within% interval(cleaned_date - 24 * 60 * 60 * 30, cleaned_date + 24 * 60 * 60 * 30)) %>% mutate(counts = n())

但它似乎不起作用,我得到了一个空列。任何帮助将不胜感激,谢谢!

一个可重现的例子如下:

输入是

  cleaned_date id
1   2008-09-11  A
2   2008-09-10  B
3   2008-09-30  B
4   2011-10-25  A
5   2011-11-14  A

我希望输出是

  cleaned_date id counts
1   2008-09-11  A      1
2   2008-09-10  B      2
3   2008-09-30  B      2
4   2011-10-25  A      2
5   2011-11-14  A      2

对于第一个条目,我想计算 2008-08-11 到 2008-10-11 时间范围内的行数,第二个条目似乎满足但我们需要按“id”分组,所以它不计算.对于第二个条目,我想计算时间范围 2008-08-10 到 2008-10-10 中的行,第 2 行和第 3 行满足,所以计数为 2。对于第三个条目,我想计算时间范围内的行2008-08-30 到 2008-10-30,第 2 行和第 3 行再次满足,依此类推。

请注意,我要操作的实际数据集有数百万行,因此使用 tidyverse 可能比使用 base R 更有效。

【问题讨论】:

  • 如果您包含一个简单的reproducible example,其中包含可用于测试和验证可能解决方案的示例输入和所需输出,则更容易为您提供帮助。
  • 当然。谢谢!我只是添加了一个小例子

标签: r datetime dplyr count tidyverse


【解决方案1】:

也许不是最优雅的解决方案。

# input data. Dates as character vector
input = data.frame(
    cleaned_date = c("2008-09-11", "2008-09-10", "2008-09-30", "2011-10-25", "2011-11-14"), 
    id = c("A", "B", "B", "A", "A")
    )

# function to create a date window n months around specified date
window <- function(x, n = 1){
    x <- rep(as.POSIXlt(x),2)
    x[1]$mon <- x[1]$mon - n
    x[2]$mon <- x[2]$mon + n
    return(format(seq(from = x[1], to = x[2], by = "day"), format="%Y-%m-%d"))
}

# find counts for each row
input$counts <- unlist(lapply(1:nrow(input), function(x){
    length(which((input$cleaned_date %in% window(input$cleaned_date[x])) & input$id == input$id[x]))
    }))

input

  cleaned_date id counts
1   2008-09-11  A      1
2   2008-09-10  B      2
3   2008-09-30  B      2
4   2011-10-25  A      2
5   2011-11-14  A      2

编辑大型数据集:

# dummy dataset with 1,000,000 rows
years <- c(2000:2020)
months <- c(1:12)
days <- c(1:20)
n <- 1000000
dates <- paste(sample(years, size = n, replace = T), sample(months, size = n, replace = T), sample(days, size = n, replace = T), sep = "-")
groups <- sample(c("A","B","C"), size = n, replace = T)
input <- data.frame(
    cleaned_date = dates,
    id = groups
)
input$cleaned_date <- format(as.POSIXlt(input$cleaned_date), format="%Y-%m-%d")

# optional, sort data by date for small boost in performance
input <- input[order(input$cleaned_date),]
counts <- NULL
#pb <- progress::progress_bar$new(total = length(unique(input$cleaned_date)))
t1 <- Sys.time()
# split up vectorization for each unique date.
for(date in unique(input$cleaned_date)){
    #pb$tick()
    w <- window(date)
    tmp <- input[which(input$cleaned_date %in% w),]
    tmp_counts <- unlist(lapply(which(tmp$cleaned_date == date), function(x){
        length(which(tmp$id == tmp$id[x]))
    }))
    counts <- c(counts, tmp_counts)
}
# add counts to dataset
input$counts <- counts 
# optional, re-order data to original format
input <- input[order(as.numeric(rownames(input))),]
print(Sys.time() - t1)

时差 3.247204 分钟

如果你想走得更快,你可以并行运行循环

library(foreach)
library(doParallel)

cores=detectCores()
cl <- makeCluster(cores[1]-1)
registerDoParallel(cl)

dates = unique(input$cleaned_date)
t1 <- Sys.time()
counts <- foreach(i=1:length(dates), .combine= "c") %dopar% {
    w <- window(dates[i])
    tmp <- input[which(input$cleaned_date %in% w),]
    tmp_counts <- unlist(lapply(which(tmp$cleaned_date == dates[i]), function(x){
        length(which(tmp$id == tmp$id[x]))
    }))
    tmp_counts
}
stopCluster(cl)
input$counts <- counts
input <- input[order(as.numeric(rownames(input))),]
print(Sys.time() - t1)

时差 37.37211 秒

请注意,我在配备 2.3 GHz 四核 Intel Core i7 和 16 GB RAM 的 MacBook Pro 上运行此程序。

【讨论】:

  • 欢迎来到 Stackoverflow。喜欢您的回答并在家里玩弄它,很高兴在工作中看到base。另一行上的window 函数,&gt; input[nrow(input)+1, ] &lt;- NA 与 2011 日期和 B id 真的让我很清楚。
  • 非常感谢!它适用于该示例,但是当我应用于实际数据集时,出现错误“h(simpleError(msg, call)) 中的错误:在为函数“which”选择方法时评估参数“x”时出错:在为函数“格式”选择方法时评估参数“x”时出错:“to”必须是有限数调用方:h(simpleError(msg, call))"
  • 这可能是因为我的数据集有太多行(大约 2 到 300 万行)并且基础 R 可能不够高效。
  • @TwoAlpha,查看适合您的大型数据集的编辑
  • 非常感谢!只是为了确认一下,对于“w
【解决方案2】:

仍然很难确切地确定您要完成什么,但这至少可以让您在指定的日期范围内获得计数:

df %>% 
  group_by(id) %>% 
  filter(cleaned_date >= "2008-08-11" & cleaned_date <= "2008-10-11") %>% 
  mutate(counts = n())

会给我们:

  cleaned_date id    counts
  <date>       <chr>  <int>
1 2008-09-11   A          1
2 2008-09-10   B          2
3 2008-09-30   B          2

【讨论】:

    猜你喜欢
    • 2022-11-28
    • 2021-10-27
    • 2020-11-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-10-19
    • 1970-01-01
    相关资源
    最近更新 更多