Use an Image as Area Fill in an R Plot

Use an image as area fill in an R plot

This is purely for novelty purposes, right?

In the code below, we cover up the portion of the dollar bill above the blue curve using geom_ribbon.

library(jpeg)
library(grid)
library(ggplot2)
library(scales)
theme_set(theme_classic())

# Load image of dollar bill and convert to raster grob
download.file("https://2marks.files.wordpress.com/2013/07/george-washington-on-one-dollar-bill.jpg",
"dollar_bill.jpg")
db = readJPEG("dollar_bill.jpg")
db = rasterGrob(db, interpolate=TRUE)

# Fake data
set.seed(3)
dat = data.frame(x=1913:2009)
dat$y2 = seq(5,950, length=nrow(dat)) + rnorm(nrow(dat), 0, 5)
dat$y1 = seq(100,5,length=nrow(dat)) + c(0, -0.5, rnorm(nrow(dat) - 2, 0, 2))

ggplot(dat, aes(x, y1)) +
annotation_custom(db, xmin=1913, xmax=2009, ymin=0, ymax=100) +
geom_ribbon(aes(ymin=y1, ymax=100), fill="white") +
geom_line(size=1, colour="blue") +
geom_line(aes(y=y2/10), size=1, colour="red") +
coord_fixed(ratio=1/2.5) +
scale_y_continuous(limits=c(0,100), expand=c(0,0),
sec.axis=sec_axis(~.*10, name="Currency in Circulation\n(billions)", labels=dollar)) +
scale_x_continuous(limits=c(1913,2009), expand=c(0,0)) +
labs(x="", y="Purchasing Power\n(1913=100)") +
theme(axis.text.y.right=element_text(colour="red"),
axis.title.y.right=element_text(colour="red"),
axis.text.y=element_text(colour="blue"),
axis.title.y=element_text(colour="blue"),
axis.title.x=element_blank())

Sample Image

Base R get raster image to fill entire plot area

By default, R allows a certain margin around the figure region to allow for labels, axes, and annotations. The default is c(5, 4, 4, 2) + 0.1.

To change these margins, do this:

par(bg = 'grey', mar=c(0,0,0,0))

Sample Image

Create an image filled chart in R using ggplot

You really only need to modify the plotting command from the answer that @PoGibas linked: here



library(png)
library(ggplot2)

genderselection <- read.table(text="
Gender Freq
F 70
M 30
", header=T)
pcts <- round(prop.table(genderselection$Freq)*100)

# Load png file from imgur as binary
con <- url("https://i.imgur.com/vFDSFYX.png",
open='rb')
rawpng <- readBin(con, what='raw', n=50000)
close(con)

img <- readPNG(rawpng)
h <- dim(img)[1]
w <- dim(img)[2]

# Find the rows where feet starts and head ends
pos1 <- which(apply(img[,,1], 1, function(y) any(y==1)))
mn1 <- min(pos1)
mx1 <- max(pos1)
pospctM <- round((mx1-mn1)*pcts[2]/100+mn1)
pospctF <- round((mx1-mn1)*pcts[1]/100+mn1)

# Fill bodies with a different color according to percentages
# Note that this relies on the fact that the png is a bitmap.
# The png is expressed as a matrix with a cell for each pixel
# and 3 layers for r,g,b.
dim(img)
#> [1] 360 360 3

# Create a 2d matrix by just taking the red values
# Image is black and white so black corresponds to 0
# white corresponds to 1. Then change the values of
# the cells to correspond to one of three categories.
imgmtx <- img[h:1,,1]
whitemtx <- (imgmtx==1)
colmtx <- matrix(rep(FALSE,h*w),nrow=h)
midpt <- round(w/2)-10
colmtx[mx1:pospctM,1:midpt] <- TRUE
colmtx[mx1:pospctF,(midpt+1):w] <- TRUE
imgmtx[whitemtx & colmtx] <- 0.5

# Need to melt the matrix into a data.frame that ggplot can understand
df <- reshape2::melt(imgmtx)
head(df)
#> Var1 Var2 value
#> 1 1 1 0
#> 2 2 1 0
#> 3 3 1 0
#> 4 4 1 0
#> 5 5 1 0
#> 6 6 1 0

cols <- c(rgb(255,255,255,maxColorValue = 255),
rgb(209,230,244,maxColorValue = 255),
rgb(42,128,183,maxColorValue = 255))

# Then use a heatmap with 3 colours for background, and percentage fills
# Converting the fill value to a factor causes a discrete scale.
# geom_tile takes three columns: x, y, fill corresponding to
# x-coord, y-coord, and colour of the cell.
ggplot(df, aes(x = Var2, y = Var1, fill = factor(value)))+
geom_tile() +
scale_fill_manual(values = cols) +
theme(legend.position = "none")

Sample Image

How to have background fill in the plot in R

Here is one way to obtain a similar result using the ggplot2 package:

library(ggplot2)
dt_tails <- function(x){
y <- dt(x,7)
y[abs(x) < 1.96] <- NA
return(y)
}
dt_7 <- function(x) dt(x,7)
p <- ggplot(data.frame(x=c(-6,6)),aes(x=x)) +
stat_function(fun=dt_7, geom="area", fill="white", colour="black")
p <- p + stat_function(fun=dt_tails, geom="area", fill='#b14025')
p <- p + theme(panel.grid.major=element_blank(),
panel.grid.minor=element_blank(),
panel.background=element_rect(fill="#eae9c8") )
plot(p)

Sample Image

Adding images to R plots using ggplot2 and ggpattern - image is missing?

I think the problem here is that there is no "coral.jpg" file in the img folder of ggpattern.

When i edit your code with one of the images present in the folder, it works fine.

x = seq(-1.5, 3.5, 0.1)

y = c( rep(1.0, 22), rep(0.2, 12), rep(0.7, 7), rep(1,10))

ref = data.frame(x = x, y = y)

library(dplyr)
library(ggplot2)
library(ggpattern)

coral = system.file("img", "magpie.jpg", package="ggpattern")

p = ggplot(ref, aes(x = x, y = y))+
scale_y_reverse(lim = c(1, 0))+
theme_classic()+
geom_ribbon_pattern(aes(x = x, ymin = 1, ymax = y),
color = "darkblue",
fill = NA,
size = 1.5,
pattern = 'image',
pattern_type = 'squish',
pattern_filename = coral) +
geom_ribbon(aes(x = x, ymin = 0, ymax = y), fill = "lightblue")

p

Sample Image

Sample Image

Fill area between two lines drawn with plot()

how about ggplot geom_ribbon?

library(ggplot2)

set.seed(1)
df <- data.frame(
x = seq(1,100),
ymin = rnorm(100,10,3),
ymax = rnorm(100,22,2)
)

ggplot(df,aes(x=x))+
geom_line(aes(x,ymin),color="red")+
geom_ribbon(aes(ymin=ymin,ymax=ymax),fill="lightblue")+
geom_line(aes(x=x,y=ymax),color="black")

Sample Image

Add image background to ggplot barplot so that image is only visible inside of bars

This reminds me of a similar problem here, where the accepted solution used geom_ribbon() to provide the masking layer.

Going on a similar vein, since the mask needs to surround individual bars in this case, we are looking to create a polygon layer that handles holes gracefully. Last I checked, geom_polygon doesn't do so great, but geom_polypath from the ggpolypath package does.

Reproducible example, using the R logo as sample image & a built-in data frame:

library(ggplot2)
library(grid)
library(jpeg)

montage <- readJPEG(system.file("img", "Rlogo.jpg", package="jpeg"))
mont <- rasterGrob(montage, width = unit(1,"npc"),
height = unit(1,"npc"))

p <- ggplot(mpg, aes(x = class)) +
annotation_custom(mont, -Inf, Inf, -Inf, Inf) +
geom_bar(color = "black", fill = NA) +
coord_flip() +
theme_bw()

p

plot 1

Create a data frame of coordinates for the masking layer:

library(dplyr)
library(tidyr)

# convert the xmin/xmax/ymin/ymax values for each bar into
# x/y coordinates for a hole in a large polygon,
# then add coordinates for the large polygon

new.data <- layer_data(p, 2L) %>%

select(ymin, ymax, xmin, xmax) %>%
mutate(group = seq(1, n())) %>%
group_by(group) %>%
summarise(coords = list(data.frame(x = c(xmin, xmax, xmax, xmin),
y = c(ymin, ymin, ymax, ymax),
order = seq(1, 4)))) %>%
ungroup() %>%
unnest() %>%

rbind(data.frame(group = 0,
x = c(-Inf, Inf, Inf, -Inf),
y = c(-Inf, -Inf, Inf, Inf),
order = seq(1, 4)))

> new.data
# A tibble: 32 x 4
group x y order
<dbl> <dbl> <dbl> <int>
1 1 0.55 0 1
2 1 1.45 0 2
3 1 1.45 5 3
4 1 0.55 5 4
5 2 1.55 0 1
6 2 2.45 0 2
7 2 2.45 47 3
8 2 1.55 47 4
9 3 2.55 0 1
10 3 3.45 0 2
# ... with 22 more rows

Add the masking layer:

library(ggpolypath)

p +
geom_polypath(data = new.data,
aes(x = x, y = y, group = group),
inherit.aes = FALSE,
rule = "evenodd",
fill = "white", color = "black")

plot 2

p.s. The old adage "just because you can, doesn't mean you should" probably applies here...



Related Topics



Leave a reply



Submit