如何按行条件将巨大的 csv 文件读入 R?

Posted

技术标签:

【中文标题】如何按行条件将巨大的 csv 文件读入 R?【英文标题】:how to read huge csv file into R by row condition? 【发布时间】:2013-09-18 10:35:27 【问题描述】:

我有一个大约 1500 万行的巨大 csv 文件,大小约为 3G。

我想将这个文件逐个读入R,每次只选择那些符合特定条件的行。

例如其中一列称为产品类型,因此我只需将一种产品类型读入R,并对其进行处理,然后输出结果,然后再转到另一种产品类型...

到目前为止,我已经阅读了不同的方法,例如将大文件上传到数据库,或者通过 colbycol 逐列读取,或者通过 ff 读取一大块行......

是否有任何纯 R 解决方案可以解决我的问题?

【问题讨论】:

您所说的“纯 R”是指坚持不使用任何软件包的基础吗?如果是,为什么?这是您目前的设置中的一些限制还是只是一种心理锻炼?如果你可以考虑一个包,这听起来可能是sqldf 的候选者。 Here 是 @eddi 提供的一个很好的回答,假设您愿意使用命令行工具,而不仅仅是 base::R 如果我们要走非 R 解决方案的道路,也请查看 csvkit 我会使用data.table,尤其是fread,如果数据适合您的RAM,并且只有在不适合的情况下才使用其他东西 data.table 是否需要先将整个文件读入内存? 【参考方案1】:

您可以使用RSQLite 包:

library(RSQLite)
# Create/Connect to a database
con <- dbConnect("SQLite", dbname = "sample_db.sqlite")

# read csv file into sql database
# Warning: this is going to take some time and disk space, 
#   as your complete CSV file is transferred into an SQLite database.
dbWriteTable(con, name="sample_table", value="Your_Big_CSV_File.csv", 
    row.names=FALSE, header=TRUE, sep = ",")

# Query your data as you like
yourData <- dbGetQuery(con, "SELECT * FROM sample_table LIMIT 10")

dbDisconnect(con)

下次您想要访问您的数据时,您可以省略 dbWriteTable,因为 SQLite 表存储在磁盘上。

注意:将 CSV 数据写入 SQLite 文件不会先将所有数据加载到内存中。因此,您最终将使用的内存将受限于您的查询返回的数据量。

【讨论】:

【参考方案2】:

只能使用 R 来做到这一点:

    打开文件连接 如果有header,则读入header信息 使用read.csv从文件中读取一行,指定colClassesnrows=1 测试该行以查看它是否满足您的条件,如果是,则将其附加到不断增长的数据框中 对文件的其余部分重复第 4 步。 关闭连接

虽然上述情况是可能的,但我认为这是不可取的。这种类型的事情可能最好通过将数据加载到数据库中,然后从 R 中查询数据库来完成。

【讨论】:

一次读一行会非常低效,最好一次读一万行(或更多)。 @Hadley,我的理解是,在幕后 R 会读取几行,然后只给你你要求的数字,当你要求更多的行时,它会从它的内部缓冲区中给你,而不是从磁盘重新读取(直到你用完缓冲区)。但是,我可能是错的。 我很确定情况并非如此,但你永远不知道。 @Hadley,这篇文章:tolstoy.newcastle.edu.au/R/help/05/12/18001.html 建议操作系统进行缓冲。无论哪种方式,我仍然建议 OP 使用数据库方法而不是直接读取。【参考方案3】:

您也可以使用 JDBC 来实现这一点。让我们创建一个示例 csv 文件。

write.table(x=mtcars, file="mtcars.csv", sep=",", row.names=F, col.names=T) # create example csv file

从此链接下载并保存 CSV JDBC 驱动程序:http://sourceforge.net/projects/csvjdbc/files/latest/download,然后设置驱动程序。

> library(RJDBC)

> path.to.jdbc.driver <- "jdbc//csvjdbc-1.0-18.jar"
> drv <- JDBC("org.relique.jdbc.csv.CsvDriver", path.to.jdbc.driver)
> conn <- dbConnect(drv, sprintf("jdbc:relique:csv:%s", getwd())) # replace getwd() with location of csv file

让我们看看 mtcars 数据集中的前 3 行:

> head(dbGetQuery(conn, "select * from mtcars"), 3)
   mpg cyl disp  hp drat    wt  qsec vs am gear carb
1   21   6  160 110  3.9  2.62 16.46  0  1    4    4
2   21   6  160 110  3.9 2.875 17.02  0  1    4    4
3 22.8   4  108  93 3.85  2.32 18.61  1  1    4    1

接下来,让我们看看 gear 的不同值和各自的计数:

> dbGetQuery(conn, "select gear, count(*) from mtcars group by gear")
  GEAR COUNT(*)
1    4       12
2    3       15
3    5        5

现在您可以使用 where 子句编写查询以过滤数据以仅选择 gear 取值为 5 的行:

> dbGetQuery(conn, "select * from mtcars where gear = '5'")
   mpg cyl  disp  hp drat    wt qsec vs am gear carb
1   26   4 120.3  91 4.43  2.14 16.7  0  1    5    2
2 30.4   4  95.1 113 3.77 1.513 16.9  1  1    5    2
3 15.8   8   351 264 4.22  3.17 14.5  0  1    5    4
4 19.7   6   145 175 3.62  2.77 15.5  0  1    5    6
5   15   8   301 335 3.54  3.57 14.6  0  1    5    8

【讨论】:

以上是关于如何按行条件将巨大的 csv 文件读入 R?的主要内容,如果未能解决你的问题,请参考以下文章

将文件夹中的多个csv文件读入R中的单个数据框[重复]

将大型 csv 文件从 S3 读入 R

将海量 CSV 文件读入 Oracle 表

无法将 unicode .csv 读入 R

使用 bigmemory 将 40 GB csv 文件读入 R

将多条csv行读入R中的单行