【问题标题】:Searching rows in a data frame in R在R中的数据框中搜索行
【发布时间】:2012-12-19 11:16:22
【问题描述】:

我的数字串不一定具有相同的长度,例如

0,0,1,2,1,0,0,0

1,1,0,1

2,1,2,0,1,0

我已将这些导入到 R 中的数据框中,例如以上三个字符串将给出以下三行(我称之为df):

我希望编写一些函数来帮助我理解数据。作为起点-给定一个数字向量x-我想要一个“过程”P 来建立包含x 作为子向量的行数,例如如果x = c(2,1) 然后P(x) = 2,如果x = c(0,0,0) 然后P(x) = 1,如果x = c(1,3) 然后P(x) = 0。我有更多类似的问题,但我希望我能够从这个问题中汲取逻辑并自己解决一些其他问题。

【问题讨论】:

  • 如果你把它变成一个字符向量并使用正则表达式会怎样?
  • @RomanLuštrik 绝对是要走的路:)
  • @RomanLuštrik 只是一个警告 - 将浮点数转换为 char 可能无法达到预期的效果(舍入问题)。只要 OP 只处理整数,regexp 看起来就是一个很好的解决方案。
  • 您可以通过直接匹配数字来避免代价高昂的转换和后续的正则表达式。 outer() 在这里非常有用。
  • 但是outer 是个内存大户...

标签: r


【解决方案1】:

编辑:正则表达式的方式是:

match.regex <- function(x,data){
  xs <- paste(x,collapse="_")
  dats <- apply(data,1,paste,collapse="_")
  sum(grepl(xs,dats))
}


> match.regex(c(1),dat)
[1] 3
> match.regex(c(0,0,0),dat)
[1] 1
> match.regex(c(1,2),dat)
[1] 2
> match.regex(5,dat)
[1] 0

令人惊讶的是,在小型和大型数据集上,这个方法比这里给出的其他方法更快,并且比我下面的解决方案快两倍。正则表达式显然得到了优化:

> benchmark(matching(c(1,2),dat),match.regex(c(1,2),dat),replications=1000)
                       test replications elapsed relative 
2 match.regex(c(1, 2), dat)         1000    0.15      1.0 
1    matching(c(1, 2), dat)         1000    0.36      2.4 

以下方法可以立即为您提供数字并更加矢量化:

matching.row <- function(x,row){
    nx <- length(x)
    sid <- which(x[1]==row)
    any(sapply(sid,function(i) all(row[seq(i,i+nx-1)]==x)))
}

matching <- function(x,data)
  sum(apply(data,1,function(i) matching.row(x,i)),na.rm=TRUE)

在这里,您首先创建一个带有索引的矩阵,该矩阵将窗口移动到与您要匹配的向量长度相同的行上。然后对照向量检查这些窗口。每一行都遵循这种方法,返回 TRUE 的行的总和就是你想要的。

> matching(c(1),dat)
[1] 3
> matching(c(0,0,0),dat)
[1] 1
> matching(c(1,2),dat)
[1] 2
> matching(5,dat)
[1] 0

【讨论】:

  • 非常好。看看这个和正则表达式之间的速度测试会很有趣。
  • ...我怀疑如果您使用fixed = TRUE 会更快。另请注意,如果要允许numeric 比较,使用== 仍会导致浮动错误不匹配。
  • @flodel fixed=TRUE 在这种情况下不会让你加快速度。在这种情况下,数字比较似乎没有多大意义。在这种情况下,您可以使用all.equal(),但您必须先剥离属性以避免出现问题。
  • 我实际上预计这种速度会获胜,理论上正则表达式只匹配字节模式(我猜测它是如何工作的),而任何数字函数都必须对数字和库查找做“东西”(再次我的猜猜它是如何工作的)。
  • 感谢您的回答。出于兴趣,你为什么使用grepl 而不是grep?在我一直在做的测试中(我对 R 比较陌生)我无法让 grepgrepl 提供不同的输出,但是当我使用上面的代码替换 @987654334 时@ 与 grep 在我的实际数据帧上输出不同。
【解决方案2】:

你需要apply一个函数到你的数据行:

apply(dat, MARGIN = 1, FUN = is.sub.array, x = c(2,1))

其中dat 是您的data.frame,is.sub.array 是一个函数,用于检查x 是否包含在更大的向量中(实际上是您的data.frame 的行)。

我不知道有任何可用的 is.sub.array 函数,所以我会这样写:

is.sub.array <- function(x, y) {
    j <- rep(TRUE, length(y))
    for (i in seq_along(x)) {
        if (i > 1) j <- c(FALSE, head(j, -1))
        j <- j & vapply(y, FUN = function(a,b) isTRUE(all.equal(a, b)),
                        FUN.VALUE = logical(1), b = x[i])
    }
    return(sum(j, na.rm = TRUE) > 0L)
}

(使用all.equal 的优点是它可以用来比较numeric 向量,这是正则表达式无法做到的。)

这里有几个例子:

apply(dat, 1, is.sub.array, x = c(1, 2))
# [1]  TRUE FALSE  TRUE
apply(dat, 1, is.sub.array, x = c(0, 0, 0))
# [1]  TRUE FALSE FALSE
apply(dat, 1, is.sub.array, x = as.numeric(c(NA, NA)))
# [1] FALSE  TRUE  TRUE

注意all.equal 对您的数据类型很敏感,因此请小心使用与您的数据类型相同(整数或数字)的x

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-10-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-07-27
    相关资源
    最近更新 更多