簡體   English   中英

如何僅將滿足條件的行從 csv 讀取到 R?

[英]How to read only lines that fulfil a condition from a csv into R?

我正在嘗試將一個大的 csv 文件讀入 R。我只想讀取和處理一些滿足特定條件的行(例如Variable2 >= 3 )。 這是一個小得多的數據集。

我想將這些行直接讀入數據幀,而不是將整個數據集加載到數據幀中,然后根據條件進行選擇,因為整個數據集不容易裝入內存。

您可以使用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打印整行)。

R 可以通過pipe()讀取該命令的輸出。

與將所有內容讀入 R 相比,這將更快、更節省內存。

當我看到這個問題並認為我會做一些基准測試時,我正在研究readr::read_csv_chunked 對於這個例子, read_csv_chunked做得很好,增加塊大小是有益的。 sqldf僅比awk快一點。

library(tidyverse)
library(sqldf)
library(data.table)
library(microbenchmark)

# Generate an example dataset with two numeric columns and 5 million rows
tibble(
  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),
  fread  = fread(cmd = "awk 'BEGIN {FS=\",\"} {if ($2 > 9000) print $0}' medium.csv"),
  check  = function(values) all(sapply(values[-1], function(x) all.equal(values[[1]], x))),
  times  = 10L
)

# Updated 2020-05-29

# Unit: seconds
#   expr   min    lq  mean  median    uq   max neval
#  readr   2.6   2.7   3.1     3.1   3.5   4.0    10
# readr2   2.3   2.3   2.4     2.4   2.6   2.7    10
#  sqldf  14.1  14.1  14.7    14.3  15.2  16.0    10
#    awk  18.2  18.3  18.7    18.5  19.3  19.6    10
#   awk2  18.1  18.2  18.6    18.4  19.1  19.4    10
#  fread  17.9  18.0  18.2    18.1  18.2  18.8    10

# R version 3.6.2 (2019-12-12)
# macOS Mojave 10.14.6        

# data.table 1.12.8
# readr      1.3.1 
# sqldf      0.4-11

您可以分塊讀取文件,處理每個塊,然后僅將子集拼接在一起。

這是一個假設文件有 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

編輯:在評論中添加了關於@lucacerone 建議的stringsAsFactors=FALSE選項。

您可以使用函數file (例如file("mydata.csv", open = "r") )以讀取模式打開文件。

您可以使用帶有選項n = 1 , l = readLines(fc, n = 1)的函數readLines一次讀取文件一行。

然后,您必須使用諸如strsplit 、正則表達式之類的函數來解析您的字符串,或者您可以嘗試使用包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
  }

}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM