R almacenamiento de objetos en una base de datos relacional
Pregunta
Con frecuencia se crea la estadística no paramétrica (loess, la densidad del núcleo, etc.) en los datos que retirarse de una base de datos relacional. Para hacer más fácil la gestión de datos me gustaría volver a almacenar salida R dentro de mi DB. Esto es fácil con tramas de datos simples de números o texto, pero no he encontrado la manera de almacenar objetos R vuelta en mi base de datos relacional. Entonces, ¿hay una manera de almacenar un vector de densidades del núcleo, por ejemplo, de nuevo en una base de datos relacional?
Ahora mismo trabajo en torno a este por el ahorro de los objetos R a un espacio de unidad de red para que otros usuarios pueden cargar los objetos según sea necesario.
Solución
Utilice la función de serialización para convertir cualquier objeto en una cadena R (cruda o carácter), a continuación, almacenar esa cadena. Ver help(serialize)
.
invertir esta para la recuperación:. Obtener la cadena, entonces unserialize()
en un objeto de R
Otros consejos
Una variable ejemplo R, que es bastante complejo:
library(nlme)
model <- lme(uptake ~ conc + Treatment, CO2, random = ~ 1 | Plant / Type)
El método mejor base de datos de almacenamiento para variables R depende de cómo desea utilizarlo.
Necesito hacer análisis dentro de la base de datos sobre los valores
En este caso, es necesario romper el objeto hacia abajo en valores que la base de datos puede manejar de forma nativa. Esto generalmente significa convertirlo en una o más tramas de datos. La forma más sencilla de hacerlo es utilizar el paquete broom
.
library(broom)
coefficients_etc <- tidy(model)
model_level_stats <- glance(model)
row_level_stats <- augment(model)
Sólo quiero almacenamiento
En este caso, desea realizar una serie sus variables R. Es decir, la conversión a ser una cadena o un blob binario. Hay varios métodos para ello.
Mis datos tiene que ser accesible por otros programas de R, y debe ser legible por humanos
Se debe almacenar los datos en un formato de texto multiplataforma; Probablemente JSON o YAML. JSON no es compatible con algunos conceptos importantes como Inf
; YAML es más general pero el apoyo en R no está tan madura. XML también es posible, pero es demasiado detallado para ser útil para el almacenamiento de grandes matrices.
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 tiene que ser accesible por otros programas de R, y no necesita ser legible por humanos
Se puede escribir los datos a un formato binario multiplataforma abierto como HFD5. Actualmente soporte para archivos HFD5 (a través de rhdf5
) está limitado, por lo objetos complejos no son compatibles. (Es probable que tengas que 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 let feather
que ahorremos tramas de datos en un formato legible por ambos R y Python. Para utilizarlo, primero tendría que convertir el objeto de modelo en tramas de datos, como se describe en la sección anterior escoba en 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 apartado anterior) a 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!
Mis datos sólo necesita ser accesible por R, y debe ser legible por humanos
Hay dos opciones para convertir una variable en una cadena: serialize
y deparse
.
p <- function(x)
{
paste0(x, collapse = "\n")
}
serialize
necesidades que se enviará a una conexión de texto, y en lugar de escribir en el archivo, se puede escribir en la consola y capturarlo.
model_serialized <- p(capture.output(serialize(model, stdout())))
nchar(model_serialized) # 23830
Uso deparse
con control = "all"
para maximizar la reversibilidad cuando se re-analizar más adelante.
model_deparsed <- p(deparse(model, control = "all"))
nchar(model_deparsed) # 22036
Mis datos sólo necesita ser accesible por R, y no necesita ser legible por humanos
Los mismos tipos de técnicas que se muestran en las secciones anteriores se puede aplicar aquí. Puede comprimir un serializado o variable y deparsed releerlo como un vector prima.
serialize
También puede escribir variables en un formato binario. En este caso, se utiliza más fácilmente con su saveRDS
envoltura.
saveRDS(model, "model.rds")
bin_rds <- readBin("model.rds", "raw", 1e6)
length(bin_rds) # 6350
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 la envoltura alrededor list
some_object
. La salida del serialize
es un vector en bruto. Sin list
, la instrucción INSERT se ejecuta para cada elemento del vector. Envolviéndolo en una lista permite RSQLite::dbGetQuery
a verlo como un elemento.
Para obtener la parte posterior de la base de datos de objetos:
some_object <- unserialize(RSQLite::dbGetQuery(db.conn, 'SELECT blob FROM data LIMIT 1')$blob[[1]])
Lo que sucede es que toma el blob
campo (que es una lista desde RSQLite no sabe cuántas filas será devuelto por la consulta). Dado que se devuelve asegura LIMIT 1
única fila 1, lo tomamos con [[1]]
, que es el vector prima original. Luego hay que unserialize
el vector prima para conseguir su objeto.
El uso de textConnection / saveRDS / loadRDS es tal vez el nivel más versátil y de alta:
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))