Jak odczytać tylko linie spełniające warunek z pliku csv na R?
Próbuję odczytać duży plik csv do R. chcę tylko czytać i pracować z niektórymi wierszami, które spełniają określony warunek (np. Variable2 >= 3
). Jest to znacznie mniejszy zbiór danych.
Chcę odczytać te linie bezpośrednio do ramki danych, zamiast załadować cały zestaw danych do ramki danych, a następnie wybrać zgodnie z warunkiem, ponieważ cały zestaw danych nie mieści się łatwo w pamięci.
5 answers
Możesz użyć funkcji read.csv.sql
w pakiecie sqldf
i filtrować za pomocą SQL select. Ze strony pomocy 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")
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-12-11 14:20:29
Zdecydowanie najprostszym (w mojej książce) jest użycie wstępnego przetwarzania.
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>
Tutaj używamy awk
. Mówimy awk
, aby użyć przecinka jako separatora pola, a następnie użyć conditon 'jeśli pierwsze pole większe niż 20', aby zdecydować, czy drukujemy (cały wiersz przez $0
).
Wyjście z tego polecenia może być odczytane przez r poprzez pipe()
.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-04-21 13:16:25
Patrzyłem na readr::read_csv_chunked
Kiedy zobaczyłem to pytanie i pomyślałem, że zrobię kilka benchmarkingu. W tym przykładzie, read_csv_chunked
robi dobrze i zwiększenie rozmiaru kawałka było korzystne. sqldf
był tylko nieznacznie szybszy niż 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
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-05-30 04:55:59
Możesz odczytać plik w kawałkach, przetworzyć każdy kawałek, a następnie połączyć tylko podzbiory razem.
Oto minimalny przykład, zakładając, że plik ma 1001 (włącznie. nagłówka) linii i tylko 100 zmieści się w pamięci. Dane mają 3 kolumny i oczekujemy, że co najwyżej 150 wierszy spełni warunek (jest to potrzebne do wstępnego przydzielenia przestrzeni dla ostatecznych danych:
# 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
Edit: Dodano opcję stringsAsFactors=FALSE
na sugestię @ lucacerone w komentarzach.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-04-21 13:43:29
Plik można otworzyć w trybie odczytu za pomocą funkcji file
(np. file("mydata.csv", open = "r")
).
Możesz odczytać plik po jednej linii na raz za pomocą funkcji readLines
z opcją n = 1
, l = readLines(fc, n = 1)
.
Następnie musisz przetworzyć swój łańcuch za pomocą funkcji, takich jak strsplit
, wyrażenia regularne, lub możesz wypróbować pakiet stringr
(dostępny od CRAN).
Jeśli linia spełnia warunki importowania danych, importujesz ją.
Podsumowując, zrobiłbym coś takiego:
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
}
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-04-21 12:46:21