R: Scale geom_point with axes (ggplot2) - r

I have written the following code to plot my x-y data on a set of re-scaleable axes, the values contained in pointSize are the correctly scaled vertical/horizontal diameters of the point I want at each plotted coordinate. How do I go about getting this to work? Right now I am just plotting points with whatever scaling is used by default in geom_point(aes(size)) and the points don't scale with the axes. Once I rescale the axes with coord_cartesian I want the plotted points to increase/decrease relative to the axes accordingly.
For example, if the point size is say 5, that means I want the horizontal and vertical diameter of the point to be 5 relative to the axes regardless of specified xyScaling.
EDIT: min in pointSize should have been min = 0, not min = -10
Minimal reproducible code:
# Sample size & x-y axes plot boundaries
sampleSize <- 100
# Set scale factor of x-y axes
xyScaling <- 1
# Set to false once sampled to rescale axis with same distributions
resample <- TRUE
if (resample == TRUE){
xSample <- replicate(sampleSize, runif(1, min = -sampleSize/2, max = sampleSize/2))
ySample <- replicate(sampleSize, runif(1, min = -sampleSize/2, max = sampleSize/2))
pointSize <- replicate(sampleSize, runif(1, min = 0, max = 10))
}
sampleDataFrame <- data.frame(xSample, ySample, pointSize)
samplePlot <- ggplot(sampleDataFrame, aes(xSample, ySample))
samplePlot +
geom_point(data = sampleDataFrame, aes(size = sampleDataFrame$pointSize[])) +
coord_cartesian(xlim = c((xyScaling*(-sampleSize/2)),(xyScaling*(sampleSize/2))),
ylim = c((xyScaling*(-sampleSize/2)),(xyScaling*(sampleSize/2)))) +
xlab("x") +
ylab("y") +
scale_size_identity(guide=FALSE)
EDIT: So I almost managed to solve the problem by using geom_rect, the following code does what I want with the caveat that the points are rectangles as opposed to ellipses/circles, I couldn't get this to work with ellipses, if anyone could guide me to the right function I would be very grateful.
sampleDataFrame <- data.frame(xSample, ySample, pointSize)
samplePlot <- ggplot(sampleDataFrame)
samplePlot +
geom_point(aes(xSample, ySample, size = 0)) +
geom_rect(aes(xmin = xSample-(pointSize/2), xmax = xSample+(pointSize/2), ymin = ySample-(pointSize/2), ymax = ySample+(pointSize/2))) +
coord_cartesian(xlim = c((xyScaling*(-sampleSize/2)),(xyScaling*(sampleSize/2))),
ylim = c((xyScaling*(-sampleSize/2)),(xyScaling*(sampleSize/2)))) +
xlab("x") +
ylab("y") +
scale_size_identity(guide=FALSE)

this has been suggested in the past, but I don't think it got implemented. One problem is that circles are only circular in the special case of cartesian coordinates with unit aspect ratio. The easiest workaround is probably to create a data.frame with xy positions describing circles (ellipses) and draw these as polygons.
library(gridExtra)
library(ggplot2)
circle <- polygon_regular(50)
pointy_points <- function(x, y, size){
do.call(rbind, mapply(function(x,y,size,id)
data.frame(x=size*circle[,1]+x, y=size*circle[,2]+y, id=id),
x=x,y=y, size=size, id=seq_along(x), SIMPLIFY=FALSE))
}
test <- pointy_points(1:10, 1:10, size=seq(0.2, 1, length.out=10))
ggplot(test, aes(x,y,group=id, fill=id)) + geom_polygon()

You could try to edit the points at the lowest-level, but it's quite fiddly,
library(ggplot2); library(grid)
p <- qplot(1:10, 1:10, size=I(10))
g <- ggplotGrob(p)
points <- g$grobs[[4]][["children"]][[2]]
g$grobs[[4]][["children"]][[2]] <-
editGrob(points, size = convertUnit(points$size, unitTo = "npc"))
grid.newpage()
grid.draw(g)

Related

Plotting axes in R

I am supposed to plot the following function:
h <- function(x) 0.08-0.06*(1-exp((-x)/(1.5)))/((x)/(1.5))-0.3*((1-exp((-x)/(1.5)))/((x)/(1.5))-exp(-((x)/(1.5)))+0.6*((1-exp((-x)/(0.5)))/((x)/(0.5))-exp(-((x)/(0.5)))))
So I did:
plot(-1:24, h(-1:24), type="l")
and it would be for x-axes. But I do not know how to change it for y.
For x I want to draw it from -1 to 24 and for y from -0.5 to 0.2.
Can anybody help?
An issue with your function is that it is undefined at 0, so you get a single point at x == -1 followed by a gap until x == 1. You'll get a better plot with more points, e.g.
x <- seq(-1, 24, len=200)
plot(x, h(x), type = "l")
(which never evaluates it at exactly zero, so the gap doesn't appear).
If you still want to change the axis limits, use
x <- seq(-1, 24, len=200)
plot(x, h(x), type = "l", ylim = c(-0.5, 2))
With ggplot2 you can do:
library(ggplot2)
data = data.frame(x = -1:24)
ggplot(data, aes(x=x)) + stat_function(fun=h) + ylim(-0.5,2)
Or
data = data.frame(x = -1:24, y = h(-1:24))
ggplot(data, aes(x=x, y=y)) + geom_line() + ylim(-0.5,2)
But a solution with plot is probably easier as its a very simple thing, ggplot is kinda overkill i guess.

Plot a x=1, y=1 and another function in R

I am new to R, and I am trying to do what it seems to be the simplest thing, but for the love of god, I cannot find out how to do it!
As the title says, I want to plot x=1, y=1 and y=1/(2*x), preferably with different colors, and after that, I want to paint the area between the x,y axis and the lines ploted. Something like this:
Thanks in advance
There are various ways to do this. For example, using library(ggplot2) you can do
# define how far beyond the intersection we calculate curve values
xmax = 1.1
xmin = 1/(2*xmax)
# calculate coordinates of the curve
x = seq(xmin, xmax, length.out = 100)
y = 1/(2*x)
# create polygon coordinates that follow the curve and ...
# ...extend down the staight lines to infinity
poly = data.frame(
x = c(x[x<1 & y<1], 1, 1, -Inf, -Inf, 0.5),
y = c(y[x<1 & y<1], 0.5, -Inf, -Inf, 1, 1))
ggplot(data.frame(x,y), aes(x,y)) +
geom_polygon(data = poly, fill='yellow') +
geom_line() +
geom_hline(aes(yintercept=1)) +
geom_vline(aes(xintercept=1)) +
coord_equal(1, c(0,1), c(0,1))

Colours across Plots / Heatmaps in R

I am creating a number of heatmaps in R, but I am having problems when it comes to keeping the colour scale consistent across graphs.
I find that the colours are scaled within a graph, is there a way to make colours consistent across graphs? Ie. So that that colour difference between a value of 0.4 and 0.5 is always the same?
Code Example:
set.seed(123)
d1 = matrix(rnorm(9, mean = 0.2, sd = 0.1), ncol = 3)
d2 = matrix(rnorm(9, mean = 0.8, sd = 0.1), ncol = 3)
mat = list(d1, d2)
for(m in mat)
heatmap(m, Rowv = NA ,Colv = NA)
You'll note in the example that cell (2,3) the first graph is similar to cell (1,3) in the second, despite being ~0.8 different
Here's a way to do it with ggplot2, if you're open to not using base graphics:
library(reshape2)
library(ggplot2)
# Set common limits for color scale
limits = range(unlist(mat))
Here's the code for two separate graphs. The last line of code for each graph ensures that they use the same z limits for setting the colors:
ggplot(melt(mat[[1]]), aes(Var1, Var2, fill=value)) +
geom_tile() +
scale_fill_continuous(limits=limits)
ggplot(melt(mat[[2]]), aes(Var1, Var2, fill=value)) +
geom_tile() +
scale_fill_continuous(limits=limits)
Another option is to plot both heatmaps in a single graph using facetting, which automatically ensures both graphs are on the same color scale:
ggplot(melt(mat), aes(Var1, Var2, fill=value)) +
geom_tile() +
facet_grid(. ~ L1)
I've used the default colors here, but for either approach you can set the color scale to be anything you wish. For example:
ggplot(melt(mat), aes(Var1, Var2, fill=value)) +
geom_tile() +
facet_grid(. ~ L1) +
scale_fill_gradient(low="red", high="green")
You could use the image function directly (heatmap uses image), though it will require some extra formatting to match the output of heatmap. You can use zlim to set the color range. Quoting from the ?image page:
the minimum and maximum z values for which colors should be plotted,
defaulting to the range of the finite values of z. Each of the given
colors will be used to color an equispaced interval of this range. The
midpoints of the intervals cover the range, so that values just
outside the range will be plotted.
# define zlim min and max for all the plots
minz = Reduce(min, mat)
maxz = Reduce(max, mat)
for(m in mat) {
image( m, zlim = c(minz, maxz), col = heat.colors(20))
}
To get closer to the formatting produced by heatmap, you can just reuse some code from the heatmap function:
for(m in mat) {
labCol = dim(m)[2]
labRow = dim(m)[1]
image(seq_len(labCol), seq_len(labRow), m, zlim = c(minz, maxz),
col = heat.colors(20), axes = FALSE, xlab = "", ylab = "",
xlim = 0.5 + c(0, labCol), ylim = 0.5 + c(0, labRow))
axis(1, 1L:labCol, labels = seq_len(labCol), las = 2, line = -0.5, tick = 0)
axis(4, 1L:labRow, labels = seq_len(labRow), las = 2, line = -0.5, tick = 0)
}
Using the breaks argument to image is another option. It allows more flexibility than zlim in setting the breakpoints for colors. Quoting from the help page, breaks is
a set of finite numeric breakpoints for the colours: must have one
more breakpoint than colour and be in increasing order. Unsorted
vectors will be sorted, with a warning.

How to make gradient color filled timeseries plot in R

How to fill area under and above (sp)line with gradient color?
This example has been drawn in Inkscape - BUT I NEED vertical gradient - NOT horizontal.
Interval from zero to positive == from white to red.
Interval from zero to negative == from white to red.
Is there any package which could do this?
I fabricated some source data....
set.seed(1)
x<-seq(from = -10, to = 10, by = 0.25)
data <- data.frame(value = sample(x, 25, replace = TRUE), time = 1:25)
plot(data$time, data$value, type = "n")
my.spline <- smooth.spline(data$time, data$value, df = 15)
lines(my.spline$x, my.spline$y, lwd = 2.5, col = "blue")
abline(h = 0)
And here's an approach in base R, where we fill the entire plot area with rectangles of graduated colour, and subsequently fill the inverse of the area of interest with white.
shade <- function(x, y, col, n=500, xlab='x', ylab='y', ...) {
# x, y: the x and y coordinates
# col: a vector of colours (hex, numeric, character), or a colorRampPalette
# n: the vertical resolution of the gradient
# ...: further args to plot()
plot(x, y, type='n', las=1, xlab=xlab, ylab=ylab, ...)
e <- par('usr')
height <- diff(e[3:4])/(n-1)
y_up <- seq(0, e[4], height)
y_down <- seq(0, e[3], -height)
ncolor <- max(length(y_up), length(y_down))
pal <- if(!is.function(col)) colorRampPalette(col)(ncolor) else col(ncolor)
# plot rectangles to simulate colour gradient
sapply(seq_len(n),
function(i) {
rect(min(x), y_up[i], max(x), y_up[i] + height, col=pal[i], border=NA)
rect(min(x), y_down[i], max(x), y_down[i] - height, col=pal[i], border=NA)
})
# plot white polygons representing the inverse of the area of interest
polygon(c(min(x), x, max(x), rev(x)),
c(e[4], ifelse(y > 0, y, 0),
rep(e[4], length(y) + 1)), col='white', border=NA)
polygon(c(min(x), x, max(x), rev(x)),
c(e[3], ifelse(y < 0, y, 0),
rep(e[3], length(y) + 1)), col='white', border=NA)
lines(x, y)
abline(h=0)
box()
}
Here are some examples:
xy <- curve(sin, -10, 10, n = 1000)
shade(xy$x, xy$y, c('white', 'blue'), 1000)
Or with colour specified by a colour ramp palette:
shade(xy$x, xy$y, heat.colors, 1000)
And applied to your data, though we first interpolate the points to a finer resolution (if we don't do this, the gradient doesn't closely follow the line where it crosses zero).
xy <- approx(my.spline$x, my.spline$y, n=1000)
shade(xy$x, xy$y, c('white', 'red'), 1000)
Here's one approach, which relies heavily on several R spatial packages.
The basic idea is to:
Plot an empty plot, the canvas onto which subsequent elements will be laid down. (Doing this first also lets you retrieve the user coordinates of the plot, needed in subsequent steps.)
Use a vectorized call to rect() to lay down a background wash of color. Getting the fiddly details of the color gradient is actually the trickiest part of doing this.
Use topology functions in rgeos to find first the closed rectangles in your figure, and then their complement. Plotting the complement with a white fill over the background wash covers up the color everywhere except within the polygons, just what you want.
Finally, use plot(..., add=TRUE), lines(), abline(), etc. to lay down whatever other details you'd like the plot to display.
library(sp)
library(rgeos)
library(raster)
library(grid)
## Extract some coordinates
x <- my.spline$x
y <- my.spline$y
hh <- 0
xy <- cbind(x,y)
## Plot an empty plot to make its coordinates available
## for next two sections
plot(data$time, data$value, type = "n", axes=FALSE, xlab="", ylab="")
## Prepare data to be used later by rect to draw the colored background
COL <- colorRampPalette(c("red", "white", "red"))(200)
xx <- par("usr")[1:2]
yy <- c(seq(min(y), hh, length.out=100), seq(hh, max(y), length.out=101))
## Prepare a mask to cover colored background (except within polygons)
## (a) Make SpatialPolygons object from plot's boundaries
EE <- as(extent(par("usr")), "SpatialPolygons")
## (b) Make SpatialPolygons object containing all closed polygons
SL1 <- SpatialLines(list(Lines(Line(xy), "A")))
SL2 <- SpatialLines(list(Lines(Line(cbind(c(0,25),c(0,0))), "B")))
polys <- gPolygonize(gNode(rbind(SL1,SL2)))
## (c) Find their difference
mask <- EE - polys
## Put everything together in a plot
plot(data$time, data$value, type = "n")
rect(xx[1], yy[-201], xx[2], yy[-1], col=COL, border=NA)
plot(mask, col="white", add=TRUE)
abline(h = hh)
plot(polys, border="red", lwd=1.5, add=TRUE)
lines(my.spline$x, my.spline$y, col = "red", lwd = 1.5)
Another possibility which uses functions from grid and gridSVG packages.
We start by generating additional data points by linear interpolation, according to methods described by #kohske here. The basic plot will then consist of two separate polygons, one for negative values and one for positive values.
After the plot has been rendered, grid.ls is used to show a list of grobs, i.e. all building block of the plot. In the list we will (among other things) find two geom_area.polygons; one representing the polygon for values <= 0, and one for values >= 0.
The fill of the polygon grobs is then manipulated using gridSVG functions: custom color gradients are created with linearGradient, and the fill of the grobs are replaced using grid.gradientFill.
The manipulation of grob gradients is nicely described in chapter 7 in the MSc thesis of Simon Potter, one of the authors of the gridSVG package.
library(grid)
library(gridSVG)
library(ggplot2)
# create a data frame of spline values
d <- data.frame(x = my.spline$x, y = my.spline$y)
# create interpolated points
d <- d[order(d$x),]
new_d <- do.call("rbind",
sapply(1:(nrow(d) -1), function(i){
f <- lm(x ~ y, d[i:(i+1), ])
if (f$qr$rank < 2) return(NULL)
r <- predict(f, newdata = data.frame(y = 0))
if(d[i, ]$x < r & r < d[i+1, ]$x)
return(data.frame(x = r, y = 0))
else return(NULL)
})
)
# combine original and interpolated data
d2 <- rbind(d, new_d)
d2
# set up basic plot
ggplot(data = d2, aes(x = x, y = y)) +
geom_area(data = subset(d2, y <= 0)) +
geom_area(data = subset(d2, y >= 0)) +
geom_line() +
geom_abline(intercept = 0, slope = 0) +
theme_bw()
# list the name of grobs and look for relevant polygons
# note that the exact numbers of the grobs may differ
grid.ls()
# GRID.gTableParent.878
# ...
# panel.3-4-3-4
# ...
# areas.gTree.834
# geom_area.polygon.832 <~~ polygon for negative values
# areas.gTree.838
# geom_area.polygon.836 <~~ polygon for positive values
# create a linear gradient for negative values, from white to red
col_neg <- linearGradient(col = c("white", "red"),
x0 = unit(1, "npc"), x1 = unit(1, "npc"),
y0 = unit(1, "npc"), y1 = unit(0, "npc"))
# replace fill of 'negative grob' with a gradient fill
grid.gradientFill("geom_area.polygon.832", col_neg, group = FALSE)
# create a linear gradient for positive values, from white to red
col_pos <- linearGradient(col = c("white", "red"),
x0 = unit(1, "npc"), x1 = unit(1, "npc"),
y0 = unit(0, "npc"), y1 = unit(1, "npc"))
# replace fill of 'positive grob' with a gradient fill
grid.gradientFill("geom_area.polygon.836", col_pos, group = FALSE)
# generate SVG output
grid.export("myplot.svg")
You could easily create different colour gradients for positive and negative polygons. E.g. if you want negative values to run from white to blue instead, replace col_pos above with:
col_pos <- linearGradient(col = c("white", "blue"),
x0 = unit(1, "npc"), x1 = unit(1, "npc"),
y0 = unit(0, "npc"), y1 = unit(1, "npc"))
This is a terrible way to trick ggplot into doing what you want. Essentially, I make a giant grid of points that are under the curve. Since there is no way of setting a gradient within a single polygon, you have to make separate polygons, hence the grid. It will be slow if you set the pixels too low.
gen.bar <- function(x, ymax, ypixel) {
if (ymax < 0) ypixel <- -abs(ypixel)
else ypixel <- abs(ypixel)
expand.grid(x=x, y=seq(0,ymax, by = ypixel))
}
# data must be in x order.
find.height <- function (x, data.x, data.y) {
base <- findInterval(x, data.x)
run <- data.x[base+1] - data.x[base]
rise <- data.y[base+1] - data.y[base]
data.y[base] + ((rise/run) * (x - data.x[base]))
}
make.grid.under.curve <- function(data.x, data.y, xpixel, ypixel) {
desired.points <- sort(unique(c(seq(min(data.x), max(data.x), xpixel), data.x)))
desired.points <- desired.points[-length(desired.points)]
heights <- find.height(desired.points, data.x, data.y)
do.call(rbind,
mapply(gen.bar, desired.points, heights,
MoreArgs = list(ypixel), SIMPLIFY=FALSE))
}
xpixel = 0.01
ypixel = 0.01
library(scales)
grid <- make.grid.under.curve(data$time, data$value, xpixel, ypixel)
ggplot(grid, aes(xmin = x, ymin = y, xmax = x+xpixel, ymax = y+ypixel,
fill=abs(y))) + geom_rect()
The colours aren't what you wanted, but it is probably too slow for serious use anyway.

R: plot circle with radius = 1 and angle 0-2pi in polar -coordinates?

I found the plotrix package in R but cannot yet find how to do this simple circle in R. Basically, how can I do a polar-plot with radius of 1 and 0:360 angles in degree, generating a circle?
Example
$$r\cos\left(\frac{2\pi}{3}\left(\frac{3\theta}{2\pi}-\left\lfloor\frac{3\theta}{2\pi}\right\rfloor\right) -\frac{\pi}{3}\right) = 1$$
Perhaps related
Trying to plot the above function, more here, the LaTex with this hack here visible.
Draw a circle with ggplot2
Regular polygons in polar coordinates
You can also make circles using geometry
circle <- function(x, y, rad = 1, nvert = 500, ...){
rads <- seq(0,2*pi,length.out = nvert)
xcoords <- cos(rads) * rad + x
ycoords <- sin(rads) * rad + y
polygon(xcoords, ycoords, ...)
}
# asp = 1 due to Hans' comment below- wouldn't let me leave a comment just saying 'thanks'
plot(-5:5, type="n", xlim = c(-5,5), ylim = c(-5,5), asp = 1)
circle(0,0,4)
circle(-1.5,1.5,.5)
circle(1.5,1.5,.5)
circle(0,0,1)
segments(-2,-2,2,-2)
You can easily get polar coordinate graphs in ggplot2.
From the ggplot2 website:
library(ggplot2)
cxc <- ggplot(mtcars, aes(x = factor(cyl))) +
geom_bar(width = 1, colour = "black")
cxc <- cxc + coord_polar()
print(cxc)
You can do very nice circular statistics with package circular. Below is one of examples from the package:
require(circular)
x <- circular(runif(50, 0, 2*pi))
rose.diag(x, bins = 18, main = 'Uniform Data',col=2)
points(x)

Resources