我尝试使用dplyr
和grep
库使用我的数据框的多列中的字符串列表来过滤我的数据框失败了。我认为这是一项简单的任务,但要么没有人问过我的具体问题,要么就像我原先认为的那样容易。
对于以下数据框...
foo <- data.frame(var.1 = c('a', 'b',' c'),
var.2 = c('b', 'd', 'e'),
var.3 = c('c', 'f', 'g'),
var.4 = c('z', 'a', 'b'))
...我希望能够逐行过滤以查找包含所有三个变量a,b和c的行。 我追捧的答案只会返回第1行,因为它包含a,b和c,并且不会返回第2行和第3行,即使它们包含三个搜索变量中的两个,它们也不包含所有三个同一行。
我遇到的问题是,grep
只允许一次指定向量或一列,而我真的只关心在同一行中的多个列中查找字符串。
我还使用dplyr
filter
使用%in%
,但它只会在存在任何变量时返回:
foo %>%
filter(var.1 %in% c('a', 'b', 'c') |
var.2 %in% c('a', 'b', 'c') |
var.3 %in% c('a', 'b', 'c'))
感谢您提供的所有帮助,如果您需要任何澄清,请与我们联系!
答案 0 :(得分:4)
这是基础R中的一种方法,我们检查foo
的元素是否等于"a"
,"b"
或"c"
,添加布尔值并检查是否每行的布尔值之和大于或等于3
Reduce("+", lapply(c("a", "b", "c"), function(x) rowSums(foo == x) > 0)) >=3
#[1] TRUE FALSE FALSE
<强>计时强>
foo = matrix(sample(letters[1:26], 1e7, replace = TRUE), ncol = 5)
system.time(Reduce("+", lapply(letters[1:20], function(x) rowSums(foo == x) > 0)) >=20)
# user system elapsed
# 3.26 0.48 3.79
system.time(apply(foo, 1, function(x) all(letters[1:20] %in% x)))
# user system elapsed
# 18.86 0.00 19.19
identical(Reduce("+", lapply(letters[1:20], function(x) rowSums(foo == x) > 0)) >=20,
apply(foo, 1, function(x) all(letters[1:20] %in% x)))
#[1] TRUE
>
答案 1 :(得分:2)
您的问题源于尝试将“tidyverse”解决方案应用于不整洁的数据。这是一个整洁的解决方案,它使用melt
来使您的数据整洁。看看这个解决方案有多整洁?
> library(reshape2)
> rows = foo %>%
mutate(id=1:nrow(foo)) %>%
melt(id="id") %>%
filter(value=="a" | value=="b" | value=="c") %>%
group_by(id) %>%
summarize(N=n()) %>%
filter(N==3) %>%
select(id) %>%
unlist
Warning message:
attributes are not identical across measure variables; they will be dropped
它为您提供了匹配行索引的向量,然后您可以使用以下内容对原始数据框进行子集化。
> foo[rows,]
var.1 var.2 var.3 var.4
1 a b c z
>