2015-05-25 15 views
33

Ho letto e uso con successo ?connections in R ma non capisco davvero cosa siano.Che cos'è esattamente una connessione in R?

Ho capito che posso scaricare un file, leggere e scrivere un file compresso, ... (cioè ho capito qual è il risultato dell'utilizzo di una connessione (aprire, fare cose, chiudere) ma davvero non capire cosa fanno effettivamente, perché devi aprirli e chiuderli e così via).

Spero che questo mi aiuti anche a capire come utilizzarli in modo più efficace (capisco principalmente la meccanica di ciò che sta accadendo in modo da poter eseguire il debug in modo efficace quando qualcosa non funziona).

risposta

57

Le connessioni sono state introdotte in R 1.2.0 e descritte da Brian Ripley nel primo numero di R NEWS (ora denominato The R Journal) di January 2001 (pagina 16-17) come interfaccia astratta di flussi IO come un file , url, socket o pipe. Nel 2013, Simon Urbanek ha aggiunto un'API C Connections.h che consente ai pacchetti R di implementare tipi di connessione personalizzati, come il pacchetto curl.

Una caratteristica di connessioni è che si può leggere o scrivere in modo incrementale pezzi di dati da/per la connessione utilizzando le funzioni readBin, writeBin, readLines e writeLines. Ciò consente l'elaborazione di dati asincrono, ad esempio quando si tratta di dati di grandi dimensioni o collegamenti di rete:

# Read the first 30 lines, 10 lines at a time 
con <- url("http://jeroen.github.io/data/diamonds.json") 
open(con, "r") 
data1 <- readLines(con, n = 10) 
data2 <- readLines(con, n = 10) 
data3 <- readLines(con, n = 10) 
close(con) 

stessa per la scrittura, ad esempio in un file:

tmp <- file(tempfile()) 
open(tmp, "w") 
writeLines("A line", tmp) 
writeLines("Another line", tmp) 
close(tmp) 

Aprire la connessione come rb o wb per leggere/scrivere dati binari (chiamati vettori prime in R):

# Read the first 3000 bytes, 1000 bytes at a time 
con <- url("http://jeroen.github.io/data/diamonds.json") 
open(con, "rb") 
data1 <- readBin(con, raw(), n = 1000) 
data2 <- readBin(con, raw(), n = 1000) 
data3 <- readBin(con, raw(), n = 1000) 
close(con) 

Il collegamento pipe() viene utilizzato per eseguire un comando di sistema e pipe text to stdin o da stdout come si farebbe con l'operatore | in una shell. Per esempio. (Lascia il bastone con gli esempi ricciolo), è possibile eseguire il programma di linea di curl di comando e inviare l'output a R:

con <- pipe("curl -H 'Accept: application/json' https://jeroen.github.io/data/diamonds.json") 
open(con, "r") 
data1 <- readLines(con, n = 10) 
data2 <- readLines(con, n = 10) 
data3 <- readLines(con, n = 10) 

Alcuni aspetti di connessioni sono un po 'di confusione: per leggere in modo incrementale i dati/scrittura è necessario in modo esplicito open() e close() la connessione. Tuttavia, readLines e writeLines aprono e chiudono automaticamente (ma non distruggono!) Una connessione non aperta. Come risultato, l'esempio seguente leggerà le prime 10 righe più e più volte, che non è molto utile:

con <- url("http://jeroen.github.io/data/diamonds.json") 
data1 <- readLines(con, n = 10) 
data2 <- readLines(con, n = 10) 
data3 <- readLines(con, n = 10) 
identical(data1, data2) 

Un altro punto è che l'API C può sia vicino e distruggere una connessione, ma R espone solo una funzione denominata close()che in realtà significa distruggere. Dopo aver chiamato close() su una connessione è distrutto e completamente inutile.

per lo streaming-elaborare i dati formano una connessione che si desidera utilizzare un modello come questo:

stream <- function(){ 
    con <- url("http://jeroen.github.io/data/diamonds.json") 
    open(con, "r") 
    on.exit(close(con)) 
    while(length(txt <- readLines(con, n = 10))){ 
    some_callback(txt) 
    } 
} 

Il pacchetto jsonlite si basa molto sui collegamenti per importare/esportare i dati ndjson:

library(jsonlite) 
library(curl) 
diamonds <- stream_in(curl("https://jeroen.github.io/data/diamonds.json")) 

Il lo streaming (di default 1000 righe alla volta) lo rende veloce ed efficiente nella memoria:

library(nycflights13) 
stream_out(flights, file(tmp <- tempfile())) 
flights2 <- stream_in(file(tmp)) 
all.equal(flights2, as.data.frame(flights)) 
.515.053.691,36321 milioni

Infine una caratteristica piacevole sulle connessioni è che il garbage collector si chiuderà automaticamente loro se si dimentica di farlo, con un avviso fastidioso:

con <- file(system.file("DESCRIPTION"), open = "r") 
rm(con) 
gc() 
+1

risposta Epic. Anche marginalmente affronta la questione dal PO. –