逐行搜索和查找数据帧的值

时间:2014-11-04 12:52:31

标签: r dataframe

我的数据框如下所示:

x1 <- c("a", "c", "f", "j")
x2 <- c("b", "c", "g", "k")
x3 <- c("b", "d", "h", NA)
x4 <- c("a", "e", "i", NA)
df <- data.frame(x1, x2, x3, x4, stringsAsFactors=F)

df

x1 x2   x3   x4
1  a  b    b    a
2  c  c    d    e
3  f  g    h    i
4  j  k <NA> <NA>

现在我有一个任意的矢量:

vec <- c("a", "i", "s", "t", "z")

我想将矢量值与数据框中的每一行进行比较,并创建一个额外的列,指示是否至少找到了一个矢量值。

结果数据框应如下所示:

  x1 x2   x3   x4 valueFound
1  a  b    b    a          1
2  c  c    d    e          0
3  f  g    h    i          1
4  j  k <NA> <NA>          0

我想在没有循环的情况下这样做。非常感谢您的支持!

拉​​米

4 个答案:

答案 0 :(得分:7)

这比基于apply的解决方案更快(尽管它有神秘的构造):

as.numeric(rowSums(`dim<-`(as.matrix(df) %in% vec, dim(df))) >= 1)
[1] 1 0 1 0

更新 - 一些基准测试

在这里,我们可以组成一些更大的数据进行测试....这些基准测试是在100k行上。

set.seed(1)
nrow <- 100000
ncol <- 10
vec <- c("a", "i", "s", "t", "z")
df <- data.frame(matrix(sample(c(letters, NA), nrow * ncol, TRUE),
                        nrow = nrow, ncol = ncol), stringsAsFactors = FALSE)

以下是我们目前采用的方法:

AM <- function() as.numeric(rowSums(`dim<-`(as.matrix(df) %in% vec, dim(df))) >= 1)
NR1 <- function() {
  apply(df,1,function(x){
    if(any(x %in% vec)){ 
      1 
    } else {
      0
    }
  })
}
NR2 <- function() apply(df, 1, function(x) any(x %in% vec) + 0)
NR3 <- function() apply(df, 1, function(x) as.numeric(any(x %in% vec)))
NR4 <- function() apply(df, 1, function(x) any(x %in% vec) %/% TRUE)
NR5 <- function() apply(df, 1, function(x) cumprod(any(x %in% vec)))
RS1 <- function() as.numeric(grepl(paste(vec, collapse="|"), do.call(paste, df)))
RS2 <- function() as.numeric(seq(nrow(df)) %in% row(df)[unlist(df) %in% vec])

我怀疑NR功能会慢一点:

system.time(NR1()) # Other NR functions are about the same
#    user  system elapsed 
#   1.172   0.000   1.196 

同样,理查德的第二种方法是:

system.time(RS2())
#    user  system elapsed 
#   0.918   0.000   0.932 

grepl和此rowSum函数留给基准测试:

library(microbenchmark)
microbenchmark(AM(), RS1())
# Unit: milliseconds
#   expr       min       lq      mean    median       uq      max neval
#   AM()  65.75296  67.2527  92.03043  84.58111 102.3199 234.6114   100
#  RS1() 253.57360 256.6148 266.89640 260.18038 264.1531 385.6525   100

答案 1 :(得分:6)

这是实现此目的的一种方法:

df$valueFound <- apply(df,1,function(x){
  if(any(x %in% vec)){ 
    1 
  } else {
    0
  }
})
##
> df
  x1 x2   x3   x4 valueFound
1  a  b    b    a          1
2  c  c    d    e          0
3  f  g    h    i          1
4  j  k <NA> <NA>          0

感谢@David Arenburg和@CathG提供了一些更简洁的方法:

  • apply(df, 1, function(x) any(x %in% vec) + 0)
  • apply(df, 1, function(x) as.numeric(any(x %in% vec)))

只是为了好玩,还有其他一些有趣的变种:

  • apply(df, 1, function(x) any(x %in% vec) %/% TRUE)
  • apply(df, 1, function(x) cumprod(any(x %in% vec)))

答案 2 :(得分:4)

由于您不想要循环,因此您可以获得广告素材并按行将列粘贴在一起,然后使用grepl将其与vec进行比较

> as.numeric(grepl(paste(vec, collapse="|"), do.call(paste, df)))
[1] 1 0 1 0 

这是第二个选项,用于将行与未列出的数据框进行比较

> as.numeric(seq(nrow(df)) %in% row(df)[unlist(df) %in% vec])
[1] 1 0 1 0

答案 3 :(得分:1)

作为另一个想法,试图保留并操作&#34;列表&#34; &#34; data.frame&#34;的结构而不是将其转换为原子(即sapplyas.matrixdo.call(_bind, ...)等)可能是有效的。在这种情况下,我们可以使用类似的东西:

as.numeric(Reduce("|", lapply(df, function(x) x %in% vec)))
#[1] 1 0 1 0

与迄今为止最快的Ananda Mahto(使用更大的&#34; df&#34;)进行比较:

AL = function() as.numeric(Reduce("|", lapply(df, function(x) x %in% vec)))
AM = function() as.numeric(rowSums(`dim<-`(as.matrix(df) %in% vec, dim(df))) >= 1)
identical(AM(), AL())
#[1] TRUE
microbenchmark::microbenchmark(AM(), AL(), times = 50)
#Unit: milliseconds
# expr      min       lq   median       uq      max neval
# AM() 49.20072 53.53789 58.03740 66.76898 86.04280    50
# AL() 45.24706 49.34271 51.43577 55.05866 74.79533    50

没有出现任何明显的效率提升,但是,我想,值得注意的是,2个循环(Reducelapply)并未证明是可能会慢 - 可能 - 预期。