How to use an image as a point in ggplot?
Asked Answered
S

3

61

Is there some way to use a specific small image as a point in a scatterplot with ggplot2. Ideally I will want to resize the images based on an variable.

Here's an example:

library(ggplot2)
p <- ggplot(mtcars, aes(wt, mpg))
p + geom_point(aes(size = qsec, shape = factor(cyl)))

So I basically want to know if there is a way to supply a specific image as the shape?

Stogner answered 2/2, 2010 at 4:31 Comment(0)
M
41

There is a library called ggimage to do that. See an intro vignette here

You just have to add a column to your data.frame with the address of the images, which can be stored on the web or locally on your computer and then you can use the geom_image():

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

# create a df

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)
                )
# plot2
  ggplot(d, aes(x, y)) + geom_image(aes(image=image), size=.05)

enter image description here

ps. Note that ggimage depends on EBImage. So to install gginamge I had to do this:

# install EBImage
  source("https://bioconductor.org/biocLite.R")
  biocLite("EBImage")
# install ggimage
  install.packages("ggimage")
Moton answered 13/4, 2017 at 11:36 Comment(7)
This is a great example to show the use of ggimage. Would highly appreciate if you give an example to use image from computer. ThanksSkirr
You only need to replace the internet address "https://www.r-project.org/logo/Rlogo.png" with the local address of your file, for example "C:/Users/Desktop/Rlogo.pngMoton
how to add legend so that the image will be in legend and annotation to it ?Inedited
Do you have a solution to prevent distortion when changing aspect ratio of plot?Maculate
I guess you can try adding + coord_equal() OR + theme(aspect.ratio=1). One of these alternatives might solve the aspect ratio problemMoton
The link is broken intro vignette hereNumen
How to show points as well (and the image aside without overlaying image if possible)Numen
O
32

Here's a minimalist geom to display raster images instead of points,

library(ggplot2)
library(grid)

## replace by a named list with matrices to be displayed
## by rasterGrob
.flaglist <- list("ar" = matrix(c("blue", "white", "blue"), 1), 
                  "fr" = matrix(c("blue", "white", "red"), 1))

flagGrob <- function(x, y, country, size=1, alpha=1){
  grob(x=x, y=y, country=country, size=size, cl = "flag")
}

drawDetails.flag <- function(x, recording=FALSE){

  for(ii in seq_along(x$country)){
    grid.raster(x$x[ii], x$y[ii], 
                width = x$size[ii]*unit(1,"mm"), height = x$size[ii]*unit(0.5,"mm"),
                image = .flaglist[[x$country[[ii]]]], interpolate=FALSE)
  }
}


scale_country <- function(..., guide = "legend") {
  sc <- discrete_scale("country", "identity", scales::identity_pal(), ..., guide = guide)

  sc$super <- ScaleDiscreteIdentity
  class(sc) <- class(ScaleDiscreteIdentity)
  sc
}

GeomFlag <- ggproto("GeomFlag", Geom,
                    required_aes = c("x", "y", "country"),
                    default_aes = aes(size = 5, country="fr"),

                    draw_key = function (data, params, size) 
                    {
                      flagGrob(0.5,0.5, country=data$country,  size=data$size)
                    },

                    draw_group = function(data, panel_scales, coord) {
                      coords <- coord$transform(data, panel_scales)     
                      flagGrob(coords$x, coords$y, coords$country, coords$size)
                    }
)

geom_flag <- function(mapping = NULL, data = NULL, stat = "identity",
                      position = "identity", na.rm = FALSE, show.legend = NA, 
                      inherit.aes = TRUE, ...) {
  layer(
    geom = GeomFlag, mapping = mapping,  data = data, stat = stat, 
    position = position, show.legend = show.legend, inherit.aes = inherit.aes,
    params = list(na.rm = na.rm, ...)
  )
}


set.seed(1234)
d <- data.frame(x=rnorm(10), y=rnorm(10), 
                country=sample(c("ar","fr"), 10, TRUE), 
                stringsAsFactors = FALSE)


ggplot(d, aes(x=x, y=y, country=country, size=x)) + 
  geom_flag() + 
  scale_country()

enter image description here

(output from the ggflags package)

Olathe answered 23/3, 2016 at 7:45 Comment(2)
I know this is a bit old at this stage, but any chance you could provide a few comments to narrate the code? It seems like creating geoms is very useful but a little opaque in how to get started.Behest
@MokeEire, a good place to start: cran.r-project.org/web/packages/ggplot2/vignettes/…Bedaub
I
4

First, here's your answer:

To show you how to use how you might better use widgets to represent data differentiation, I refer you to the example of chernoff faces at the R graph gallery.:

alt text
(source: free.fr)

All the code to generate this example is available at the site.

Alternatively, look ggplot's stat_spoke for a simple widget: alt text
(source: had.co.nz)

grImport provides a mechanism to import simple PDF images into your plot for use as points.

Now follows a critique of your example.


This is not a scatterplot. It's essentially a flowed list of ordered data points where colour is used to indicate one of the text variables, and an uninformative and redundant widget has been used to frame the data but otherwise provides no visual feedback in terms of size or shape.

It is not a good graph, because it completely fails to answer the stated question "Does Paying More Lead To Better Results", and leaves the reader to struggle draw that conclusion (and that other graph, as necessary) by themselves.

In addition, the authors have wasted the x, y axes - which could have been well used to position elements by outgoing and results, to provide a visual understanding of value-for-money. Instead they have opted to order the icons by the ratio of per head cost to average graduation rate, which is sort of useful, but doesn't answer the stated question, and fails to allow a direct visual comparison of relative ratio between colleges, or the relationship between cost and value.

As I say, in my opinion, this is a bad graph, and your readers would not be well served by having you replicate it.

Ichnography answered 2/2, 2010 at 8:49 Comment(3)
Thanks for the answer! I removed that link because I think that you're right. More generally, unless I'm missing something, it doesn't seem like the faces() function, nor stat_spoke really address my need. faces() only shows faces...you can't alter the image. And stat_spoke doesn't allow you change the image either. But grImport looks very promising! Thanks!Stogner
Your image links are broken...I poked around for a bit trying to fix them but was unsuccessful.Persistent
It looks like the question has been edited to the degree that the answer no longer makes sense; as well as having broken links to images.Ursala

© 2022 - 2024 — McMap. All rights reserved.