Why does this R ggplot2 code bring up a blank display device? - r

While SO is not usually used for help with bugs, this one shows particularly simple and particularly annoying behavior. If you are a ggplot2 user, you can reproduce it in 10 seconds or less.
As this GitHub issue: ggplot_gtable creates blank display says, the following code
library(ggplot2)
stat = qplot(Sepal.Length, Petal.Length, data = iris, color = Species)
ggplot_gtable(ggplot_build(stat))
will produce a blank device. Note that since ggplot2 is a graphics library, some commands can bring up a graphics device to show the relevant plot. Specifically, just running ggplot_build(stat) will bring up a plot. But that doesn't explain this behaviour.
I'm not sure how to debug this (print statements don't really seem appropriate or useful), and the ggplot2 development community seems to be on vacation or something, so if any experienced R user can offer suggestions on how to debug this effectively, I would appreciate it. This is a trivial but incredibly annoying bug. Every time I run code which looks like the snippet, it brings up a blank device which the display switches focus to, and so I have to click it away before I can continue.
It is possible that I'm doing something horribly wrong and am the only person who can reproduce this bug. It is also possible, for some reason I can't imagine, that this is normal behavior. If you think either of these things are true, please let me know.
I'm using ggplot2 0.9.3.1 (latest release) on Debian squeeze.

Some grid grobs have units that can only be resolved at drawing time, that is to say once a device window is open. This is the case of text grobs, for instance, as their size can depend (in the most general case) of the cex and fontsize arguments of the parent(s) viewports (which can be nested, etc.)
library(grid)
widthDetails(textGrob("hi"))
The current version of ggplot2 appears to use widthDetails in the code to build the legend grobs (guides_build function). It is conceivable that this could be replaced by grobWidth, unless the grob size is too convoluted.

I wonder if it is related to this thread from 3 years ago on R-Help with this workaround from #G.Grothendieck (copied material follows)
https://stat.ethz.ch/pipermail/r-help/2010-December/263754.html
library(lattice)
library(zoo)
df <- data.frame(y = matrix(rnorm(24), nrow = 6), x = 1:6)
xyplot(zoo(df[1:4], df$x), type = "p")
plot.object <- xyplot(zoo(df[1:4], df$x), type = "p")
# problem: a Quartz device is opened (on Mac OS X 10.6)
Grothendieck wrote in response:
This also opens up a window on Windows. It occurs within lattice
when lattice issues a trellis.par.get . A workaround would be to open
a device directed to null. On Windows this would work. I assume if
you use "/dev/null" it would work on your machine.
png("NUL")
plot.object <- ...
dev.off()

I am still having this issue with R 3.6.1 in 2019.
My lack of reputation doesn't allow me to comment. So I am writing yet another answer:
Things become a bit tricky if your plot uses non-standard fonts, say
library (ggplot2)
stat = qplot(Sepal.Length, Petal.Length, data = iris, color = Species) +
theme(text = element_text (family="DejaVu Sans"))
As was explained in the answer by baptiste, the output device is needed in order to determine some text dimensions. With the non-standard font, however, you need the appropriate output device. If you just use a pdf(file=NULL) as baptiste said, you get the dimensions wrong (and a warning that 'DejaVu Sans' was substituted by another font which is responsible for the false dimensions). In order to resolve this, I had to open an output device that is able to render the non-standard font (with a temporary file, say):
cairo_pdf (tempfile (fileext=".pdf"))
grob = ggplot_gtable (ggplot_build (stat))
dummy = dev.off ()
Hope this information is useful.

Problem seems to be caused by "color = Species".
If replaced by "group = Species", then no more blank display device.

Related

Saving filled contour plots to PDF in R gives pixelated results

Saving the output of one of R's built-in examples for the filled.contour function to PDF:
pdf('test.pdf')
require("grDevices")
filled.contour(volcano, asp = 1)
dev.off()
produces a discretised result (see below). Is there any way to fix this? System info:
> sessionInfo()
R version 3.6.3 (2020-02-29)
Platform: x86_64-pc-linux-gnu (64-bit)
Running under: Ubuntu 20.04.1 LTS
Edit after the accepted answer:
The problem is actually reported in the documentation of the pdf function:
If you see problems with PDF output, do remember that the
problem is much more likely to be in your viewer than in R ...
Symptoms for which the viewer has been at fault are apparent
grids on image plots ...
Unfortunately the default viewers on most Linux and macOS
systems have these problems, and no obvious way to turn off
graphics anti-aliasing.
It's an odd place to document the problem because the subdivision into rectangular segments is clearly caused by filled.contour and not by pdf. Otherwise ggplot2's output would also suffer from the same issue.
That is likely a result of antialiasing: when you display the image, it draws squares one at a time. As they are being drawn, the edge is a mix of the square colours and the white background, so it is drawn lighter.
Unfortunately, this isn't something that's really under your control. It's the PDF previewer that is introducing the artifacts. See this page https://codedocean.wordpress.com/2014/02/03/anti-aliasing-and-image-plots/ for a discussion.
The recommendation there worked for me: use the png() device with type = "cairo". This gives bitmapped output rather than the vector output of a pdf().
png('test.png',type="cairo")
filled.contour(volcano, asp = 1)
dev.off()
Edited to add:
I don't think you can do better with filled.contour, but if you are willing to switch to ggplot2 graphics you can. When it draws filled contours it appears to do it using polygons, not the image style plot that filled.contour uses. This still shows the same antialiasing bugs in the previewer, but now the lines appear along the borders between colours, which is much less irritating. For example:
df <- data.frame(x = as.numeric(row(volcano)-1)/(nrow(volcano)-1),
y = as.numeric(col(volcano)-1)/(ncol(volcano)-1),
z = as.numeric(volcano))
pdf('test.pdf')
library(ggplot2)
ggplot(df, aes(x=x, y=y, z=z)) +
geom_contour_filled()
dev.off()
I don't know how to get the same palette as filled.contour uses, i.e. function(n) hcl.colors(n, "YlOrRd", rev = TRUE). Maybe someone else can show us in a comment.

r- emojifont package's font-awesome command not displaying icons in Windows 10

When i use the fontawesome command from the emojifont package I get a square not the icon i requested. I'm fairly new to R, there may be something very simple i'm missing but this has had me stuck for some time.
I'm running windows 10, R version is up to date, using RStudio.
Reportedly these commands are working on a linux computer properly see previous discussion: Replace categorical values with traffic light colors
in another question the a user says to run Windows(). This made no difference, is it significant? using font awesome in ggplot
Example code
> library(emojifont)
> load.fontawesome()
> fontawesome('fa-circle')
[1] ""
> fontawesome('fa-apple')
[1] ""
> fontawesome('fa-at')
[1] ""
Update:
I can reproduce some simple examples online but not others.
set.seed(123)
x <- rnorm(10)
set.seed(321)
y <- rnorm(10)
plot(x, y, cex=0)
text(x, y, labels=emoji('cow'), cex=1.5, col='steelblue', family='EmojiOne')
d <- data.frame(x=x, y=y,
label = sample(c(emoji('cow'), emoji('camel')), 10, replace=TRUE),
type = sample(LETTERS[1:3], 10, replace=TRUE))
library("ggplot2")
ggplot(d, aes(x, y, color=type, label=label)) +
geom_text(family="EmojiOne", size=6)
This works properly as you can see below
However this code only produces squares (and most other examples i have tried)
set.seed(2016-03-09)
fa <- fontawesome(c('fa-github', 'fa-weibo', 'fa-twitter', 'fa-android', 'fa-coffee'))
d <- data.frame(x=rnorm(20),
y=rnorm(20),
label=sample(fa, 20, replace=T))
ggplot(d, aes(x, y, color=label, label=label)) +
geom_text(family='fontawesome-webfont', size=6)+
xlab(NULL)+ylab(NULL) +
theme(legend.text=element_text(family='fontawesome-webfont'))
In the Image above (and most examples i have tried) I get the squares instead of Icons. Any thoughts as to why?
I don't think it's supposed to print on the console, but only in plots.
Basically, what you are doing is asking emojifont/fontawesome, which of the possible over 1 million codepoints in Unicode correspond to a "circle" (it's code point f51d), and then print that character.
In plotting, you can say that you want that character to be printed in a certain font, and if you choose "fontawesome-webfont", such as as in the examples from emojifont, those circles will be plotted.
ggplot(d, aes(x, y, color=label, label=label)) +
geom_text(family='fontawesome-webfont', size=6)
Now if you would have chosen a different font-family, those labels would be printed in a different font.
Except that in practice, someone making a font will be carefully thinking about how to draw their a's, and b's, etc. But they won't have much time to think about how to draw "codepoint f51d", even if they knew that was only supposed to be a circle. And they don't expect anyone to ever use such an obscure codepoint.
So they take care of characters they expect to be used, and just use a placeholder for other characters, such as ""
And in the console, you will have a default font, depending on what console you are using, e.g. in my Rstudio-installation "Lucida console" is used. Not sure what font is shown in your console, but apparently it doesn't have a nice representation fro "codepoint f51d"
So in order to show it, you need to show it in the right font: fontawesome. Which means either plotting, or setting your console to use fontawesome. Although I'm not sure if that last one will work effectively, it might cause simple characters such as "A" to be showing placeholders as well, or maybe they've used it for showing something entirely different.
And the thing about using windows() is because the standard-rstudio-plotwindow sometimes uses its own defaults, and might not find fontawesome. But that has only to do with plotting in RStudio.
I had the same problem, but changing the font to family="EmojiOne" e.g.
geom_text(family="EmojiOne", size=6)
seemed to fix it for me.

How to rasterize a single layer of a ggplot?

Matplotlib allows to rasterize individual elements of a plot and save it as a mixed pixel/vector graphic (.pdf) (see e.g. this answer). How can the same achieved in R with ggplot2?
The following is a toy problem in which I would like to rasterize only the geom_point layer.
set.seed(1)
x <- rlnorm(10000,4)
y <- 1+rpois(length(x),lambda=x/10+1/x)
z <- sample(letters[1:2],length(x), replace=TRUE)
p <- ggplot(data.frame(x,y,z),aes(x=x,y=y)) +
facet_wrap("z") +
geom_point(size=0.1,alpha=0.1) +
scale_x_log10()+scale_y_log10() +
geom_smooth(method="gam",formula = y ~ s(x, bs = "cs"))
print(p)
ggsave("out.pdf", p)
When saved as .pdf as is, Adobe reader DC needs ~1s to render the figure. Below you can see a .png version:
Of course, it is often possible to avoid the problem by not plotting raw data
Thanks to the ggrastr package by Viktor Petukhov & Evan Biederstedt, it is now possible to rasterize individual layers. However, currently (2018-08-13), only geom_point and geom_tile are supported. and work by Teun van den Brand it is now possible to rasterize any individual ggplot layer by wrapping it in ggrastr::rasterise():
# install.packages('devtools')
# remotes::install_github('VPetukhov/ggrastr')
df %>% ggplot(aes(x=x, y=y)) +
# this layer will be rasterized:
ggrastr::rasterise(geom_point(size=0.1, alpha=0.1)) +
# this one will still be "vector":
geom_smooth()
Previously, only a few geoms were supported:
To use it, you had to replace geom_point by ggrastr::geom_point_rast.
For example:
# install.packages('devtools')
# devtools::install_github('VPetukhov/ggrastr')
library(ggplot2)
set.seed(1)
x <- rlnorm(10000, 4)
y <- 1+rpois(length(x), lambda = x/10+1/x)
z <- sample(letters[1:2], length(x), replace = TRUE)
ggplot(data.frame(x, y, z), aes(x=x, y=y)) +
facet_wrap("z") +
ggrastr::geom_point_rast(size=0.1, alpha=0.1) +
scale_x_log10() + scale_y_log10() +
geom_smooth(method="gam", formula = y ~ s(x, bs = "cs"))
ggsave("out.pdf")
This yields a pdf that contains only the geom_point layer as raster and everything else as vector graphic. Overall the figure looks as the one in the question, but zooming in reveals the difference:
Compare this to an all-raster graphic:
I think you've set yourself up to not have this question answered. You write:
I expect an answer to provide an extension to ggplot2 that allows to export plots with rasterized layers with minimal changes to to existing plotting commands, i.e. as wrapper for geom_... commands or as an additional parameter to these or a ggsave command that expects a list of unevaluated parts of a plot command (every second to be rasterized), not a hacky workaround as provided in the linked question.
This is a major development effort that could easily require several weeks or more of effort by a highly skilled developer. It's unlikely anybody will do this just because of a Stack Overflow question. In lieu of a functioning implementation, I'll describe here how one could implement what you're asking for and why it's rather challenging.
The players
Let's start with the key players we'll be dealing with. At the highest level sits the ggplot2 library. It takes data frames and turns them into figures. ggplot2 itself doesn't know anything about low-level drawing, though. It only deals with lines, polygons, text, etc., which it hands off to the grid library in the form of graphics objects (grobs).
The grid library itself is a fairly high-level library. It also doesn't know much about low-level drawing. It primarily deals with lines, polygons, text, etc., which it hands off to an R graphics device. The device does the actual drawing.
There are many different R graphics devices. Enter ?Devices in an R command line to see an incomplete list. There are vector-graphics devices, such as pdf, postscript, or svg, raster devices such as png, jpeg, or tiff, and interactive devices such as X11 or quartz. Obviously, rasterization as a concept only makes sense for vector-graphics devices, since raster devices raster everything anyways. Importantly, neither ggplot2 nor grid know or care which graphics device you're currently drawing on. They deal with graphical objects that can be drawn on any device.
Ideal high-level interface
The high-level interface should consist of an option rasterize in the layer() function of ggplot2. In this way, one could simply write, e.g., geom_point(rasterize = TRUE) to rasterize the points layer. This would work transparently for all geoms and stats, since they all call layer().
Possible implementations
I see four possible routes of implementation, ordered from most impossible to least.
1. Ideally, the layer() function would simply hand off the rasterize option to the grid library, which would hand it off to the graphics device to tell it which parts of the plot to rasterize. This approach would require major changes in the graphics device API. I don't see this happening. Not in my lifetime, at least.
2. Alternatively, one could write a new grob type that can take any arbitrary grob and rasterize it on demand when the grob is drawn on a graphics device. This approach would not require changes in the graphics device API, but it would require detailed knowledge of the low-level implementation of the grid library. It would also possibly make interactive viewing of such figures very slow.
3. A slightly simpler alternative to 2. would be to rasterize the arbitrary grob only once, on grob construction, and then reuse whenever that grob is drawn. This would be quite a bit faster on interactive graphics devices but the drawing would get distorted if the aspect ratio is changed interactively. Nevertheless, since the primary use of this functionality would be to generate pdf output (I assume), this option might be sufficient.
4. Finally, rasterization could also happen in the layer() function, and that function could simply place a regular raster grob into the grob tree. That solution is similar to the technique described here. Technically, it's not much different from 3. Either way, one needs to write code to rasterize a grob tree and then replace it by a raster grob.
Technical hurdles
To rasterize parts of the grob tree, we'd have to send them to an R raster graphics device to render. However, there isn't one that renders to memory. So, one would have to render to a temporary file (e.g., using png()), and then read the file back in. That's possible but ugly. It also depends on functionality (such as png()) that isn't guaranteed to be available on every R installation.
Second, to render parts of the grob tree separately from the overall rendering, we'll have to open a new graphics device in addition to the one currently open. That's possible but can lead to unexpected bugs. I'm dealing with such bugs all the time, see e.g. here or here for issues related to code using this technique. Whoever implements the rasterization functionality would have to deal with such issues.
Finally, we'll have to get the rasterization code accepted into the ggplot2 library, since we need to replace the layer() function and I don't think there's a way to do that from a separate package. Given how hackish the rasterization solutions are going to be (see previous two paragraphs), that may be a tall order.

Plot in R won't change color

This is probably going to sound like a really stupid question but my plot in R will not change color!
this is my line in R
plot(MasterModCfs$V3,MasterModCfs$V5,col="blue")
I have tried it with spaces and without, with different colors, reloading, everything I could think of. If it's important, MasterModCfs contains literally thousands of data values.
I did one of the examples just to check
cars <- c(1, 3, 6, 4, 9)
plot(cars)
plot(cars, col="blue")
and it's blue. So that works.
Why won't my plot change colors?
Googling the same problem, I came across this present page - but subsequently found the source of my own issue through trial and error so posting here in case it helps. My x axis values were set up as factors - when I re-converted them to a normal string using as.character(), I was able to re-apply my own colours finally.
Without knowing what your par() settings are and what the classes of your columns, it's impossible to say.
You might try adding 'pch=21, bg="red"' to your plot command and see if that changes anything. It might give you a hint.
I suggest using the aes command in ggplot2. You can try
ggplot(data = MasterModCfs) + geom_point (mapping =aes (x=V3, y=V5), color="blue")
That should work!
Using plot directly I am not sure how to do it. I try your simple example (with cars) but it also fails if you add another vector for the "y" axis (as you wish to do in the end). To be honest, I have no idea why it works using only "cars" and the color.

Producing a vector graphics image (i.e. metafile) in R suitable for printing in Word 2007

First a caveat: I posted this question here on SuperUser, but it is clearly the wrong place to ask R questions. I recognize that it is not directly a programming question, but I believe it can be solved by changing how plots are produced (i.e. by coding appropriately). So I hope readers find this appropriate for the forum.
R plots usually consist entirely of vector graphics elements (i.e. points, lines, polygons, text). R permits you to save your figure (or copy-paste) in various formats including various raster formats, as a PDF, or as a Windows meta-file.
I usually save my images as PDFs and print them. This renders the images exactly as I intended them on paper, in the highest quality. I avoid raster formats (e.g. JPG, TIFF) for printing as inevitably the quality is poorer and publishers prefer vector formats.
However, I need to make a large multi-page desktop published document using Microsoft Word 2007, and therefore using PDFs is not an option. When I import my figures from meta-files, or copy and paste directly from R into Word both the screen and print rendering of the image changes slightly (e.g. polygons and their fills become slightly misaligned).
Given that I want to retain high vector quality (and not use raster formats), what can I do to make R vector graphics work with Word? (Of course Sweave and LaTeX would be nice, but again, not a realistic option).
Consider this example:
plot(c(1:100), c(1:100), pch=20)
## Copy and paste to Word 2007 as Windows metafile
## Print
## Quality is poorer (e.g. dot fills misaligned with borders)
pdf("printsPerfectly.pdf")
plot(c(1:100), c(1:100), pch=20)
dev.off()
## Now print PDF
## Quality is as expected
EDIT: Further to suggestions by #John I produced it as an EPS postscript file (see below), inserted it as a picture into Word. Because ultimately it will be printed from a PDF created from Word, I converted it to a PDF using default Word 2007 settings, printed it on my HP Laserjet P1606dn laser printer, and then took aphotograph to illustrate the issue of polygons borders and fills misaligning (image on left, below). I also produced it directly as PDF from R using pdf() and printed the PDF and took a photograph (image on right, below).
It may seem like small potatoes! But when you have gone to a lot of trouble to achieve high quality, it is disappointing to be thwarted at the end. In addition, it is not really obvious here, but the numerals are not as high-quality (left) as in the PDF (right), disregarding differences in focus on the photograph.
The accepted answer to me is not acceptable, since if one goes to the trouble of making a nice vector based figure, the last thing one would like to do is just rasterize it to a bitmap... Unless it's an increadibly complex graph that takes ages to render in vector format, or something like that, but for most graphs that's not the case.
The best solution is to export to Word directly in native Office vector format. I just made a new package, export, that allows one to do exactly that an allows export of either graphs or statistical tables to Word and Powerpoint, see
https://cran.r-project.org/web/packages/export/index.html and for demo see
https://github.com/tomwenseleers/export
For example:
library(devtools)
devtools::install_github("tomwenseleers/export")
library(export)
?graph2ppt
?graph2doc
?table2ppt
?table2doc
## export of ggplot2 plot
library(ggplot2)
qplot(Sepal.Length, Petal.Length, data = iris, color = Species,
size = Petal.Width, alpha = I(0.7))
# export to Word
graph2doc(file="ggplot2_plot.docx", width=7, height=5)
# export to Powerpoint
graph2ppt(file="ggplot2_plot.pptx", width=7, height=5)
You can also export to enhanced metafile using the function
graph2emf(file="ggplot2_plot.emf", width=7, height=5)
but the quality of the native Office format is better.
For final production you can also readily print it to PDF from Powerpoint if need be, and it will stay nicely in vector format then.
Your only option is to use high resolution raster graphics. Once you're over 300 dpi it will be completely indistinguishable from vector printed; it will just make larger files.. Your copy and paste method is coming in at 72 dpi and will look terrible. If you import from a file you can get the resolution in the file and things will be much better. Fortunately Office 2007 is supposed to handle png images, which have the best compression for typical graphs. Let's say you wanted the image 4" wide and 6" high...
png('printsGreat.png', width = 4, height = 6, units = 'in', res = 300)
plot(c(1:100), c(1:100), pch=20)
dev.off()
Also, Office 2007 is supposed to be able to handle EPS files and R postscript files are by default EPS compatible when you print one page.
postscript("printsPerfectly.eps", width = 4, height = 6, horizontal = FALSE, onefile = FALSE)
plot(c(1:100), c(1:100), pch=20)
dev.off()
But if you don't have luck with them go back to the high resolution image.
My preferred solution is to use the windows metafile device for plotting, e.g.:
win.metafile("mygraph.wmf")
print(gg1)
dev.off()
This produces a *.wmf file that can be copy-pasted into the word file.
The devEMF package seems to produce graphics that look nicer than the default wmf when pasted into PowerPoint.
Since I tried to produce png at high res in R and it didn't seem to work on my PC (if I set the resolution higher than, say, 300 dpi, R would produce an error like "cannot start png device"), the way I found was to save the figure using postscript() and then use GSView to convert the ps file into png with 600 dpi resolution. MS Word consumes the png's happily and the quality of print seems to be perfect.
What #Tom Wenseleers said:
The current best answer above to me is not acceptable, since if one
goes to the trouble of making a nice vector based figure, the last
thing one would like to do is just rasterize it to a bitmap... Unless
it's an increadibly complex graph that takes ages to render in vector
format, or something like that, but for most graphs that's not the
case.
For me, there is a new best answer to this question, since graph2ppt and graph2doc tend to move axis labels around (which apparently cannot be fixed; see here: https://github.com/davidgohel/rvg/blob/master/R/body_add_vg.R and here: export::graph2office moves axis labels around).
I think that .svg is the most appropriate vector format for usage with publication graphics. The only drawback is that older versions of e.g. MS Word cannot handle it. IN R, you could use the native graphics::svg - device. However, I'd recommend to use CairoSVG from the Cairo - Package, especially when you are working with non-native fonts (e.g. via the extrafont - package), because in contrast to graphics::svg, Cairo::CairoSVG embeds fonts quite nicely (without relying on GhostScript, if I am right).
If you are working with an older version of MS Word, you could use incscape (a free vector graphic editor) and convert your graph to .wmf, for example (which might be better than printing to .wmf directly, because R rasterizes points when exporting .wmf files).
An example:
## create plot
library (ggplot2)
library (extrafont)
# note: if you want to use other fonts than the standard ones - in this example "ChantillyLH" -
# you must register your fonts via
# font_import () ##run only once (type "y" in the console)
# and
# loadfonts (device = "win") ##run only once.
# Otherwise, the extrafont - package is not needed.
beautiful_plot <-
ggplot (data = iris, mapping = aes (x = Sepal.Length, y = Petal.Length)) +
geom_point () +
theme (text = element_text (size = 18,
family = "ChantillyLH")
)
# export SVG
library (Cairo)
CairoSVG ("My_Path/My_Plot.svg", width = 6, height = 6)
print (beautiful_plot)
dev.off ()
# the resulting SVG-file is in the the "My_Path" - Folder.
In Incscape, it looks like this:
Newer versions of Word can import raster graphics from SVG files. R 3.6.2 has built-in support for creating SVG files with the svg function - no extra packages needed.
Your example then becomes
svg("printsPerfectly.svg", width=4, height=4)
plot(c(1:100), c(1:100), pch=20)
dev.off()
Note that there is a known issue when you try to create PDF files from Word documents with embedded SVG files with thin lines. If you are using thin lines, e.g. with lwd=0.7 somewhere, you need to apply this workaround.

Resources