Frage

Ich habe sehr große Tabellen (30 Millionen Zeilen), dass ich zu Last als Datenrahmen in R. read.table() möchte hat viele praktische Funktionen, aber es scheint, wie es eine Menge Logik in der Umsetzung ist, dass die Dinge verlangsamen würde Nieder. In meinem Fall, ich nehme ich die Typen der Spalten im Voraus weiß, hat die Tabelle keine Spaltenüberschriften oder Zeilennamen zu enthalten, und habe keine pathologischen Zeichen, dass ich Sorge habe über.

Ich weiß, dass als eine Liste mit scan() in einer Tabelle zu lesen ziemlich schnell sein kann, z.

datalist <- scan('myfile',sep='\t',list(url='',popularity=0,mintime=0,maxtime=0)))

Aber einige meiner Versuche, dies zu einem Datenrahmen zu konvertieren erscheinen die Leistung der oben um einen Faktor von 6 zu verringern:

df <- as.data.frame(scan('myfile',sep='\t',list(url='',popularity=0,mintime=0,maxtime=0))))

Gibt es einen besseren Weg, dies zu tun? Oder womöglich ganz andere Herangehensweise an das Problem?

War es hilfreich?

Lösung

Ein Update, einige Jahre später

Diese Antwort ist alt, und R hat sich weiterentwickelt. Tweaking read.table ein bisschen schneller zu laufen hat sehr wenig Nutzen. Ihre Optionen sind:

  1. Mit fread in data.table für Daten aus csv / tabstoppgetrennten Dateien direkt in R. Siehe MNEL Antwort .

  2. Mit read_table in readr (auf CRAN ab April 2015). Dies funktioniert ähnlich wie fread oben. Das readme in dem Link, um die Differenz zwischen den beiden Funktionen erklärt (readr derzeit behauptet "1.5-2x langsamer" als data.table::fread zu sein).

  3. read.csv.raw von iotools zum schnellen Lesen von CSV-Dateien.

  4. Der Versuch, zu speichern, so viele Daten wie Sie in Datenbanken können anstatt flachen Dateien. (Sowie ein besseres Dauerspeichermedium zu sein, wird die Daten in einem binären Format zu und von R geleitet, die schneller ist.) read.csv.sql in der sqldf Paket wie beschrieben, in Antwort JD Longs, liest sie importiert Daten in eine temporäre SQLite-Datenbank und dann auch in R. Siehe auch: RODBC Paket, und umgekehrt Abschnitt der abhängig DBI Paket Seite. MonetDB.R gibt Ihnen einen Datentyp, der vorgibt, eine zu sein aber Datenrahmen ist wirklich ein MonetDB unterhalb Dies steigert die Leistung. Importieren von Daten mit der monetdb.read.csv Funktion. dplyr können Sie direkt in verschiedene Arten gespeichert mit Daten arbeiten von Datenbank.

  5. Das Speichern von Daten in binären Formaten können auch zur Verbesserung der Leistung von Nutzen sein. Verwenden saveRDS / readRDS (siehe unten), die h5 oder rhdf5 Pakete für HDF5 Format oder write_fst / read_fst aus der fst Paket.


Die ursprüngliche Antwort

Es gibt ein paar einfache Dinge, um zu versuchen, ob Sie read.table oder Scan verwenden.

  1. Set nrows = die Anzahl der Datensätze in Ihren Daten (nmax in scan).

  2. Stellen Sie sicher, dass comment.char="" Interpretation der Kommentare auszuschalten.

  3. definiert Explizit die Klassen jeden Spalt colClasses in read.table verwendet wird.

  4. multi.line=FALSE Einstellung kann auch die Leistung in Scan verbessern.

Wenn keine dieser Sache arbeiten, dann verwenden Sie einen der Profilierungs Pakete um zu bestimmen, welche Zeilen Dinge nach unten verlangsamen. Vielleicht können Sie eine abgespeckte Version von read.table schreiben basierend auf den Ergebnissen.

Die andere Alternative ist die Filterung Ihrer Daten, bevor Sie es in R lesen.

Oder, wenn das Problem ist, dass man es in regelmäßig zu lesen, dann diese Methoden verwenden, um die Daten in einmal zu lesen, dann speichern Sie den Datenrahmen als binäres Blob mit save saveRDS , dann sind Sie beim nächsten Mal schneller abrufen können mit load readRDS.

Andere Tipps

Hier ist ein Beispiel, das fread von data.table 1.8.7

nutzt

Die Beispiele stammen aus der Hilfeseite zu fread, mit den Timings auf meinem 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]

Standard read.table

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

optimiertem read.table

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

Zusammenfassung:

##    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

Ich habe diese Frage nicht von Anfang an und fragte eine ähnliche Frage ein paar Tage später. Ich werde meine vorherige Frage nach unten nehmen, aber ich dachte, dass ich hier eine Antwort hinzufügen würde zu erklären, wie ich sqldf() verwendet, dies zu tun.

Es gab wenig Diskussion zu der beste Weg, 2 GB oder mehr von Textdaten in einen R-Datenrahmen zu importieren. Gestern schrieb ich eine Blog-Post über sqldf() unter Verwendung der Daten in SQLite als Bereitstellungsbereich zu importieren, und saugen sie dann von SQLite in R. Das funktioniert wirklich gut für mich. Ich konnte in 2GB (3 Spalten, 40mm Reihen) von Daten in <5 Minuten ziehen. Im Gegensatz dazu lief der read.csv Befehl die ganze Nacht und nie vollendet.

Hier ist mein Test-Code:

die Testdaten ein:

bigdf <- data.frame(dim=sample(letters, replace=T, 4e7), fact1=rnorm(4e7), fact2=rnorm(4e7, 20, 50))
write.csv(bigdf, 'bigdf.csv', quote = F)

ich neu gestartet R, bevor Sie die folgenden Import-Routine ausgeführt wird:

library(sqldf)
f <- file("bigdf.csv")
system.time(bigdf <- sqldf("select * from f", dbname = tempfile(), file.format = list(header = T, row.names = F)))

Ich lasse die folgende Zeile die ganze Nacht laufen, aber es nie beendet:

system.time(big.df <- read.csv('bigdf.csv'))

Seltsam, antwortete niemand den unteren Teil der Frage seit Jahren, obwohl dies ein sehr wichtiger ist - data.frames sind einfach Listen mit den richtigen Eigenschaften, so dass, wenn Sie große Daten haben Sie nicht wollen, zu verwenden as.data.frame oder ähnlich eine Liste. Es ist viel schneller zu einfach „drehen“, um eine Liste in einen Datenrahmen an Ort und Stelle:

attr(df, "row.names") <- .set_row_names(length(df[[1]]))
class(df) <- "data.frame"

Das macht keine Kopie der Daten, so dass es sofort ist (im Gegensatz zu allen anderen Methoden). Er geht davon aus, dass Sie bereits names() auf der Liste entsprechend eingestellt haben.

[Wie für große Daten in R Laden - ich persönlich Dump sie durch Spalte in binäre Dateien und die Verwendung readBin() - das ist bei weitem die schnellste Methode (außer mmapping) und wird nur durch die Plattengeschwindigkeit begrenzt. ASCII-Dateien Parsen ist von Natur aus langsam (auch in C) im Vergleich zu binären Daten.]

Dies war bisher fragte auf R-Help , so dass der wert zu überprüfen.

Ein Vorschlag war, es zu verwenden readChar() und dann String-Manipulation zu tun auf dem Ergebnis mit strsplit() und substr(). Sie können die Logik in readChar beteiligt sehen weniger viel als read.table.

Ich weiß nicht, wenn der Speicher ein Problem hier ist, aber man könnte auch nutzt Hadoop , die ein MapReduce Rahmen für den Umgang mit großen Datenmengen ausgelegt ist. Dazu würden Sie die hsTableReader-Funktion verwenden. Dies ist ein Beispiel (aber es hat eine Lernkurve Hadoop lernen):

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)

Die Grundidee ist es, das Datenimport in Stücke zu brechen. Man könnte sogar so weit gehen, als einen der parallelen Frameworks zu verwenden (zB Schnee) und den Datenimport in parallel ausführen, indem Sie die Datei segmentieren, aber wahrscheinlich für großen Datensätze, die nicht helfen, da Sie in den Speichern Einschränkungen ausgeführt werden sollen, weshalb Karten reduzieren, ist ein besserer Ansatz.

Eine geringfügige zusätzliche Punkte erwähnenswert. Wenn Sie eine sehr große Datei, die Sie die Anzahl der Zeilen im Fluge berechnen können (wenn kein Header) mit (wo bedGraph der Name der Datei in Ihrem Arbeitsverzeichnis ist):

>numRow=as.integer(system(paste("wc -l", bedGraph, "| sed 's/[^0-9.]*\\([0-9.]*\\).*/\\1/'"), intern=T))

Sie können dann verwenden, entweder in 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

Eine Alternative ist das vroom Paket zu verwenden. Jetzt auf CRAN. vroom nicht die gesamte Datei laden, es Indizes, wo jeder Datensatz befindet, und wird später zu lesen, wenn Sie es verwenden.

  

Sie zahlen nur für das, was Sie verwenden.

Siehe Einführung in vroom , Lassen Sie sich mit vroom und die vroom Benchmarks .

Die grundlegende Übersicht ist, dass die anfängliche Lesen einer großen Datei, wird viel schneller sein, und nachfolgende Änderungen an den Daten können geringfügig langsamer sein. Also je nachdem, was Ihre Anwendung ist, könnte es die beste Option sein.

Sehen Sie ein vereinfachtes Beispiel von vroom Benchmarks unten, die wichtigsten Teile sind die super schnelle Lesezeiten zu sehen, aber leicht Sämann Operationen wie Summe etc ..

package                 read    print   sample   filter  aggregate   total
read.delim              1m      21.5s   1ms      315ms   764ms       1m 22.6s
readr                   33.1s   90ms    2ms      202ms   825ms       34.2s
data.table              15.7s   13ms    1ms      129ms   394ms       16.3s
vroom (altrep) dplyr    1.7s    89ms    1.7s     1.3s    1.9s        6.7s

Oft Ich denke, es ist nur gute Praxis zu halten größere Datenbanken in einer Datenbank (z Postgres). Ich benutze nicht etwas zu viel größer als (nrow * ncol) ncell = 10 M, die ziemlich klein ist; aber ich finde oft möchte ich R erstellen und zu halten, speicherintensive Diagramme nur während ich Abfrage aus mehreren Datenbanken. In Zukunft von 32 GB Laptops, verschwinden einige dieser Arten von Gedächtnisstörungen. Aber der Reiz, eine Datenbank für die Verwendung der Daten zu halten und dann Speicher mit R für die resultierenden Abfrageergebnisse und Grafiken kann noch nützlich sein. Einige Vorteile sind:

(1) Die Daten bleiben in der Datenbank geladen. Sie verbinden einfach in pgAdmin auf die Datenbanken, die Sie wünschen, wenn Sie Ihren Laptop zurück einschalten.

(2) Es ist wahr, R viele raffinierte statistische und Grafikoperationen als SQL tun können. Aber ich denke, SQL besser zu Abfrage von großen Datenmengen als R ausgelegt ist.

# 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)

Anstelle des herkömmlichen read.table I Feel fread ist eine schnellere Funktion. Angeben zusätzliche Attribute wie nur die gewünschten Spalten auswählen, unter Angabe colclasses und String als Faktoren, die die Zeit in Anspruch nehmen zu importieren, um die Datei zu reduzieren.

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"))
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top