R: 使用dplyr计算提前1小时出现的次数。

问题描述 投票:0回答:1

我想知道如何使用dplyr来计算每个id在1小时前的每次出现的次数。尝试使用for循环,但没有得到我想要的结果。通过堆栈,尝试寻找各种方法,但无济于事。任何建议或帮助都非常感激。谢谢你的帮助

数据集。https:/drive.google.comfiled1U186SeBWYyTnJVgUPmow7yknr6K9vu8iview?usp=共享。

  id           date_time count
1  1 2019-12-27 00:00:00    NA
2  2 2019-12-27 00:00:00    NA
3  2 2019-12-27 00:55:00    NA
4  2 2019-12-27 01:00:00    NA
5  2 2019-12-28 01:00:00    NA
6  3 2019-12-27 22:00:00    NA
7  3 2019-12-27 22:31:00    NA
8  3 2019-12-28 14:32:00    NA

理想的输出

  id           date_time count
1  1 2019-12-27 00:00:00    1     #Count = 1 since there is no other cases 1 hour ahead but itself, only 1 case of id=1 
2  2 2019-12-27 00:00:00    3     #Count = 3 as there are 3 cases from 00:00 to 01:00 on 27/12
3  2 2019-12-27 00:55:00    2     #Count = 2 as there are 2 cases from 00:55 to 01:55 on 27/12
4  2 2019-12-27 01:00:00    1     #Count = 1 as only itself from 01:00 to 02:00 on 27/12
5  2 2019-12-28 01:00:00    1     #Count = 1 as only itself from 01:00 to 02:00 on 28/12
6  3 2019-12-27 22:00:00    2
7  3 2019-12-27 22:31:00    1
8  3 2019-12-28 14:32:00    1

我的代码(我卡了)。

library(tidyverse)

data <- read.csv('test.csv')
data$date_time <- as.POSIXct(data$date_time)
data$count <- NA

data %>% 
  group_by(id) %>%
  arrange(date_time, .by_group=TRUE)

#Doesn't give the desired output
for (i in 1:nrow(data)){
  data$count[i] <- nrow(data[data$date_time<=data$date_time[i]+1*60*60 & data$date_time>=data$date_time[i],])
}
r dplyr posixct
1个回答
4
投票

如果OP只是寻找 tidyverse 解决办法。我很乐意删除这个。

下面是一个使用 data.table 非酋加盟。

DT[, onehrlater := date_time + 60*60] 
DT[, count :=
  DT[DT, on=.(id, date_time>=date_time, date_time<=onehrlater),
    by=.EACHI, .N]$N
]

怎么看这个。

1) DT[, onehrlater := date_time + 60*60] 创建一个新的POSIX日期时间列,时间晚一小时。:= 通过引用更新原数据集。

2) DT[DT, on=.(id, date_time>=date_time, date_time<=onehrlater) 执行一个自非等式连接,使所有具有i)相同id,ii)日期_时间在这一行的日期_时间之后,iii)日期_时间在这一行的日期_时间之前的一小时后的记录都连接到这一行。

3) by=.EACHI, .N 返回这些行中的每一行的计数。而 $N 访问这个自非等价加入的输出。而 DT[, count := ...] 通过引用更新原始数据集。

输出。

   id           date_time          onehrlater count
1:  1 2019-12-27 00:00:00 2019-12-27 01:00:00     1
2:  2 2019-12-27 00:00:00 2019-12-27 01:00:00     3
3:  2 2019-12-27 00:55:00 2019-12-27 01:55:00     2
4:  2 2019-12-27 01:00:00 2019-12-27 02:00:00     1
5:  2 2019-12-28 01:00:00 2019-12-28 02:00:00     1
6:  3 2019-12-27 22:00:00 2019-12-27 23:00:00     2
7:  3 2019-12-27 22:31:00 2019-12-27 23:31:00     1
8:  3 2019-12-28 14:32:00 2019-12-28 15:32:00     1

数据。

library(data.table)
DT <- fread("id           date_time 
1 2019-12-27T00:00:00
2 2019-12-27T00:00:00
2 2019-12-27T00:55:00
2 2019-12-27T01:00:00
2 2019-12-28T01:00:00
3 2019-12-27T22:00:00
3 2019-12-27T22:31:00
3 2019-12-28T14:32:00")
DT[, date_time := as.POSIXct(date_time, format="%Y-%m-%dT%T")]

3
投票

这个问题可以用 非等价交换 (在 data.table 说)。) 遗憾的是,这个功能还不能与 dplyr,AFAIK。

这里是一个使用SQL的实现。

library(sqldf)
sqldf("
select d1.id, d1.date_time, count(d2.date_time) as count 
  from dat as d1, dat as d2
  where d1.id = d2.id and d1.date_time between d2.date_time and (d2.date_time + 60*60)
  group by d2.id, d2.date_time")
  id           date_time count
1  1 2019-12-27 00:00:00     1
2  2 2019-12-27 00:00:00     3
3  2 2019-12-27 00:55:00     2
4  2 2019-12-27 01:00:00     1
5  2 2019-12-28 01:00:00     1
6  3 2019-12-27 22:00:00     2
7  3 2019-12-27 22:31:00     1
8  3 2019-12-28 14:32:00     1

数据

# reading directly from google drive, see https://stackoverflow.com/a/33142446/3817004
dat <- data.table::fread(
  "https://drive.google.com/uc?id=1U186SeBWYyTnJVgUPmow7yknr6K9vu8i&export=download")[
    , date_time := anytime::anytime(date_time)]

1
投票

也许吧 fuzzyjoin 也许在这里会有帮助。您可以为每一行数据创建时间范围(将时间范围设置为 end_time 到3600秒或每次后1小时)。) 然后,你可以和自己做一个模糊的连接,其中的 date_time 介于此范围之间的,算作小时内。

library(tidyverse)
library(fuzzyjoin)

df %>%
  mutate(row_id = row_number(),
         end_time = date_time + 3600) %>%
  fuzzy_inner_join(df, 
                  by = c("id", "date_time" = "date_time", "end_time" = "date_time"), 
                  match_fun = list(`==`, `<=`, `>=`)) %>%
  group_by(row_id) %>%
  summarise(id = first(id.x),
            date_time = first(date_time.x),
            count = n())

产量

# A tibble: 8 x 4
  row_id    id date_time           count
   <int> <int> <dttm>              <int>
1      1     1 2019-12-27 00:00:00     1
2      2     2 2019-12-27 00:00:00     3
3      3     2 2019-12-27 00:55:00     2
4      4     2 2019-12-27 01:00:00     1
5      5     2 2019-12-28 01:00:00     1
6      6     3 2019-12-27 22:00:00     2
7      7     3 2019-12-27 22:31:00     1
8      8     3 2019-12-28 14:32:00     1

1
投票

我可能会在这里写一个小的帮助函数和split-apply-bind方法一起使用,而不是写个 group_by:

f <- function(x)
{
  sapply(1:nrow(x), function(i) {
    y <- as.numeric(difftime(x$date_time, x$date_time[i], units = "min"))
    sum(y >= 0 & y <= 60)
  })
}

df %>% mutate(count = do.call(c, df %>% split(df$id) %>% lapply(f)))
#>   id           date_time count
#> 1  1 2019-12-27 00:00:00     1
#> 2  2 2019-12-27 00:00:00     3
#> 3  2 2019-12-27 00:55:00     2
#> 4  2 2019-12-27 01:00:00     1
#> 5  2 2019-12-28 01:00:00     1
#> 6  3 2019-12-27 22:00:00     2
#> 7  3 2019-12-27 22:31:00     1
#> 8  3 2019-12-28 14:32:00     1

0
投票

我把数据按id分割,然后对每一行计算出所选行后的日期时间在1小时范围内的有多少。

my_data <- tribble(
  ~id,   ~date_time, 
  1, "2019-12-27 00:00:00",
  2, "2019-12-27 00:00:00",    
  2, "2019-12-27 00:55:00",    
  2, "2019-12-27 01:00:00",   
  2, "2019-12-28 01:00:00",    
  3, "2019-12-27 22:00:10",    
  3, "2019-12-27 22:31:00",    
  3, "2019-12-28 14:32:00"    
)

my_data <- my_data %>%
  mutate(
    date_time = lubridate::ymd_hms(date_time)
  ) %>%
  split(.$id) %>%
  map(~.x %>% mutate(diff = c(0, diff(date_time)) / 60))

counts <- my_data %>%
  map(function(id_data) 
    map_dbl(seq_len(nrow(id_data)),
        ~{
          start_diff <- id_data %>% 
            slice(.x) %>%
            pluck("diff")

          id_data[.x:nrow(id_data),] %>%
            filter(diff - start_diff < 1) %>%
            nrow()
        }
    )
  )

my_data <- my_data %>%
  map2(counts, ~.x %>% mutate(counts = .y)) %>%
  bind_rows() %>%
  select(-diff)

0
投票

你只需要调整一下你的循环逻辑。

res <- data.frame() # empty df for results

for(i in unique(data$id)){
  tmp      <- data[data$id == i,]  # logic is on the Id level

  for(r in 1:nrow(tmp)){
    tmp          <- tmp[ifelse(tmp$date_time <= tmp$date_time[1]+3600,T,F),] # logical test based on 1 hour window
    tmp$count[1] <- nrow(tmp)       # count
    tmp          <- tmp[1,]         # result is on the row level
    res          <- rbind(res, tmp) # populate results
  }
}

这样就可以得到:

> res
  id           date_time count
1  1 2019-12-27 00:00:00     1
2  2 2019-12-27 00:00:00     3
3  2 2019-12-27 00:00:00     1
4  2 2019-12-27 00:00:00     1
5  2 2019-12-27 00:00:00     1
6  3 2019-12-27 22:00:00     2
7  3 2019-12-27 22:00:00     1
8  3 2019-12-27 22:00:00     1
© www.soinside.com 2019 - 2024. All rights reserved.