如何在 R 中按每 n 分钟分组时间

3

我有一个包含很多时间序列的数据帧:

1   0:03    B   1
2   0:05    A   1
3   0:05    A   1
4   0:05    B   1
5   0:10    A   1
6   0:10    B   1
7   0:14    B   1
8   0:18    A   1
9   0:20    A   1
10  0:23    B   1
11  0:30    A   1

我想将时间序列分组为每6分钟一组,并计算A和B的频率:
1   0:06    A   2
2   0:06    B   2
3   0:12    A   1
4   0:12    B   1
5   0:18    A   1
6   0:24    A   1
7   0:24    B   1
8   0:18    A   1
9   0:30    A   1

此外,时间序列的类别是字符型。我该怎么办?

2
请仔细检查您的输出数据框,时间顺序可能不正确,看起来有些奇怪。另外,您想要的区间是[0, 6),[6, 12),[12, 18)等还是(0, 6],(6, 12],(12, 18]等? - Marian Minar
2个回答

7

下面是一种将时间转换为POSIXct,将时间按6分钟间隔进行cut,然后进行count的方法。

首先,需要指定数据的年、月、日、时、分和秒。这有助于将其扩展到更大的数据集。

library(tidyverse)
library(lubridate)

# sample data
d <- data.frame(t = paste0("2019-06-02 ", 
                           c("0:03","0:06","0:09","0:12","0:15",
                             "0:18","0:21","0:24","0:27","0:30"), 
                           ":00"),
                g = c("A","A","B","B","B"))

d$t <- ymd_hms(d$t) # convert to POSIXct with `lubridate::ymd_hms()`

如果您检查新日期列的class,将看到它是"POSIXct"。

> class(d$t)
[1] "POSIXct" "POSIXt" 

现在数据已转为 "POSIXct" 格式,您可以按分钟间隔进行 cut!我们将把这个新的分组因子添加为一个名为 tc 的新列。
d$tc <- cut(d$t, breaks = "6 min")  
d
                     t g                  tc
1  2019-06-02 00:03:00 A 2019-06-02 00:03:00
2  2019-06-02 00:06:00 A 2019-06-02 00:03:00
3  2019-06-02 00:09:00 B 2019-06-02 00:09:00
4  2019-06-02 00:12:00 B 2019-06-02 00:09:00
5  2019-06-02 00:15:00 B 2019-06-02 00:15:00
6  2019-06-02 00:18:00 A 2019-06-02 00:15:00
7  2019-06-02 00:21:00 A 2019-06-02 00:21:00
8  2019-06-02 00:24:00 B 2019-06-02 00:21:00
9  2019-06-02 00:27:00 B 2019-06-02 00:27:00
10 2019-06-02 00:30:00 B 2019-06-02 00:27:00

现在您可以按照新区间(tc)和分组列(g)进行group_by,并计算出现频率。在组中获取观察频率是一种相当常见的操作,因此dplyr提供了count来完成此操作:
count(d, g, tc)
# A tibble: 7 x 3
  g     tc                      n
  <fct> <fct>               <int>
1 A     2019-06-02 00:03:00     2
2 A     2019-06-02 00:15:00     1
3 A     2019-06-02 00:21:00     1
4 B     2019-06-02 00:09:00     2
5 B     2019-06-02 00:15:00     1
6 B     2019-06-02 00:21:00     1
7 B     2019-06-02 00:27:00     2

如果您在控制台中运行?dplyr::count(),您会发现count(d, tc)只是group_by(d, g, tc) %>% summarise(n = n())的一个包装器。

最理想的情况是使用POSIXct值,但可以绕过此问题(将HH:MM更改为仅MM)。另外,您回答中的输出df看起来并不像OP的理想df(在撰写本文时存在一些问题);它缺少A和B计数。这是有意为之吗? - Marian Minar
缺少A和B计数是我的错(请参见编辑后的答案)!感谢您指出。关于HH:MM,cut仅适用于POSIXct,并且OP建议实际数据很大,因此转换POSIXct似乎是明智的,特别是如果数据开始跨越几天。 - Rich Pauloo
没问题,这就是我们所追求的。我同意,POSIXct 就是正确的方式。 - Marian Minar
请使用 OP 提供的数据,而不是从头开始编造自己的数据。这将有助于 OP 将您的解决方案应用到他的数据集中,并验证您的解决方案是否返回了预期的结果。-谢谢。 - Uwe

0
根据样本数据集,时间序列以一天中的时间为给定值,即没有日期信息。 data.table包含ITime类,它是一个以一天中的秒数表示的时间类。使用data.table,我们可以使用滚动连接将时间映射到6分钟间隔的上限(右闭区间):
library(data.table)

# coerce from character to class ITime
setDT(ts)[, time := as.ITime(time)]

# create sequence of breaks
breaks <- as.ITime(seq(as.ITime("0:00"), as.ITime("23:59:59"), as.ITime("0:06")))

# rolling join and aggregate
ts[, CJ(breaks, group, unique = TRUE)
   ][ts, on = .(group, breaks = time), roll = -Inf, .(x.breaks, group)
     ][, .N, by = .(upper = x.breaks, group)]

返回

      upper group N
1: 00:06:00     B 2
2: 00:06:00     A 2
3: 00:12:00     A 1
4: 00:12:00     B 1
5: 00:18:00     B 1
6: 00:18:00     A 1
7: 00:24:00     A 1
8: 00:24:00     B 1
9: 00:30:00     A 1

附录

如果滚动连接的方向改变(roll = +Inf 代替 roll = -Inf),我们得到左闭区间

ts[, CJ(breaks, group, unique = TRUE)
   ][ts, on = .(group, breaks = time), roll = +Inf, .(x.breaks, group)
     ][, .N, by = .(lower = x.breaks, group)]

这会显著改变结果:

      lower group N
1: 00:00:00     B 2
2: 00:00:00     A 2
3: 00:06:00     A 1
4: 00:06:00     B 1
5: 00:12:00     B 1
6: 00:18:00     A 2
7: 00:18:00     B 1
8: 00:30:00     A 1

数据

library(data.table)
ts <- fread("
1   0:03    B   1
2   0:05    A   1
3   0:05    A   1
4   0:05    B   1
5   0:10    A   1
6   0:10    B   1
7   0:14    B   1
8   0:18    A   1
9   0:20    A   1
10  0:23    B   1
11  0:30    A   1"
, header = FALSE
, col.names = c("rn", "time", "group", "value"))

看到你的 GitHub 问题,不确定我是否理解了请求,但这个也似乎可以工作:clab = structure(seq(0L, 86400L-360L, by=360L), class="ITime"); DT[, .N, keyby=.(cut(as.ITime(V2) %% 86400, clab, labels=clab[-1], include.lowest = TRUE), V3)] - Frank

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接