修剪一个巨大的(3.5 GB)csv文件以读入R

时间:2010-06-22 16:00:09

标签: r csv

所以我有一个数据文件(分号分隔),它有很多细节和不完整的行(导致Access和SQL扼流)。它的县级数据集分为段,子段和子子段(总共约200个因子),为期40年。简而言之,它是巨大的,如果我试着简单地阅读它,它就不适合记忆。

所以我的问题是这个,因为我想要所有的县,但只有一年(而且只是最高级别的段......最终导致大约100,000行),最好的方法是什么把这个汇总到R?

目前我正在尝试用Python来消除不相关的年份,通过一次读取和操作一行来绕过文件大小限制,但我更喜欢只有R的解决方案(CRAN包OK)。是否有类似的方式在R中一次读取一个文件?

非常感谢任何想法。

更新

  • 约束
    • 需要使用我的机器,因此没有EC2实例
    • 尽可能使用R-only。在这种情况下速度和资源不是问题...只要我的机器没有爆炸......
    • 如下所示,数据包含混合类型,我需要稍后进行操作
  • 数据
    • 数据为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个可用年份中挑选两个(2009-2010从1980年到2020年),这样数据可以适用于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 ...因为,Python在5分钟内通过3.5GB文件,而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的
    • 说实话,文档在这一点上对我进行了恐吓,所以我没有去尝试它。它看起来像是需要对象在内存中,如果是这样的话,这将失败。
  • bigmemory
    • 这种方法干净地链接到数据,但它一次只能处理一种类型。结果,当放入big.table时,我的所有角色向量都会丢失。如果我需要为将来设计大型数据集,我会考虑仅使用数字来保持此选项的存活。
  • 扫描
    • 扫描似乎与大内存有类似的类型问题,但具有readLines的所有机制。简而言之,这次只是不合适。

13 个答案:

答案 0 :(得分:37)

我尝试使用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)

答案 1 :(得分:10)

  

是否有类似的方法在R中一次读取一个文件?

是。 readChar()函数将读取一个字符块而不假设它们以空值终止。如果您想一次读取一行中的数据,可以使用 readLines()。如果你读取一个块或一行,做一个操作,然后写出数据,就可以避免内存问题。虽然如果你想在亚马逊的EC2上启动一个大内存实例,你可以获得高达64GB的内存。这应该保存你的文件加上足够的空间来操纵数据。

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

如果您发现自己想要将许多分隔数据读入R中,您至少应该研究sqldf包,它允许您从R直接导入sqldf然后对R内部的数据进行操作。我发现sqldf如this previous question中所述,成为将数据导入R的最快方法之一。

答案 2 :(得分:9)

我不是这方面的专家,但你可能会考虑尝试MapReduce,这基本上意味着采取“分而治之”的方法。 R有几种选择,包括:

  1. mapReduce(纯R)
  2. RHIPE(使用Hadoop);有关子集化文件的示例,请参见示例6.2.2 in the documentation
  3. 或者,R提供了几个软件包来处理外部存储器(磁盘上)的大数据。您可以将整个数据集加载到bigmemory对象中并在R中完全进行缩减。请参阅http://www.bigmemory.org/以获取一组工具来处理此问题。

答案 3 :(得分:6)

ff包是处理大文件的透明方式。

您可能会看到包website和/或presentation

我希望这会有所帮助

答案 4 :(得分:5)

您可以import data to SQLite database然后使用RSQLite来选择子集。

答案 5 :(得分:5)

有一个名为colbycol的全新软件包,它允许您只从大量文本文件中读取所需的变量:

http://colbycol.r-forge.r-project.org/

它将任何参数传递给read.table,因此组合应该让你非常紧密地进行分组。

答案 6 :(得分: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行。

答案 7 :(得分:4)

您考虑过bigmemory了吗? 查看thisthis

答案 8 :(得分:3)

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

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

答案 9 :(得分:3)

scan()同时包含nlines参数和skip参数。有没有什么理由你可以用它来读取一段时间的行,检查日期以确定它是否合适?如果输入文件是按日期排序的,那么您可以存储一个索引,该索引可以告诉您将跳过哪些内容以及将来加速该过程的内容。

答案 10 :(得分:1)

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

答案 11 :(得分:0)

现在,2017年,我建议去寻找火花和火花。

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

  • 它非常适合小记忆(2017年意义上的小记忆)

然而,开始这可能是一种令人生畏的经历......

答案 12 :(得分:-3)

我会选择DB,然后进行一些查询以通过DBI提取您需要的样本

请避免将3,5 GB的csv文件导入SQLite。或至少仔细检查您的HUGE数据库是否符合SQLite限制http://www.sqlite.org/limits.html

这是一个该死的大数据库。如果你需要速度,我会选择MySQL。但是要准备等待很长时间才能完成导入。除非你有一些非传统的硬件或者你是在写未来......

亚马逊的EC2对于实例化运行R和MySQL的服务器也是一个很好的解决方案。

我的两个简陋的便士值得。