Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/shell/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
筛选R/Rcpp中data.frame列表列内容的最快方法_R_Performance_Data.table_Dplyr_Rcpp - Fatal编程技术网

筛选R/Rcpp中data.frame列表列内容的最快方法

筛选R/Rcpp中data.frame列表列内容的最快方法,r,performance,data.table,dplyr,rcpp,R,Performance,Data.table,Dplyr,Rcpp,我有一个data.frame: df <- structure(list(id = 1:3, vars = list("a", c("a", "b", "c"), c("b", "c"))), .Names = c("id", "vars"), row.names = c(NA, -3L), class = "data.frame") 我想根据setdiff(vars,删除此项) 但是要删除字符(0)变量,我必须执行以下操作: res %>% unnest(vars) # and

我有一个data.frame:

df <- structure(list(id = 1:3, vars = list("a", c("a", "b", "c"), c("b", 
"c"))), .Names = c("id", "vars"), row.names = c(NA, -3L), class = "data.frame")
我想根据
setdiff(vars,删除此项)

但是要删除
字符(0)
变量,我必须执行以下操作:

res %>% unnest(vars) # and then do the equivalent of nest(vars) again after...
实际数据集:
  • 560K行和3800K行,它们还有10个以上的列(要随身携带)
(这相当慢,这导致了问题…)

R
中,最快的方法是什么?
  • 是否有
    dplyr
    /
    数据表
    /其他更快的方法
  • 如何使用
    Rcpp
更新/扩展:
  • 是否可以就地修改列,而不是复制
    lappy(vars,setdiff(…
    结果)

  • 如果必须是单独的步骤,那么过滤
    vars==character(0)
    最有效的方法是什么


抛开任何算法改进不谈,类似的
data.table
解决方案会自动变得更快,因为您不必为了添加列而复制整个内容:

library(data.table)
dt = as.data.table(df)  # or use setDT to convert in place

dt[, newcol := lapply(vars, setdiff, 'a')][sapply(newcol, length) != 0]
#   id  vars newcol
#1:  2 a,b,c    b,c
#2:  3   b,c    b,c
您还可以通过在末尾添加
[,vars:=NULL]
来删除原始列(成本基本为0)。或者,如果您不需要该信息,也可以简单地覆盖初始列,即
dt[,vars:=lappy(vars,setdiff,'a')]


现在,就算法改进而言,假设您的
id
值对于每个
vars
都是唯一的(如果不是,则添加一个新的唯一标识符),我认为这会更快,并自动进行过滤:

dt[, unlist(vars), by = id][!V1 %in% 'a', .(vars = list(V1)), by = id]
#   id vars
#1:  2  b,c
#2:  3  b,c
要继续其他列,我认为最简单的方法是合并回:

dt[, othercol := 5:7]

# notice the keyby
dt[, unlist(vars), by = id][!V1 %in% 'a', .(vars = list(V1)), keyby = id][dt, nomatch = 0]
#   id vars i.vars othercol
#1:  2  b,c  a,b,c        6
#2:  3  b,c    b,c        7
还有一种方法:

# prep
DT <- data.table(df)
DT[,vstr:=paste0(sort(unlist(vars)),collapse="_"),by=1:nrow(DT)]
setkey(DT,vstr)

get_badkeys <- function(x) 
  unlist(sapply(1:length(x),function(n) combn(sort(x),n,paste0,collapse="_")))

# choose values to exclude
baduns  <- c("a","b")

# subset
DT[!J(get_badkeys(baduns))]
模拟:

讨论:

对于
eddi()
hannahh()
,结果几乎不会随着
nvals
nbads
maxlen
而改变。相反,当
baduns
超过20时,
frank()
变得异常缓慢(大约20秒以上);它还随着
nbads
maxlen
的增加而增大,比其他两个稍微差一点

按比例增加
nobs
eddi()
领先
hannahh()
的比例保持不变,大约是10倍。与
frank()
相比,它有时缩小,有时保持不变。在
nobs=1e5
最好的情况下,对于
frank()
eddi()
的速度仍然是3倍

如果我们从字符的
valset
切换到
frank()
必须强制为其by row
paste0
操作的字符,那么
eddi()
hannahh()
都会随着
nobs
的增长而击败它


重复执行此操作的基准。这可能是显而易见的,但如果您必须“多次”执行此操作(…很难说有多少次),创建键列比为每组
baduns
进行子集设置要好。在上面的模拟中,
eddi()
的速度大约是
frank()的5倍
,所以如果我做了10多次子集设置,我会选择后者

maxbadlen    <- 2
set_o_baduns <- replicate(10,sample(valset,size=sample(maxbadlen,1)))

system.time({
    DT <- data.table(exdf)
    DT[,vstr:=paste0(sort(unlist(vars)),collapse="_"),by=1:nrow(DT)]
    setkey(DT,vstr)

    for (i in 1:10) DT[!J(get_badkeys(set_o_baduns[[i]]))]
})
# user  system elapsed 
# 0.29    0.00    0.29

system.time({
    dt = as.data.table(exdf)
    for (i in 1:10) dt[, 
      unlist(vars), by = id][!V1 %in% set_o_baduns[[i]],
      .(vars = list(V1)), keyby = id][dt, nomatch = 0]
})
# user  system elapsed 
# 0.39    0.00    0.39

system.time({
    for (i in 1:10) hannahh(exdf,set_o_baduns[[i]])
})
# user  system elapsed 
# 4.10    0.00    4.13
maxbadlen还有一个想法:

df %>% 
  rowwise() %>% 
  do(id = .$id, vars = .$vars, newcol = setdiff(.$vars, "a")) %>%
  mutate(length = length(newcol)) %>%
  ungroup()
其中:

#  id    vars newcol length
#1  1       a             0
#2  2 a, b, c   b, c      2
#3  3    b, c   b, c      2
#  id    vars newcol length
#1  2 a, b, c   b, c      2
#2  3    b, c   b, c      2
然后可以在
length>0上进行筛选,以仅保留非空
newcol

df %>% 
  rowwise() %>% 
  do(id = .$id, vars = .$vars, newcol = setdiff(.$vars, "a")) %>%
  mutate(length = length(newcol)) %>%
  ungroup() %>%
  filter(length > 0)
其中:

#  id    vars newcol length
#1  1       a             0
#2  2 a, b, c   b, c      2
#3  3    b, c   b, c      2
#  id    vars newcol length
#1  2 a, b, c   b, c      2
#2  3    b, c   b, c      2

注意:正如@Arun在评论中提到的,这种方法非常慢。你最好使用
数据表
解决方案。

通过“过滤器”你似乎是指删除行,在
setdiff
之后,你会得到
字符(0)
…只有当列表是单例
列表(“a”)时才会出现这种情况
。是这样吗?无论如何,要谈论速度,您可能需要生成一些示例数据。@Frank是的。通常我想删除
setdiff
计算结果为
字符(0)的行
。控制是否删除会很方便。我认为只有第一个@name会被ping。eddi意味着
data.table
方式会进行适当的修改。我认为最好的做法是,如果内容是分析密集型的,就不要使用列表列。相反,为子集创建虚拟对象或分类/字符串变量(如我的回答所示)。[续…]它在逻辑上不如对列表列进行子集设置和筛选那么优雅,但出于美观考虑,您也可以保留后者……只是不要将其用于昂贵或重复的分析操作。就automagic而言,键入分类变量确实会使子集设置变得相当即时(至少在我回答中的示例数据中)--这是
数据。table
显示了它的强度。@Frank在简单的情况下,是的,但如果OP一次取出几个字母,则需要检查所有combinations@Frank别忘了OP提到他们有一堆额外的列要携带,我认为使用
rowwise()
+
do())
I应该只保留
id
newcol
,然后在原始数据帧上使用
id
进行
left_join()
,对吗?@stevenbaupré如果
id
是行号(看起来),简单地用它子集要比加入它快得多。不过,我不知道这是否可以在单个管道表达式中实现。好的,你的方法比我的快5倍。我还研究了更广泛的模拟参数集,发现当
baduns
(要排除的元素)集“非常大”时,我的方法速度会大大减慢(超过20人),在这一点上,它比OP的慢。在处理非字符时,它也慢得多。如果我们可以将
by=
与列表列一起使用,以便我的
paste0
可以按组而不是按行进行,那么我的方法可能会赶上。如果多次使用键,那么使用键的点仍然成立。我想每个键都对dif有用不同的上下文。您的是目前为止最快的一次性操作。
do()
maxbadlen    <- 2
set_o_baduns <- replicate(10,sample(valset,size=sample(maxbadlen,1)))

system.time({
    DT <- data.table(exdf)
    DT[,vstr:=paste0(sort(unlist(vars)),collapse="_"),by=1:nrow(DT)]
    setkey(DT,vstr)

    for (i in 1:10) DT[!J(get_badkeys(set_o_baduns[[i]]))]
})
# user  system elapsed 
# 0.29    0.00    0.29

system.time({
    dt = as.data.table(exdf)
    for (i in 1:10) dt[, 
      unlist(vars), by = id][!V1 %in% set_o_baduns[[i]],
      .(vars = list(V1)), keyby = id][dt, nomatch = 0]
})
# user  system elapsed 
# 0.39    0.00    0.39

system.time({
    for (i in 1:10) hannahh(exdf,set_o_baduns[[i]])
})
# user  system elapsed 
# 4.10    0.00    4.13
df %>% 
  rowwise() %>% 
  do(id = .$id, vars = .$vars, newcol = setdiff(.$vars, "a")) %>%
  mutate(length = length(newcol)) %>%
  ungroup()
#  id    vars newcol length
#1  1       a             0
#2  2 a, b, c   b, c      2
#3  3    b, c   b, c      2
df %>% 
  rowwise() %>% 
  do(id = .$id, vars = .$vars, newcol = setdiff(.$vars, "a")) %>%
  mutate(length = length(newcol)) %>%
  ungroup() %>%
  filter(length > 0)
#  id    vars newcol length
#1  2 a, b, c   b, c      2
#2  3    b, c   b, c      2