2009-09-08 15 views
7

Często tworzę statystyki nieparametryczne (lessy, gęstości jądra itp.) Na danych, które wyciągam z relacyjnej bazy danych. Aby ułatwić zarządzanie danymi, chciałbym zapisać wyjście R z powrotem w moim DB. Jest to łatwe z prostymi ramkami danych liczb lub tekstu, ale nie mam pojęcia, jak przechowywać obiekty R z powrotem w mojej relacyjnej bazie danych. Czy istnieje sposób przechowywania wektora gęstości jądra, na przykład z powrotem do relacyjnej bazy danych?Przechowywanie obiektów R w relacyjnej bazie danych

W tej chwili pracuję nad tym, zapisując obiekty R na dysku sieciowym, aby inni mogli załadować obiekty w razie potrzeby.

Odpowiedz

9

Użyj funkcji serializacji, aby przekształcić dowolny obiekt R na ciąg (ciąg lub znak), a następnie zapisz ten ciąg. Zobacz help(serialize).

Odwróć to w celu pobrania: pobierz ciąg znaków, a następnie unserialize() do obiektu R.

9

Zmienna R przykład, to dość skomplikowane:

library(nlme) 
model <- lme(uptake ~ conc + Treatment, CO2, random = ~ 1 | Plant/Type) 

Najlepszą metodą bazy danych do przechowywania zmiennych R zależy jak chcesz go użyć.

muszę zrobić w-bazy analityczne na wartościach

W tym przypadku, trzeba rozbić obiekt w dół do wartości, że baza danych może obsługiwać natywnie. Zwykle oznacza to przekształcenie go w jedną lub więcej ramek danych. Najłatwiej to zrobić, korzystając z pakietu broom.

library(broom) 
coefficients_etc <- tidy(model) 
model_level_stats <- glance(model) 
row_level_stats <- augment(model) 

Chcę tylko przechowywanie

W tym przypadku chcesz serializacji zmienne R. Oznacza to, że przekształcają je w ciąg znaków lub binarne bloki. Istnieje kilka metod na to.


Moje dane muszą być dostępne dla programów innych niż R, i musi być czytelny dla człowieka

Należy przechowywać swoje dane w formacie tekstowym cross-platform; prawdopodobnie JSON lub YAML. JSON nie obsługuje niektórych ważnych pojęć, takich jak Inf; YAML jest bardziej ogólny, ale wsparcie w R nie jest tak dojrzałe. XML jest również możliwy, ale jest zbyt szczegółowy, aby mógł być użyteczny do przechowywania dużych tablic.

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 

Moje dane muszą być dostępne dla programów innych niż R, i nie musi być czytelny dla człowieka

Można napisać swoje dane do otwartego, cross-platform format binarny, taki jak HFD5. Obecnie obsługa plików HFD5 (przez rhdf5) jest ograniczona, więc złożone obiekty nie są obsługiwane. (Prawdopodobnie będziesz musiał unclass wszystkiego.)

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 

feather pakiet pozwala ci zaoszczędzić ramki danych w formacie czytelnym zarówno R i Python.Aby tego użyć, musisz najpierw przekonwertować obiekt modelu na ramki danych, jak opisano w sekcji miotły wcześniej w odpowiedzi.

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 

Inną alternatywą jest, aby zapisać wersję tekstową zmiennej (patrz poprzedni rozdział) do skompresowanego pliku i przechowywać swoje bajtów w bazie danych.

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! 

moje dane tylko musi być dostępny przez R, i musi być czytelny dla człowieka

Istnieją dwie opcje do toczenia zmienną do łańcucha: serialize i deparse.

p <- function(x) 
{ 
    paste0(x, collapse = "\n") 
} 

serialize musi zostać wysłany do połączenia tekstu i zamiast pisać do pliku, możesz napisać do konsoli i uchwycić go.

model_serialized <- p(capture.output(serialize(model, stdout()))) 
nchar(model_serialized) # 23830 

Zastosowanie deparse z control = "all" maksymalizacja odwracalność podczas ponownego analizowania później.

model_deparsed <- p(deparse(model, control = "all")) 
nchar(model_deparsed) # 22036 

moich danych tylko musi być dostępny przez R, i nie musi być czytelny dla człowieka

Te same rodzaje technik przedstawionych w poprzednich sekcjach można zastosować tutaj . Możesz spakować zmienną serializowaną lub zmienioną i ponownie odczytać ją jako nieprzetworzony wektor.

serialize może również pisać zmienne w formacie binarnym. W takim przypadku najłatwiej jest go użyć z opakowaniem saveRDS.

saveRDS(model, "model.rds") 
bin_rds <- readBin("model.rds", "raw", 1e6) 
length(bin_rds) # 6350 
+0

Ten ostatni wydaje się być niewydajny. 'saveRDS' zapisuje obiekt do pliku, a następnie' readBin' odczytuje go do pamięci. O ile mi wiadomo, 'serialize' zapisuje bezpośrednio do pamięci za pomocą' connection = NULL'. –

2

Korzystanie textConnection/saveRDS/loadRDS jest chyba najbardziej wszechstronny i wysoki poziom:

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

Dla sqlite (i ewentualnie innych):

CREATE TABLE data (blob BLOB); 

Teraz w R:

RSQLite::dbGetQuery(db.conn, 'INSERT INTO data VALUES (:blob)', params = list(blob = list(serialize(some_object))) 

Uwaga na opakowanie list około some_object. Wyjście z serialize jest wektorem surowym. Bez list instrukcja INSERT zostanie wykonana dla każdego elementu wektora. Zawijanie go na liście pozwala mu zobaczyć RSQLite::dbGetQuery jako jeden element.

Aby uzyskać obiekt z powrotem z bazy danych:

some_object <- unserialize(RSQLite::dbGetQuery(db.conn, 'SELECT blob FROM data LIMIT 1')$blob[[1]]) 

Co się dzieje tutaj jest wziąć pole blob (co jest lista od RSQLite nie wie, ile wierszy zostaną zwrócone przez zapytanie) . Ponieważ LIMIT 1 zapewnia, że ​​zwracany jest tylko jeden wiersz, przyjmujemy go z [[1]], który jest oryginalnym surowym wektorem. Następnie musisz unserialize surowego wektora, aby uzyskać swój obiekt.

Powiązane problemy