5

(This is an extension to the question Icons as x-axis labels in R. It looks for a ggplot solution instead of a plot one. Since ggplot is based on grid and plot is based on graphics, the approach is very different)

I would like to plot something like this (from this paper) where icons, in this case small graphs, are used as tick labels.

enter image description here

The accepted answer from the original question is:

library(igraph)    
npoints <- 15
y <- rexp(npoints)
x <- seq(npoints)

# reserve some extra space on bottom margin (outer margin)
par(oma=c(3,0,0,0))
plot(y, xlab=NA, xaxt='n',  pch=15, cex=2, col="red")
lines(y, col='red', lwd=2)

# graph numbers 
x = 1:npoints   

# add offset to first graph for centering
x[1] = x[1] + 0.4
x1 = grconvertX(x=x-0.4, from = 'user', to = 'ndc')
x2 = grconvertX(x=x+0.4, from = 'user', to = 'ndc')

for(i in x){  

  print(paste(i, x1[i], x2[i], sep='; '))

  # remove plot margins (mar) around igraphs, so they appear bigger and 
  # `figure margins too large' error is avoided
  par(fig=c(x1[i],x2[i],0,0.2), new=TRUE, mar=c(0,0,0,0))
  plot(graph.ring(i), vertex.label=NA)  
}

enter image description here

How can we make a similar plot using ggplot?

This is the closer I get:

library(ggplot2)
library(grImport)
library(igraph)

npoints <- 5
y <- rexp(npoints)
x <- seq(npoints)

pics  <- vector(mode="list", length=npoints)
for(i in 1:npoints){
  fileps <- paste0("motif",i,".ps")
  filexml <- paste0("motif",i,".xml")

  # Postscript file
  postscript(file = fileps, fonts=c("serif", "Palatino"))
  plot(graph.ring(i), vertex.label.family="serif", edge.label.family="Palatino")
  dev.off()

  # Convert to xml accessible for symbolsGrob
  PostScriptTrace(fileps, filexml)
  pics[i] <- readPicture(filexml)
}
xpos <- -0.20+x/npoints
my_g <- do.call("grobTree", Map(symbolsGrob, pics, x=xpos, y=0))
qplot(x, y, geom = c("line", "point")) + annotation_custom(my_g, xmin=-Inf, xmax=Inf, ymax=0.4, ymin=0.3)

enter image description here

Community
  • 1
  • 1
alberto
  • 2,625
  • 4
  • 29
  • 48
  • possible duplicate of [Icons as x-axis labels in R](http://stackoverflow.com/questions/29922673/icons-as-x-axis-labels-in-r) – figurine Apr 29 '15 at 09:31
  • 2
    Not at all!. This one asks for a ggplot solution. – alberto Apr 29 '15 at 09:36
  • Is there a particular reason why you need a solution in ggplot2? – figurine Apr 29 '15 at 09:55
  • 3
    Three: a) use the capabilities (ease of use, functions, etc) of ggplot to further improve the plot in the near future. (b) I use ggplot in other parts of my code (homogeneity) (c) fun and curiosity. – alberto Apr 29 '15 at 10:03
  • 1
    is this what you're looking for? http://stackoverflow.com/questions/8905101/how-can-i-use-a-graphic-imported-with-grimport-as-axis-tick-labels-in-ggplot2-u – hrbrmstr Apr 29 '15 at 10:10
  • 1
    @hrbrmstr note also that use opts( axis.text.x = my_axis()) which is deprecated – alberto Apr 29 '15 at 13:30
  • Regarding `ops()`, I think in many cases you can directly replace `opts()` with `theme()` and it'll get you the same thing. – Alex A. Apr 30 '15 at 18:26
  • @Alex however it does not work for `axis.text.x`. Theme is more restricted to colors and so. – alberto Apr 30 '15 at 18:29
  • 1
    @hrbrmstr the solution you link does not work at all for the most recent versions of `ggplot2` – alberto Apr 30 '15 at 18:31
  • 1
    grob editing has to be the way to go, tho. it's not fun, and if i had more spare cycles I'd give it a go. – hrbrmstr Apr 30 '15 at 20:23
  • I'm playing with it in the meanwhile :) – alberto Apr 30 '15 at 21:02

1 Answers1

3

This builds of your attempt.

(I used set.seed(1) before the rexp function and also tweaked the graph to increase the edge thickness: plot(graph.ring(i), vertex.label=NA, edge.width=30))

Continuing from above:

# Initial plot
p <- qplot(x, y, geom = c("line", "point")) 

# Use the plot to get the x-positions of the labels
g <- ggplotGrob(p)    
xpos <- g$grobs[[grep("axis-b", g$layout$name)]]$children[[2]]$grobs[[2]]$x

# Create grob tree 
my_g <- do.call("grobTree", Map(symbolsGrob, pics, x=xpos, y=0.5))

# Make second plot
# Add extra space under the plot for the images 
# Remove x-axis details
# Note the annotation is below the lower y-axis limit
# The limits were selected by inspection
p2 <- p + annotation_custom(my_g, xmin=-Inf, xmax=Inf, ymax=-0.1, ymin=-0.2) + 
            theme(axis.text.x = element_blank(), 
                  plot.margin=unit(c(1,1,2,1), "cm"))

# remove clipping so the images render
g <- ggplotGrob(p2)
g$layout$clip[g$layout$name=="panel"] <- "off"

grid.newpage()
grid.draw(g)

enter image description here

There will be a way to do this properly / in line with the lovely previous solution, but anyways ...

user20650
  • 24,654
  • 5
  • 56
  • 91
  • With ggplot2 version 3.0.0, you can now disable clipping with `my_plot + coord_cartesian(clip = 'off')`, which is simpler than the grid solution (i.e. `g$layout$clip[g$layout$name=="panel"] <- "off"`) – bschneidr Oct 03 '18 at 18:46
  • @bschneidr ; please feel free to update the answer if there a better ways to do it, or grob paths have broken etc – user20650 Oct 03 '18 at 18:59