Labelling the plots with images on graph in ggplot2 - r

So I have this R script that can produce a scatter plot with labels of each point. Sth like this:
img1<-"http://blog.gettyimages.com/wp-content/uploads/2013/01/Siberian-Tiger-Running-Through-Snow-Tom-Brakefield-Getty-Images-200353826-001-628x419.jpg"
img2<-"http://blog.gettyimages.com/wp-content/uploads/2013/01/Hurricane-Sandy-Andrew-Burton-Getty-Images-154986556.jpg"
imgdata<-data.frame(c(img1,img2,img1,img2,img1,img2,img1,img2,img1,img2))
colnames(imgdata)<-"images"
txtdata<-data.frame(c("A","B","C","D","E","F","G","H","I","J"))
plotdata<-data.frame(seq(1:10),seq(11:20),txtdata,imgdata)
colnames(plotdata)<-c("var1","var2","texts","images")
ggplot(data=plotdata, aes(plotdata[,1],plotdata[,2])) +
geom_point(data=plotdata, aes(plotdata[,1],plotdata[,2])) +
geom_text(aes(label=plotdata$points,size=2, hjust=2))
This gives a scatter plot, where each point is labelled as "A", "B", "C"... etc.
What I want to do is almost the same, except instead of texts, I want to label each point with the image that are in the links of a vector or data frame (in this case in "imgdata"). Note that I selected these images just as examples; I have much more of them, so I can't manually download them.

You can use annotation_custom, but it will be a lot of work because each image has to be rendered as a raster object and its location specified. I saved the images as png files to create this example.
library(ggplot2)
library(png)
library(grid)
img1 <- readPNG("c:/test/img1.png")
g1<- rasterGrob(img1, interpolate=TRUE)
img2 <- readPNG("c:/test/img2.png")
g2<- rasterGrob(img2, interpolate=TRUE)
plotdata<-data.frame(seq(1:2),seq(11:12))
ggplot(data=plotdata) + scale_y_continuous(limits=c(0,4))+ scale_x_continuous(limits=c(0,4))+
geom_point(data=plotdata, aes(plotdata[,1],plotdata[,2])) +
annotation_custom(g1,xmin=1, xmax=1.5,ymin=1, ymax=1.5)+
annotation_custom(g2,xmin=2, xmax=2.5,ymin=2, ymax=2.5)

Related

Extracting the exact coordinates of a mouse click in an interactive plot

In short: I'm looking for a way to get the exact coordinates of a series of mouse positions (on-clicks) in an interactive x/y scatter plot rendered by ggplot2 and ggplotly.
I'm aware that plotly (and several other interactive plotting packages for R) can be combined with Shiny, where a box- or lazzo select can return a list of all data points within the selected subspace. This list will be HUGE in most of the datasets I'm analysing, however, and I need to be able to do the analysis reproducibly in an R markdown format (writing a few, mostly less than 5-6, point coordinates is much more readable). Furthermore, I have to know the exact positions of the clicks to be able to extract points within the same polygon of points in a different dataset, so a list of points within the selection in one dataset is not useful.
The grid.locator() function from the grid package does almost what I'm looking for (the one wrapped in fx gglocator), however I hope there is a way to do the same within an interactive plot rendered by plotly (or maybe something else that I don't know of?) as the data sets are often HUGE (see the plot below) and thus being able to zoom in and out interactively is very much appreciated during several iterations of analysis.
Normally I have to rescale the axes several times to simulate zooming in and out which is exhausting when doing it MANY times. As you can see in the plot above, there is a LOT of information in the plots to explore (the plot is about 300MB in memory).
Below is a small reprex of how I'm currently doing it using grid.locator on a static plot:
library(ggplot2)
library(grid)
p <- ggplot(mtcars, aes(wt, mpg)) +
geom_point()
locator <- function(p) {
# Build ggplot object
ggobj <- ggplot_build(p)
# Extract coordinates
xr <- ggobj$layout$panel_ranges[[1]]$x.range
yr <- ggobj$layout$panel_ranges[[1]]$y.range
# Variable for selected points
selection <- data.frame(x = as.numeric(), y = as.numeric())
colnames(selection) <- c(ggobj$plot$mapping$x, ggobj$plot$mapping$y)
# Detect and move to plot area viewport
suppressWarnings(print(ggobj$plot))
panels <- unlist(current.vpTree()) %>%
grep("panel", ., fixed = TRUE, value = TRUE)
p_n <- length(panels)
seekViewport(panels, recording=TRUE)
pushViewport(viewport(width=1, height=1))
# Select point, plot, store and repeat
for (i in 1:10){
tmp <- grid.locator('native')
if (is.null(tmp)) break
grid.points(tmp$x,tmp$y, pch = 16, gp=gpar(cex=0.5, col="darkred"))
selection[i, ] <- as.numeric(tmp)
}
grid.polygon(x= unit(selection[,1], "native"), y= unit(selection[,2], "native"), gp=gpar(fill=NA))
#return a data frame with the coordinates of the selection
return(selection)
}
locator(p)
and from here use the point.in.polygon function to subset the data based on the selection.
A possible solution could be to add, say 100x100, invisible points to the plot and then use the plotly_click feature of event_data() in a Shiny app, but this is not at all ideal.
Thanks in advance for your ideas or solutions, I hope my question was clear enough.
-- Kasper
I used ggplot2. Besides the materials at https://shiny.rstudio.com/articles/plot-interaction.html, I'd like to mention the following:
Firstly, when you create the plot, don't use "print( )" within "renderPlot( )", or the coordinates would be wrong. For instance, if you have the following in UI:
plotOutput("myplot", click = "myclick")
The following in the Server would work:
output$myplot <- renderPlot({
p = ggplot(data = mtcars, aes(x=mpg, y=hp)) + geom_point()
p
})
But the clicking coordinates would be wrong if you do:
output$myplot <- renderPlot({
p = ggplot(data = mtcars, aes(x=mpg, y=hp)) + geom_point()
print(p)
})
Then, you could store the coordinates by adding to the Server:
mydata = reactiveValues(x_values = c(), y_values = c())
observeEvent(input$myclick, {
mydata$x_values = c(mydata$x_values, input$myclick$x)
mydata$y_values = c(mydata$y_values, input$myclick$y)
})
In addition to X-Y coordinates, when you use facet with ggplot2, you refer to the clicked facet panel by
input$myclick$panelvar1

Plot multiple ggplot2 on same page

I have a working loop which generates and can save individual plots from each file saved in a directory.
I want to plot all of the returned plots in a single file as a 2x2 grid over multiple pages but cannot do this.
I have tried to save the plot objects in a list
pltList <- list()
pltList[]
for (f in 1:length(files)){
plot_object <- ggplot2(...) #make ggplot2 plot
print(plot_object)
pltList[[f]] <- plot_object #save ggplot2 plot in list
}
jpeg(filename.jpg)
par(mfrow=c(2,2)) #to generate 2x2 plot per page
print(pltList[[1]])
print(pltList[[2]])
...
print(pltList[[f]])
dev.off()
The problem is that the resulting saved .jpg file only contains the last plot and not a 2x2 grid of all plots over many pages which is what I want.
EDIT
My first problem is how to save each plot from the loop in the list - how can I view the saved objects from the list to make sure they have been saved correctly?
When I do print(pltList[1]), the resulting output is:
function (x, y, ...)
UseMethod("plot")
<bytecode: 0x0000000010f43b78>
<environment: namespace:graphics>
rather than the actual plot. It seems that the plots are not being saved in the list as expected. How can I correct for this?
Hopefully, once this is fixed, your plotting suggestions will work.
I did recently the same. I used grid.arrange().
library(ggplot2)
library(gridExtra)
library(grid)
p1<-ggplot()+geom_line(aes(x=1:10,y=1:10))
p2<-ggplot()+geom_line(aes(x=1:10,y=1:10))
p3<-ggplot()+geom_line(aes(x=1:10,y=1:10))
p4<-ggplot()+geom_line(aes(x=1:10,y=1:10))
grid.arrange(p1,p2,p3,p4, ncol=1, top=textGrob("Multiple Plots", gp=gpar(fontsize=12, font = 2)))
Assuming you need a PDF output where every page has multiple plots plotted as one, e.g.: if there are 12 plots then 4 plots per page.
Try this example:
library(ggplot2)
library(cowplot)
# list of 12 dummy plots, only title is changing.
pltList <- lapply(1:12, function(i){
ggplot(mtcars,aes(mpg,cyl)) +
geom_point() +
ggtitle(paste("Title",i))})
# outputs 3 jpeg files with 4 plots each.
for(i in seq(1,12,4))
ggsave(paste0("Temp",i,".jpeg"),
plot_grid(pltList[[i]],
pltList[[i+1]],
pltList[[i+2]],
pltList[[i+3]],nrow = 2))
# or we can output into 1 PDF with 3 pages using print
pdf("TempPDF.pdf")
for(i in seq(1,12,4))
print(plot_grid(pltList[[i]],
pltList[[i+1]],
pltList[[i+2]],
pltList[[i+3]],nrow = 2))
dev.off()
EDIT:
Another way using gridExtra, as suggested by #user20650:
library(gridExtra)
#output as PDF
pdf("multipage.pdf")
#use gridExtra to put plots together
marrangeGrob(pltList, nrow=2, ncol=2)
dev.off()

Using image files as plot patterns in R

I am producing a stratigraphy plot which should look something like the following
I've got to the point where I can plot the layout of the plot using some dummy data and the following code
Strat <- c(657,657,657,657,657,657,657,657,657,657,601,601,601,601,601,601,601,601,601,601,610,610,610,610,610,610,610,610,610,610)
Distance <- c(7.87,17.89,22.09,42.84,50.65,55.00,65.74,69.38,72.36,75.31,7.87,17.89,22.09,42.84,50.65,55.00,65.74,69.38,72.36,75.31,7.87,17.89,22.09,42.84,50.65,55.00,65.74,69.38,72.36,75.31)
Altitude <- c(565.05,191.98,808.12,609.19,579.10,657.08,708.00,671.44,312.10,356.14,565.05,191.98,808.12,609.19,579.10,657.08,708.00,671.44,312.10,356.14,565.05,191.98,808.12,609.19,579.10,657.08,708.00,671.44,312.10,356.14)
strat_max <- c(565.05,191.98,808.12,609.19,579.10,657.08,708.00,671.44,312.10,356.14,565.04,176.23,795.52,608.06,567.89,641.83,698.69,664.50,310.21,350.11,526.47,147.30,762.49,601.99,544.22,632.54,689.33,636.40,282.71,313.56)
strat_min <- c(565.04,176.23,795.52,608.06,567.89,641.83,698.69,664.50,310.21,350.11,526.47,147.30,762.49,601.99,544.22,632.54,689.33,636.40,282.71,313.56,463.31,81.01,718.11,594.38,539.53,616.18,670.79,602.96,249.59,289.63)
strat <- cbind(Strat, Distance, Altitude, strat_max, strat_min)
strat <- as.data.frame(strat)
attach(strat)
ggplot(strat, aes(x=Distance, y=Altitude, colour=factor(Strat))) +
geom_linerange(aes(x=Distance, ymax=strat_max, ymin=strat_min, colour=factor(Strat)), lwd=10) +
geom_line(lty=1, lwd=1.5, colour="black") +
xlab("Distance") + ylab("Altitude") +
theme_bw() + scale_colour_discrete(name="Stratigraphy Type")
However, I have been unable to add the relevant patterns. Each rock/ sediment type has a standard plotting pattern for use in stratigraphic plots from the USGS and I would like to relate the code in strat$Strat to the relevant pattern and use that as the pattern
Does anyone know how to import these (e.g. as PNG files) and then use them as patterns? I had thought to try and call them as colours but I don't know if that would work and there's probably no framework for telling R how to repeat them. Currently I am writing the plot as shown and then adding in the patterns in adobe illustrator
Any insight appreciated!

R Highlighting some of the boundaries (or borders) of the region in a shape file with spplot()

I am working on a shape file and like to highlight some of the boundaries (borders) of the regions (as figure 1):
Figure 1: some but not all of the regions (borders) of the shape file are highlighted
(Source: https://dl.dropboxusercontent.com/u/48721006/highlighted.png)
The highlighting is achieved with ArcMap. I can't figure out how to do the same with R (particularly with the spplot()). Any suggestions on this?
To get the shape file
library(sp)
library(maptools)
con <- url("http://gadm.org/data/rda/ZAF_adm2.RData")
print(load(con))
close(con)
plot(gadm)
Many thanks!
G
What I would do: (1) plot the complete set; (2) take a subset; (3) plot the subset with a different line type. For subsetting shape files, check this question.
plot(gadm)
# check class and structure of the data
class(gadm)
head(gadm#data)
# take a subset based on ID_2
some_polygons = subset(gadm,ID_2>=38840 & ID_2<38850)
plot(some_polygons, add=T, border='cyan', lwd=2)

fast way to read in png, add grid & coords, and output

I have a lot of png files of a floor plan (mapping) layout that I want to:
read into R
Add grid lines
Add coordinates per cell in grid
Output
There are 1000 of these files so I'm looking for a speedy method. What would be a fast way to accomplish this task. These don't need to be publication quality as I'm looking for certain behavior clusters within cells and want recording the coordinated for these events for each of the 100 frames (pngs).
Here is a MWE that produces 10 png files:
x <- y <- seq(-4*pi, 4*pi, len = 27)
r <- sqrt(outer(x^2, y^2, "+"))
dir.create("delete_me")
wd <- getwd()
setwd("delete_me")
lapply(1:10, function(x){
png(sprintf("file_%s.png", x))
image(z = z <- cos(r^2)*exp(-r/x))
dev.off()
})
setwd(wd)
The final output will look like this for each png (with all the coords filled in).
I assume grid will be the way to create the gridlines quickly but am not sure about reading the png in quickly or plotting the coordinates (assume we'll use a 10 x 10 grid on each png).
How about using ggplot() and annotation_custom() to plot the image across the entire plot area, then manually overplot the grid lines.
(In the image, I trimmed the excess whitespace and axis from the png file in advance)
# pre-req libraries
require(ggplot2)
require(grid) # rasterGrob function
require(png) # to read the PNG file
width<-10
height<-10
# generate the points and labels for the grid
points<-data.frame(expand.grid(w=1:width,h=1:height))
points$labs<-paste0("(",points$w,",",points$h,")")
points$x<-points$w-0.5 # center
points$y<-points$h-0.5
# make the gridline co-ordinates
gridx<-data.frame(x=0:width,xend=0:width,y=rep(0,width+1),yend=rep(height,width+1))
gridy<-data.frame(x=rep(0,height+1),xend=rep(width,height+1),y=0:height,yend=0:height)
grids<-rbind(gridx,gridy)
# function to plot using ggplot with annotation_custom for the image
plotgrid<-function(file){
g<-ggplot(points)+theme_bw()+
annotation_custom(rasterGrob(readPNG(file),0,0,1,1,just=c("left","bottom")),0,width,0,height)+
geom_text(aes(x=x,y=y,label=labs))+
geom_segment(aes(x=x,xend=xend,y=y,yend=yend),data=grids) +
coord_cartesian(c(0,width),c(0,height))
return(g)
}
# run the function for each file in the folder
setwd("delete_me")
lapply(list.files(),function(x)plotgrid(x))
setwd(wd)

Resources