Not sure if this is the appropriate place to ask this question, so please feel free to delete it...
I've been preparing a little introduction to course lately, and in the section about saving plots I noticed something odd. When I created a figure on my work PC, it looked the way it should. But when I ran the exact same code again on my Mac at home, the image looked somewhat blurred. I tried this for the png, tiff, and jpeg formats, the result is the same, the plot looks nicer when produced on a PC. I include two graphs below so you can see for yourself. (The first the the PC plot, the second the Mac plot). Although the PC produces the better quality, the figure produced by the Mac is larger (19 vs 8 KB). Below is another image, where both figures are magnified (Mac left, PC right). While the PC draws a crisp line, the Mac produces some kind of a shading around the lines. Can anyone explain why this is the case and how I can produce better quality plots on the Mac? I played around with the options, in particular the type argument, but couldn't neither find a solution, nor find help on the internet.
Here is the code to generate the plot:
set.seed(2)
BMI<-rnorm(n=1000, m=24.2, sd=2.2)
png(file="fig3_m2.png", width=700, height=700, res=120)
par(mgp=c(1.7,.6,0), mar=c(3,3,2,2), yaxs="i")
x <- hist(BMI, freq=F, main="Distribution of BMI",
xlab="Body Mass Index", col="lightgreen",
ylim=c(0,.2),
breaks=seq(min(BMI), max(BMI), length=18))
box()
dev.off()
Related
My goal is to read an image file in either the PNG or JPEG format and plot various data over said image and save it to disk.
I also want the image to take up all available space in the produced plot, no axes or labels or anything. I'm a bit concerned that this might be relevant to my problem.
Code example
Below is my current code that currently only tries to output the same image as you put in. Later I plan on plotting data points corresponding to coordinates over the image. I've used some sample code found here in order to remove the axes and be able to have the image in the background of the plot.
library(jpeg)
library(grid)
img <- readJPEG(system.file("img", "Rlogo.jpg", package="jpeg"),native=TRUE)
jpeg(filename = "Rlogo-2.jpg", width=100,height=76, quality = 100,res=299)
op<-par(mar=rep(0,4))
plot(0:100,type="n", axes="FALSE",ann="FALSE")
lim <- par()
rasterImage(img, lim$usr[1], lim$usr[3], lim$usr[2], lim$usr[4])
dev.off()
Example output
This is an example output of my above code in a comparison with the original image:
The image to the left is the original and the right one is the modified one. As you can see it seems as if the image I read and plot somehow is smaller than the original image and when saved to the original dimensions it appears blurred.
I've been pulling my hair over this one for hours and I don't seem to get anywhere. This is my first attempt to plot data over images and I'm aware of my lack of knowledge about how R represents images and I've mostly been using the basic graphics to do relatively simple plots before.
I'm currently considering doing this in Python instead but I'm afraid that'll come back and bite me when it comes to the actual plotting of the data.
I run R version 3.1.0 on x86_64 running Windows 7.
Just to summarize, since you already found the culprit, there are two issues present here:
Firstly, the blurring appears to be caused by the jpeg device on Windows. There is no such problem on Ubuntu Linux and it disappears if you use the Cairo-device instead, as you did already discover. Cairo-devices are great for pdf:s too since they embed all the fonts etc. making the figure look the same across platforms.
Secondly, R adds 4% extra margin to the x and y axes by default to prevent graphics from being chopped off near the edge of the plot area. It can be corrected by setting xaxs="i" and yaxs="i".
par(mfrow=1:2)
plot(1:5, 1:5) # Left
plot(1:5, 1:5, xaxs="i", yaxs="i") # Right
In your case the difference is subtle but still would cause everything to be slightly misaligned.
I have a long time series of 10000 observations that I want to visualize. The problem is, if I just plot it normally the time-dimension will be squished and none of the fine detail of the time-series that I want to visualize will be apparent. For example:
plot((sin(1:10000/100)+rnorm(10000)/5),type='l')
What I would like is to somehow plot the following together side by side in one gigantically long plot without using par(mfrow=c(1,100)). I then want to export this very wide plot and simply scroll across to vizualise the whole series.
plot((sin(1:10000/100)+rnorm(10000)/5)[1:100],type='l')
plot((sin(1:10000/100)+rnorm(10000)/5)[101:200],type='l')
plot((sin(1:10000/100)+rnorm(10000)/5)[201:300],type='l')
.....
Eventually I would like to have 3 or 4 of these gigantically wide plots on top of each other with a par(mfrow=c(4,1)).
I know that the answer has something to do with the pin setting in par, but I keep getting Error in plot.new() : plot region too large. I'm guessing this has something to do with the interaction of pin with the other par parameters
Bonus points are awarded if we can get the pixel height and width exactly right. It is preferable that the plot doesn't skip random pixels due to the export sizing being imperfect.
Further bonus points if the image can be encoded in a .html. and viewed this way
An alternative that you might consider is svg, which will produce something of better quality than png/jpeg in any case.
Something like
svg(width = 2000, height = 7)
par(mfrow=c(4,1), mar = c(4, 4, 0, 2))
for (i in 1:4){
plot((sin(1:10000/100)+rnorm(10000)/5),type='l',
bty = "l", xaxs = "i")
}
dev.off()
will produce a very wide plot, just over 1MB in size, which renders quite nicely in Chrome.
Note the width and height are in inches here.
P.S. svg also offers the potential for interactive graphics. Just seen a nice example allowing the user to select a region of a long time series to zoom in on, see Figure 22 in Dynamic and Interactive R Graphics for the Web: The gridSVG Package, a draft paper by Paul Murrell and Simon Potter.
It could be a Cairo-specific problem, or it could be a lack of RAM on your machine. The following code works fine for me on a Windows 7 machine with 8GB RAM.
png("wide.png", width = 1e5, height = 500)
plot((sin(1:10000/100)+rnorm(10000)/5),type='l')
dev.off()
If I change the width to 1e6 pixels, then R successfully creates the file (it took about a minute), but no image viewing software that I have available can display an image that large.
I would go on some alternative route. First of all, what exactly is the point of viewing the entire plot at hi-res? If you're searching for some sort of anomalies or irregularities, well, that's what data processing is for :-) . Think about something like finding allx > 3sigma, or doing an FFT, etc.
Next, if you really want to examine the whole thing by eye, how about writing some R-TclTK code or using dynamicGraph or iplots or zoom to produce an interactive graph that you can scroll thru "live."
ETA: IIRC RStudio has tools for interactive graph scrolling and zoom as well.
I'm trying to generate a 3d scatterplot using rgl. It looks great on my screen, but whenever I export it as a PDF (or any other postscript format) it completely ignores any size specifications I use.
(I'm running RGui v.2.15.1 and rgl v.0.92.892 on a Macbook under Mountain Lion.)
For example:
library(rgl)
set.seed(1982)
points3d(runif(5),runif(5),runif(5), size=20)
# points look huge
rgl.postscript('testplot.pdf', fmt='pdf')
# points look tiny
Does anyone have an idea for a way to get this to work? The resolution of the images I get using rgl.snapshot don't look so good, and I would really like to get a vector image for this plot.
Also, I followed this thread and I got text to resize just fine, but not points. So I thought one way to work around this would be to plot my points as text using a circle as my character, but I couldn't get rgl to accept symbols or expressions either...
Confirmed on Windows, look like some paper size scaling problem. You might try
spheres3d(runif(5),runif(5),runif(5),radius=0.1)
as a workaround if you can live with real 3d.
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.
In R, I met a running error as follows:
> png("p3_sa_para.png", 4, 2)
> par(mfrow=c(1,2))
> plot(c(1:10), ylab="Beta",xlab="Iteration")
Error in plot.new() : figure margins too large
> plot(c(1:10), ylab="Gamma",xlab="Iteration")
Error in plot.new() : figure margins too large
> dev.off()
X11cairo
2
I have already made the image size small to be 4 by 2, why it still complains "figure margins too large"? How can I solve this problem with png?
It is strange that if I change png to pdf, then it will work. I also wonder why?
Thanks and regards!
The png() function uses pixels not inches, so try something like
png("p3_sa_para.png", 640, 480)
And to answer your second question, yes, pdf() uses inches because a vector-graphics format has no notion of pixels. The help(png) and help(pdf) functions are your friends.
The problem can simply arise from using a certain IDE. I was using Rstudio, and I got a slew of errors. My exact same code worked fine in the console.
Even I was getting the error on R-Studio, while the plot was appearing fine on the console. A simple restart of RStudio solved the problem! Having said that, RStudio's support page suggests that resetting graphics device dev.off() may help. http://support.rstudio.org/help/kb/troubleshooting/problem-with-plots-or-graphics-device
This is a common issue for plotting specially when you are using IDE which has a place for generating and showing you the plot, thought it's a general issue and there is a logic behind it:
when you tell R to plot something, R first look at the data and then looks at the area it has at it's disposal so that it cal do the plotting.
The png() and similar commands:
In your case you gave the plot a 4 by 2 pixel area to plot it, so you can solve it by increasing the area in a size that can fit your plot. (as Dirk Eddelbuettel mentioned)
In case of IDE
This is much simpler in most cases, just increase the plotting area by dragging the margins and then re-run your code (close any par() if you have any opened before and create new one)