Szybkie odczytywanie bardzo dużych tabel jako ramek danych
Mam bardzo duże tabele (30 milionów wierszy), które chciałbym załadować jako ramki danych w R. read.table()
ma wiele wygodnych funkcji, ale wydaje się, że w implementacji jest wiele logiki, która spowolniłaby działanie. W moim przypadku zakładam, że znam typy kolumn z wyprzedzeniem, tabela nie zawiera nagłówków kolumn ani nazw wierszy i nie ma żadnych patologicznych znaków, którymi muszę się martwić.
Wiem, że czytanie w tabeli jako lista używanie scan()
może być dość szybkie, np.:
datalist <- scan('myfile',sep='\t',list(url='',popularity=0,mintime=0,maxtime=0)))
Ale niektóre z moich prób przekonwertowania tego na ramkę danych wydają się zmniejszać wydajność powyższego o współczynnik 6:
df <- as.data.frame(scan('myfile',sep='\t',list(url='',popularity=0,mintime=0,maxtime=0))))
Czy jest na to lepszy sposób? A może zupełnie inne podejście do problemu? 8 answers
W 1998 roku został wybrany do Izby Gmin.]}
Ta odpowiedź jest Stara, A R ruszył dalej. Podkręcanie Za pomocą Za pomocą Staramy się przechowywać jak najwięcej danych w bazach danych, a nie plikach płaskich. (Oprócz tego, że jest lepszym trwałym nośnikiem danych, dane są przekazywane do i z R w formacie binarnym, który jest szybszy.) Przechowywanie danych w formatach binarnych może być również przydatne do poprawy wydajności. Użycie Oryginalna odpowiedź Jest kilka prostych rzeczy do wypróbowania, niezależnie od tego, czy używasz read.tabela lub skan. Set Upewnij się, że Jawnie Definiuj klasy każdej kolumny używając Ustawienie Jeśli żadna z tych rzeczy nie działa, użyj jednego z pakietów profilujących, aby określić, które linie spowalniają wszystko. Być może możesz napisać skróconą wersję Inną alternatywą jest filtrowanie danych przed odczytaniem ich do R. Lub, jeśli problem polega na tym, że musisz czytać je regularnie, użyj tych metod, aby odczytać dane raz, a następnie zapisz ramkę danych jako binarny obiekt blob z read.table
bieganie nieco szybciej ma cenne niewiele korzyści. Twoje opcje to:
fread
w data.table
aby zaimportować dane z plików CSV / tab-delimited bezpośrednio do R. Zobacz odpowiedź mnel.read_table
w readr
(od kwietnia 2015). To działa podobnie jak fread
powyżej. readme w linku wyjaśnia różnicę między dwiema funkcjami (readr
obecnie twierdzi ,że jest "1,5-2x wolniejsza" niż data.table::fread
).read.csv.raw
od iotools
zapewnia trzecią opcję szybkiego odczytu plików CSV.read.csv.sql
w sqldf
pakiet, jak opisano w odpowiedzi JD Long, importuje dane do tymczasowej bazy danych SQLite, a następnie wczytuje je do R. Zobacz także: RODBC
pakiet, a odwrotna część zależy od DBI
pakiet strona. MonetDB.R
daje typ danych, który udaje ramkę danych, ale tak naprawdę jest MonetDB pod spodem, zwiększając wydajność. Import danych z its monetdb.read.csv
funkcja. dplyr
pozwala możesz pracować bezpośrednio z danymi przechowywanymi w kilku rodzajach baz danych.saveRDS
/readRDS
(patrz niżej),h5
lub rhdf5
pakiety dla formatu HDF5 lub write_fst
/read_fst
z fst
paczka.
nrows
=liczba rekordów w Twoich danych (nmax
W scan
).comment.char=""
, aby wyłączyć interpretację komentarzy.colClasses
W read.table
.multi.line=FALSE
może również poprawić wydajność skanowania.read.table
Na podstawie wyników.save
saveRDS
, następnie następnym razem można go odzyskać szybciej z load
readRDS
.
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
2018-02-12 09:35:30
Oto przykład, który wykorzystuje fread
z data.table
1.8.7
Przykłady pochodzą ze strony pomocy do fread
, z czasem na moim windows XP Core 2 duo E8400.
library(data.table)
# Demo speedup
n=1e6
DT = data.table( a=sample(1:1000,n,replace=TRUE),
b=sample(1:1000,n,replace=TRUE),
c=rnorm(n),
d=sample(c("foo","bar","baz","qux","quux"),n,replace=TRUE),
e=rnorm(n),
f=sample(1:1000,n,replace=TRUE) )
DT[2,b:=NA_integer_]
DT[4,c:=NA_real_]
DT[3,d:=NA_character_]
DT[5,d:=""]
DT[2,e:=+Inf]
DT[3,e:=-Inf]
Standardowy odczyt.tabela
write.table(DT,"test.csv",sep=",",row.names=FALSE,quote=FALSE)
cat("File size (MB):",round(file.info("test.csv")$size/1024^2),"\n")
## File size (MB): 51
system.time(DF1 <- read.csv("test.csv",stringsAsFactors=FALSE))
## user system elapsed
## 24.71 0.15 25.42
# second run will be faster
system.time(DF1 <- read.csv("test.csv",stringsAsFactors=FALSE))
## user system elapsed
## 17.85 0.07 17.98
Zoptymalizowany odczyt.tabela
system.time(DF2 <- read.table("test.csv",header=TRUE,sep=",",quote="",
stringsAsFactors=FALSE,comment.char="",nrows=n,
colClasses=c("integer","integer","numeric",
"character","numeric","integer")))
## user system elapsed
## 10.20 0.03 10.32
Fread
require(data.table)
system.time(DT <- fread("test.csv"))
## user system elapsed
## 3.12 0.01 3.22
Sqldf
require(sqldf)
system.time(SQLDF <- read.csv.sql("test.csv",dbname=NULL))
## user system elapsed
## 12.49 0.09 12.69
# sqldf as on SO
f <- file("test.csv")
system.time(SQLf <- sqldf("select * from f", dbname = tempfile(), file.format = list(header = T, row.names = F)))
## user system elapsed
## 10.21 0.47 10.73
Ff / ffdf
require(ff)
system.time(FFDF <- read.csv.ffdf(file="test.csv",nrows=n))
## user system elapsed
## 10.85 0.10 10.99
W podsumowaniu:
## user system elapsed Method
## 24.71 0.15 25.42 read.csv (first time)
## 17.85 0.07 17.98 read.csv (second time)
## 10.20 0.03 10.32 Optimized read.table
## 3.12 0.01 3.22 fread
## 12.49 0.09 12.69 sqldf
## 10.21 0.47 10.73 sqldf on SO
## 10.85 0.10 10.99 ffdf
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
2013-02-25 01:56:36
Początkowo nie widziałem tego pytania i zadałem podobne pytanie kilka dni później. Zamierzam zdjąć moje poprzednie pytanie, ale pomyślałem, że dodam tutaj odpowiedź, aby wyjaśnić, jak użyłem sqldf()
, aby to zrobić.
Nie było trochę dyskusji na temat najlepszego sposobu zaimportowania 2 GB lub więcej danych tekstowych do ramki danych R. Wczoraj napisałem post na blogu o używaniu sqldf()
do importowania danych do SQLite jako obszaru postojowego, a następnie ssania ich z SQLite do R. To działa naprawdę dobrze dla mnie. Udało mi się pobrać 2GB (3 kolumny, 40mm wiersze) danych w read.csv działała całą noc i nigdy nie została ukończona.
Oto Mój kod testowy:
Ustaw dane testowe:
bigdf <- data.frame(dim=sample(letters, replace=T, 4e7), fact1=rnorm(4e7), fact2=rnorm(4e7, 20, 50))
write.csv(bigdf, 'bigdf.csv', quote = F)
Uruchomiłem ponownie R Przed uruchomieniem następującej procedury importu:
library(sqldf)
f <- file("bigdf.csv")
system.time(bigdf <- sqldf("select * from f", dbname = tempfile(), file.format = list(header = T, row.names = F)))
Pozwoliłem, aby następująca linia działała całą noc, ale nigdy nie została ukończona:
system.time(big.df <- read.csv('bigdf.csv'))
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
2011-11-21 21:36:20
O dziwo, nikt nie odpowiedział na dolną część pytania od lat, nawet jeśli jest to ważne -- data.frame
S są po prostu listami z odpowiednimi atrybutami, więc jeśli masz duże dane, nie chcesz używać as.data.frame
lub podobnych do listy. O wiele szybciej jest po prostu "zamienić" listę w ramkę danych w miejscu:
attr(df, "row.names") <- .set_row_names(length(df[[1]]))
class(df) <- "data.frame"
To nie tworzy kopii danych, więc jest natychmiastowa(w przeciwieństwie do wszystkich innych metod). Zakłada ona, że odpowiednio Ustawiłeś names()
na liście.
[Jak dla Ładowanie dużych danych do R -- osobiście wrzucam je kolumną do plików binarnych i używam readBin()
- jest to zdecydowanie najszybsza Metoda (inna niż mm) i jest ograniczona tylko szybkością dysku. Parsowanie plików ASCII jest z natury powolne (nawet w C) w porównaniu z danymi binarnymi.]
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
2013-02-24 21:42:54
To było wcześniej zapytane na R-Help, warto to przejrzeć.
Jedną z sugestii było użycie readChar()
, a następnie wykonanie manipulacji łańcuchami na wyniku za pomocą strsplit()
i substr()
. Widać, że logika związana z readChar jest znacznie mniejsza niż read.stolik.
Nie wiem, czy pamięć jest tutaj problemem, ale możesz również rzucić okiem naPakiet HadoopStreaming . Ten używa Hadoop , który jest frameworkiem MapReduce zaprojektowanym do obsługi dużych zbiorów danych. W tym celu należy użyć funkcji hsTableReader. Jest to przykład (ale ma krzywą uczenia się, aby nauczyć się Hadoop): {]}
str <- "key1\t3.9\nkey1\t8.9\nkey1\t1.2\nkey1\t3.9\nkey1\t8.9\nkey1\t1.2\nkey2\t9.9\nkey2\"
cat(str)
cols = list(key='',val=0)
con <- textConnection(str, open = "r")
hsTableReader(con,cols,chunkSize=6,FUN=print,ignoreKey=TRUE)
close(con)
Podstawową ideą jest rozbicie importu danych na kawałki. Snow) i uruchomić import danych równolegle poprzez segmentację pliku, ale najprawdopodobniej w przypadku dużych zestawów danych, które nie pomogą, ponieważ napotkasz ograniczenia pamięci, dlatego map-reduce jest bardzo przydatny. lepsze podejście.
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
2011-10-11 09:34:14
Drobne dodatkowe punkty warte wspomnienia. Jeśli masz bardzo duży plik, możesz w locie obliczyć liczbę wierszy (jeśli nie ma nagłówka) używając (gdzie bedGraph
jest nazwą Twojego pliku w katalogu roboczym):
>numRow=as.integer(system(paste("wc -l", bedGraph, "| sed 's/[^0-9.]*\\([0-9.]*\\).*/\\1/'"), intern=T))
Możesz użyć tego albo w read.csv
, read.table
...
>system.time((BG=read.table(bedGraph, nrows=numRow, col.names=c('chr', 'start', 'end', 'score'),colClasses=c('character', rep('integer',3)))))
user system elapsed
25.877 0.887 26.752
>object.size(BG)
203949432 bytes
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
2013-11-28 17:20:55
Często myślę, że dobrą praktyką jest trzymanie większych baz danych wewnątrz bazy (np. Postgres). Nie używam niczego znacznie większego niż (nrow * ncol) Ncell = 10M, co jest dość małe; ale często uważam, że chcę r tworzyć i przechowywać wykresy intensywne pamięci tylko podczas zapytań z wielu baz danych. W przyszłości laptopów o pojemności 32 GB niektóre tego typu problemy z pamięcią znikną. Ale urok używania bazy danych do przechowywania danych, a następnie używania pamięci R do wynikowe wyniki zapytań i wykresy nadal mogą być przydatne. Niektóre zalety to:
(1) dane pozostają załadowane w bazie danych. Po włączeniu laptopa po prostu ponownie łączysz się w pgadmin z wybranymi bazami danych.
(2) prawdą jest, że R może wykonać o wiele więcej sprytnych operacji statystycznych i graficznych niż SQL. Ale myślę, że SQL jest lepiej zaprojektowany do odpytywania dużych ilości danych niż R.
# Looking at Voter/Registrant Age by Decade
library(RPostgreSQL);library(lattice)
con <- dbConnect(PostgreSQL(), user= "postgres", password="password",
port="2345", host="localhost", dbname="WC2014_08_01_2014")
Decade_BD_1980_42 <- dbGetQuery(con,"Select PrecinctID,Count(PrecinctID),extract(DECADE from Birthdate) from voterdb where extract(DECADE from Birthdate)::numeric > 198 and PrecinctID in (Select * from LD42) Group By PrecinctID,date_part Order by Count DESC;")
Decade_RD_1980_42 <- dbGetQuery(con,"Select PrecinctID,Count(PrecinctID),extract(DECADE from RegistrationDate) from voterdb where extract(DECADE from RegistrationDate)::numeric > 198 and PrecinctID in (Select * from LD42) Group By PrecinctID,date_part Order by Count DESC;")
with(Decade_BD_1980_42,(barchart(~count | as.factor(precinctid))));
mtext("42LD Birthdays later than 1980 by Precinct",side=1,line=0)
with(Decade_RD_1980_42,(barchart(~count | as.factor(precinctid))));
mtext("42LD Registration Dates later than 1980 by Precinct",side=1,line=0)
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-08-24 15:54:25
Zamiast konwencjonalnego odczytu.stół uważam, że fread jest szybszą funkcją. Określenie dodatkowych atrybutów, takich jak select only the required columns, określenie colclasses i string jako czynników skraca czas potrzebny na zaimportowanie pliku.
data_frame <- fread("filename.csv",sep=",",header=FALSE,stringsAsFactors=FALSE,select=c(1,4,5,6,7),colClasses=c("as.numeric","as.character","as.numeric","as.Date","as.Factor"))
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
2015-04-18 07:22:01