I want all bars to have the same width.
My code works when not saving it to a pdf:
library(ggplot2)
dat <- as.data.frame(mtcars)
# convert rownames to a separate column
dat <- cbind(rownames(dat), data.frame(dat, row.names=NULL))
names(dat)[which(names(dat) == "rownames(dat)")] <- "type"
p <- ggplot(data=dat[1:10,]) +
geom_col(aes(x = reorder(type, wt), y = wt), position=position_dodge2(width = 0.1, preserve = "single"), width = 0.1) +
coord_flip()
p
But when I try to save the plot in a pdf, then the bars have slightly different widths.
pdf(paste0(path_out,
"test.pdf"),
width=10, height=4)
print(p)
dev.off()
With my real data it is even worse:
I am using Windows 7 Enterprise. How can I fix this?
Related
I am trying to display sales (y-axis) week over week (x-axis) for 890 vendors. I want to display this data using a facet wrap to quickly see where vendors are having a spike in sales. The plot in my RStudio console looks as such. This Makes sense, as rendering the plot here isnt the best view, however how can I properly format my plots onto a PDF even if it requires multiple pages of PDFs. Code for plot
ggplot(Holiday_Spike_Table, aes(x = FSCL_WK, y = SLS))+
geom_col()+
facet_wrap(~MVNDR_NM)
Here's how to make multiple pages - each with 5x5 vendors.
I'll use some dummy data with 890 vendors.
library("tidyverse")
df <- data.frame(
vendor = rep(seq_len(890), each = 30) ,
x = rep.int(seq_len(30), 890),
y = runif(890 * 30),
group = (rep(seq_len(890), each = 30) - 1) %/% 25
)
Split into groups of 25 vedors. Each plot has a 5x5 facet.
plots <-
df %>%
group_by(group) %>%
group_map(function(g, ...) ggplot(g, aes(x, y)) + geom_line() + facet_wrap(~vendor, ncol = 5))
Save so that each plot has its own page.
ggsave("plots.pdf", gridExtra::marrangeGrob(plots, nrow = 1, ncol = 1))
Why not plug all plots individually in one single pdf and get rid of facet_wrap completely.
Reproducible example:
library(ggplot2)
library(stringi)
## generate dummy data
df <- data.frame(x=sample(1:10000, 1000, replace=TRUE),y=sample(1:100000, 1000, replace=TRUE))
df$id <- stri_rand_strings(n = nrow(df)/10, length = 4, pattern = "[A-Za-z0-9]")
Following gives a similar plot as yours
ggplot(data = df,aes(x=x,y=y)) + geom_point() + geom_smooth() + facet_wrap(~id)
Getting rid of facet_wrap completely and flushing the plots to a single pdf
pdf("pathtopdf.pdf", onefile = TRUE)
for (i in unique(df$id)) {
plot(ggplot(df[df$id==i,], aes(x = x, y = x)) + geom_point() + geom_smooth())
}
dev.off()
This should seem relatively straightforward but I can't find an argument which would allow me to do this and I've searched Google and Stack for an answer.
Sample code:
library(ggplot2)
library(plotly)
dat <- data.frame(cond = factor(rep(c("A","B"), each=200)), rating = c(rnorm(200),rnorm(200, mean=.8)))
p <- ggplot(dat, aes(x=cond, y=rating, fill=cond)) + geom_boxplot()
p <- ggplotly(p)
This outputs the first graph, I would want something like the second.
I tried including colour=cond but that gets rid of the median.
Two possible hacks for consideration, using the same dataset as Marco Sandri's answer.
Hack 1. If you don't really need it to work in plotly, just static ggplot image:
ggplot(dat, aes(x=cond, y=rating, fill=cond)) +
geom_boxplot() +
geom_boxplot(aes(color = cond),
fatten = NULL, fill = NA, coef = 0, outlier.alpha = 0,
show.legend = F)
This overlays the original boxplot with a version that's essentially an outline of the outer box, hiding the median (fatten = NULL), fill colour (fill = NA), whiskers (coef = 0) & outliers (outlier.alpha = 0).
However, it doesn't appear to work well with plotly. I've tested it with the dev version of ggplot2 (as recommended by plotly) to no avail. See output below:
Hack 2. If you need it to work in plotly:
ggplot(dat %>%
group_by(cond) %>%
mutate(rating.IQR = case_when(rating <= quantile(rating, 0.3) ~ quantile(rating, 0.25),
TRUE ~ quantile(rating, 0.75))),
aes(x=cond, y=rating, fill=cond)) +
geom_boxplot() +
geom_boxplot(aes(color = cond, y = rating.IQR),
fatten = NULL, fill = NA)
(ggplot output is same as above)
plotly doesn't seem to understand the coef = 0 & output.alpha = 0 commands, so this hack creates a modified version of the y variable, such that everything below P30 is set to P25, and everything above is set to P75. This creates a boxplot with no outliers, no whiskers, and the median sits together with the upper box limit at P75.
It's more cumbersome, but it works in plotly:
Here is an inelegant solution based on grobs:
set.seed(1)
dat <- data.frame(cond = factor(rep(c("A","B"), each=200)),
rating = c(rnorm(200),rnorm(200, mean=.8)))
library(ggplot2)
library(plotly)
p <- ggplot(dat, aes(x=cond, y=rating, fill=cond)) + geom_boxplot()
# Generate a ggplot2 plot grob
g <- ggplotGrob(p)
# The first box-and-whiskers grob
box_whisk1 <- g$grobs[[6]]$children[[3]]$children[[1]]
pos.box1 <- which(grepl("geom_crossbar",names(box_whisk1$children)))
g$grobs[[6]]$children[[3]]$children[[1]]$children[[pos.box1]]$children[[1]]$gp$col <-
g$grobs[[6]]$children[[3]]$children[[1]]$children[[pos.box1]]$children[[1]]$gp$fill
# The second box-and-whiskers grob
box_whisk2 <- g$grobs[[6]]$children[[3]]$children[[2]]
pos.box2 <- which(grepl("geom_crossbar",names(box_whisk2$children)))
g$grobs[[6]]$children[[3]]$children[[2]]$children[[pos.box2]]$children[[1]]$gp$col <-
g$grobs[[6]]$children[[3]]$children[[2]]$children[[pos.box2]]$children[[1]]$gp$fill
library(grid)
grid.draw(g)
P.S. To my knowledge, the above code cannot be used for generating plotly graphs.
For the general plot in R, legend is used to number a figure.
set.seed(100)
Mydata=rnorm(65)
Year=1950:2014
plot(x=Year,y=Mydata,type = "l")
legend("topleft","(a)",bty = "n")
I wonder how we can do the same thing using ggplot2. Thanks.
Using grid it can be done independently of the data:
library(ggplot2)
qplot(Year, Mydata, geom = "line")
library(grid)
grid.text("(a)", 0.15, 0.85)
As of version 2.2.0, ggplot2 allows to plot subtitles and captions which can be utilized for this purpose.
subtitle (top left)
# create data frame as required by ggplot2
mydf <- data.frame(Year, Mydata)
library(ggplot2)
p <- ggplot(mydf, aes(Year, Mydata)) +
geom_line()
# plot subtitle (top left)
p + labs(subtitle = "(a)")
caption (bottom right)
# plot caption (bottom right)
p + labs(caption = "(a)")
A way with annotate:
library(ggplot2)
set.seed(100)
Mydata=rnorm(65)
Year=1950:2014
data <- data.frame(Mydata = Mydata, Year = Year)
#plot
ggplot(data, aes(Year, Mydata)) +
geom_line() +
annotate('text', x = 1960, y = 2, label = '(a)')
Output:
The following code used to work but doesn't anymore. Does anybody have an idea what's going on? It must be some change in the underlying gtable code.
require(cowplot) # for plot_grid()
require(grid) # for unit_max()
# make two plots
plot.iris <- ggplot(iris, aes(Sepal.Length, Sepal.Width)) +
geom_point() + facet_grid(. ~ Species) + stat_smooth(method = "lm") +
background_grid(major = 'y', minor = "none") +
panel_border()
plot.mpg <- ggplot(mpg, aes(x = cty, y = hwy, colour = factor(cyl))) +
geom_point(size=2.5)
g.iris <- ggplotGrob(plot.iris) # convert to gtable
g.mpg <- ggplotGrob(plot.mpg) # convert to gtable
iris.widths <- g.iris$widths[1:3] # extract the first three widths,
# corresponding to left margin, y lab, and y axis
mpg.widths <- g.mpg$widths[1:3] # same for mpg plot
max.widths <- unit.pmax(iris.widths, mpg.widths) # calculate maximum widths
g.iris$widths[1:3] <- max.widths # assign max. widths to iris gtable
g.mpg$widths[1:3] <- max.widths # assign max widths to mpg gtable
plot_grid(g.iris, g.mpg, labels = "AUTO", ncol = 1)
The resulting plot is the following:
What it should look like is this (with the y axis lines perfectly aligned vertically):
The error seems to happen in this line:
g.iris$widths[1:3] <- max.widths
Any insight would be appreciated.
Note that similar solutions have been used for a long time, see e.g. here. And the plot_grid() function in cowplot uses code like this also to align plots, and it still works. So this has me completely mystified.
Edit
With grid version 3.3.0, this is no longer an issue. That is, the lines of code containing grid:::unit.list() below can be deleted.
The issue is to do with the way units are set. Look at g.iris$widths. You'll notice that the numerals are there but the units have been dropped. See this question and answer, and this one. After converting the plots to gtables, you need: g.iris$widths = grid:::unit.list(g.iris$widths)
require(grid) # for unit_max()
require(cowplot)
# make two plots
plot.iris <- ggplot(iris, aes(Sepal.Length, Sepal.Width)) +
geom_point() + facet_grid(. ~ Species) + stat_smooth(method = "lm") +
background_grid(major = 'y', minor = "none") +
panel_border()
plot.mpg <- ggplot(mpg, aes(x = cty, y = hwy, colour = factor(cyl))) +
geom_point(size=2.5)
g.iris <- ggplotGrob(plot.iris) # convert to gtable
g.mpg <- ggplotGrob(plot.mpg) # convert to gtable
g.iris$widths = grid:::unit.list(g.iris$widths)
g.mpg$widths = grid:::unit.list(g.mpg$widths)
iris.widths <- g.iris$widths[1:3] # extract the first three widths,
# corresponding to left margin, y lab, and y axis
mpg.widths <- g.mpg$widths[1:3] # same for mpg plot
max.widths <- unit.pmax(iris.widths, mpg.widths) # calculate maximum widths
g.iris$widths[1:3] <- max.widths # assign max. widths to iris gtable
g.mpg$widths[1:3] <- max.widths # assign max widths to mpg gtable
plot_grid(g.iris, g.mpg, labels = "AUTO", ncol = 1)
I'd like to read an image from the web. e.g.
http://api.altmetric.com/donut/502878_64x64.png
and insert it into the top right of a ggplot
df <- data.frame(x=1:10, y=sample(1:100,10))
# a fake plot to try it on.
ggplot(df, aes(x,y)) + geom_point(size = 2)
How would I do this?
You are looking for annotation_raster and readPNG
mypngfile <- download.file('http://api.altmetric.com/donut/502878_64x64.png', destfile = 'mypng.png', mode = 'wb')
library(png)
mypng <- readPNG('mypng.png')
p <- qplot(mpg, wt, data = mtcars) + theme_bw()
p + annotation_raster(mypng, ymin = 4.5,ymax= 5,xmin = 30,xmax = 35) +
geom_point()
These new features (and more examples) are described here
The correct solution was this:
# This was one of my issues, reading a png from the web
my_image <- readPNG(getURLContent('http://path.to/image.png'))
p1 + annotation_raster(my_image, ymin = 4,ymax= 5,xmin = 30,xmax = 40)
Just adding an update from the terrific package Magick, which will even allow a GIF to be overlaid on ggplot images:
library(ggplot2)
library(magick)
library(here) # For making the script run without a wd
library(magrittr) # For piping the logo
# Make a simple plot and save it
ggplot(mpg, aes(displ, hwy, colour = class)) +
geom_point() +
ggtitle("Cars") +
ggsave(filename = paste0(here("/"), last_plot()$labels$title, ".png"),
width = 5, height = 4, dpi = 300)
# Call back the plot
# Now call back the plot
background <- image_read(paste0(here("/"), "Cars.png"))
# And bring in a logo
logo_raw <- image_read("https://i.imgur.com/e1IneGq.jpg")
frames <- lapply(logo_raw, function(frame) {
image_composite(background, frame, offset = "+70+800")
})
animation <- image_animate(image_join(frames))
image_write(animation, "~/Cars_Travolta.gif")