通过在每个组中相交来减少分组的数据帧

时间:2019-10-17 10:47:38

标签: r dplyr data.table

在纯dplyr或data.table中是否有一种方法可以按一列进行分组,然后减少每一组,以使它们最终只包含与另一列的值相交的行?

这里是一个例子:

library(tibble)
library(dplyr)
classification <- rep(c("A", "B", "C"), each = 10)
set.seed(123)
id.list <- lapply(1:3, function(i) sample(letters[1:7], 10, replace = T))
dtbl <- tibble(classification = classification, id = unlist(id.list), value = round(runif(30, 50, 99)))

这将产生以下结果:

# A tibble: 30 x 3
   classification id    value
   <chr>          <chr> <dbl>
 1 A              g        84
 2 A              g        89
 3 A              c        51
 4 A              f        73
 5 A              c        87
 6 A              b        61
 7 A              b        66
 8 A              f        61
 9 A              c        57
10 A              e        70
11 B              d        70
12 B              f        68
13 B              f        57
14 B              a        57
15 B              b        61
16 B              c        73
17 B              e        63
18 B              c        92
19 B              c        52
20 B              a        72
21 C              d        89
22 C              a        56
23 C              a        77
24 C              e        60
25 C              c        56
26 C              b        87
27 C              g        94
28 C              b        68
29 C              a        83
30 C              f        55

出现在每个分类组中的id的值为bcef。我可以通过执行以下操作获得所需的结果:

common.id <- Reduce(intersect, id.list)
dtbl.intersect <- filter(dtbl, id %in% common.id) %>% 
  arrange(classification, id, value)

哪个给我:

# A tibble: 20 x 3
   classification id    value
   <chr>          <chr> <dbl>
 1 A              b        61
 2 A              b        66
 3 A              c        51
 4 A              c        57
 5 A              c        87
 6 A              e        70
 7 A              f        61
 8 A              f        73
 9 B              b        61
10 B              c        52
11 B              c        73
12 B              c        92
13 B              e        63
14 B              f        57
15 B              f        68
16 C              b        68
17 C              b        87
18 C              c        56
19 C              e        60
20 C              f        55

但是我不喜欢为了创建common.id而必须脱离dplyr管道。是否可以在dplyr或data.table中执行整个过程?

编辑 正如答复中正确指出的那样:我正在寻找一种不使用id.list列表的解决方案(这只是MWE的一部分)。一个更广泛的问题是:dplyr / data.table分组操作是否创建了一个可由reduce函数使用的“某处”列表?

4 个答案:

答案 0 :(得分:3)

您可以按以下方式使用$> posts = Post.where(state: 'published') #=> #<ActiveRecord::Relation [#<Post id: 50, title: 'Wonderful', user_id: 39, state: 'published'>, #<Post id: 53, title: 'Alpha', user_id: 39, state: 'published'>]> $> posts.assign_attributes(state: 'pending') #=> nil $> posts #=> #<ActiveRecord::Relation [#<Post id: 50, title: 'Wonderful', user_id: 39, state: 'pending'>, #<Post id: 53, title: 'Alpha', user_id: 39, state: 'pending'>]> 软件包:

data.table

答案 1 :(得分:1)

您可以(添加purrr来做到)

dtbl %>%
 filter(id %in% reduce(id.list, intersect)) %>%
 arrange(classification, id, value)

或者:

dtbl %>%
 filter(id %in% Reduce(intersect, id.list)) %>%
 arrange(classification, id, value)

或者该问题是否更着重于创建id.list:

dtbl %>%
 mutate(n = n_distinct(classification)) %>%
 group_by(id) %>%
 filter(n_distinct(classification) == n) %>%
 select(-n) %>%
 arrange(classification, id, value)

答案 2 :(得分:1)

有2种使用data.table的(速度)可比较的方法:

mtd0 <- function(DT) {
    IDs <- DT[, unique(id)]
    invisible(DT[, IDs <<- intersect(IDs, id), classification])
    DT[id %in% IDs][order(classification, id, value)]
}

mtd1 <- function(DT) {
    DT[DT[, .(id=Reduce(intersect, split(id, classification)))], on=.(id), nomatch=0L][
        order(classification, id, value)]
}

我认为您的id.list使我们感到困惑,尽管您只是使用它来创建MWE,但使我们认为它可以作为独立变量使用。

数据:

library(data.table) #data.table_1.12.4
set.seed(0L)
nr <- 1e7
nclass <- 1e4
nid <- 1e2
dat <- data.table(classification=sample(nclass, nr, TRUE),
    id=sample(nid, nr, TRUE))[, value := .I]
setorder(dat, classification, id, value)

来自bench::mark(mtd0(dat), mtd1(dat))的时间:

# A tibble: 2 x 13
  expression      min   median `itr/sec` mem_alloc `gc/sec` n_itr  n_gc total_time result                   memory                time     gc              
  <bch:expr> <bch:tm> <bch:tm>     <dbl> <bch:byt>    <dbl> <int> <dbl>   <bch:tm> <list>                   <list>                <list>   <list>          
1 mtd0(DT)   810.17ms 810.17ms     1.23      897MB     3.70     1     3   810.17ms <df[,3] [6,197,991 x 3]> <df[,3] [80,334 x 3]> <bch:tm> <tibble [1 x 3]>
2 mtd1(DT)      1.33s    1.33s     0.752     854MB     3.01     1     4      1.33s <df[,3] [6,197,991 x 3]> <df[,3] [94,322 x 3]> <bch:tm> <tibble [1 x 3]>

答案 3 :(得分:0)

这就是我要做的:

setDT(dtbl)
result <- dtbl[id %chin% Reduce(intersect, id.list)] # %chin% is fast %in% for characters
setorder(result, classification, id, value)