I would like to include a sequence of concentric circles as a grid in a plot of points. The goal is to give the viewer an idea of which points in the plot have approximately the same magnitude.
I created a hack to do this:
add_circle_grid <- function(g,ncirc = 10){
gb <- ggplot_build(g)
xl <- gb$panel$ranges[[1]]$x.range
yl <- gb$panel$ranges[[1]]$y.range
rmax = sqrt(max(xl)^2+max(yl)^2)
theta=seq(from=0,by=.01,to=2*pi)
for(n in 1:ncirc){
r <- n*rmax/ncirc
circle <- data.frame(x=r*sin(theta),y=r*cos(theta))
g<- g+geom_path(data=circle,aes(x=x,y=y),alpha=.2)
}
return(g+xlim(xl)+ylim(yl))
}
xy<-data.frame(x=rnorm(100),y=rnorm(100))
ggplot(xy,aes(x,y))+geom_point()
ggg<-add_circle_grid(ggplot(xy,aes(x,y))+geom_point())
print(ggg)
But I was wondering if there is a more ggplot way to do this. I also considered using polar coordinates but it does not allow me to set x- and y-limits in the same way.
Finally, I wouldn't mind little text labels indicating the radius of each circle.
EDIT
Perhaps this is asking too much but there are two other things that I would like.
The axis limits should stay the same (which can be done via ggplot_build)
Can this work with facets? As far as I can tell you would need to somehow figure out the facets if I want to add the circles dynamically.
set.seed(1)
xy <- data.frame(x=rnorm(100),y=rnorm(100))
rmax = sqrt(max(xy$x)^2+max(xy$y)^2)
theta=seq(from=0,by=.01,to=2*pi)
ncirc=10
dat.circ = do.call(rbind,
lapply(seq_len(ncirc),function(n){
r <- n*rmax/ncirc
data.frame(x=r*sin(theta),y=r*cos(theta),r=round(r,2))
}))
rr <- unique(dat.circ$r)
dat.text=data.frame(x=rr*cos(30),y=rr*sin(30),label=rr)
library(ggplot2)
ggplot(xy,aes(x,y))+
geom_point() +
geom_path(data=dat.circ,alpha=.2,aes(group=factor(r))) +
geom_text(data=dat.text,aes(label=rr),vjust=-1)
How about this with ggplot2 and grid:
require(ggplot2)
require(grid)
x<-(runif(100)-0.5)*4
y<-(runif(100)-0.5)*4
circ_rads<-seq(0.25,2,0.25)
qplot(x,y)+
lapply(circ_rads,FUN=function(x)annotation_custom(circleGrob(gp=gpar(fill="transparent",color="black")),-x,x,-x,x))+
geom_text(aes(x=0,y=circ_rads+0.1,label=circ_rads)) + coord_fixed(ratio = 1)
Related
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
I want to plot SpatialPolygonsDataFrame as a semi-transparent main object (with legend on the right), but I want to plot yet additional raster (hillshade) as a background - just to make nicer map. I would need something like:
spplot(polygons, sp.layout = list(list("raster", myRaster)))
but looking at ?spplot, it doesn't seem to be possible to specify the raster in sp.layout. I can't specify the raster as the main object, because the main object are the polygons dataFrame, whose value scale I want to plot in the legend on the right side.
How is it possible to plot an additional raster in spplot?
Here's one way to do it. There's probably a neater way to achieve it without plotting the polygon object twice, though...
library(sp)
library(rasterVis)
r <- raster(nrow=18, ncol=36)
r[] <- runif(ncell(r)) * 10
r[r > 8] <- NA
pol <- rasterToPolygons(r, function(x) x > 6)
spplot(pol) + levelplot(r) + spplot(pol)
Or alternatively:
library(latticeExtra)
spplot(pol) + spplot(r) + spplot(pol)
EDIT
As per the comment by #OscarPerpiñán, a better way to do this is:
spplot(pol) + as.layer(spplot(r), under = TRUE)
I'm trying to create a 3D plot in R-Project. I know there was a question like this before but I couldn't solve my problems with the answers there.
What I have is:
Vdot_L = c(0,1,2,3,4,5,6,7,8,9,10)
Qdot_verd = c(2000,2100,2200,2300,2400,2500,2600,2700,2800,2900,3000)
zeta_ex = 0.4
T_U = 293.15 #K
T_verd = 273.15 #K
T_cond=Vdot_L*2+T_U
epsilon_k = zeta_ex * T_verd/(T_cond - T_verd)
Pfun <- function(a,b) {a/b}
P <- outer(Qdot_verd, epsilon_k, FUN="Pfun")
What I'd like to create is a colored surface plot with Vdot_L on the x-Axis, Qdot_verd on the y-Axis and P on the z-Axis. I'm thanful for every help.
So something like this??
library(rgl)
zlim <- range(P,na.rm=T)
zlen <- zlim[2] - zlim[1] + 1
color.range <- rev(rainbow(zlen)) # height color lookup table
colors <- color.range[P-zlim[1]+1] # assign colors to heights for each point
persp3d(Vdot_L, Qdot_verd, P, col=colors)
Have you investigated the Plot3D package?
http://cran.r-project.org/web/packages/plot3D/plot3D.pdf
There's a method in here called surf3d which seems like it would do what you want. After importing the package, cast your values to matrix and write:
surf3d(Vdot_L, Qdot_verd, P)
There's also a color parameter which you can adjust.
Alternatively, use rgl, and avoid the matrix issue:
rgl.surface(Vdot_L, Qdot_verd, P)
Also check out these posts for more info:
R: 3D surface plot from 2D matrix
How to create 3D - MATLAB style - surface plots in R
I'm constructing a plot that uses geom_tile and then outputting it to .pdf (using pdf("filename",...)). However, when I do, the .pdf result has tiny lines (striations, as one person put it) running through it. I've attached an image showing the problem.
Googling let to this thread, but the only real advice in there was to try passing size=0 to geom_tile, which I did with no effect. Any suggestions on how I can fix these? I'd like to use this as a figure in a paper, but it's not going to work like this.
Minimal code:
require(ggplot2)
require(scales)
require(reshape)
volcano3d <- melt(volcano)
names(volcano3d) <- c("x", "y", "z")
v <- ggplot(volcano3d, aes(x, y, z = z))
pdf("mew.pdf")
print(v + geom_tile(aes(fill=z)) + stat_contour(size=2) + scale_fill_gradient("z"))
This happens because the default colour of the tiles in geom_tile seems to be white.
To fix this, you need to map the colour to z in the same way as fill.
print(v +
geom_tile(aes(fill=z, colour=z), size=1) +
stat_contour(size=2) +
scale_fill_gradient("z")
)
Try to use geom_raster:
pdf("mew.pdf")
print(v + geom_raster(aes(fill=z)) + stat_contour(size=2) + scale_fill_gradient("z"))
dev.off()
good quality in my environment.
I cannot reproduce the problem on my computer (Windows 7), but I remember it was a problem discussed on the list for certain configurations. Brian Ripley (if I remember) recommended
CairoPDF("mew.pdf") # Package Cairo
to get around this
In the interests of skinning this cat, and going into waaay too much detail, this code decomposes the R image into a mesh of quads (as used by rgl), and then shows the difference between a raster plot and a "tile" or "rect" plot.
library(raster)
im <- raster::raster(volcano)
## this is the image in rgl corner-vertex form
msh <- quadmesh::quadmesh(im)
## manual labour for colour scaling
dif <- diff(range(values(im)))
mn <- min(values(im))
scl <- function(x) (x - mn)/dif
This the the traditional R 'image', which draws a little tile or 'rect()' for every pixel.
list_image <- list(x = xFromCol(im), y = rev(yFromRow(im)), z = t(as.matrix(im)[nrow(im):1, ]))
image(list_image)
It's slow, and though it calls the source of 'rect()' under the hood, we can't also set the border colour. Use 'useRaster = TRUE' to use 'rasterImage' for more efficient drawing time, control over interpolation, and ultimately - file size.
Now let's plot the image again, but by explicitly calling rect for every pixel. ('quadmesh' probably not the easiest way to demonstrate, it's just fresh in my mind).
## worker function to plot rect from vertex index
rectfun <- function(x, vb, ...) rect(vb[1, x[1]], vb[2,x[1]], vb[1,x[3]], vb[2,x[3]], ...)
## draw just the borders on the original, traditional image
apply(msh$ib, 2, rectfun, msh$vb, border = "white")
Now try again with 'rect'.
## redraw the entire image, with rect calls
##(not efficient, but essentially the same as what image does with useRaster = FALSE)
cols <- heat.colors(12)
## just to clear the plot, and maintain the plot space
image(im, col = "black")
for (i in seq(ncol(msh$ib))) {
rectfun(msh$ib[,i], msh$vb, col = cols[scl(im[i]) * (length(cols)-1) + 1], border = "dodgerblue")
}
See this example
This was created in matlab by making two scatter plots independently, creating images of each, then using the imagesc to draw them into the same figure and then finally setting the alpha of the top image to 0.5.
I would like to do this in R or matlab without using images, since creating an image does not preserve the axis scale information, nor can I overlay a grid (e.g. using 'grid on' in matlab). Ideally I wold like to do this properly in matlab, but would also be happy with a solution in R. It seems like it should be possible but I can't for the life of me figure it out.
So generally, I would like to be able to set the alpha of an entire plotted object (i.e. of a matlab plot handle in matlab parlance...)
Thanks,
Ben.
EDIT: The data in the above example is actually 2D. The plotted points are from a computer simulation. Each point represents 'amplitude' (y-axis) (an emergent property specific to the simulation I'm running), plotted against 'performance' (x-axis).
EDIT 2: There are 1796400 points in each data set.
Using ggplot2 you can add together two geom_point's and make them transparent using the alpha parameter. ggplot2 als adds up transparency, and I think this is what you want. This should work, although I haven't run this.
dat = data.frame(x = runif(1000), y = runif(1000), cat = rep(c("A","B"), each = 500))
ggplot(aes(x = x, y = y, color = cat), data = dat) + geom_point(alpha = 0.3)
ggplot2 is awesome!
This is an example of calculating and drawing a convex hull:
library(automap)
library(ggplot2)
library(plyr)
loadMeuse()
theme_set(theme_bw())
meuse = as.data.frame(meuse)
chull_per_soil = ddply(meuse, .(soil),
function(sub) sub[chull(sub$x, sub$y),c("x","y")])
ggplot(aes(x = x, y = y), data = meuse) +
geom_point(aes(size = log(zinc), color = ffreq)) +
geom_polygon(aes(color = soil), data = chull_per_soil, fill = NA) +
coord_equal()
which leads to the following illustration:
You could first export the two data sets as bitmap images, re-import them, add transparency:
library(grid)
N <- 1e7 # Warning: slow
d <- data.frame(x1=rnorm(N),
x2=rnorm(N, 0.8, 0.9),
y=rnorm(N, 0.8, 0.2),
z=rnorm(N, 0.2, 0.4))
v <- with(d, dataViewport(c(x1,x2),c(y, z)))
png("layer1.png", bg="transparent")
with(d, grid.points(x1,y, vp=v,default="native",pch=".",gp=gpar(col="blue")))
dev.off()
png("layer2.png", bg="transparent")
with(d, grid.points(x2,z, vp=v,default="native",pch=".",gp=gpar(col="red")))
dev.off()
library(png)
i1 <- readPNG("layer1.png", native=FALSE)
i2 <- readPNG("layer2.png", native=FALSE)
ghostize <- function(r, alpha=0.5)
matrix(adjustcolor(rgb(r[,,1],r[,,2],r[,,3],r[,,4]), alpha.f=alpha), nrow=dim(r)[1])
grid.newpage()
grid.rect(gp=gpar(fill="white"))
grid.raster(ghostize(i1))
grid.raster(ghostize(i2))
you can add these as layers in, say, ggplot2.
Use the transparency capability of color descriptions. You can define a color as a sequence of four 2-byte words: muddy <- "#888888FF" . The first three pairs set the RGB colors (00 to FF); the final pair sets the transparency level.
AFAIK, your best option with Matlab is to just make your own plot function. The scatter plot points unfortunately do not yet have a transparency attribute so you cannot affect it. However, if you create, say, most crudely, a bunch of loops which draw many tiny circles, you can then easily give them an alpha value and obtain a transparent set of data points.