database - software - Almacenamiento de objetos R en una base de datos relacional
r software (4)
Use la función de serialización para convertir cualquier objeto R en una cadena (en bruto o de caracteres), luego almacene esa cadena. Ver help(serialize)
.
Invierta esto para recuperarlo: obtenga la secuencia y luego unserialize()
en un objeto R.
Frecuentemente creo estadísticas no paramétricas (loess, densidades de kernel, etc.) en datos que saco de una base de datos relacional. Para facilitar la administración de datos, me gustaría almacenar la salida R dentro de mi base de datos. Esto es fácil con marcos de datos simples de números o texto, pero no he descubierto cómo almacenar objetos R en mi base de datos relacional. Entonces, ¿hay alguna manera de almacenar un vector de densidades de kernel, por ejemplo, de nuevo en una base de datos relacional?
En este momento, soluciono esto guardando los objetos R en un espacio de disco de red para que otros puedan cargar los objetos según sea necesario.
Un ejemplo de variable R, eso es bastante complejo:
library(nlme)
model <- lme(uptake ~ conc + Treatment, CO2, random = ~ 1 | Plant / Type)
El mejor método de base de datos de almacenamiento para las variables R depende de cómo quiera usarlo.
Necesito hacer análisis en la base de datos sobre los valores
En este caso, debe dividir el objeto en valores que la base de datos pueda manejar de forma nativa. Esto generalmente significa convertirlo en uno o más marcos de datos. La forma más fácil de hacerlo es usar el paquete de broom
.
library(broom)
coefficients_etc <- tidy(model)
model_level_stats <- glance(model)
row_level_stats <- augment(model)
Solo quiero almacenamiento
En este caso, quiere serializar sus variables R. Es decir, convertirlos en una cadena o un blob binario. Hay varios métodos para esto.
Mis datos deben ser accesibles por programas que no sean R, y deben ser legibles por el ser humano
Debe almacenar sus datos en un formato de texto multiplataforma; probablemente JSON o YAML. JSON no admite algunos conceptos importantes como Inf
; YAML es más general, pero el soporte en R no es tan maduro. XML también es posible, pero es demasiado detallado para ser útil para almacenar matrices grandes.
library(RJSONIO)
model_as_json <- toJSON(model)
nchar(model_as_json) # 17916
library(yaml)
# yaml package doesn''t yet support conversion of language objects,
# so preprocessing is needed
model2 <- within(
model,
{
call <- as.character(call)
terms <- as.character(terms)
}
)
model_as_yaml <- as.yaml(model2)
nchar(model_as_yaml) # 14493
Mis datos tienen que ser accesibles por programas que no sean R, y no necesitan ser legibles por humanos
Puede escribir sus datos en un formato binario abierto y multiplataforma como HFD5. Actualmente, la compatibilidad con los archivos HFD5 (a través de rhdf5
) es limitada, por lo que los objetos complejos no son compatibles. (Probablemente necesites unclass
todo).
library(rhdf5)
h5save(rapply(model2, unclass, how = "replace"), file = "model.h5")
bin_h5 <- readBin("model.h5", "raw", 1e6)
length(bin_h5) # 88291 not very efficient in this case
El paquete de feather
permite guardar marcos de datos en un formato legible por R y Python. Para usar esto, primero debe convertir el objeto modelo en marcos de datos, como se describe en la sección de escoba al principio de la respuesta.
library(feather)
library(broom)
write_feather(augment(model), "co2_row.feather") # 5474 bytes
write_feather(tidy(model), "co2_coeff.feather") # 2093 bytes
write_feather(glance(model), "co2_model.feather") # 562 bytes
Otra alternativa es guardar una versión de texto de la variable (ver sección anterior) en un archivo comprimido y almacenar sus bytes en la base de datos.
writeLines(model_as_json)
tar("model.tar.bz", "model.txt", compression = "bzip2")
bin_bzip <- readBin("model.tar.bz", "raw", 1e6)
length(bin_bzip) # only 42 bytes!
Solo R necesita acceder a mis datos y debe ser legible por el ser humano
Hay dos opciones para convertir una variable en una cadena: serialize
y deparse
.
p <- function(x)
{
paste0(x, collapse = "/n")
}
serialize
necesita ser enviado a una conexión de texto, y en lugar de escribir en un archivo, puede escribir en la consola y capturarlo.
model_serialized <- p(capture.output(serialize(model, stdout())))
nchar(model_serialized) # 23830
Use deparse
con control = "all"
para maximizar la reversibilidad al volver a analizar más tarde.
model_deparsed <- p(deparse(model, control = "all"))
nchar(model_deparsed) # 22036
Solo R necesita acceder a mis datos y no es necesario que sean legibles por el ser humano
El mismo tipo de técnicas que se muestran en las secciones anteriores se puede aplicar aquí. Puede comprimir una variable serializada o deparsed y volver a leerla como un vector sin formato.
serialize
también puede escribir variables en un formato binario. En este caso, es más fácil de usar con su envoltorio saveRDS
.
saveRDS(model, "model.rds")
bin_rds <- readBin("model.rds", "raw", 1e6)
length(bin_rds) # 6350
Usar textConnection / saveRDS / loadRDS es quizás el nivel más versátil y alto:
zz<-textConnection(''tempConnection'', ''wb'')
saveRDS(myData, zz, ascii = T)
TEXT<-paste(textConnectionValue(zz), collapse=''/n'')
#write TEXT into SQL
...
closeAllConnections() #if the connection persists, new data will be appended
#reading back:
#1. pull from SQL into queryResult
...
#2. recover the object
recoveredData <- readRDS(textConnection(queryResult$TEXT))
Para sqlite
(y posiblemente otros):
CREATE TABLE data (blob BLOB);
Ahora en R
:
RSQLite::dbGetQuery(db.conn, ''INSERT INTO data VALUES (:blob)'', params = list(blob = list(serialize(some_object)))
Tenga en cuenta el contenedor de list
alrededor de some_object
. El resultado de serialize
es un vector sin formato. Sin list
, la instrucción INSERT se ejecutará para cada elemento vectorial. Envolverlo en una lista permite que RSQLite::dbGetQuery
vea como un elemento.
Para recuperar el objeto de la base de datos:
some_object <- unserialize(RSQLite::dbGetQuery(db.conn, ''SELECT blob FROM data LIMIT 1'')$blob[[1]])
Lo que sucede aquí es que tomes el blob
campo (que es una lista ya que RSQLite no sabe cuántas filas serán devueltas por la consulta). Como LIMIT 1
asegura que solo se devuelve 1 fila, la tomamos con [[1]]
, que es el vector bruto original. Luego necesita unserialize
el vector sin procesar para obtener su objeto.