我如何只读行履行条件从CSV到R?

我想读取一个大的csv文件到R中。尽pipe文件很大,我只想处理满足特定条件的一些行(例如Variable2> = 3)。 这是一个小得多的数据集。 我想直接将这些行读入数据框,而不是将整个数据集加载到数据框中,然后根据条件进行select。 主要原因是数据集不容易适应桌面或笔记本电脑的内存。 我正在寻找一个只使用R的解决scheme,并不需要python或其他语言。 谢谢。

您可以在sqldf包中使用read.csv.sql函数,并使用SQL select进行筛选。 从read.csv.sql的帮助页面:

 library(sqldf) write.csv(iris, "iris.csv", quote = FALSE, row.names = FALSE) iris2 <- read.csv.sql("iris.csv", sql = "select * from file where Sepal.Length > 5", eol = "\n") 

到目前为止,最简单的(在我的书中)是使用预处理。

 R> DF <- data.frame(n=1:26, l=LETTERS) R> write.csv(DF, file="/tmp/data.csv", row.names=FALSE) R> read.csv(pipe("awk 'BEGIN {FS=\",\"} {if ($1 > 20) print $0}' /tmp/data.csv"), + header=FALSE) V1 V2 1 21 U 2 22 V 3 23 W 4 24 X 5 25 Y 6 26 Z R> 

这里我们使用awk 。 我们告诉awk使用逗号作为字段分隔符,然后使用“如果第一个字段大于20”的条件来决定是否打印(整个行通过$0 )。

该命令的输出可以通过pipe()读取。

这将比读取R中的所有内容更快,更有记忆效率。

您可以按块读取文件,处理每个块,然后只将这些子集拼接在一起。

这是一个最小的例子,假设文件有1001行(包括头),只有100行可以放入内存。 数据有3列,我们预计至多有150行满足条件(这是需要预先分配空间的最终数据:

 # initialize empty data.frame (150 x 3) max.rows <- 150 final.df <- data.frame(Variable1=rep(NA, max.rows=150), Variable2=NA, Variable3=NA) # read the first chunk outside the loop temp <- read.csv('big_file.csv', nrows=100, stringsAsFactors=FALSE) temp <- temp[temp$Variable2 >= 3, ] ## subset to useful columns final.df[1:nrow(temp), ] <- temp ## add to the data last.row = nrow(temp) ## keep track of row index, incl. header for (i in 1:9){ ## nine chunks remaining to be read temp <- read.csv('big_file.csv', skip=i*100+1, nrow=100, header=FALSE, stringsAsFactors=FALSE) temp <- temp[temp$Variable2 >= 3, ] final.df[(last.row+1):(last.row+nrow(temp)), ] <- temp last.row <- last.row + nrow(temp) ## increment the current count } final.df <- final.df[1:last.row, ] ## only keep filled rows rm(temp) ## remove last chunk to free memory 

编辑:添加stringsAsFactors=FALSE选项@ lucacerone的build议在评论。

我看到readr::read_csv_chunked当我看到这个问题,并认为我会做一些基准。 对于这个例子, read_csv_chunked做得很好,增加块的大小是有益的。 sqldf只比awk sqldf快。

 library(tidyverse) library(sqldf) library(microbenchmark) # Generate an example dataset with two numeric columns and 5 million rows data_frame( norm = rnorm(5e6, mean = 5000, sd = 1000), unif = runif(5e6, min = 0, max = 10000) ) %>% write_csv('medium.csv') microbenchmark( readr = read_csv_chunked('medium.csv', callback = DataFrameCallback$new(function(x, pos) subset(x, unif > 9000)), col_types = 'dd', progress = F), readr2 = read_csv_chunked('medium.csv', callback = DataFrameCallback$new(function(x, pos) subset(x, unif > 9000)), col_types = 'dd', progress = F, chunk_size = 1000000), sqldf = read.csv.sql('medium.csv', sql = 'select * from file where unif > 9000', eol = '\n'), awk = read.csv(pipe("awk 'BEGIN {FS=\",\"} {if ($2 > 9000) print $0}' medium.csv")), awk2 = read_csv(pipe("awk 'BEGIN {FS=\",\"} {if ($2 > 9000) print $0}' medium.csv"), col_types = 'dd', progress = F), check = function(values) all(sapply(values[-1], function(x) all.equal(values[[1]], x))), times = 10L ) # Unit: seconds # expr min lq mean median uq max neval # readr 5.58 5.79 6.16 5.98 6.68 7.12 10 # readr2 2.94 2.98 3.07 3.03 3.06 3.43 10 # sqldf 13.59 13.74 14.20 13.91 14.64 15.49 10 # awk 16.83 16.86 17.07 16.92 17.29 17.77 10 # awk2 16.86 16.91 16.99 16.92 16.97 17.57 10 

您可以使用functionfile (例如file("mydata.csv", open = "r") )以读模式打开文件。

您可以使用带有选项n = 1l = readLines(fc, n = 1)的函数readLines ,一次读取一行文件。

然后你必须使用strsplit ,正则expression式等函数来parsing你的string,或者你可以尝试一下stringr (可从CRAN获得)。

如果该行符合导入数据的条件,则将其导入。

总结一下,我会做这样的事情:

 df = data.frame(var1=character(), var2=int(), stringsAsFactors = FALSE) fc = file("myfile.csv", open = "r") i = 0 while(length( (l <- readLines(fc, n = 1) ) > 0 )){ # note the parenthesis surrounding l <- readLines.. ##parse l here: and check whether you need to import the data. if (need_to_add_data){ i=i+1 df[i,] = #list of data to import } }