修剪巨大的(3.5 GB)CSV文件以读入R


87

因此,我有一个数据文件(以分号分隔),该文件包含很多详细信息和不完整的行(导致Access和SQL阻塞)。它是40年的县级数据集,分为多个细分,子细分和子细分(共200个因子)。简而言之,它是巨大的,如果我尝试简单地阅读它,它将不适合内存。

所以我的问题是,考虑到我想要所有的县,但是只有一年(并且只有最高级别的细分……最终导致大约100,000行),什么是最好的方法此汇总到R?

目前,我正在尝试与Python无关的事情,通过一次读取和操作一行来绕过文件大小限制,但是我更喜欢仅R的解决方案(CRAN包可以)。有没有类似的方法可以一次在R中读取文件?

任何想法将不胜感激。

更新:

  • 约束条件
    • 需要使用我的机器,所以没有EC2实例
    • 尽可能仅R。在这种情况下,速度和资源不是问题...只要我的机器不爆炸...
    • 如下所示,数据包含混合类型,稍后我需要对其进行操作
  • 数据
    • 数据为3.5GB,约850万行和17列
    • 几千行(〜2k)格式错误,只有一列而不是17列
      • 这些完全不重要,可以删除
    • 我只需要该文件中的约100,000行(见下文)

数据示例:

County; State; Year; Quarter; Segment; Sub-Segment; Sub-Sub-Segment; GDP; ...
Ada County;NC;2009;4;FIRE;Financial;Banks;80.1; ...
Ada County;NC;2010;1;FIRE;Financial;Banks;82.5; ...
NC  [Malformed row]
[8.5 Mill rows]

我想删掉一些列并从40个可用年(1980-2020年的2009-2010年)中选择两个,以便使数据适合R:

County; State; Year; Quarter; Segment; GDP; ...
Ada County;NC;2009;4;FIRE;80.1; ...
Ada County;NC;2010;1;FIRE;82.5; ...
[~200,000 rows]

结果:

修正所有提出的建议后,我决定由JD和Marek提出的readLines效果最好。我给了Marek支票,因为他提供了示例实现。

我在这里为我的最终答案复制了Marek实现的稍作改编的版本,使用strsplit和cat仅保留我想要的列。

还应当指出,这是MUCH比Python效率较低......在,巨蟒通过要吃掉3.5GB的文件中5分钟,同时R取约60 ...但如果你只为R,那么这是罚单。

## Open a connection separately to hold the cursor position
file.in <- file('bad_data.txt', 'rt')
file.out <- file('chopped_data.txt', 'wt')
line <- readLines(file.in, n=1)
line.split <- strsplit(line, ';')
# Stitching together only the columns we want
cat(line.split[[1]][1:5], line.split[[1]][8], sep = ';', file = file.out, fill = TRUE)
## Use a loop to read in the rest of the lines
line <- readLines(file.in, n=1)
while (length(line)) {
  line.split <- strsplit(line, ';')
  if (length(line.split[[1]]) > 1) {
    if (line.split[[1]][3] == '2009') {
        cat(line.split[[1]][1:5], line.split[[1]][8], sep = ';', file = file.out, fill = TRUE)
    }
  }
  line<- readLines(file.in, n=1)
}
close(file.in)
close(file.out)

失败的方法:

  • sqldf
    • 如果数据格式正确,这绝对是我以后将要用于此类问题的方法。但是,如果不是这样,则SQLite会阻塞。
  • MapReduce
    • 老实说,文档使我对此感到有些恐惧,所以我没有去尝试它。看起来它也要求该对象也要在内存中,如果是这样的话,这将使问题无济于事。
  • 大内存
    • 这种方法与数据干净地链接在一起,但是一次只能处理一种类型。结果,我的所有字符向量在放入big.table时都掉了。但是,如果我需要为将来设计大型数据集,我会考虑仅使用数字只是为了使此选项有效。
  • 扫描
    • 扫描似乎具有与大内存相似的类型问题,但是具有readLines的所有机制。简而言之,这次不符合要求。

3
如果您的条件足够简单,则可以使用sed和/或awk创建可以直接读取的CSV格式精简版。由于这不是解决方案,而是答案,因此,我将其保留为评论。
Hank Gay

我同意汉克-你应该使用正确的工具的工作,如果它是简单的数据清洗/删除无关行/列命令行流工具,如排序/ SED / AWK是巨大的,将是这样较少的资源除了R密集还是python-如果您提供文件格式的示例,我们可能会举一个例子
Aaron Statham 2010年

大。让我们知道您的发现。
Shane 2010年

@Hank&Aaron:我一般都是使用正确的工具来完成工作,但是鉴于这是在工作的Windows机器上进行的,而且我正在学习R,所以我认为这是一个很好的练习,放弃最佳实践并尝试将其作为R-only。
FTWynn10年

2
为了将来参考,请查看data.table R包。该fread功能比快得多read.table。使用类似的东西x = fread(file_path_here, data.table=FALSE)将其作为data.frame对象加载。
paleo13年

Answers:


39

我的尝试readLines。这部分代码是根据csv选定的年份创建的。

file_in <- file("in.csv","r")
file_out <- file("out.csv","a")
x <- readLines(file_in, n=1)
writeLines(x, file_out) # copy headers

B <- 300000 # depends how large is one pack
while(length(x)) {
    ind <- grep("^[^;]*;[^;]*; 20(09|10)", x)
    if (length(ind)) writeLines(x[ind], file_out)
    x <- readLines(file_in, n=B)
}
close(file_in)
close(file_out)

这几乎就是我刚刚写的。考虑到内存限制,混合类型和格式错误的行,我认为这也是最佳答案。
FTWynn10年

10

我不是专家,但是您可以考虑尝试MapReduce,这基本上意味着采取“分而治之”的方法。R为此提供了几种选择,包括:

  1. mapReduce(纯R)
  2. RHIPE(使用Hadoop);有关子设置文件的示例,请参见文档中的示例6.2.2

另外,R提供了一些软件包来处理大数据,这些大数据在内存之外(到磁盘上)。您可以将整个数据集加载到一个bigmemory对象中,然后完全在R中进行约简。有关处理此问题的工具,请参见http://www.bigmemory.org/


很好的建议,但我对MapReduce及其同类产品经验不足。我将不得不阅读它。
FTWynn10年

bigmemory在这种情况下,您可能更容易先尝试。
Shane 2010年

10

有没有类似的方法可以一次在R中读取文件?

是。readChar()函数将在字符的块读取,而不假定它们是空终止。如果您想一次读取一行中的数据,则可以使用readLines()。如果读取块或行,请执行操作,然后将数据写出,可以避免出现内存问题。尽管如果您想在Amazon EC2上启动大内存实例,则可以获取高达64GB的RAM。那应该保存您的文件,还有足够的空间来处理数据。

如果需要更高的速度,那么Shane建议使用Map Reduce是一个很好的建议。但是,如果您打算在EC2上使用大内存实例,则应该查看多核软件包,以使用计算机上的所有内核。

如果您发现自己想将大量带分隔符的数据读入R中,则至少应研究sqldf程序包,该程序包可让您从R中直接导入sqldf,然后对R中的数据进行操作。我发现sqldf是其中之一如上一个问题所述,将大量数据导入R的最快方法。


我会记住一个EC2实例,但此刻我必须坚持使用台式机,它的内存为2GB。sqldf绝对像我的想法。但是,它也会使格式错误的行阻塞(应该有17列,但几千行只有一个)。这是否需要其他预处理方法,或者我缺少选项?
FTWynn10年


6

ff软件包是处理大型文件的透明方法。

您可能会看到软件包的网站和/或有关它的介绍

我希望这有帮助



5

那么使用readrread_*_chunked家庭呢?

因此,在您的情况下:

testfile.csv

County; State; Year; Quarter; Segment; Sub-Segment; Sub-Sub-Segment; GDP
Ada County;NC;2009;4;FIRE;Financial;Banks;80.1
Ada County;NC;2010;1;FIRE;Financial;Banks;82.5
lol
Ada County;NC;2013;1;FIRE;Financial;Banks;82.5

实际代码

require(readr)
f <- function(x, pos) subset(x, Year %in% c(2009, 2010))
read_csv2_chunked("testfile.csv", DataFrameCallback$new(f), chunk_size = 1)

这适用f于每个块,记住列名并最终组合过滤后的结果。请参阅?callback哪个是此示例的来源。

结果是:

# A tibble: 2 × 8
      County State  Year Quarter Segment `Sub-Segment` `Sub-Sub-Segment`   GDP
*      <chr> <chr> <int>   <int>   <chr>         <chr>             <chr> <dbl>
1 Ada County    NC  2009       4    FIRE     Financial             Banks   801
2 Ada County    NC  2010       1    FIRE     Financial             Banks   825

您甚至可以增加,chunk_size但是在此示例中只有4行。



3

也许您可以迁移到MySQL或PostgreSQL,以防止自己受到MS Access的限制。

使用基于DBI(在CRAN上可用)的数据库连接器将R连接到这些系统非常容易。


Touche使用更好的数据库工具,但是由于这会带来管理上的麻烦(必须爱上大公司中的那些管理法规),所以我试图坚持自己所拥有的。另外,我的目标是尽可能减少我收到的文本文件之间的转换。
FTWynn10年

3

scan()同时具有nlines参数和skip参数。您是否有某种原因可以仅使用它一次读取一行行,检查日期以查看是否合适?如果输入文件按日期排序,则可以存储一个索引,该索引告诉您应该跳过和跳过的行,这将在将来加快该过程。


我会检查出来的,但是该文件没有按日期等有用的命令排序。提供者似乎认为,按给定县所属的区域进行排序更为重要
。//

我认为您误解了他的建议:逐块读取文件,仅从每个块中提取所需的行。不需要订购文件。
Karl Forner 2013年

1

如今,3.5GB并没有那么大,我可以以每小时2.80美元的价格访问亚马逊云上带有244GB RAM(r3.8xlarge)的计算机。您需要花费几个小时找出使用大数据类型解决方案解决问题的方法?您的时间值多少钱?是的,您将需要一两个小时才能弄清楚如何使用AWS-但您可以免费学习基础知识,上传数据并将前10k行读入R以检查其是否有效,然后可以启动一个大内存实例,例如r3.8xlarge,并全部读取!只是我的2c。


0

现在,2017年,我建议您使用spark和sparkR。

  • 语法可以用简单的,类似dplyr的方式编写

  • 它非常适合小内存(在2017年意义上很小)

但是,入门可能会令人生畏。


-3

我会去一个数据库,然后进行一些查询以通过DBI提取您需要的样本

请避免将3.5 GB的csv文件导入SQLite。或者至少要仔细检查您的HUGE数据库是否符合SQLite限制, http:

这是一个该死的大数据库。如果您需要速度,我会选择MySQL。但要准备等待大量时间才能完成导入。除非您有一些非常规的硬件,或者您是从未来开始写作...

对于实例化运行R和MySQL的服务器,Amazon的EC2也是很好的解决方案。

我的两个小钱值得。


18
sqlite 3.5Gb的大小如何?只要您使用适当的文件系统,就不会有问题(我经常为单用户应用程序使用> 30Gb sqlite dbs)
Aaron Statham 2010年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.