【问题标题】:R: Fast query/match dataframeR:快速查询/匹配数据框
【发布时间】:2017-11-08 14:01:51
【问题描述】:

说,我有一个如下的数据框:

library(dplyr)
library(microbenchmark)
library(ggplot2)

dfr <- data.frame(name=c("bill","john","alice","sara"),
job=c("accounting","business","finance","business"),
stringsAsFactors=F)

   name        job
1  bill accounting
2  john   business
3 alice    finance
4  sara   business

我有一个带有名称的查询(字符向量)。

qe <- c("john","bill","mark","bill")

我想在数据框位置“名称”中搜索查询的每个元素,并在某些条件下检索整行:

  • 必须保留查询中的重复项
  • 必须保留查询顺序
  • 必须保留不匹配的查询(添加 NA)

我的尝试是以下功能:

loopy <- function(qe=NULL,dfr=NULL)
{
  elist <- vector("list",length=length(qe))
  for(i in 1:length(qe))
  {
    g <- grep(qe[i],dfr$name)
    if(length(g)==0) {
      elist[[i]] <- data.frame(name=qe[i],job=NA,stringsAsFactors=F)
    }else{
      elist[[i]] <- dfr[g,]
    }
  }
  return(bind_rows(elist))
}

loopy(qe,dfr)

  name        job
1 john   business
2 bill accounting
3 mark       <NA>
4 bill accounting

它似乎可以工作,但是对于我有几千行的真实数据来说太慢了。我想我会尝试一种 dplyr 方法。

dp_lj <- function(qe=NULL,dfr=NULL)
{
  edf <- data.frame(name=qe,stringsAsFactors=F)
  edf <- left_join(edf,dfr,by="name")
  return(edf)
}
dp_lj(qe,dfr)

  name        job
1 john   business
2 bill accounting
3 mark       <NA>
4 bill accounting

left_join 似乎给出了我想要的结果。但是,令人惊讶的是,这比我的循环函数要慢。摆弄着,我想出了一个匹配的方法。

matchy <- function(qe=NULL,dfr=NULL)
{
  edf <- dfr[match(qe,dfr$name),]
  pos <- match(NA,edf$name)
  if(!is.na(pos)) edf[pos,]$name <- qe[pos]
  rownames(edf) <- 1:nrow(edf)
  return(edf)
}
matchy(qe,dfr)

  name        job
1 john   business
2 bill accounting
3 mark       <NA>
4 bill accounting

这是迄今为止最快的。尝试使用%in% 并没有真正奏效 dfr[dfr$name %in% q,].

autoplot(microbenchmark(loopy(qe,dfr),dp_lj(qe,dfr),matchy(qe,dfr),times=500))

Unit: microseconds
            expr     min       lq      mean    median       uq      max neval cld
  loopy(qe, dfr) 426.274 461.0390  528.1194  481.7795  518.915 2659.955   500  b 
  dp_lj(qe, dfr) 919.311 982.9155 1146.0196 1030.1260 1129.088 4589.438   500   c
 matchy(qe, dfr) 128.396 154.4710  185.1209  169.0875  186.471  736.397   500 a 

我很好奇是否有不需要太多努力的更快解决方案。此外,我还没有测试这里看到的性能在真正的大型数据集上运行时是否具有可比性。

编辑 ---------------------------------- --------------------------

按照建议添加了 data.table 方法。

dt <- function(qe=NULL,dfr=NULL)
{
  setDT(dfr)
  qe <- data.table(name=qe)
  merge(qe, dfr, "name", all.x = TRUE, sort = FALSE)
}

使用长度为 18 的查询和 100,000 行的数据框进行了测试。更好地比较了现实生活中的表现。

mb <- microbenchmark(loopy(qe,dfr),dp_lj(qe,dfr),matchy(qe,dfr),dt(qe,dfr),times=200)
autoplot(mb)

【问题讨论】:

  • 您可以查看fastmatch 包。这有一个用于match 的插入功能,可以在多次搜索中粉碎match
  • 您是否使用这么小的样本数据运行了基准测试?如果是这样,结果不可靠
  • 我也是这么想的。我将尝试一个大型数据集。
  • 使用带键的 data.table,DT[query] 应该很快。
  • 为了我的目的,表(数据框)没有重复的名称。只有查询可以。但是,我想,考虑一下这种情况也会很好。

标签: r performance


【解决方案1】:

1) 如果您每次都将数据转换为 data.table

withDT <- function(qe=NULL,dfr=NULL) {
  dfr2 <- as.data.table(dfr)
  setkey(dfr2, name)
  return(dfr2[qe])
}

N <- 200000
set.seed(123)
dfr <- data.frame(name= sample(1:30, N, replace = T),
                  job=sample(c("accounting","business","finance","business"), N, replace = T),
                  stringsAsFactors=F)
head(dfr)
qe <- 1:41

r <- microbenchmark(loopy(qe,dfr),dp_lj(qe,dfr),
                    matchy(qe,dfr),
                    withDT(qe, dfr),times=10)
autoplot(r)
r
Unit: milliseconds
            expr         min          lq        mean      median          uq         max neval cld
  loopy(qe, dfr) 2683.964539 2704.321688 2765.214321 2717.469448 2802.026508 3038.711852    10   b
  dp_lj(qe, dfr)   10.799809   11.251819   12.675861   12.028161   12.849094   18.555524    10  a 
 matchy(qe, dfr)    1.741636    1.863862    2.892739    2.336564    2.616785    9.003637    10  a 
 withDT(qe, dfr)    3.348534    4.007463    5.836388    5.936607    6.656327   10.751368    10  a 

但是如果你创建一次data.table并设置一次key,那么它会更快

dt <- as.data.table(dfr)
setkey(dt, name)

withDT2 <- function(qe=NULL,dfr=NULL) {
  return(dfr[qe])
}

all.equal(withDT(qe, dfr), withDT2(qe, dt))

r <- microbenchmark(loopy(qe,dfr),dp_lj(qe,dfr),
                    matchy(qe,dfr),
                    withDT(qe, dfr),
                    withDT2(qe, dt),times=10)
autoplot(r)
r
            expr         min          lq         mean       median          uq         max neval cld
  loopy(qe, dfr) 2721379.707 2753441.327 2822803.2351 2795276.9895 2862874.678 3060082.414    10   b
  dp_lj(qe, dfr)    9181.361    9769.071   11084.7774   10708.5405   11709.925   16254.730    10  a 
 matchy(qe, dfr)    1783.983    1785.265    2798.4553    2183.7020    2659.131    8235.637    10  a 
 withDT(qe, dfr)    3280.845    3725.156    5076.5360    4097.7685    4714.831   13630.247    10  a 
 withDT2(qe, dt)     244.131     272.683     792.7672     520.9835     612.733    3895.182    10  a 

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-01-13
    • 2015-05-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-08-29
    • 2012-02-16
    • 2014-08-07
    相关资源
    最近更新 更多