直接使用 dplyr 改变数据库表中的变量

2023-11-23

这是 MonetDBLite 数据库文件中的 mtcars 数据。

library(MonetDBLite)
library(tidyverse)
library(DBI)

dbdir <- getwd()
con <- dbConnect(MonetDBLite::MonetDBLite(), dbdir)

dbWriteTable(conn = con, name = "mtcars_1", value = mtcars)

data_mt <- con %>% tbl("mtcars_1")

我想使用 dplyr mutate 创建新变量并将其添加(提交!)到数据库表中?就像是

data_mt %>% select(mpg, cyl) %>% mutate(var = mpg/cyl) %>% dbCommit(con)

当我们这样做时,期望的输出应该是相同的:

dbSendQuery(con, "ALTER TABLE mtcars_1 ADD COLUMN var DOUBLE PRECISION")
dbSendQuery(con, "UPDATE mtcars_1 SET var=mpg/cyl") 

怎样才能做到这一点呢?


这里有几个函数,create and update.tbl_lazy.

他们分别实现CREATE TABLE,这很简单,并且ALTER TABLE/UPDATE一对则不然:

CREATE

create <- function(data,name){
  DBI::dbSendQuery(data$src$con,
                   paste("CREATE TABLE", name,"AS", dbplyr::sql_render(data)))
  dplyr::tbl(data$src$con,name)
}

example:

library(dbplyr)
library(DBI)
con <- DBI::dbConnect(RSQLite::SQLite(), path = ":memory:")
copy_to(con, head(iris,3),"iris")

tbl(con,"iris") %>% mutate(Sepal.Area= Sepal.Length * Sepal.Width) %>% create("iris_2")

# # Source:   table<iris_2> [?? x 6]
# # Database: sqlite 3.22.0 []
#   Sepal.Length Sepal.Width Petal.Length Petal.Width Species Sepal.Area
#          <dbl>       <dbl>        <dbl>       <dbl> <chr>        <dbl>
# 1          5.1         3.5          1.4         0.2 setosa        17.8
# 2          4.9         3            1.4         0.2 setosa        14.7
# 3          4.7         3.2          1.3         0.2 setosa        15.0

UPDATE

update.tbl_lazy <- function(.data,...,new_type="DOUBLE PRECISION"){
  quos <- rlang::quos(...)
  dots <- rlang::exprs_auto_name(quos, printer = tidy_text)

  # extract key parameters from query
  sql <- dbplyr::sql_render(.data)
  con  <- .data$src$con
  table_name <-gsub(".*?(FROM (`|\")(.+?)(`|\")).*","\\3",sql)
  if(grepl("\nWHERE ",sql)) where <-  regmatches(sql, regexpr("WHERE .*",sql))
  else where <- ""
  new_cols <- setdiff(names(dots),colnames(.data))

  # Add empty columns to base table
  if(length(new_cols)){
    alter_queries <- paste("ALTER TABLE",table_name,"ADD COLUMN",new_cols,new_type)
    purrr::walk(alter_queries, ~{
      rs <- DBI::dbSendStatement(con, .)
      DBI::dbClearResult(rs)})}

  # translate unevaluated dot arguments to SQL instructions as character
  translations  <- purrr::map_chr(dots, ~ translate_sql(!!! .))
  # messy hack to make translations work
  translations <- gsub("OVER \\(\\)","",translations) 

  # 2 possibilities: called group_by or (called filter or called nothing)
  if(identical(.data$ops$name,"group_by")){
    # ERROR if `filter` and `group_by` both used
    if(where != "") stop("Using both `filter` and `group by` is not supported")

    # Build aggregated table
    gb_cols   <- paste0('"',.data$ops$dots,'"',collapse=", ")
    gb_query0 <- paste(translations,"AS", names(dots),collapse=", ")
    gb_query  <- paste("CREATE TABLE TEMP_GB_TABLE AS SELECT",
                       gb_cols,", ",gb_query0,
                       "FROM", table_name,"GROUP BY", gb_cols)
    rs <- DBI::dbSendStatement(con, gb_query)
    DBI::dbClearResult(rs)

    # Delete temp table on exit
    on.exit({
      rs <- DBI::dbSendStatement(con,"DROP TABLE TEMP_GB_TABLE")
      DBI::dbClearResult(rs)
    })

    # Build update query
    gb_on <- paste0(table_name,'."',.data$ops$dots,'" = TEMP_GB_TABLE."', .data$ops$dots,'"',collapse=" AND ")
    update_query0 <- paste0(names(dots)," = (SELECT ", names(dots), " FROM TEMP_GB_TABLE WHERE ",gb_on,")",
                            collapse=", ")
    update_query <- paste("UPDATE", table_name, "SET", update_query0)
    rs <- DBI::dbSendStatement(con, update_query)
    DBI::dbClearResult(rs)

  } else {

    # Build update query in case of no group_by and optional where
    update_query0 <- paste(names(dots),'=',translations,collapse=", ")
    update_query  <- paste("UPDATE", table_name,"SET", update_query0,where)
    rs <- DBI::dbSendStatement(con, update_query)
    DBI::dbClearResult(rs)
  }
  tbl(con,table_name)
}

示例1,定义 2 个新的数字列:

tbl(con,"iris") %>% update(x=pmax(Sepal.Length,Sepal.Width),
                           y=pmin(Sepal.Length,Sepal.Width))

# # Source:   table<iris> [?? x 7]
# # Database: sqlite 3.22.0 []
#   Sepal.Length Sepal.Width Petal.Length Petal.Width Species     x     y
#          <dbl>       <dbl>        <dbl>       <dbl> <chr>   <dbl> <dbl>
# 1          5.1         3.5          1.4         0.2 setosa    5.1   3.5
# 2          4.9         3            1.4         0.2 setosa    4.9   3  
# 3          4.7         3.2          1.3         0.2 setosa    4.7   3.2

示例2,修改现有列,创建 2 个不同类型的新列:

tbl(con,"iris") %>%
  update(x= Sepal.Length*Sepal.Width,
         z= 2*y,
         a= Species %||% Species,               
         new_type = c("DOUBLE","VARCHAR(255)"))

# # Source:   table<iris> [?? x 9]
# # Database: sqlite 3.22.0 []
#   Sepal.Length Sepal.Width Petal.Length Petal.Width Species     x     y     z a           
#          <dbl>       <dbl>        <dbl>       <dbl> <chr>   <dbl> <dbl> <dbl> <chr>       
# 1          5.1         3.5          1.4         0.2 setosa   17.8   3.5   7   setosasetosa
# 2          4.9         3            1.4         0.2 setosa   14.7   3     6   setosasetosa
# 3          4.7         3.2          1.3         0.2 setosa   15.0   3.2   6.4 setosasetosa

实施例3,更新其中:

tbl(con,"iris") %>% filter(Sepal.Width > 3) %>% update(a="foo")

# # Source:   table<iris> [?? x 9]
# # Database: sqlite 3.22.0 []
#   Sepal.Length Sepal.Width Petal.Length Petal.Width Species     x     y     z a           
#          <dbl>       <dbl>        <dbl>       <dbl> <chr>   <dbl> <dbl> <dbl> <chr>       
# 1          5.1         3.5          1.4         0.2 setosa   17.8   3.5   7   foo         
# 2          4.9         3            1.4         0.2 setosa   14.7   3     6   setosasetosa
# 3          4.7         3.2          1.3         0.2 setosa   15.0   3.2   6.4 foo

实施例4: 按组更新

tbl(con,"iris") %>%
  group_by(Species, Petal.Width) %>%
  update(new_col1 = sum(Sepal.Width,na.rm=TRUE), # using a R function
         new_col2 = MAX(Sepal.Length))           # using native SQL

# # Source:   SQL [?? x 11]
# # Database: sqlite 3.22.0 []
#   Sepal.Length Sepal.Width Petal.Length Petal.Width Species        x     y     z a            new_col1 new_col2
#          <dbl>       <dbl>        <dbl>       <dbl> <chr>      <dbl> <dbl> <dbl> <chr>           <dbl>    <dbl>
# 1          5.1         3.5          1.4         0.2 setosa         1     2   7   foo               6.5      5.1
# 2          4.9         3            1.4         0.2 setosa         1     2   6   setosasetosa      6.5      5.1
# 3          7           3.2          4.7         1.4 versicolor     1     2   6.4 foo               3.2      7 

一般注意事项

  • 代码使用用途dbplyr::translate_sql所以我们可以像以前一样使用 R 函数或本机函数mutate calls.

  • update只能在1次之后使用filter致电或一group_by调用或每个零,其他任何东西,你都会得到错误或意外的结果。

  • The group_by实现非常hacky,因此没有空间动态定义列或按操作分组,请坚持基础知识。

  • update and create都返回tbl(con, table_name),这意味着您可以链接尽可能多的create or update根据您的意愿拨打电话,并支付适当的金额group_by and filter之间。事实上,我的 4 个例子都可以链接起来。

  • 为了钉钉子,create不受同样的限制,您可以拥有尽可能多的dbplyr在调用之前按需要有趣。

  • 我没有实现类型检测,所以我需要new_type参数,它被回收在paste的呼唤alter_queries我的代码中的定义,因此它可以是单个值或向量。

解决后者的一种方法是从translations变量,找到它们的类型dbGetQuery(con,"PRAGMA table_info(iris)")。然后我们需要所有现有类型之间的强制规则,我们已经设置好了。但由于不同的 DBMS 有不同的类型,我想不出通用的方法来做到这一点,我也不知道MonetDBLite.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

直接使用 dplyr 改变数据库表中的变量 的相关文章

  • 如何避免循环

    大家好 我是 R 新手 我有两个面板数据文件 其中包含 id date 和 ret 列 文件 A 的数据比文件 B 多得多 但我主要处理文件 B 数据 id 和 date 的组合是唯一标识符 有没有一种优雅的方式来查找 B 中的每个 id
  • 无法使用include_graphics在Rmarkdown中插入png(错误:文件不是PNG格式)

    这个错误很奇怪 当我编织文档时 出现以下错误 Quitting from lines 42 43 sigminer doc Rmd Error in png readPNG path native TRUE info TRUE file i
  • R 中第三维的平均值

    R中是否有任何快速方法或内置函数来计算基于第三维的平均值 例如我的数组是 1 1 2 1 1 3 2 2 4 2 1 2 1 11 13 2 12 14 3 1 2 1 21 23 2 22 24 我的输出是 1 2 1 mean 1 11
  • R+Hadoop:如何从HDFS读取CSV文件并执行mapreduce?

    在以下示例中 small ints to dfs 1 1000 mapreduce input small ints map function k v cbind v v 2 MapReduce函数的数据输入是一个名为small ints的
  • 列表列中的设置操作

    我正在尝试做集合运算在存储在列表列中的向量之间 例如this https stackoverflow com questions 38712196 text file to dataframe with a list column DT l
  • 在r包中重新导出数据集

    In R包 有可能重新导出函数 这使得很容易回收相同的函数 而不必在不同的包之间重复代码 例如 devtools session info函数是重新导出sessioninfo session info export importFrom s
  • R:使用 RGDAL 和 RASTER 包时抛出错误

    给所有可能相关的人 这是源代码 GRA D1 lt raster files 1 Sets up an empty output raster GRA D1 lt writeStart GRA D1 filename GRA D1 tif
  • 加拿大人口普查地图分区 R

    我对 R 和映射非常陌生 我想创建某些数据的映射 我有一组名为 D Montreal 的数据 显示 2010 年前往蒙特利尔的加拿大人口普查部门游客来自哪个国家 我想使用此数据创建一个地图 以显示有多少人来自不同地区 也许可以通过对根据人数
  • 在闪亮的应用程序和多个页面中进行身份验证

    在我正在开发的系统中 我有 3 个不同的参与者 用户 管理员 支持团队 使用 Shiny App 我想知道如何向这三个参与者进行身份验证 每个参与者只能访问他们的页面 我发现使用闪亮的服务器专业版可以实现这一点 但它不是免费的 有什么方法可
  • 更改列的顺序

    我正在处理一个包含 gt 40 列的大型数据框 我希望能够移动列 而不必指定所有列名称 例如 a lt c 1 5 b lt c 4 3 2 1 1 Percent lt c 40 30 20 10 10 Labels lt c Cat D
  • 有条件地将可选组替换为 gsub

    一位用户问我如何做到这一点如何使 ggplot 图例中的选定单词变为斜体 https stackoverflow com questions 76054997 how to italicize select words in a ggplo
  • 使用 gbuffer 在 R 中缓冲(地理)空间点

    我正在尝试缓冲数据集中半径为 100 公里的点 我正在使用该功能gBuffer从包装中rgeos 这是我到目前为止所拥有的 head sampledf postalcode lat lon city province 1 A0A0A0 47
  • 使用facet时ggplot2控制每行的面板数量?

    Is it possible to control the number of panels per row in a ggplot I can only get an equal number of panels on each row
  • 为 RStudio Server 1.0.44 配置日志目录

    我在 CentOS 7 上运行 RStudio Server 1 0 44 根据文档 https support rstudio com hc en us articles 200554766 RStudio Server Applicat
  • 在 R 中使用 gamlss::lms 选择百分位数曲线

    我正在使用 gamlss 包中的示例代码来绘制百分位数曲线 library gamlss data abdom lms y x data abdom n cyc 30 它正在绘制自己的一组百分位数曲线 如何选择只绘制第 10 50 和 90
  • 在knitr中打印漂亮的交叉表

    我想要的是使用 R Markdown 和 knit 从 RStudio 打印漂亮的交叉表 无论是在 pdf 文件中 还是在 html 文件中 我怀疑我错过了一些非常明显的东西 因为我不敢相信这是如此困难 我使用 xtabs 或 ftable
  • left_join 表示列不存在,即使它存在

    我想用两个不同的变量 tp join 连接两个数据框 出现错误 表示无法在第二个数据帧中找到变量 但是当我运行函数 colnames 时 会显示列名称 为什么会这样呢 df new lt left join master settlemen
  • R:邻接表到邻接矩阵

    Bonjour 我想将邻接列表 3 列 转换为邻接矩阵 在这个论坛中 我找到了多个有关如何将边列表转换为邻接矩阵的示例 我成功地为两列列表做到了这一点 我已经尝试了在网上可以找到的所有解决方案 但似乎我错过了一小步 我尝试过的 我的变量是用
  • Rglpk - 梦幻足球阵容优化器 - For 循环输出的 Rbind

    我有一个使用 Rgplk 的梦幻足球阵容优化器 它使用for循环生成多个最佳阵容 其数量由用户输入 代码如下 Lineups lt list for i in 1 Lineup no matrix lt rbind as numeric D
  • 为 ggplot 定义新的尺度轴变换

    我正在尝试创建一个squared使用 y 轴变换scales trans new但遇到错误 MWE data data frame x 1 10 y runif 10 z rnorm 10 10 library ggplot2 ggplot

随机推荐