在R语言中处理大文件

8

我有15个数据文件,每个文件大小约为4.5GB。每个文件代表约17,000位客户一个月的数据。所有数据一共涵盖了15个月份内17,000位客户的信息。我想重新格式化这些数据,不再是15个月份的文件,而是针对每位客户的17,000个文件,包含他们的全部数据。我编写了一个脚本来实现这个目标:

#the variable 'files' is a vector of locations of the 15 month files
exists = NULL  #This vector keeps track of customers who have a file created for them
for (w in 1:15){  #for each of the 15 month files
  month = fread(files[w],select = c(2,3,6,16))  #read in the data I want
  custlist = unique(month$CustomerID) #a list of all customers in this month file
  for (i in 1:length(custlist)){ #for each customer in this month file
    curcust = custlist[i] #the current customer
    newchunk = subset(month,CustomerID == curcust) #all the data for this customer
    filename = sprintf("cust%s",curcust) #what the filename is for this customer will be, or is
    if ((curcust %in% exists) == TRUE){ #check if a file has been created for this customer. If a file has been created, open it, add to it, and read it back
      custfile = fread(strwrap(sprintf("C:/custFiles/%s.csv",filename)))#read in file
      custfile$V1 = NULL #remove an extra column the fread adds
      custfile= rbind(custfile,newchunk)#combine read in data with our new data
      write.csv(custfile,file = strwrap(sprintf("C:/custFiles/%s.csv",filename)))
    } else { #if it has not been created, write newchunk to a csv
      write.csv(newchunk,file = strwrap(sprintf("C:/custFiles/%s.csv",filename)))
      exists = rbind(exists,curcust,deparse.level = 0) #add customer to list of existing files
    }
  }
 }

这段脚本是可行的(至少我很确定)。问题在于它运行速度极慢。按照目前的速度,完成这个任务将需要一周甚至更久,而我没有那么多时间。有没有什么更好、更快的方法可以用R语言实现这个任务?或者我应该尝试使用SQL来完成?虽然我以前从未使用过SQL,但你们中是否有人能够向我展示如何使用它来完成类似的操作呢?希望能得到任何建议。


3
你真的想要17,000个文件吗?一旦文件被读入,R具有准确、高效地将客户分离的能力。 - Rich Scriven
我真的很想能够一次访问任何客户的数据集。由于内存限制,我只能在R中一次保存一个月的文件,即我只能访问客户数据集的1/15。拥有17,000个单独的文件将使我能够循环遍历并对任何客户的数据集执行操作。我想不出更好的组织方式了。 - Ore M
我假设这些是最细粒度的数据。根据您对未来数据分析的需求,您可以考虑通过删除冗余信息(例如时间),将您的月度数据进行子聚合,以便进行分析。因此,您可以减少数据集的行数,并将它们rbind到一个csv文件或数据库表中,以供分析使用,但仍保留所有月度文件作为历史备份。 - jangorecki
2
我认为这正是视图变得有趣的地方...它们定义了您需要查询数据的方式。通过最大化 SQL 部分的工作,最终您的 R 会话不需要太多内存。(实际上,我意识到您在谈论具有多个文件而不是一个大型数据库的数据表的情况下)。 - Dominic Comtois
我也同意@DominicComtois的观点 - 你需要一个数据库。但是回答你最初的问题:不要在循环内部使用rbind和昂贵的内存复制操作,而是使用fread,然后使用append=TRUE将子集write.table。这样会更快。 - daroczig
我是一个 data.table 的用户,但是对于这个问题,你应该看看 dplyr。它使用一种语法来加载外部数据库、查询它们以及在 R 中进行操作。而且它可以与 data.table 一起使用。 - Mark Danese
2个回答

17
作为@Dominic Comtois,我也建议使用SQL。
R可以处理相当大的数据集 - 有一个不错的基准测试结果表明,它能够处理20亿行数据,超过了Python - 但由于R主要在内存中运行,因此您需要一台性能良好的计算机才能使其正常工作。尽管如此,在您的情况下,每次加载不超过4.5GB的文件应该足够在个人计算机上完成,详见第二种方法以获得快速的非数据库解决方案。
您可以利用R将数据加载到SQL数据库中,并从数据库查询这些数据。 如果您不了解SQL,您可能想使用一些简单的数据库。从R开始最简单的方法是使用RSQLite(不幸的是,自v1.1以后它不再“轻量级”)。您无需安装或管理任何外部依赖项。RSQLite包含嵌入式数据库引擎。
library(RSQLite)
library(data.table)
conn <- dbConnect(dbDriver("SQLite"), dbname="mydbfile.db")
monthfiles <- c("month1","month2") # ...
# write data
for(monthfile in monthfiles){
  dbWriteTable(conn, "mytablename", fread(monthfile), append=TRUE)
  cat("data for",monthfile,"loaded to db\n")
}
# query data
df <- dbGetQuery(conn, "select * from mytablename where customerid = 1")
# when working with bigger sets of data I would recommend to do below
setDT(df)
dbDisconnect(conn)

这就是全部内容。通常情况下,您可以使用SQL而不必担心与数据库相关的许多开销。

如果您更喜欢按照帖子中的方法进行,我认为您可以通过在data.table中聚合时按组执行write.csv来大大加快速度。

library(data.table)
monthfiles <- c("month1","month2") # ...
# write data
for(monthfile in monthfiles){
  fread(monthfile)[, write.csv(.SD,file=paste0(CustomerID,".csv"), append=TRUE), by=CustomerID]
  cat("data for",monthfile,"written to csv\n")
}

因此,您可以利用data.table中的快速去重功能,在分组时执行子集操作,这也非常快速。以下是该方法的可行示例。

library(data.table)
data.table(a=1:4,b=5:6)[,write.csv(.SD,file=paste0(b,".csv")),b]

更新 2016-12-05:
从data.table 1.9.8+开始,您可以将write.csv替换为fwrite,示例请参见此答案


5
我认为你已经得到了答案。但是为了加强它,请参阅官方文档。 R数据导入和导出 其中指出:
一般来说,像R这样的统计系统并不特别适合处理大规模数据。其他一些系统在这方面比R更擅长,本手册的部分重点是建议我们可以让另一个系统来完成R中的功能!(例如Therneau&Grambsch(2000)评论说,他们更喜欢在SAS中进行数据操作,然后再使用S中的survival包进行分析。)数据库操作系统通常非常适合操作和提取数据:这里讨论了几个与DBMS交互的软件包。
因此,存储海量数据显然不是R的主要优势,但它提供了与几个专门针对此问题的工具的接口。在我的工作中,轻量级的SQLite解决方案已足够,即使在某种程度上是出于个人偏好。搜索“使用SQLite的缺点”,您可能不会发现太多可以阻止您使用它的东西。
你应该会发现SQLite的文档相当易于理解。如果你有足够的编程经验,做一两个教程就能很快地掌握SQL。我没有看到你的代码中有什么过于复杂的东西,因此最常见和基本的查询,如CREATE TABLE、SELECT ... WHERE,可能已经满足了你的所有需求。 编辑 使用DBMS的另一个优点是你可以创建视图,以便轻松访问其他数据组织模式。通过创建视图,你可以回到“按月份可视化”的状态,而不必重新编写任何表格或重复任何数据。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接