I am trying to make figures for a manuscript, that should be written with MS Word, which does not accept figures in pdf format. The journal asks first draft with figures embedded in the Word file. These figures should have resolution minimum of 300 dpi and have a width of either 169 mm or 81 mm (two/one column). I notice that when I specify the resolution of the picture to 300 (res = 300), the font size is bound to this value. This works fine with some figures (the first example, example.png), and worse with others (example2.png). How can I control the font size so that the dimensions and resolution of the figure remain fixed?
library(ggplot2)
library(grid)
data(iris)
vplayout <- function(x, y) viewport(layout.pos.row = x, layout.pos.col = y)
p <- ggplot(iris, aes(Species, Petal.Length))
q <- ggplot(iris, aes(Species, Petal.Width))
len <- p + geom_boxplot()
wid <- q + geom_boxplot()
png("example.png", width = 169, height = 100, units = "mm", res = 300)
grid.newpage()
pushViewport(viewport(layout = grid.layout(1, 2)))
print(len, vp = vplayout(1, 1))
print(wid, vp = vplayout(1, 2))
dev.off()
png("example2.png", width = 81, height = 100, units = "mm", res = 300)
grid.newpage()
pushViewport(viewport(layout = grid.layout(1, 2)))
print(len, vp = vplayout(1, 1))
print(wid, vp = vplayout(1, 2))
dev.off()
In other words, I would like to decrease the font size in example2.png, but keep the layout and dimensions of the two plots as they are.
Example.png
Example2.png
Using the base_size argument to the theme_XXX() function, you can change the overall font sizes for all the text.
png("example2.png", width = 81, height = 100, units = "mm", res = 300)
grid.newpage()
pushViewport(viewport(layout = grid.layout(1, 2)))
print(len + theme_gray(base_size=12*(81/169)), vp = vplayout(1, 1))
print(wid + theme_gray(base_size=12*(81/169)), vp = vplayout(1, 2))
dev.off()
Simply change the pointsize attribute of png. For example:
png(filename="plot4.png", width=580, height=700, pointsize=20)
As far as I can tell the fontsize is the same regardless of the resolution and device size. You can check it in the following examples:
library(grid)
png("example1.png", width = 30, height = 5, units = "mm", res = 200)
g <- grid.text("testing font size", gp=gpar(fontsize=12))
width <- convertUnit(grobWidth(g), "mm")
height <- convertUnit(grobHeight(g), "mm", "y")
grid.rect(width=width, height=height, gp=gpar(lty=2, fill=NA))
dev.off()
png("example2.png", width = 60, height = 5, units = "mm", res = 500)
grid.text("testing font size", gp=gpar(fontsize=12))
grid.rect(width=width, height=height, gp=gpar(lty=2, fill=NA))
dev.off()
But it seems you actually wanted to change the font size.
Related
I'm currently working on a ggplot column chart and I'm trying to add a logo to the bottom right. This is the code to the chart:
df <- data.frame(Names = c("2001", "2004", "2008", "2012", "2018"),
Value = c(47053, 68117, 171535, 241214, 234365))
p <- ggplot(df, aes(x = Names, y = Value)) +
geom_col(fill = "#DB4D43") + theme_classic() +
geom_text(aes(label = Value, y = Value + 0.05),
position = position_dodge(0.9),
vjust = 0)
I followed this tutorial I found online, but for some reason, it won't let me adjust the size of the logo and it ends up looking too small no matter what I type on the image_scale function.
img <- image_read("Logo.png")
img <- image_scale(img,"200")
img <- image_scale(img, "x200")
g <- rasterGrob(img)
size = unit(4, "cm")
heights = unit.c(unit(1, "npc") - size,size)
widths = unit.c(unit(1, "npc") - size, size)
lo = grid.layout(2, 2, widths = widths, heights = heights)
grid.show.layout(lo)
grid.newpage()
pushViewport(viewport(layout = lo))
pushViewport(viewport(layout.pos.row=1:1, layout.pos.col = 1:2))
print(p, newpage=FALSE)
popViewport()
pushViewport(viewport(layout.pos.row=2:2, layout.pos.col = 2:2))
print(grid.draw(g), newpage=FALSE)
popViewport()
g = grid.grab()
grid.newpage()
grid.draw(g)
rm(list=ls())
I found another tutorial and, after trying this, it doesn't show anything at all when I run it.
mypng <- readPNG('Logo.png')
print(mypng)
logocomp <- p + annotation_raster(mypng, ymin = 4.5,ymax= 5,xmin = 30,xmax = 35)
You could use the cowplot package to easily add an image to any plot made with ggplot. I used the R logo as the image that needs to be added to the plot (using magick package to read it). One advantage of using cowplot is that you can easily specify the size and position of both the plot and the image.
library(cowplot)
library(magick)
img <- image_read("Logo.png")
# Set the canvas where you are going to draw the plot and the image
ggdraw() +
# Draw the plot in the canvas setting the x and y positions, which go from 0,0
# (lower left corner) to 1,1 (upper right corner) and set the width and height of
# the plot. It's advisable that x + width = 1 and y + height = 1, to avoid clipping
# the plot
draw_plot(p,x = 0, y = 0.15, width = 1, height = 0.85) +
# Draw image in the canvas using the same concept as for the plot. Might need to
# play with the x, y, width and height values to obtain the desired result
draw_image(img,x = 0.85, y = 0.02, width = 0.15, height = 0.15)
Try using grid.raster, something like:
grid::grid.raster(img, x = 0.15, y = 0.05, width = unit(0.5, 'inches'))
x and y to define location of the image.
Adjust the number in unit() to resize the plot.
I was running the example here, and noticed that the horizontal arrow connecting the Total to the Ineligible boxGrobs doesn't always touch the left-edge of the the Ineligible boxGrob.
It seems to depend on the width of the viewing window in RStudio. This does not seem to be the case for the vertical arrow, which always seem to perfectly connect to the top of the correct boxGrob.
Is there a way to force the arrow to touch the side of the box and not go any further? I am trying to save the output to a pdf, and by default it seems to use a wider plotting window so all of my horizontal arrows don't align with the correct boxes.
Narrow plotting window:
Wide plotting window:
I have tried manually creating a viewport with a wider area, but that didn't change anything in the pdf:
Code:
library(grid)
library(Gmisc)
vp <- grid::viewport(x = 10, y = 10, clip = 'on', xscale = c(0, 10),
yscale = c(0, 10), default.units = 'inch')
grid::pushViewport(vp)
leftx <- .25
midx <- .5
rightx <- .75
width <- .4
gp <- gpar(fill = "lightgrey")
# add box/connectors to the plot
(total <- boxGrob("Total\n N = NNN",
x=midx, y=.9, box_gp = gp, width = width))
(rando <- boxGrob("Randomized\n N = NNN",
x=midx, y=.75, box_gp = gp, width = width))
connectGrob(total, rando, "v")
(inel <- boxGrob("Ineligible\n N = NNN",
x=rightx, y=.825, box_gp = gp, width = .25, height = .05))
connectGrob(total, inel, "-")
For the time being, this problem can be solved using absolute unit.
Example code:
(inel <- boxGrob("Ineligible\n N = NNN",
x=rightx, y=.825, box_gp = gp, width = unit(2, "inch"), height = .05))
I'm trying to align a legend at the bottom of a chart, centered respectively to that chart. But I'm having trouble aligning it.
The pictures below show the current rendering, where you can clearly see the legend is misaligned (red line for guidance).
library(grid)
draw <- function() {
masterLayout <- grid.layout(
nrow = 4,
ncol = 1,
heights = unit(c(0.1, 0.7, 0.1, 0.1), rep("null", 4)))
vp1 <- viewport(layout.pos.row=1, layout.pos.col = 1, name="title")
vp2 <- viewport(layout.pos.row=2, layout.pos.col = 1, name="plot")
vp3 <- viewport(layout.pos.row=3, layout.pos.col = 1, name="legend")
vp4 <- viewport(layout.pos.row=4, layout.pos.col = 1, name="caption")
pushViewport(
vpTree(viewport(layout = masterLayout, name = "master"),
vpList(vp1, vp2, vp3, vp4)))
## Draw main plot
seekViewport("plot")
pushViewport(viewport(width=unit(.8, "npc")))
grid.rect(gp=gpar("fill"="red")) # dummy chart
popViewport(2)
## Draw legend
seekViewport("legend")
colors <- list(first="red", second="green", third="blue")
data.names <- names(colors)
legend.cols <- length(data.names)
pushViewport(viewport(
width = unit(0.8, "npc"),
layout = grid.layout(ncol=legend.cols * 2,
nrow=1,
widths=unit(2.5, "cm"),
heights=unit(0.25, "npc"))))
idx <- 0
for(name in data.names) {
idx <- idx + 1
pushViewport(viewport(layout.pos.row=1, layout.pos.col=idx))
grid.circle(x=0, r=0.35, gp=gpar(fill=colors[[name]], col=NA))
popViewport()
idx <- idx + 1
pushViewport(viewport(layout.pos.row=1, layout.pos.col=idx))
grid.text(x=unit(-0.8, "npc"), "text", just="left")
popViewport()
}
popViewport(2)
}
draw()
I don't understand why you're doing so much with individual viewports. It makes it very complex. I would have thought it was much easier to have one viewport for the legend and then control the x coordinate of the text and circles relative to that. Something like this; I'm not sure it's exactly what you want but it feels it should be easy to control if you need to tweak it:
library(grid)
draw <- function() {
masterLayout <- grid.layout(
nrow = 4,
ncol = 1,
heights = unit(c(0.1, 0.7, 0.1, 0.1), rep("null", 4)))
vp1 <- viewport(layout.pos.row=1, layout.pos.col = 1, name="title")
vp2 <- viewport(layout.pos.row=2, layout.pos.col = 1, name="plot")
vp3 <- viewport(layout.pos.row=3, layout.pos.col = 1, name="legend")
vp4 <- viewport(layout.pos.row=4, layout.pos.col = 1, name="caption")
pushViewport(
vpTree(viewport(layout = masterLayout, name = "master"),
vpList(vp1, vp2, vp3, vp4)))
## Draw main plot
seekViewport("plot")
pushViewport(viewport(width=unit(.8, "npc")))
grid.rect(gp=gpar("fill"="red")) # dummy chart
popViewport(2)
## Draw legend
seekViewport("legend")
colors <- list(first="red", second="green", third="blue")
lab_centers <- seq(from = 0.2, to = 0.8, length = length(colors))
disp <- 0.03 # how far to left of centre circle is, and to right text is, in each label
for(i in 1:length(colors)){
grid.circle(x = lab_centers[i] - disp, r = 0.1, gp=gpar(fill = colors[[i]], col=NA))
grid.text("text", x = lab_centers[i] + disp)
}
popViewport(2)
}
draw()
grid.lines(c(0.5, 0.5), c(0, 1))
If your legend labels aren't all the same length, you probably need to left align them and tweak the way I've used a disp parameter but shouldn't be too hard.
I would like to export a data frame as a (png) image. I've tried with this code, but the table get clipped vertically.
library(ggplot2)
library(gridExtra)
df <- data.frame(a=1:30, b=1:30)
png("test.png")
p<-tableGrob(df)
grid.arrange(p)
dev.off()
Is there a way to avoid this behaviour without having to set manually the size of the image?
You can change this behavior by specifying a height and width.
png("test.png", height=1000, width=200)
p<-tableGrob(df)
grid.arrange(p)
dev.off()
Anyway, it is usually not very helpful to save tables as pictures.
You can do like this:
library(gridExtra)
png("test.png", height = 50*nrow(df), width = 200*ncol(df))
grid.table(df)
dev.off()
This works just fine:
library(gridExtra)
df = data.frame("variables" = c("d_agr","d_def","d_frig","d_hidro","d_roads","d_silos"),
"coeficient" = c(0.18,0.19,-0.01,-0.25,-0.17,0.09))
png("output.png", width=480,height=480,bg = "white")
grid.table(df)
dev.off()
To get the size of the table you have to generate a tableGrob first, than you can get the height and width parameters. The parameters are "grobwidth", they have to be converted into inch.
Here is my solution (base on gridExtra vignettes), it works fine for me.
library(grid)
library(gridExtra)
gridFtable <- function(d, pd = 4, fontsize = 10, fontfamily = "PT Mono") {
## set plot theme
t1 <- ttheme_default(padding = unit(c(pd, pd), "mm"), base_size = fontsize, base_family = fontfamily)
## character table with added row and column names
extended_matrix <- cbind(c("", rownames(d)), rbind(colnames(d), as.matrix(d)))
## get grob values
g <- tableGrob(extended_matrix, theme = t1)
## convert widths from grobwidth to inch
widthsIn <- lapply(g$widths, function(x) {convertUnit(x, unitTo = "inch", valueOnly = TRUE)})
heigthsIn <- lapply(g$heights, function(x) {convertUnit(x, unitTo = "inch", valueOnly = TRUE)})
## calculate width and height of the table
w <- sum(unlist(widthsIn)) - 1*convertUnit(unit(pd, "mm"), unitTo = "inch", valueOnly = TRUE)
h <- sum(unlist(heigthsIn)) - 1*convertUnit(unit(pd, "mm"), unitTo = "inch", valueOnly = TRUE)
return(list(grobData = g, data = d, width = w, heigth = h, theme = t1))
}
saveTable <- gridFtable(data.frame(a=1:30, b=1:30))
png(file = "./test.png", width = saveTable$width, height = saveTable$heigth, units = "in", res = 100)
grid.newpage()
grid.table(saveTable$data, rows = NULL, theme = saveTable$theme)
dev.off()
getwd()
I have stacked into the question: I need to plot the image with DPI=1200 and specific print size.
By default the png looks ok...
png("test.png",width=3.25,height=3.25,units="in",res=1200)
par(mar=c(5,5,2,2),xaxs = "i",yaxs = "i",cex.axis=1.3,cex.lab=1.4)
plot(perf,avg="vertical",spread.estimate="stddev",col="black",lty=3, lwd=3)
dev.off()
But when I apply this code, the image became really terrible it's not scaling (fit) to the size that is needed. What did I miss? How to "fit" the image to the plot?
,
A reproducible example:
the_plot <- function()
{
x <- seq(0, 1, length.out = 100)
y <- pbeta(x, 1, 10)
plot(
x,
y,
xlab = "False Positive Rate",
ylab = "Average true positive rate",
type = "l"
)
}
James's suggestion of using pointsize, in combination with the various cex parameters, can produce reasonable results.
png(
"test.png",
width = 3.25,
height = 3.25,
units = "in",
res = 1200,
pointsize = 4
)
par(
mar = c(5, 5, 2, 2),
xaxs = "i",
yaxs = "i",
cex.axis = 2,
cex.lab = 2
)
the_plot()
dev.off()
Of course the better solution is to abandon this fiddling with base graphics and use a system that will handle the resolution scaling for you. For example,
library(ggplot2)
ggplot_alternative <- function()
{
the_data <- data.frame(
x <- seq(0, 1, length.out = 100),
y = pbeta(x, 1, 10)
)
ggplot(the_data, aes(x, y)) +
geom_line() +
xlab("False Positive Rate") +
ylab("Average true positive rate") +
coord_cartesian(0:1, 0:1)
}
ggsave(
"ggtest.png",
ggplot_alternative(),
width = 3.25,
height = 3.25,
dpi = 1200
)
If you'd like to use base graphics, you may have a look at this. An extract:
You can correct this with the res= argument to png, which specifies the number of pixels per inch. The smaller this number, the larger the plot area in inches, and the smaller the text relative to the graph itself.
An alternate solution to lowering the size of the various components with pointsize and the cex functions is to increase the size of the graph to compensate. This maintains the scale by increasing the size of everything instead of only some components. Your graph will be larger when exported, but will retain the improved resolution if manually decreased in size should you wish to retain the original smaller size.
The png default settings are dpi=72, height=480, width=480. So to maintain the same scale, you need to multiply height and width by the resolution/72. Using your example of width = height = 3.25 inches and a desired resolution dpi of 1200, we will adjust by 1200/72 (equal to 50/3):
reso <- 1200
length <- 3.25*reso/72
png("test.png",units="in",res=reso,height=length,width=length)
par(mar=c(5,5,2,2),xaxs = "i",yaxs = "i",cex.axis=1.3,cex.lab=1.4)
plot(perf,avg="vertical",spread.estimate="stddev",col="black",lty=3, lwd=3)
dev.off()