Ответ 1
Соединения были введены в R 1.2.0 и описаны Брайаном Рипли в первом выпуске R NEWS (теперь называемом журналом R) Январь 2001 (стр. 16-17) в качестве абстрактного интерфейса для потоков ввода-вывода, таких как файл, url, socket или pipe. В 2013 году Саймон Урбанек добавил Connections.h API C, который позволяет R-пакетам реализовать пользовательские типы соединений, такие как curl.
Одна из особенностей соединений заключается в том, что вы можете постепенно считывать или записывать фрагменты данных из/в соединение с помощью функций readBin
, writeBin
, readLines
и writeLines
. Это позволяет обрабатывать асинхронные данные, например, при работе с большими данными или сетевыми соединениями:
# 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)
То же самое для записи, например. в файл:
tmp <- file(tempfile())
open(tmp, "w")
writeLines("A line", tmp)
writeLines("Another line", tmp)
close(tmp)
Откройте соединение как rb
или wb
для чтения/записи двоичных данных (называемых необработанными векторами в 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)
Соединение pipe()
используется для запуска системной команды и текста канала в stdin
или из stdout
, как это было бы с оператором |
в оболочке. Например. (давайте придерживаться примеров скручивания), вы можете запустить программу командной строки curl
и передать вывод в 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)
Некоторые аспекты соединений немного запутывают: для постепенного чтения/записи данных необходимо явно указывать open()
и close()
соединение. Однако readLines
и writeLines
автоматически открывают и закрывают (но не уничтожают!) Неоткрытое соединение. В результате приведенный ниже пример будет читать первые 10 строк снова и снова, что не очень полезно:
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)
Еще одна проблема заключается в том, что API C может закрывать и уничтожать соединение, но R предоставляет только функцию close()
, которая на самом деле означает destroy. После вызова close()
в соединении он уничтожается и полностью бесполезен.
Для потоковой обработки данных формируется соединение, в котором вы хотите использовать шаблон следующим образом:
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)
}
}
Пакет jsonlite
в значительной степени зависит от соединений для импорта/экспорта ndjson:
library(jsonlite)
library(curl)
diamonds <- stream_in(curl("https://jeroen.github.io/data/diamonds.json"))
Потоковая передача (по умолчанию 1000 строк за раз) делает ее быстрой и эффективной для памяти:
library(nycflights13)
stream_out(flights, file(tmp <- tempfile()))
flights2 <- stream_in(file(tmp))
all.equal(flights2, as.data.frame(flights))
Наконец, одна приятная особенность соединений заключается в том, что сборщик мусора автоматически закроет их, если вы забудете это сделать, с раздражающим предупреждением:
con <- file(system.file("DESCRIPTION"), open = "r")
rm(con)
gc()