R Plyr Sapply似乎真的很慢

时间:2015-03-27 10:11:29

标签: r plyr sapply

我认为我有一个非常简单的数据帧转换,但由于某种原因我无法理解它似乎需要一个永恒,这让我怀疑它可能没有做我所希望的。任何人都能解释一下吗?

第1部分 - 将源数据转换为单独的列(实际df有2.6M行)。

...鉴于

> V1 <- c("E11 2286 1", "ECAT 2286 1", "M11 2286 1", "M12 2286 1", "MCAT 2286 1", "C24 2287 1")
> df <- data.frame(V1)
> df
           V1
1  E11 2286 1
2 ECAT 2286 1
3  M11 2286 1
4  M12 2286 1
5 MCAT 2286 1
6  C24 2287 1

我想创建两个新列(itemID&amp; topic)并使用V1中相应行的substr填充每个列。

我可以这样做;

> require(stringr)
> df$itemID <- sapply(1:nrow(df), function(i) str_split(df[i,"V1"]," ")[[1]][[2]] )
> df$topic  <- sapply(1:nrow(df), function(i) str_split(df[i,"V1"]," ")[[1]][[1]] )

但这需要几分钟,似乎应该有一种更有效的方式。所以我首先尝试使用sapply;

> sapply(1:nrow(df), function(i) {
                                    t <- str_split(df[i,"V1"]," ")
                                    df$itemID <- t[[1]][[2]] 
                                    df$topic  <- t[[1]][[1]] 
                                  })

一个多小时后,什么都没有。所以我保释,因为当个别命令花了不到20分钟时,这显然无处可去。

下一个选项是在一个任务上尝试ddply,这也失败了。

> require(plyr)
> require(stringr)
> df$itemID <- ddply(df, .(V1), str_split(df$V1," ")[[1]][[2]], .progress="text"  )

Error in get(as.character(FUN), mode = "function", envir = envir) : 
  object '2286' of mode 'function' was not found

因此,任何人都可以完成此任务的第一部分;

  • i)告诉我哪种方法可能更快(sapply或ddply),
  • ii)使用该方法提供了如何将列V1拆分为两个必需组件列的解决方案?

第2部分 - 收集itemID的所有主题 对于奖励积分...我需要的任务的第二部分是获取2.6M行(现在在3列中)并折叠每一行的itemID,以便所有主题都保存在一个单元格中。

输出应该看起来像......

  itemID    topic
1 2286      E11,ECAT,M11,M12,MCAT
2 2287      C24

有人可以建议一种简单的方法来将行收集到一个单元格中吗?

2 个答案:

答案 0 :(得分:6)

我们可以使用几个选项来提高速度。

<强> 1。 stringi

stringi包中的函数通常更快。我们可以使用stri_extract_all_regex使用适当的regex提取字母数字字符。在这里,我根据示例显示使用[[:alnum:]]{2,}rbind列表元素(do.call(rbind.data.frame,..)),使用setNames更改列名,将'data.frame'转换为'data.table'(setDT)和{ {1}}按“itemID”(paste - 分组的“主题”元素是toString的包装。)

paste(., collapse=', ')

<强> 2。 dplyr / tidyr

我们可以使用library(stringi) library(data.table) setDT(setNames(do.call(rbind.data.frame,stri_extract_all_regex(df$V1, '[[:alnum:]]{2,}')), c('topic', 'itemID')))[, list(topic=toString(topic)), itemID] # itemID topic #1: 2286 E11, ECAT, M11, M12, MCAT #2: 2287 C24 中的extract将单个列转换为多列,方法是指定相应的正则表达式,tidyr按'itemID'分组的'{1}}'主题'元素

paste

答案 1 :(得分:4)

这个怎么样?使用data.table v1.9.5

require(data.table)
cols = c("topic", "itemID", "tmp")
setDT(df)[, c(cols) := tstrsplit(V1, " ", fixed=TRUE, type.convert=TRUE)]
df[, .(topic=paste(topic, collapse=", ")), by=itemID]
#    itemID                     topic
# 1:   2286 E11, ECAT, M11, M12, MCAT
# 2:   2287                       C24

260万行基准:

N = 2.6e6L
x = paste(rep(letters, length.out=N), sample(1e4, N, TRUE), "1", sep=" ")
dat = data.frame(x, stringsAsFactors=FALSE)
nrow(dat) # 2.6 million

# dplyr+tidyr
system.time({ans1 <- extract(dat, x, into= c('topic', 'itemID'), 
         '([^ ]+) ([^ ]+).*', convert=TRUE) %>% 
          group_by(itemID) %>% 
         summarise(topic=toString(topic))})
#    user  system elapsed 
#  45.643   0.854  46.777 

# data.table
system.time({
    cols = c("topic", "itemID", "tmp")
    setDT(dat)[, c(cols) := tstrsplit(x, " ", fixed=TRUE, type.convert=TRUE)]
    ans2 <- dat[, .(topic=paste(topic, collapse=", ")), by=itemID]
})    
#    user  system elapsed 
#   1.906   0.064   1.981 

identical(as.data.frame(ans1), setDF(ans2[order(itemID)]))
# [1] TRUE

加速 ~24x


更新:首先运行data.table回答,然后dplyr回答 7s 44s 的运行时间结果,产生 ~6.3x 的加速。在dplyr之后运行时,data.table方法似乎有一些缓存效率。