2

How can I convert a plot image to base64 encoding without writing it to disk first (i.e. directly from the R environment)?

Note this shows how to do it from an image file. I am unable to use files since I am running this in an environment with an R interpreter only (i.e. ephemeral storage only)

I have tried using base64Encode() like so

library(ggplot2)
library(dplyr)
df <- data.frame(
  gp = factor(rep(letters[1:3], each = 10)),
  y = rnorm(30)
)
ds <- plyr::ddply(df, "gp", plyr::summarise, mean = mean(y), sd = sd(y))

a <- ggplot(df, aes(gp, y)) +
  geom_point() +
  geom_point(data = ds, aes(y = mean), colour = 'red', size = 3) 

library(RCurl)
a %>% base64Encode(.)

But this simply converts the underlying plot data (not the image itself) to base64.

How can I convert an image from the R environment to base64?

stevec
  • 41,291
  • 27
  • 223
  • 311

2 Answers2

1

Your a object, as you identified, is not an actual plot, but the "raw data from the plot". Strictly speaking, it's a representation of what you want plotted, the mapping between variables and elements, and all the aesthetics (theme, colours, etc.). Try the following:

str(a, max.level=1)

(and increment max.level bit by bit).

When you view a in the console, R is actually calling print(a) - that's why you will not get the plot outputted if you just run a script from a command line where you try to output a plot by calling a by itself.

When calling print(a) (or indirectly from an interactive session), ggplot2 builds the plot by mapping the variables to the x- and y-axis, colours, maps out facets, etc. etc. The result of that is then drawn on a graphical device, either a plot window or a file. You can actually catch the graphical representation of this drawing with ggplotGrob and then further manipulate the actual drawing, before it is sent to the screen or file.

How does this help?

You need to use print(a) instead of directly encode a (as you have noticed).

I.e. to produce a base64-encoded plot, you can do:

library(base64enc)
## convert image to base64 encoded string

fn <- tempfile(fileext='.png')
png(fn)
print(a)
dev.off()

base64enc::base64encode(fn)

But: This does require you can write to temporary files. And to be honest, I have a hard time believing you are prevented from this.

And unfortunately, I am not aware of any graphical devices (png, bmp, etc.) were you can write directly to a memorystream or variable instead of a physical file.

MrGumble
  • 5,631
  • 1
  • 18
  • 33
  • In my case I was using a PAAS (heroku) that had idiosyncratic rules about where I could write to, so didn't have access to temporary locations that `tempfile()` tries to access. Instead, I did find a solution by googling the PAAS documentation to learn directly where it *would* allow me to write to, and generating my own DIY temporary files (hacky it may be), like so: `runif(1, 1000000000, 9999999999) %>% round %>% as.character %>% paste0("tmp/", ., ".png")`. Thanks for the answer and explanation – stevec Apr 23 '20 at 08:06
  • TBH, wading through this was good learning, as I wasn't even aware plots had to be 'written' before they existed (I assumed they could existing solely in RAM). I am still not sure if it's a convention unique to R, or if that's how all graphics work – stevec Apr 23 '20 at 08:10
  • If you **know** where you can create temporary files, you can specify this as an argument: `tempfile(tempdir="tmp/", fileext=".png")` (possibly without trailing slash). – MrGumble Apr 23 '20 at 08:31
  • 1
    Regarding the requirement for writing to file, AFAIK, it's purely due to the backend png/jpeg/bmp libraries used. If you peak into `png`, it calls `.External(C_devga, paste0("png:", filename), ...` – MrGumble Apr 23 '20 at 08:33
-1

it seems to work with objects from a certain type. The class of the object when you used ggplot() is not the same than when you used plot().

class(a)
# [1] "gg"     "ggplot"
class(my_plot)
# [1] "NULL"

Why not using ggplot() all the time ?

my_ggplot <- ggplot(mapping = aes(x = 1:6, y = c(1,3,6,2,7,5))) + geom_point()

my_ggplot %>% base64Encode(.)
# [1] "bGlzdCgp"
# attr(,"class")
# [1] "base64"
demarsylvain
  • 2,103
  • 2
  • 14
  • 33
  • While `my_ggplot %>% base64Encode(.)` from your example doesn't error, the base64 output ("bGlzdCgp") actually converts to `list()` (not an image, but a string). See [here](https://www.base64decode.org/) – stevec Feb 13 '19 at 14:59
  • I just realised that so does the other one I posted in the question. So I'm not actually converting to images as I thought, but rather converting a string representation of the raw data from the plot – stevec Feb 13 '19 at 15:02
  • I have edited the question to reflect that I require the image, hope this is useful – stevec Feb 13 '19 at 15:07