将多个csv文件更快地读入data.table R.

时间:2015-07-09 11:39:17

标签: r performance for-loop data.table

我有900000个csv文件,我想将它们组合成一个大的data.table。对于这种情况,我创建了一个for loop,它逐个读取每个文件并将它们添加到data.table。问题是它的执行速度变慢,所用的时间呈指数级增长。如果有人可以帮助我让代码运行得更快,那就太棒了。每个csv文件都有300行和15列。 我到目前为止使用的代码:

library(data.table)
setwd("~/My/Folder")

WD="~/My/Folder"
data<-data.table(read.csv(text="X,Field1,PostId,ThreadId,UserId,Timestamp,Upvotes,Downvotes,Flagged,Approved,Deleted,Replies,ReplyTo,Content,Sentiment"))

csv.list<- list.files(WD)
k=1

for (i in csv.list){
  temp.data<-read.csv(i)
  data<-data.table(rbind(data,temp.data))

  if (k %% 100 == 0)
    print(k/length(csv.list))

  k<-k+1
}

7 个答案:

答案 0 :(得分:10)

假设您的文件是传统的csv,我会使用data.table::fread,因为它更快。如果您使用的是类Linux操作系统,我会使用它允许shell命令的事实。假定您的输入文件是我要执行的文件夹中唯一的csv文件:

dt <- fread("tail -n-1 -q ~/My/Folder/*.csv")

之后您需要手动设置列名。

如果您想将内容保存在R中,我会使用lapplyrbindlist

lst <- lapply(csv.list, fread)
dt <- rbindlist(lst)

您也可以使用plyr::ldply

dt <- setDT(ldply(csv.list, fread))

这样做的好处是,您可以使用.progress = "text"来读取阅读进度。

以上所有假设文件都具有相同的格式并具有标题行。

答案 1 :(得分:4)

使用 //Code Snippet // var arr = [1, 3, 2]; var arr_temp = []; arr.forEach(function (i) { return arr_temp.push(i + i); }); console.log(arr_temp); Nick Kennedy's answer上构建时,通过启用plyr::ldply选项可以大约提高50%的速度,同时读取每个大约30-40 MB的400 csv文件。

带进度条的原始答案

.parallel

使用文字进度条启用dt <- setDT(ldply(csv.list, fread, .progress="text")

.parallel

答案 2 :(得分:3)

根据@Repmat的建议,使用rbind.fill。正如@Christian Borck所建议的,使用fread来获得更快的读取速度。

require(data.table)
require(plyr)

files <- list.files("dir/name")
df <- rbind.fill(lapply(files, fread, header=TRUE))

或者您可以使用do.call,但rbind.fill更快(http://www.r-bloggers.com/the-rbinding-race-for-vs-do-call-vs-rbind-fill/

df <- do.call(rbind, lapply(files, fread, header=TRUE))

或者您可以使用data.table包see this

答案 3 :(得分:2)

您正在for循环中增长数据表 - 这就是为什么它需要永远。如果你想保持for循环,首先创建一个空数据框(在循环之前),它具有你需要的尺寸(行x列),并将它放在RAM中。

然后在每次迭代中写入此空帧。

否则使用包plyr中的rbind.fill - 并避免循环altogehter。 要使用rbind.fill:

require(plyr)
data <- rbind.fill(df1, df2, df3, ... , dfN)

要传递df的名称,您可以/应该使用应用函数。

答案 4 :(得分:1)

我使用@Repmat作为当前使用rbind()的解决方案,每次调用时都会将整个data.table复制到内存中(这就是时间呈指数级增长的原因)。虽然另一种方法是创建一个空的csv文件,首先只包含标题,然后简单地将所有文件的数据附加到此csv文件。

write.table(fread(i), file = "your_final_csv_file", sep = ";",
            col.names = FALSE, row.names=FALSE, append=TRUE, quote=FALSE)

这样您就不必担心将数据放入data.table中的正确索引。另外作为提示:fread()是data.table文件阅读器,它比read.csv快得多。

在generell中,R不会是我数据修改任务的首选。

答案 5 :(得分:0)

一个建议是首先将它们合并为10个左右,然后合并这些组,依此类推。这样做的好处是,如果单个合并失败,您就不会失去所有工作。你现在这样做的方式不仅会导致执行速度呈指数级增长,而且每次失败都会让你不得不从一开始就重新开始。

这种方式也会降低rbind调用中涉及的数据帧的平均大小,因为大多数数据帧将被附加到小数据帧,最后只会附加几个大数据帧。这应该可以消除大部分呈指数增长的执行时间。

我认为无论你做什么,都会有很多工作。

答案 6 :(得分:0)

在假设您可以信任所有输入数据并且每条记录肯定是唯一的情况下要考虑的一些事项:

  • 考虑创建导入的表而不使用索引。随着索引变得越来越大,在导入过程中管理它们的时间越来越长 - 所以听起来这可能正在发生。如果这是您的问题,稍后创建索引仍需要很长时间。

  • 或者,根据您正在讨论的数据量,您可能需要考虑一种分区数据的方法(通常通过日期范围完成)。根据您的数据库,您可以使用单独索引的分区 - 简化索引工作。

  • 如果演示代码无法解析为数据库文件导入实用程序,请使用此类实用程序。

  • 在导入文件之前,可能需要将文件处理为更大的数据集。例如,您可以在加载前将100个文件合并为一个较大的文件并比较时间来试验这一点。

如果您无法使用分区(取决于环境和数据库人员的经验),您可以使用自制的方法将数据分隔到各个表中。例如data201401到data201412。但是,您必须使用自己的实用程序来跨边界查询。

虽然绝对不是一个更好的选择,但你可以在紧要关头做一些事情 - 它可以让你轻松退出/过期老年记录而无需调整相关指数。如果需要,它还允许您通过“分区”加载预处理的传入数据。