How do I overlay an image on to a ggplot? - r

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")

Related

ggplot2: same bar widths when saving pdf

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?

how to fit multiple ggplot charts on a a4 pdf?

Consider this simple example
library(dplyr)
library(ggplot)
library(patchwork)
mytib <- tibble(group = as.factor(c(1,1,1,1,2,2,2,2,3,3,3,3)),
y = c(1,2,3,42,50,400,3,3,2,3,3,4),
x = c('a','b','c','d','a','b','c','d','a','b','c','d'))
p1 <- mytib %>% ggplot(aes(x = x, y = y, fill = group)) +
geom_col() + ggtitle('this is a messy chart')+
coord_flip()+
xlab('Hello') +
ylab('This is a nice comment')
Now I use patchwork to combine the charts so that I obtain 3 rows of 3 charts each on a regular a4 pdf page
(p1 + p1 + p1)/
(p1 + p1 + p1)/
(p1 + p1 + p1)
ggsave(file="a4_output.pdf", width = 210, height = 297, units = "mm")
The output is a nice a4 pdf but the problem is that the charts on the pdf are very stretched. Is there a way to preserve their original ratio (on the pdf) so that they look less stretched, even three on a single row? I dont mind if they look smaller.
Any ideas?
Thanks!
The problem is the changing plot ratio. Now, coord_flip doesn't work with fixed ratio - but user Axeman explained how to deal with this problem - use ggstance!! I changed the plot mildly, using geom_colh, switched your x and y, and added a fixed ratio. Now we can use your plot layout, or simply pack the plots in a list and use wrap_plots. I did not use the reprex output because I am sharing the screenshot from the pdf output.
library(tidyverse)
library(patchwork)
library(ggstance)
mytib <- tibble(group = as.factor(c(1,1,1,1,2,2,2,2,3,3,3,3)),
y = c(1,2,3,42,50,400,3,3,2,3,3,4),
x = c('a','b','c','d','a','b','c','d','a','b','c','d'))
p1 <-
mytib %>% ggplot(aes(x = y, y = x, fill = group)) +
geom_colh() + ggtitle('this is a messy chart')+
coord_fixed(100)+
xlab('Hello') +
ylab('This is a nice comment')
plotlist <- list()
for(i in 1:9) { plotlist[[i]] <- p1 }
ggsave(plot = wrap_plots(plotlist), file="a4_output.pdf", width = 210, height = 297, units = "mm")
Another solution is to add another row - not with an empty plot, but using plot_layout
(p1 + p1 + p1)/
(p1 + p1 + p1)/
(p1 + p1 + p1) +
plot_layout(nrow = 4)

R: Remove border when plotting with ggplot2 + ggExtra + cowplot

I couldn't find the way not to plot the outer frame when combining graphs through ggplot2 + ggExtra + cowplot. I am not sure where I have to tell R, but suspect the issue to lie in ggExtra. Here is an example:
require(ggplot2)
require(cowplot)
require(ggExtra)
# Creat a graph
A <- ggplot(mpg, aes(x = cty, y = hwy, colour = factor(cyl))) + geom_point(size = 2.5)
# Add marginal histogram
B <- ggExtra::ggMarginal(A,type = 'histogram', margins = 'x', size = 9)
# Combine through cowplot
combo <- plot_grid(B,B,labels=c("A","B"))
plot(combo) # looks fine
# Re-combine through cowplot
plot_grid(B,combo,ncol=1,rel_heights = c(2,3)) # that's where I got an unwanted nasty frame around 'combo'
Any hint would be greatly appreciated!
p <- plot_grid(B,combo,ncol=1,rel_heights = c(2,3))
p <- p + panel_border(remove = TRUE)
https://rdrr.io/cran/cowplot/man/panel_border.html

R plot background map from Geotiff with ggplot2

With the R base plot, I can plot any geotiff with the following command:
library("raster")
plot(raster("geo.tiff"))
For example, downloading this data, I would do the follwing:
setwd("C:/download") # same folder as the ZIP-File
map <- raster("smr25musterdaten/SMR_25/SMR_25KOMB_508dpi_LZW/SMR25_LV03_KOMB_Mosaic.tif")
How do you Plot GeoTif Files in ggplot2?
EDIT:
1: I've replaced the greyscale map from the sample files with a coloured map to ilustrate the problem of the missing colortable.
2: With the help of Pascals answer, I was able to adapt and improve this solution and make it more dynamic to the input tif. I will post the answer below.
Here is an alternative using function gplot from rasterVis package.
library(rasterVis)
library(ggplot2)
setwd("C:/download") # same folder as the ZIP-File
map <- raster("smr25musterdaten/SMR_25/SMR_25KGRS_508dpi_LZW/SMR25_LV03_KGRS_Mosaic.tif")
gplot(map, maxpixels = 5e5) +
geom_tile(aes(fill = value)) +
facet_wrap(~ variable) +
scale_fill_gradient(low = 'white', high = 'black') +
coord_equal()
If you want to use the color table:
coltab <- colortable(map)
coltab <- coltab[(unique(map))+1]
gplot(map, maxpixels=5e5) +
geom_tile(aes(fill = value)) +
facet_wrap(~ variable) +
scale_fill_gradientn(colours=coltab, guide=FALSE) +
coord_equal()
With colors:
Like I noted in my original question, I was able to solve the problem with Pascals input and this solution. This is the way the colors came out correctly:
library(rasterVis) # in order to use raster in ggplot
setwd("C:/download") # same folder as the ZIP-File
map <- raster("smr25musterdaten/SMR_25/SMR_25KOMB_508dpi_LZW/SMR25_LV03_KOMB_Mosaic.tif") # sample data from [here][2]
# turn raster into data.frame and copy the colortable
map.df <- data.frame(rasterToPoints(map))
colTab <- colortable(map)
# give the colors their apropriate names:
names(colTab) <- 0:(length(colTab) - 1)
# only define the colors used in the raster image
from <- min(map.df[[3]], na.rm = T)+1
to <- max(map.df[[3]], na.rm = T)+1
used_cols <- colTab[from:to]
# plot:
gplot(map, maxpixels = 5e5) +
facet_wrap(~ variable) +
geom_tile(aes(fill = value)) +
scale_fill_gradientn(colours=used_cols) +
coord_equal()
I've improved the solution and created a little function that allows a direct import into ggplot (with the neat option of turing it into greyscale).
require(rasterVis)
require(raster)
require(ggplot2)
setwd("C:/download") # same folder as the ZIP-File
map <- raster("smr25musterdaten/SMR_25/SMR_25KOMB_508dpi_LZW/SMR25_LV03_KOMB_Mosaic.tif")
# Function to get the colourtable with the option of returing it in greyscale
getColTab <- function(rasterfile, greyscale = F){
colTab <- raster::colortable(rasterfile)
if(greyscale == T){
colTab <- col2rgb(colTab)
colTab <- colTab[1,]*0.2126 + colTab[2,]*0.7152 + colTab[3,]*0.0722
colTab <- rgb(colTab,colTab,colTab, maxColorValue = 255)
}
names(colTab) <- 0:(length(colTab)-1)
return(colTab)
}
gplot(map, maxpixels = 10e5) +
geom_tile(aes(fill = factor(value))) +
scale_fill_manual(values = getColTab(map),guide = "none") +
coord_equal()

Stacked bubble chart, "bottom aligned"

New to programming and first time post.
I'm trying to create a stacked bubble chart to display how a population breaks down into it's proportions. My aim is to write this as a function so that I can use it repeatedly easily, but I need to get the meat of the code sorted before turning it to a function.
This is the type of plot I would like:
This is the code I've tried so far:
library(ggplot2)
# some data
observations = c(850, 500, 200, 50)
plot_data = data.frame(
"x" = rep.int(1,length(observations))
,"y" = rep.int(1,length(observations))
, "size" = rep.int(1,length(observations))
,"colour" = c(1:length(observations))
)
# convert to percentage for relative sizes
for (i in 1:length(observations))
{
plot_data$size[i] = (observations[i]/max(observations))*100
}
ggplot(plot_data,aes(x = x, y = y)) +
geom_point(aes(size = size, color = colour)) +
scale_size_identity() +
scale_y_continuous (limits = c(0.5, 1.5)) +
theme(legend.position = "none")
This produces a bullseye type image.
My approach has been to try and work out how the circle radii are calculated, and then update the y value in the for loop for each entry such that all the circles touch at the base - this is where I have been failing.
So my question:
How can I work out what the y coordinates for each circle needs to be?
Thank you for any help and hints.
I think this simplifies the answer that Henrick found:
circle <- function(center, radius, group) {
th <- seq(0, 2*pi, len=200)
data.frame(group=group,
x=center[1] + radius*cos(th),
y=center[2] + radius*sin(th))
}
# Create a named vector for your values
obs <- c(Org1=500, Org2=850, Org3=50, Org4=200)
# this reverse sorts them (so the stacked layered circles work)
# and makes it a list
obs <- as.list(rev(sort(obs)))
# need the radii
rads <- lapply(obs, "/", 2)
# need the max
x <- max(sapply(rads, "["))
# build a data frame of created circles
do.call(rbind.data.frame, lapply(1:length(rads), function(i) {
circle(c(x, rads[[i]]), rads[[i]], names(rads[i]))
})) -> dat
# make the plot
gg <- ggplot(dat)
gg <- gg + geom_polygon(aes(x=x, y=y, group=group, fill=group),
color="black")
gg <- gg + coord_equal()
gg <- gg + ggthemes::theme_map()
gg <- gg + theme(legend.position="right")
gg
You can tweak the guides/colors with standard ggplot functions.

Resources