在 R 中,我试图将绘图呈现为字节流而不是写入文件

In R, I am trying to render plots to a byte stream rather than writing to a file

我在无头 linux 服务器上安装了 R,但我没有对文件系统的写入权限。这迫使我走上了将图片保存到数据库(本例中为 PostgreSQL)的道路。

我四处寻找解决方案,但我找到的每个解决方案通常都会先保存到文件中,然后将文件作为字节读回 R,然后将字节存储在数据库中。

10 年前的这个 Whosebug post 指出所有图形设备都是基于文件的。从那时起,R 是否发生了变化以允许我获取字节? How to save R plot image to database?

这个解决方案看起来很有趣,但它仍然需要写权限:

我想尝试使用 text.connection() 或 capture.output(),希望它创建一个包含图像字节的文本向量。

下面是我累的两件事:

logmodel_solar <- glm(hasfire ~ cs_rh_min + cs_air_max_temp + cs_precip + cs_solar, data=df, family = binomial("logit"))
zz <- textConnection("foo", "w")
sink(zz)
plot(logmodel_solar)
sink()
close(zz)

这会使 R 崩溃或给我这个

> cat(zz, sep = "\n")
3

然后我尝试了这个:

logmodel_solar <- glm(hasfire ~ cs_rh_min + cs_air_max_temp + cs_precip + cs_solar, data=df, family = binomial("logit"))
yy <- capture.output(plot(logmodel_solar))

>  cat(yy, sep = "\n")

> yy
character(0)

所以我想我的问题是:

  1. R是否仍然只输出图形到文件
  2. 如果不是,我如何获取字节

我从一个同事那里得到了答案,他说我花了几个月的时间才弄明白这个问题。它不直观但有效。

# https://www.rdocumentation.org/packages/Cairo/versions/1.5-12.2/topics/Cairo
# https://www.rdocumentation.org/packages/cairoDevice/versions/2.28.2
# An intermediate drawing device that can output to buffer but allows normal graphing commands to work
require(cairoDevice)

# https://www.rdocumentation.org/packages/RGtk2/versions/2.8.7
# Allows us to interact with the GTK buffer so we can get the bytes rather than saving to disc
require(RGtk2)

########## here we read in the data and run a logistic regression model
pg = dbDriver("PostgreSQL")
con = dbConnect(pg, user="postgres", password="password",
                host="localhost", port=5432, dbname="fire")

query_statement <- paste("select hasfire, date_time, cs_precip, cs_air_max_temp,",
                                       "cs_air_min_temp, cs_soil_max_temp, cs_soil_min_temp, cs_solar, cs_eto, cs_rh_max, cs_rh_min, hasfire",
                                       "from final.analysis")
df <- dbGetQuery(con, query_statement)

logmodel_solar <- glm(hasfire ~ cs_rh_min + cs_air_max_temp + cs_precip + cs_solar, data=df, family = binomial("logit"))

################## 现在我们从模型中绘制一个图形

# Define the size in pixels and bits per pixel of the image we want.
# We create a Gtk2 pixmap with these parameter
# In this case we are making a 500 pixel x 500 pixel image with 24 bits per pixel
# https://www.rdocumentation.org/packages/RGtk2/versions/2.8.5/topics/gdkPixmapNew
# https://developer.gnome.org/pygtk/stable/class-gdkpixmap.html
pixmap <- gdkPixmapNew(w=500, h=500, depth=24)

# Now we convert the pixmap to a Cairo graphics device. After that we can use the
# Cairo device like any normal R graphic device (i.e. R plot commands draw to it)
# Cairo https://www.rdocumentation.org/packages/Cairo/versions/1.5-12.2/topics/Cairo
# https://www.rdocumentation.org/packages/cairoDevice/versions/2.28.2/topics/asCairoDevice
asCairoDevice(pixmap)

#Normal plot command
plot(logmodel_solar)

# Convert out image of the plot to an RGB(A) representation in another buffer
# Since we want to go to a buffer the first parameter, dest, is null
# Next we give pixmap as the source and then we pass in the colormap from pixmap
# The first two 0s setting the origin of the image
# The next two are set to 0 because our destination is null
# The next two parameters are the width and height respectively to get from the image
# So if we wanted to subset our plot we could set different origin coordinates and smaller dimensions to get
# RGB(A) https://en.wikipedia.org/wiki/RGBA_color_model
# https://www.rdocumentation.org/packages/RGtk2/versions/2.20.31/topics/gdkPixbufGetFromDrawable
plot_pixbuf <- gdkPixbufGetFromDrawable(NULL, pixmap,pixmap$getColormap(),0, 0, 0, 0, 500, 500)

# Now we convert plot_pixbuf above to a binary object that is in the format of the image we want.
# first we pass in the PixBuffer from above, then we choose our conversion format
# Values for the format are currently "jpeg", "tiff", "png", "ico" or "bmp"
# For statistical graphs you should try to use a lossless format such as tiff or png
# The next two parameters set the option_keys and option_values respectively.
# We are not setting any so we just pass in a 0 length character vector.
# The $buffer on the end tells R we want the buffer attribute from the converted object
# https://developer.gnome.org/gdk-pixbuf/stable/gdk-pixbuf-File-saving.html
# https://www.rdocumentation.org/packages/RGtk2/versions/2.20.31/topics/gdkPixbufSaveToBufferv
buffer <- gdkPixbufSaveToBufferv(plot_pixbuf, "png",character(0),character(0))$buffer

# Now we return out buffer
return(buffer)