【问题标题】:`data.table` global search - filter rows given pattern match in `any` column`data.table` 全局搜索 - 在 `any` 列中过滤给定模式匹配的行
【发布时间】:2014-03-13 14:16:36
【问题描述】:

是否有任何现有的便利功能可以过滤data.table 中的行,给定搜索模式,查看所有列

names(DT)

[1] "Name" "LongName" "SomeOtherCol" "NumericCol" "bar" "foo"

类似这样的东西,适用于任意数量的列:

DT[Name %like% pattern | LongName %like% pattern | SomeOtherCol %like% pattern | bar %like% pattern | foo %like% pattern]

【问题讨论】:

  • +1 您是否尝试复制数据表功能? (我是说jQuery插件)
  • @Michele 不熟悉datatables,但example here 有“搜索”文本输入字段,其作用与上述几乎相同
  • ...您放置的链接正是datatables
  • 感谢上帝的 internetz :)

标签: r regex data.table


【解决方案1】:

一种方法是遍历列,应用您的正则表达式,这将返回一个逻辑 data.table。然后可以使用rowSums 获取行。

dt <- data.table(a=c("Aa1","bb","1c"),b=c("A1","a1","1C"), c=letters[1:3])
# "a1" is the pattern to search for
ldt <- dt[, lapply(.SD, function(x) grepl("a1", x, perl=TRUE))] 
dt[rowSums(ldt)>0]
#      a  b c
# 1: Aa1 A1 a
# 2:  bb a1 b

【讨论】:

  • 如果你的列太多,一种更快但内存效率更低的方法是将data.table 转换为matrix,然后应用grep - 因为它会应用它直接整个矩阵。
  • 非常感谢@Arun。您是否合理地期望这比该方法更快,而我首先附加字符串以达到类似于a %like% pattern | b %like% pattern |... 的表达式(甚至直接使用grepl)然后DT[eval(combined.expression)]?我正要建立这种方法,但我宁愿相信你对此的知情意见是否值得努力
  • 我猜你的会更快(因为它避免了rowSums).. 至少在更大的data.tables 上。
  • 我接受了您的解决方案,因为它在基准测试中稍快(请参阅我的回答),并且可以说更简洁。
【解决方案2】:

解决方案 3:

首先构造逻辑grep expression 附加所有列。然后eval一口气把整体表情:

dt <- data.table(a=c("a1","bb","1c"),b=c("A1","BB","1C"))

search.data.table <- function(x, pattern) {
  nms <- names(x)
  string <- eval(expression(paste0("grepl('",
                                   pattern, 
                                   "', ",
                                   nms,",
                                   ignore.case=TRUE, perl=FALSE)",
                                   collapse = " | ")))
  x[eval(as.call(parse(text=string))[[1]])]
}

search.data.table(dt, "a1")
#      a  b c
# 1: Aa1 A1 a
# 2:  bb a1 b

基准测试

# functions

Raffael <- function(x, pattern) {
# unfortunately this implementation throws an error so I can't run the benchmark test. 
# Any help?
  combined <- apply(x,1,function(r) paste(r,collapse="/%/"))
  grepped <- grepl(pattern,apply(x,1,function(r) paste(r,collapse="/")))
  x[grepped,]
}

Arun <- function(x, pattern) {
  ldt <- x[, lapply(.SD, function(x) grepl(pattern, x, perl=TRUE, ignore.case=TRUE))] 
  x[rowSums(ldt)>0]
}

DanielKrizian <- function(x, pattern) {
  nms <- names(x)
  string <- eval(expression(paste0("grepl('", pattern, "', ",nms,", ignore.case=TRUE,      perl=FALSE)",collapse = " | ")))
  x[eval(as.call(parse(text=string))[[1]])]
}

# generate 1000 x 1000 benchmark data.table

require(data.table)
expr <- quote(paste0(sample(c(LETTERS,tolower(LETTERS),0:9),12, replace=T)
                 ,collapse=""))
set.seed(1)
BIGISH <- data.table(matrix(replicate(1000*1000,eval(expr)),nrow = 1000))
object.size(BIGISH) # 68520912 bytes

# test

benchmark(
  DK <- DanielKrizian(BIGISH,"qx"),
  A <- Arun(BIGISH,"qx"),
  replications=100)

结果

                               test replications elapsed relative user.self sys.self user.child sys.child
2           A <- Arun(BIGISH, "qx")          100   57.72    1.000     51.95     0.44         NA        NA
1 DK <- DanielKrizian(BIGISH, "qx")          100   59.28    1.027     53.72     0.50         NA        NA

identical(DK,A)
[1] TRUE

【讨论】:

  • 添加基准时,请在相对较大的数据上添加基准结果。
  • @Arun,编辑了测试数据,等待回家运行。欢迎提出更好的数据建议
  • @Arun,将数据大小更改为 1000 行 x 1000 列
【解决方案3】:

我不认为这是最好的方法。但它的目的是:

> dt <- data.table(a=c("a1","bb","1c"),b=c("A1","BB","1C"))
> dt
    a  b
1: a1 A1
2: bb BB
3: 1c 1C

> combined <- apply(dt,1,function(r) paste(r,collapse="/%/"))
> combined
[1] "a1/%/A1" "bb/%/BB" "1c/%/1C"

> grepped <- grepl("[a-z][0-9]",apply(dt,1,function(r) paste(r,collapse="/")))
> grepped
[1]  TRUE FALSE FALSE

> dt[grepped,]
    a  b
1: a1 A1

“/%/”必须是与模式无关的内容,并且可以可靠地分隔列。

这些步骤当然可以组合成一个表达式。

【讨论】:

  • 感谢您的启发,我将尝试提供data.table 本地的解决方案以获得更好的性能
  • 就速度而言,我认为这不是一个好主意。根据data.table 的大小,paste 可能会非常耗时。
  • 当我尝试对速度进行基准测试时,不知何故无法让您的解决方案发挥作用。请在下面的答案中查看实现(函数“Raffael”)。
猜你喜欢
  • 2011-08-24
  • 2019-12-08
  • 2019-09-23
  • 2021-08-03
  • 2022-01-22
  • 1970-01-01
  • 2016-02-02
  • 1970-01-01
  • 2014-01-15
相关资源
最近更新 更多