在ggplot2中使用geom_function调用一个用postgreSQL plr编写的函数

Using geom_function in ggplot2 to call a function written in postgreSQL plr

我很难从 R 脚本调用 postgreSQL 中的 plr 函数并在 ggplot2 中使用它 - geom_function。以下示例已极度简化,但希望能说明问题。

假设我有以下 plr 函数:

CREATE OR REPLACE FUNCTION public.mypgfunc(
    x numeric,
    a numeric)
    RETURNS numeric
    LANGUAGE 'plr'
    COST 100
    VOLATILE PARALLEL UNSAFE
AS $BODY$
    return (x*a)
$BODY$;

要从 ggplot 调用它 - geom_function 我想,我可以编写以下简单的包装函数来执行查询(我使用 rpostgres 包):

myWrapper <- function(x , a) {
  con <- dbConnect(drv = RPostgres::Postgres() , dbname='mydb')
  
  q <- dbSendQuery(con , "select mypgfunc(  ,  )")
  dbBind(q , c(x,a))
  y <- dbFetch(q)
  dbClearResult(q)
  
  dbDisconnect(con)
  
  return(y)
}

但是如果我现在从 ggplot 调用这个函数,我会收到以下警告消息和一个空图:

Computation failed in stat_function(): Query requires 2 params; 102 supplied.

ggplot 代码如下所示:

ggplot() +
  geom_function(fun = myWrapper , args = list(a = 5))

如果我改为在 R 中编写 plr 函数并从 geom_function 调用它,一切正常。 如果我直接调用 myWrapper(在 ggplot 之外),分别只有一个 x 和 a 的值,一切也都正常。

那么,我需要更改什么?

我现在不太了解在 R 中使用 SQL 数据库。但我想我知道你出错的原因。

如果您查看 stat_function 的帮助页面(这是 geom_function 在幕后使用的内容),您将看到,默认情况下,它会在任意范围内创建 101 个 x 值范围(我认为是[0,1])。

这就是错误消息提到“查询需要 2 个参数;提供 102 个”的原因。当您调用 dbBind(q , c(x,a)).

时,您正在将 x(长度为 101 的向量)与 a(长度为 1)连接起来

x的大小由参数n定义。所以如果你 运行:

ggplot() + geom_function(fun = myWrapper , args = list(a = 5), n=1)

您将 x 的单个值传递给 myWrapper(我认为 x=0),您应该得到与您在上一句中描述的情况相同的结果(我收到一条警告消息,因为 ggplot 无法绘制只有 1 个点的线)。

因此,基本上,您需要对向量的每个值进行单独查询 x。一种直接的方法是遍历 x:

的值
y <- NULL
for (xx in x) {
    q <- dbSendQuery(con , "select mypgfunc(  ,  )")
    dbBind(q , c(xx, a))
    if (is.null(y)) {
        y <- dbFetch(q)
    } else {
        y <- rbind(y, dbFetch(q))
    }        
    dbClearResult(q)
}

就像我说的,我没有在 R 中使用 SQL。所以我相信有更聪明的方法可以做到这一点,你不需要调用丑陋的 for 循环(也不确定你是否需要循环内的 dbSendQuery)。

我做了更多研究并找到了另一个避免 for 循环的解决方案。

我只需要将 myWrapper 函数更改为以下代码:

myWrapper <- function(x , a) {
  con <- dbConnect(drv = RPostgres::Postgres() , dbname='mydb')
  
  a <- rep(a , length(x))
  q <- dbSendQuery(con , "select mypgfunc( ::numeric , ::numeric )")
  dbBind(q , list(x,a))
  y <- dbFetch(q)
  dbClearResult(q)
  
  dbDisconnect(con)
  
  return(y[,1])
}