【问题标题】:Storing R Objects in a relational database在关系数据库中存储 R 对象
【发布时间】:2010-11-26 13:51:34
【问题描述】:

我经常对从关系数据库中提取的数据创建非参数统计信息(黄土、核密度等)。为了使数据管理更容易,我想将 R 输出存储回我的数据库中。使用简单的数字或文本数据框很容易做到这一点,但我还没有弄清楚如何将 R 对象存储回我的关系数据库中。那么有没有办法将内核密度向量存储回关系数据库?

现在我通过将 R 对象保存到网络驱动器空间来解决此问题,以便其他人可以根据需要加载对象。

【问题讨论】:

    标签: database r object r-faq


    【解决方案1】:

    使用序列化功能将任何 R 对象转换为(原始或字符)字符串,然后存储该字符串。见help(serialize)

    反向检索:获取字符串,然后将unserialize() 转换为 R 对象。

    【讨论】:

      【解决方案2】:

      一个相当复杂的示例 R 变量:

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

      R 变量的最佳存储数据库方法取决于您希望如何使用它。

      我需要对值进行数据库内分析

      在这种情况下,您需要将对象分解为数据库可以本地处理的值。这通常意味着将其转换为一个或多个数据帧。最简单的方法是使用broom 包。

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

      我只想要存储空间

      在这种情况下,您想要序列化您的 R 变量。也就是说,将它们转换为字符串或二进制 blob。有几种方法可以做到这一点。


      我的数据必须能够被 R 以外的程序访问,并且必须是人类可读的

      您应该以跨平台的文本格式存储您的数据;可能是 JSON 或 YAML。 JSON 不支持Inf 等一些重要概念; YAML 更通用,但 R 中的支持并不成熟。 XML 也是可能的,但是对于存储大型数组来说太冗长了。

      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
      

      我的数据必须可以被 R 以外的程序访问,并且不需要是人类可读的

      您可以将数据写入开放的跨平台二进制格式,例如 HFD5。目前对 HFD5 文件(通过rhdf5)的支持有限,因此不支持复杂对象。 (您可能需要unclass 一切。)

      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 包可让您以 R 和 Python 均可读取的格式保存数据帧。要使用它,您首先必须将模型对象转换为数据框,如答案前面的扫帚部分所述。

      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
      

      另一种选择是将变量的文本版本(参见上一节)保存到压缩文件中,并将其字节存储在数据库中。

      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!
      

      我的数据只需要 R 可以访问,并且需要是人类可读的

      将变量转换为字符串有两种选择:serializedeparse

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

      serialize 需要发送到文本连接,而不是写入文件,您可以写入控制台并捕获它。

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

      deparsecontrol = "all" 结合使用,以便在以后重新解析时最大化可逆性。

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

      我的数据只需要 R 可以访问,不需要是人类可读的

      前面部分中展示的相同类型的技术可以在这里应用。您可以压缩一个序列化或解析的变量,然后将其作为原始向量重新读取。

      serialize 也可以将变量写入二进制格式。在这种情况下,它最容易使用它的包装器saveRDS

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

      【讨论】:

      • 最后一个似乎非常效率低下。 saveRDS 将对象写入文件,然后readBin 将其读取到内存。据我所知,serialize 使用connection = NULL 直接写入内存。
      【解决方案3】:

      使用 textConnection / saveRDS / loadRDS 可能是最通用和最高级的:

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

      【讨论】:

        【解决方案4】:

        对于sqlite(可能还有其他人):

        CREATE TABLE data (blob BLOB);
        

        现在在R:

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

        注意some_object 周围的list 包装器。 serialize 的输出是一个原始向量。如果没有list,将对每个向量元素执行 INSERT 语句。将其包装在列表中允许RSQLite::dbGetQuery 将其视为一个元素。

        从数据库中取回对象:

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

        这里发生的情况是您获取字段blob(这是一个列表,因为 RSQLite 不知道查询将返回多少行)。由于 LIMIT 1 保证只返回 1 行,我们将其与 [[1]] 一起使用,这是原始的原始向量。然后你需要unserialize原始向量来获取你的对象。

        【讨论】:

          【解决方案5】:

          [100% 正常工作 - 2020 年 2 月 27 日]

          说明: 如果要将模型存储到 POSTGRES 表中,请执行以下步骤,然后查询并加载它。一个重要的部分是ascii = TRUE,否则在序列化时会产生错误

          db <- pgsql_connect #connection to your database
          
          serialized_model <- rawToChar(serialize(model_fit, NULL, ascii=TRUE))
          
          insert_query <-'INSERT INTO table (model) VALUES ($1)'
          rs <- dbSendQuery(db, insert_query, list(serialized_model))
          dbClearResult(rs)
          
          serialized_model <- dbGetQuery(db, "select model from table order by created_at desc limit 1")
          
          model_fit2 <- unserialize(charToRaw(as.character(serialized_model[,c('model')])))
          model_fit2
          

          【讨论】:

          • 你能澄清一下 - “模型”列文本吗?也许在其中放一个CREATE TABLE 以准确显示table 的样子?
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2022-11-17
          • 2014-08-18
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-01-10
          相关资源
          最近更新 更多