Hot questions for Using Ggplot2 in ggimage

Question:

I would like to map OHS incidents over a PNG of a hospital floorplan using ggplot2. I have tried reading this non-geographic map in as a ggimage.

So far I have tried the following with the dataset (14462) observations that I have.

Example dataset

toy <- data.frame(patient_ID = c(1001,1001,1002,1003,1004), 
               SEX = c("M","M","F","F","F"),
              phenotype = c("Psychiatric","Psychiatric", "Cardio",
                            "Cancer","Psychiatric"),
                            x = c(0.5737, 0.5737, 0.6968, 0.4704, 0.6838),
                            y= c(0.3484, 0.3484, 0.62, 0.5216, 0.2486))

I have tried reading the file in as a raster and then using ggmaps but the difficulty is no legend.

library(ggmap)
library(png)
library(ggplot2)
library(data.table)

toy <- fread("toy.csv")

# read in image 

r <- readPNG("ICU-crop.png")

#use ggimage to convert to plot and add gender
ggimage(r, scale_axes = TRUE) +
  geom_point(aes(x = x, y = y, colour = SEX), data = toy,
             size = I(1), fill = NA)

I would really like to use ggplot but need the legend. I am not sure what other methods I can use to ggplot over a PNG.


Answer:

The real "magic" for this is just fullpage=FALSE in the call to ggimage(), but you'll have to clean up axes a bit then:

ggimage(r, fullpage = FALSE, scale_axes = TRUE) +
 geom_point(aes(x = x, y = y, colour = SEX), 
            data = toy, size = I(1), fill = NA) +
  labs(x=NULL, y=NULL) +
  theme(axis.text=element_blank()) +
  theme(axis.ticks=element_blank())

You should prbly clean up the legend, too and I'd suggest lightening the base image a bit since it will be difficult to see the contrast between it and the colors you're going to overlay (unless they're large objects).

Question:

When researching this answer, I tried to draw the image strip via geom_image() from the ggimage package but couldn't get it to work. geom_image() modifies the images aspect ratios, and I don't know how to prevent it from doing it (or whether that is even possible). It's also not clear to me in what units size is measured. From how the code behaves, maybe it's in npc coordinates running from 0 to 1, regardless of the ggplot2 coordinate system?

Here is the code I used:

require(ggimage)
df_img <- data.frame(phase = c("Interphase", "Prophase", "Metaphase", "Anaphase", "Telophase"),
                     image = c("http://www.microbehunter.com/wp/wp-content/uploads/2009/lily_interphase.jpg",
                               "http://www.microbehunter.com/wp/wp-content/uploads/2009/lily_prophase.jpg",
                               "http://www.microbehunter.com/wp/wp-content/uploads/2009/lily_metaphase2.jpg",
                               "http://www.microbehunter.com/wp/wp-content/uploads/2009/lily_anaphase2.jpg",
                               "http://www.microbehunter.com/wp/wp-content/uploads/2009/lily_telophase.jpg"))
df_img$phase <- factor(df_img$phase, levels=df_img$phase)

ggplot(df_img, aes(x = phase, y = 0, image = image)) + geom_image(size = 0.18)

And this is the resulting image:

This is what the image should look like:

Note: This is a question specifically about the behavior of ggimage. I know how to generate the correct image using other approaches, e.g. by using draw_image() from cowplot.


Answer:

ggplot(df_img, aes(x = phase, y = 0.25, image = image)) +
  geom_image(size = 0.5, by="height")+
  scale_size_identity()

will produce

If by is specified, size is mapped and interpreted as npc in the data space, either as width or height, and the aspect ratio maintained for the current device (not after resizing). If size is Inf, the picture stretches to fill the whole panel.

Question:

The following graph can be produced from the R code given below. The images used in the graph are fetched from website. I'm wondering how to use images from computer.

library("ggplot2")
library("ggimage")

set.seed(2017-02-21)
d <- data.frame(x = rnorm(10),
                y = rnorm(10),
                image = sample(c("https://www.r-project.org/logo/Rlogo.png",
                                 "https://jeroenooms.github.io/images/frink.png"),
                               size=10, replace = TRUE)
                )

ggplot(d, aes(x, y)) + geom_image(aes(image=image), size=.05)

Answer:

It is very simple, you have just to set the directory where you have saved the .png files you want to use:

library("ggplot2")
library("ggimage")
set.seed(2017-02-21)
d <- data.frame(x = rnorm(10),
                y = rnorm(10),
                image = sample(c("C:/YourDirectory/juventus.png",
                                 "C:/YourDirectory/sampdoria.png"),
                               size=10, replace = TRUE)
)

ggplot(d, aes(x, y)) + geom_image(aes(image=image), size=.05)

and you get the following result:

Question:

I am trying to place images to a plot that needs to have fixed coordinates (x, y values are GPS coordinates and I want the map to scale correctly). If the ranges of x and y don't match the images are flatted.

I don't know if this is a bug or desired behavior. Is there a way how to make the image with original aspect ratio? The only thing that I came up with is to put invisible points to the corners to make the plot square again.

Simple example is as following:

require(tidyverse)
require(ggimage)

plot_image <- function(x_size, y_size) {

  dta_points <- crossing(x = c(-x_size, x_size), y = c(-y_size, y_size))
  dta_img <- data_frame(x = 0, y = 0, image = 'https://www.r-project.org/logo/Rlogo.png')

  ggplot(NULL, aes(x, y)) + 
    geom_point(data = dta_points) +
    geom_image(data = dta_img, aes(image = image), size = 1) +
    ggtitle(paste0('x_size: ', x_size, ', y_size: ', y_size)) +
    coord_fixed()
}

plot_image(x_size = 1, y_size = 1)
plot_image(x_size = 0.1, y_size = 1)
plot_image(x_size = 1, y_size = 0.1)


Answer:

try geom_custom,

library(ggplot2)
library(png)
library(grid)
library(egg)

i <- readPNG(system.file("img", "Rlogo.png", package="png"))
img <- data.frame(x = 6, y = 3)
img$g <-  list(rasterGrob(i, width=unit(24,"pt")))

ggplot(iris, aes(x = Sepal.Length, y = Petal.Length)) + 
  geom_point() +
  geom_custom(data=img, aes(x,y,data=g), grob_fun=identity) +
  coord_fixed()

Question:

How could I move the image of the arrow over the 2004 bar? Thanks in advance!

This is my code:

data %>% 
  ggplot(aes(x=year, y=value, fill = gender)) +
  geom_bar(stat = "identity") +
  scale_fill_manual(values= c('#1380c9', '#ff6262')) +
  scale_y_continuous(limits=c(0, 1000)) +
  theme(axis.text.x = element_text(angle = 45, hjust = 1, size=8))+
  annotation_custom(rasterGrob(image, interpolate=TRUE, height = 0.08, width = 0.08))

Answer:

You can specify the location and size of your image using the xmin, xmax, ymin, and ymax arguments in annotation_custom. Just leave the default size settings in rasterGrob

library(ggplot2)
library(grid)

data %>%
  ggplot(aes(x=year, y=value, fill = gender)) +
  geom_bar(stat = "identity") +
  scale_fill_manual(values= c('#1380c9', '#ff6262')) +
  scale_y_continuous(limits=c(0, 1000)) +
  theme(axis.text.x = element_text(angle = 45, hjust = 1, size=8)) +
  annotation_custom(rasterGrob(image, interpolate=TRUE),
                    xmin = 3.6, xmax = 5.5, ymin = 700, ymax= 850)

Of course, I don't have your data or the same arrow graphic, but this gives me:

Here's the data I used:

data <- structure(list(year = structure(c(1L, 2L, 3L, 4L, 5L, 6L, 7L, 
8L, 9L, 10L, 11L, 12L, 13L, 14L, 15L, 16L, 17L, 18L, 1L, 2L, 
3L, 4L, 5L, 6L, 7L, 8L, 9L, 10L, 11L, 12L, 13L, 14L, 15L, 16L, 
17L, 18L), class = "factor", .Label = c("2000", "2001", "2002", 
"2003", "2004", "2005", "2006", "2007", "2008", "2009", "2010", 
"2011", "2012", "2013", "2014", "2015", "2016", "2017")), value = c(124.941847570307, 
157.345732968883, 116.574855503598, 213.811232085512, 260, 117.181264635279, 
169.497162097139, 179.532988205169, 173.03125406614, 137.784464513746, 
210.471246738034, 165.593729456457, 125.150376778328, 61.4120045129, 
194.997236725724, 148.202655639391, 149.352389476042, 187.753448427412, 
324.636635852943, 317.817039636525, 327.569321148247, 323.464089021932, 
500, 240.319449124099, 318.594772436841, 298.31613781413, 295.32613479884, 
255.877428483022, 285.655498346741, 312.538246805991, 340.760386545871, 
296.91636817971, 311.630148346781, 298.385848782513, 258.688213295142, 
287.55016310101), gender = structure(c(2L, 2L, 2L, 2L, 2L, 2L, 
2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 1L, 1L, 1L, 1L, 
1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L), .Label = c("Hombres", 
"Mujeres"), class = "factor")), row.names = c(NA, -36L), class = "data.frame")

In the name of making this reproducible, I saved an arrow image in my home directory and did

library(png)
image <- readPNG(path.expand("~/arrow.png"))

But in case anyone wants to test thus out, here's a low-res test image:

arrow <- c(22L, 23L, 24L, 42L, 43L, 44L, 52L, 62L, 63L, 64L, 72L, 73L,
82L, 83L, 84L, 92L, 93L, 94L, 102L, 103L, 104L, 113L, 114L, 115L,
122L, 123L, 124L, 125L, 133L, 134L, 135L, 136L, 142L, 143L, 144L,
145L, 153L, 154L, 155L, 156L, 157L, 163L, 164L, 165L, 166L, 167L,
168L, 169L, 170L, 171L, 172L, 173L, 174L, 175L, 176L, 177L, 178L,
183L, 184L, 185L, 186L, 187L, 188L, 189L, 190L, 191L, 192L, 193L,
194L, 195L, 196L, 197L, 198L, 199L, 204L, 205L, 206L, 207L, 208L,
209L, 210L, 211L, 212L, 213L, 214L, 215L, 216L, 217L, 218L, 219L,
225L, 226L, 227L, 228L, 229L, 230L, 231L, 232L, 233L, 234L, 235L,
236L, 237L, 238L, 253L, 254L, 255L, 256L, 257L, 273L, 274L, 275L,
276L, 293L, 294L, 295L, 312L, 313L, 314L, 332L, 333L, 352L)

image <- array(rep(0, 20*19*4), dim=c(20,19,4))
image[,,4][arrow] <- 1

Question:

I can not figure out how to use .gif figures as points in x-y plotting, and rotate each point according to data in the dataset.

Demo dataset:

library("ggplot2")
library("ggimage")
N=5
d <- data.frame(x = rnorm(N),
            y = rnorm(N),
            image = rep("https://www.r-project.org/logo/Rlogo.png", N),
            size = seq(.05, 0.15, length.out = N),
            angle = seq(0, 45, length.out = N) )

The follwing will make a plot where all Rlogo's are rotated 45 degrees:

ggplot(d, aes(x, y)) + 
  geom_image(aes(image=image, size=I(size)), angle = 45)
# Plot with tilted png

But how to set the rotation angle individually for each point? The snippet below simply does not work and makes a plot without tilting any points..

ggplot(d, aes(x, y)) + 
  geom_image(aes(image=image, size=I(size), angle = I(angle)))
#plotting but without tilting the points

Setting the plot angle outside the aes does not help.

ggplot(d, aes(x, y)) + geom_image(aes(image=image, size=I(size)), angle = I(d$angle))
# No plotting: Error in valid.viewport(x, y, width, height, just, gp, clip, xscale, yscale, invalid 'angle' in viewport

So, anyone having a good idea for this? Thanks in advance:-)


Answer:

You could try this, although the real solution would be to write a better geom,

library("ggplot2")
library("egg")
library("grid")
N=5
d <- data.frame(x = rnorm(N),
                y = rnorm(N),
                image = replicate(N, system.file("img", "Rlogo.png", package="png")),
                size = seq(.05, 0.15, length.out = N),
                angle = seq(0, 45, length.out = N),
                stringsAsFactors = FALSE)

grobs <- purrr::map2(d$image, runif(N,0,180),
                     function(i,a) list(raster = png::readPNG(i), angle=a) )
d$grob <- I(grobs)

custom_grob <- function(data, x=0.5, y=0.5){
  grob(data=data$raster,angle=data$angle,x=x,y=y, cl="custom")
}
preDrawDetails.custom <- function(x){
  pushViewport(viewport(x=x$x,y=x$y, angle = x$angle))
}
postDrawDetails.custom <- function(x){
  upViewport()
}
drawDetails.custom <- function(x, recording=FALSE, ...){
  grid.raster(x$data, interpolate = FALSE, width=unit(1,"cm"), height=unit(1,"cm"))
}
ggplot(d, aes(x, y)) +
  theme_bw() +
  geom_custom(data = d, aes(data = grob), 
              grob_fun = custom_grob)

enter image description here

Question:

I wonder how to get ggplot2 graph with full plot size image

library("ggplot2")
library("ggimage")
set.seed(12345)
df1 <- data.frame(x = rnorm(1),
                y = rnorm(1),
                image = "https://www.r-project.org/logo/Rlogo.png")

ggplot(df1, aes(x, y)) + 
  geom_image(aes(image=image), size=.05)

df2 <- data.frame()
ggplot(df2) +
  geom_point() 


Answer:

library("ggimage")
set.seed(12345)
df1 <- data.frame(x = rnorm(1),
                  y = rnorm(1),
                  image = "https://www.r-project.org/logo/Rlogo.png")

ggplot(df1, aes(x, y)) + 
    geom_image(aes(image=image), size=1)