筛选数据框,匹配向量中的所有值。

8

我希望通过包含ID的行来筛选数据框x,这些行的Hour匹配testVector所有值。

ID <- c('A','A','A','A','A','B','B','B','B','C','C')
Hour <- c('0','2','5','6','9','0','2','5','6','0','2')

x <- data.frame(ID, Hour)
x
   ID Hour
1   A    0
2   A    2
3   A    5
4   A    6
5   A    9
6   B    0
7   B    2
8   B    5
9   B    6
10  C    0
11  C    2

testVector <- c('0','2','5')

解决方案应该生成以下数据框:
x
       ID Hour
    1   A    0
    2   A    2
    3   A    5
    4   A    6
    5   A    9
    6   B    0
    7   B    2
    8   B    5
    9   B    6

由于缺失了“小时”5的ID C的所有值都被删除。请注意,我希望保留与testVector匹配的ID的所有“小时”值。
最好使用dplyr来解决问题,但任何一种解决方案都欢迎。
根据其他相关问题在SO上的描述,我猜测我需要一些%in%和all的组合,但我无法完全理解它。
3个回答

4

你结合使用的%in%all听起来很有前途,在基础R中,你可以按照以下方式利用它们:

to_keep = sapply(lapply(split(x,x$ID),function(x) {unique(x$Hour)}), 
                                              function(x) {all(testVector %in% x)})
x = x[x$ID %in% names(to_keep)[to_keep],]

或者同样的方式,但跳过不必要的lapply,并根据评论中的d.b.更有效率:

temp = sapply(split(x, x$ID), function(a) all(testVector %in% a$Hour))
x[temp[match(x$ID, names(temp))],]

输出:

  ID Hour
1  A    0
2  A    2
3  A    5
4  A    6
5  A    9
6  B    0
7  B    2
8  B    5
9  B    6

希望这能帮到你!

另外,temp = sapply(split(x, x$ID), function(a) all(testVector %in% a$Hour)); x[temp[match(x$ID, names(temp))],] - d.b
谢谢!你能否解释一下你的代码是如何工作的?特别是,我不明白嵌套的lapply/sapply是如何工作的。 - holastello
1
@holastello,我来解释一下d.b.的版本:首先,我们根据x$ID列将数据框分成组。然后,我们使用sapply循环遍历该列表,并在function(a)之后使用函数来测试testVector中的所有元素是否也在a$Hour中。我希望这样解释可以让你明白,如果还有不懂的地方,请告诉我!在我的解决方案中,我使用了另一个lapply来先获取唯一值,但这是不必要的。 - Florian
2
@d.b. 这是一个更简洁的解决方案,感谢分享!如果您同意的话,我将其添加到我的答案中。这就是为什么我喜欢使用Stack Overflow,它是一个从他人学习的好地方。 - Florian

2

这是另一种使用dplyr的解决方案,而无需离开管道:

ID <- c('A','A','A','A','A','B','B','B','B','C','C')
Hour <- c('0','2','5','6','9','0','2','5','6','0','2')

x <- data.frame(ID, Hour)

testVector <- c('0','2','5')

x %>%
  group_by(ID) %>%
  mutate(contains = Hour %in% testVector) %>%
  summarise(all = sum(contains)) %>%
  filter(all > 2) %>%
  select(-all) %>%
  inner_join(x)

##       ID   Hour
##   <fctr> <fctr>
## 1      A      0
## 2      A      2
## 3      A      5
## 4      A      6
## 5      A      9
## 6      B      0
## 7      B      2
## 8      B      5
## 9      B      6

2
我有点困惑。为什么不用 x %>% group_by(ID) %>% filter(all(testVector %in% Hour)) - Frank
1
@Frank 是的,简单多了! - cryo111

2

这里有一种使用base R中的table选项的方法。

i1 <- !rowSums(table(x)[, testVector]==0)
subset(x, ID %in% names(i1)[i1])
#   ID Hour
#1  A    0
#2  A    2
#3  A    5
#4  A    6
#5  A    9
#6  B    0
#7  B    2
#8  B    5
#9  B    6

或者可以使用 data.table 实现。

library(data.table)
setDT(x)[, .SD[all(testVector %in% Hour)], ID]
#    ID Hour
#1:  A    0
#2:  A    2
#3:  A    5
#4:  A    6
#5:  A    9
#6:  B    0
#7:  B    2
#8:  B    5
#9:  B    6

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