【问题标题】:Extract rows for the first occurrence of a variable in a group提取组中变量第一次出现的行
【发布时间】:2016-11-02 07:57:10
【问题描述】:

我有一个庞大的数据集(超过 200 万行,包含 100 多个变量;下面是一个小样本)。对于每个subj_trial 组,我想在message 中找到包含在“.wav”中的每个唯一变量的第一次出现。它应该只是包含而不是结尾(即 *.wav),因为某些行在 message 字段中有一堆信息(在示例中未显示,抱歉)。

输出只有这三列的 data.frame 是可以的,但这不是必需的。稍后我需要使用timestamp 列进行分析。

我发现了这个问题:Extract rows for the first occurrence of a variable in a data frame,但在我的一生中,我无法用这个例子来适应我的情况。

以下是一些示例数据:

   subj_trial     message timestamp
1         1_1 message 459    755616
2         1_1           .    755618
3         1_1   test1.wav    755662
4         1_1           .    765712
5         1_1   test1.wav    767918
6         1_2           .    769342
7         1_2   test2.wav    775662
8         1_2           .    786412
9         1_2   test2.wav    797460
10        1_2           .    807626
11        1_3   test3.wav    817794
12        1_3  warning 11    827960
13        2_1 message 481    817313
14        2_1   test1.wav    817347
15        2_1           .    834959
16        2_1   test1.wav    855007
17        2_1           .    880107
18        2_2           .    895723
19        2_2   test2.wav    922671
20        2_2           .    958003
21        2_2   test2.wav    994385
22        2_3           .   1016217
23        2_3   test3.wav   1036899
24        2_3           .   1047331
25        2_3   test3.wav   1142527

这是我正在处理的一个非常小的例子,在这里。对于每个subj_trial 组,大概有 3000 行,并且有超过 700 个组。

这是我想要的一个例子。

  subj_trial   message timestamp
1        1_1 test1.wav    755662
2        1_2 test2.wav    775662
3        1_3 test3.wav    817794
4        2_1 test1.wav    817347
5        2_2 test2.wav    922671
6        2_3 test3.wav   1036899

我已经弄清楚如何通过这样做在整个数据集上获取 message 中的唯一值:

unique_message <- df[match(unique(df$message), df$message),]

但我不知道如何按组进行操作。我也尝试在dplyr 包中使用group_by,但也无法使其正常工作。朋友们,请怜悯并给我指路。谢谢!

【问题讨论】:

  • @SerbanTanasa 这样做没有帮助,因为这将是一组的 25 行,主要是“。”在message 字段中,并且只有一个 .wav 值的实例。我提供的例子很好。
  • @SerbanTanasa 好的,我知道您关心的是格式而不是内容。谢谢你告诉我。

标签: r


【解决方案1】:

如果您有兴趣,这里还有一个dplyr 解决方案:

dat %>%
  filter(grepl("\\.wav", message)) %>%
  group_by(subj_trial) %>%
  top_n(n=1, wt=desc(timestamp))

首先,将数据过滤为仅在消息列中包含 *.wav 的数据。然后按主题试验对数据进行分组,并返回时间戳最小的顶部结果。这假设您想要 最小 时间戳,不一定是数据集中的第一个时间戳(即,如果具有较大时间戳的记录首先出现,则不会返回)。我不清楚您在寻找哪个,但也许在您的情况下没有区别。

因为我总是对data.tabledplyrapproaches 之间的效率差异感到好奇,所以我做了一个microbenchmark 测试。看起来在这种情况下,data.table 具有轻微的速度优势:

library(microbenchmark)
library(data.table)

set.seed(1)
dat <- data.frame(subj_trial=paste0(sample(1:20,1e6,replace=TRUE),"_",sample(1:20,1e6,replace=TRUE)),
                  message=sample(c(".wav","others"), 1e6, replace=TRUE),
                  timestamp=round(seq(from=1000, to=9142527, length.out = 1e6))) 

dat2 <- dat
setDT(dat2)

microbenchmark({dat %>%
  filter(grepl("\\.wav", message)) %>%
  group_by(subj_trial) %>%
  top_n(1, wt=desc(timestamp))},
  {dat2[grepl("\\.wav", message), .SD[1], by=subj_trial]})

结果:

Unit: milliseconds

expr

dat %>% filter(grepl("\\\\.wav", message)) %>% group_by(subj_trial) %>% top_n(1, wt = desc(timestamp))
dat2[grepl("\\\\.wav", message), .SD[1], by = subj_trial] 
      min       lq     mean   median       uq      max neval cld
 332.9693 357.7426 387.2245 367.6443 380.9935 637.9223   100   b
 263.0292 272.8627 293.4976 281.4568 285.7699 582.9954   100  a 

【讨论】:

  • 您好,感谢您的帮助!三年过去了,它仍然很出色!
【解决方案2】:

同样使用 data.table,但公式更简洁:

setDT(DT)
DT[,.SD[grep("\\.wav",message)[1]],by=subj_trial]

编辑:正如下面的评论所建议的,

DT[grepl("\\.wav", message), .SD[1], by=subj_trial]

可能会更快,因为它使用布尔逻辑和优化的I 子集。

.SD 是一个 data.table,其中包含每个组的 DT 数据子集,不包括 by(或 keyby)中使用的任何列。

by 有点像 SQL 中的 group by 运算符。它指定分组列。

grep(pattern, x) 返回xpattern 的所有匹配项的索引,其中x 是一个向量。 \\.wav 之前的 \\ 防止 grep 将 . 视为特殊字符(在 grep 的解析中,未转义的 . 表示“任何东西”)。

vector_name[1] 返回名为vector_name 的向量的第一个元素。它可以在函数的结果上调用,例如上面的 grep。

data.table 公式为DT[I,J,by] -- I 是子集或连接,J 是要执行的操作,by 是分组元素。在我们的例子中,I 被忽略(因此前导 ,),因为我们想要处理完整的集合。 J 是对所有 .SD 列的操作。 by 是您希望结果分组的列。

【讨论】:

  • 打败我-我打算建议dat[grepl("\\.wav", message), .SD[1], by=subj_trial]
  • 刚刚在 set.seed(1); dat &lt;- data.table(subj_trial=sample(1:1e5,1e6,replace=TRUE), message=sample(c(".wav","others"), 1e6, replace=TRUE)) 上运行了一个快速基准测试。根据我上面的评论,将 grepl 移动到 data.table 的 i 中使其更快 很多(30 秒对 0.3 秒)
【解决方案3】:

使用data.table:

library(data.table)
setDT(DT)
DT[,{
  id=head(grep("\\.wav",message),1)
  list(message=message[id],timestamp=timestamp[id])
},subj_trial]

#    subj_trial   message timestamp
# 1:        1_1 test1.wav    755662
# 2:        1_2 test2.wav    775662
# 3:        1_3 test3.wav    817794
# 4:        2_1 test1.wav    817347
# 5:        2_2 test2.wav    922671
# 6:        2_3 test3.wav   1036899

【讨论】:

  • 您愿意解释一下这是如何工作的吗?看起来非常不可读。
猜你喜欢
  • 2013-11-25
  • 2019-02-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-12-21
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多