编写自定义 R 函数以在 dbplyr SQL 中使用
Writing a custom R function to use in dbplyr SQL
我对任何与 SQL 相关的事情都很陌生,但我觉得我想做的事情一定是可行的。我看过相关问题,但他们似乎并没有尝试做与我完全相同的事情。
我想编写一个在 dbplyr 管道中使用的函数,这样如果我以后再次需要它,我就不必重复代码。
我意识到并非 R 中的所有函数都能很好地转换为 SQL,但在这种情况下,case_when()
可以。当我使用裸代码时它工作正常,但我不知道如何将它包装在一个函数中。我编写的函数直接传递给 SQL,显然它不是 SQL.
中存在的函数
是否可以让 dbplyr 查看我函数中的代码,并将其转换为 SQL?
library(pacman)
p_load(dplyr, dbplyr, RSQLite)
# Set up fake database
con <- DBI::dbConnect(RSQLite::SQLite(), dbname = ":memory:")
test = tibble(a = c("a", "b", "c"))
test
#> # A tibble: 3 x 1
#> a
#> <chr>
#> 1 a
#> 2 b
#> 3 c
copy_to(con, test)
test_db <- tbl(con, "test")
在 test_db
上明确使用 case_when()
效果很好。
test_db %>%
mutate(b = case_when(
a == "a" ~ "AAA",
TRUE ~ a
))
#> # Source: lazy query [?? x 2]
#> # Database: sqlite 3.30.1 [:memory:]
#> a b
#> <chr> <chr>
#> 1 a AAA
#> 2 b b
#> 3 c c
然后我将我的 case_when()
放入一个函数中,这样我就可以重用它。当 运行 直接在 test
.
上时效果很好
func_aaa <- function(x) {
case_when(
x == "a" ~ "AAA",
TRUE ~ x
)
}
# The function works fine on the regular dataframe
test %>%
mutate(b = func_aaa(a))
#> # A tibble: 3 x 2
#> a b
#> <chr> <chr>
#> 1 a AAA
#> 2 b b
#> 3 c c
但它不适用于 test_db
。查看它生成的查询,很明显 dbplyr 只是将我的函数名直接传递给 SQL.
test_db %>%
mutate(b = func_aaa(a))
#> Error: no such function: func_aaa
test_db %>%
mutate(b = func_aaa(a)) %>%
show_query()
#> <SQL>
#> SELECT `a`, func_aaa(`a`) AS `b`
#> FROM `test`
由 reprex package (v0.3.0)
于 2020-05-13 创建
可能有更简单的方法,但一种选择是使用对 returns 整个数据帧进行操作的函数,而不是 运行 mutate 中的函数:
func_aaa2 = function(data, var, newvar, val, newval) {
data %>%
mutate({{newvar}} := case_when(
{{var}} == val ~ newval,
TRUE ~ {{var}}))
}
test_db %>%
func_aaa2(a, "b", "a", "aaa")
a b
<chr> <chr>
1 a aaa
2 b b
3 c c
test_db %>%
func_aaa2(a, "b", "a", "aaa") %>% show_query()
SELECT `a`, CASE
WHEN (`a` = 'a') THEN ('aaa')
WHEN (1) THEN (`a`)
END AS `b`
FROM `test`
我对任何与 SQL 相关的事情都很陌生,但我觉得我想做的事情一定是可行的。我看过相关问题,但他们似乎并没有尝试做与我完全相同的事情。
我想编写一个在 dbplyr 管道中使用的函数,这样如果我以后再次需要它,我就不必重复代码。
我意识到并非 R 中的所有函数都能很好地转换为 SQL,但在这种情况下,case_when()
可以。当我使用裸代码时它工作正常,但我不知道如何将它包装在一个函数中。我编写的函数直接传递给 SQL,显然它不是 SQL.
是否可以让 dbplyr 查看我函数中的代码,并将其转换为 SQL?
library(pacman)
p_load(dplyr, dbplyr, RSQLite)
# Set up fake database
con <- DBI::dbConnect(RSQLite::SQLite(), dbname = ":memory:")
test = tibble(a = c("a", "b", "c"))
test
#> # A tibble: 3 x 1
#> a
#> <chr>
#> 1 a
#> 2 b
#> 3 c
copy_to(con, test)
test_db <- tbl(con, "test")
在 test_db
上明确使用 case_when()
效果很好。
test_db %>%
mutate(b = case_when(
a == "a" ~ "AAA",
TRUE ~ a
))
#> # Source: lazy query [?? x 2]
#> # Database: sqlite 3.30.1 [:memory:]
#> a b
#> <chr> <chr>
#> 1 a AAA
#> 2 b b
#> 3 c c
然后我将我的 case_when()
放入一个函数中,这样我就可以重用它。当 运行 直接在 test
.
func_aaa <- function(x) {
case_when(
x == "a" ~ "AAA",
TRUE ~ x
)
}
# The function works fine on the regular dataframe
test %>%
mutate(b = func_aaa(a))
#> # A tibble: 3 x 2
#> a b
#> <chr> <chr>
#> 1 a AAA
#> 2 b b
#> 3 c c
但它不适用于 test_db
。查看它生成的查询,很明显 dbplyr 只是将我的函数名直接传递给 SQL.
test_db %>%
mutate(b = func_aaa(a))
#> Error: no such function: func_aaa
test_db %>%
mutate(b = func_aaa(a)) %>%
show_query()
#> <SQL>
#> SELECT `a`, func_aaa(`a`) AS `b`
#> FROM `test`
由 reprex package (v0.3.0)
于 2020-05-13 创建可能有更简单的方法,但一种选择是使用对 returns 整个数据帧进行操作的函数,而不是 运行 mutate 中的函数:
func_aaa2 = function(data, var, newvar, val, newval) {
data %>%
mutate({{newvar}} := case_when(
{{var}} == val ~ newval,
TRUE ~ {{var}}))
}
test_db %>%
func_aaa2(a, "b", "a", "aaa")
a b <chr> <chr> 1 a aaa 2 b b 3 c c
test_db %>%
func_aaa2(a, "b", "a", "aaa") %>% show_query()
SELECT `a`, CASE WHEN (`a` = 'a') THEN ('aaa') WHEN (1) THEN (`a`) END AS `b` FROM `test`