Creating a colour gradient around zero in filled.contour - r

I am new to R and am struggling to find an answer to what I thought would be a relatively common question. I am creating a world map of a variable using filled.contour. For example:
z=matrix(rnorm(7008),nrow=96)
x=seq(-176.25,180, by=3.75)
y=seq(-90,90, by=2.5)
filled.contour(x,y,z, plot.axes={axis(1); axis(2); map(add=TRUE, interior=FALSE)} )
In which x & y are longitude and latitude, and z is a data matrix. I have spent time applying my own colours and levels, however I want to have a gradient of colour with white assigned as zero. With negative number grading to dark blue and positive to dark red through green and yellow.
I have tried to use the color.scale function from the 'plotrix' package
cellcol=matrix(rep("#000000",7008),nrow=96) # replicating the size of my matrix z
cellcol[z>0]=color.scale(z[z>0], c(0,1,1),c(1,1,0),0) # values above zero grading to red
cellcol[z<0]=color.scale(z[z<0], 0, 0,c(0,1)) # values below zero grading to blue
However now I am stuck. Is this the best way to go about doing this? If so how do I input this into the the filled.contour code above? I'm sure it is simple yet can't get it to work.
Thanks for any help in advance.

I have only managed to achieve what you want using ggplot2.
You can try the following (I submerged the volcano data as an example):
library(ggplot2)
library(reshape2)
## Just an example, I subtract the mean to have positive and negative values for z
dd <- volcano-mean(volcano)
## Creates a data.frame with columns x, y, z
dd <- melt(dd)
names(dd) <- c('x','y','z')
## Does the contour plot
d <- ggplot(dd, aes(x,y,z=z))
d + geom_tile(aes(fill=z)) + scale_fill_gradient2(low="blue", high="red")
I wrote a small function that does what you want to achieve:
myFilled.contour <- function(x = seq(0, 1, length.out = nrow(z)),
y = seq(0, 1, length.out = ncol(z)),
z, nlevels=30, ...) {
ma <- max(abs(z))
lvls <- seq(-ma, ma, length.out = nlevels)
cols <- colorRampPalette(c("blue","white","red")) (nlevels - 1)
filled.contour(x, y, z, plot.axes={axis(1); axis(2)},
col=cols, levels=lvls, ...)
}
Using filled.contour and again the submerged volcano dd:
myFilled.contour(z=d)
Using your data:
myFilled.contour(x,y,z)
Caveat: The legend includes levels not used in the contour plot.
Hope it helps,
alex

Related

How do I plot species as different colours in a point pattern (ppp) using spatstat in R?

The set up is this: There are 10 trees within a 20 by 20 m quadrat in a forest. For each tree we know the species, the diameter (in cm), and the location within the quadrat using x,y coordinates.
I would like to plot the trees within the quadrat, where the size of the points are to scale, and each species is represented by a different colour circle.
Use this data for an example:
tag <- as.character(c(1,2,3,4,5,6,7,8,9,10))
species <- c("A","A","A","A","B","B","B","C","C","D")
diameter <- c(50,20,55,30,30,45,15,20,35,45)
x <- c(9,4,5,14,8,19,9,12,10,2)
y <- c(6,7,15,16,12,4,19,2,14,9)
df <- data.frame(tag, species, diameter, x, y)
First I create the point pattern
species_map <- ppp(df$x, df$y, c(0,20), c(0,20))
Then I mark the species and diameter
marks(species_map) <- data.frame(m1 = df$species, m2=(df$diameter))
Now I can plot the point pattern and each point is to scale thanks to the marks on the diameter.
The "markscale" bit is set to 0.01 because the diamter measurements are in cm and the quadrat size is defined in meters.
plot(species_map, which.marks=2, markscale=.01)
Now I want to make the circles of different species different colours, but this is where I'm stuck.
If I try to make a plot that includes both of my marks I just get 2 separate plots, with one using different size points to represent diameter (correctly) and one using different characters to represent different species.
plot(species_map, which.marks= c(1,2), markscale=.01)
How can I get this plot to represent different species using different colors of the same character while ALSO plotting the points to scale?
And how can I make it produce 1 single plot?
Thank you in advance.
Jay
Strangely enough I can't think of a really elegant way to do this. My
best bet is to split the data into separate point patterns by species
and loop through the species and plot. Is that enough for you?
library(spatstat)
tag <- as.character(c(1,2,3,4,5,6,7,8,9,10))
species <- c("A","A","A","A","B","B","B","C","C","D")
diameter <- c(50,20,55,30,30,45,15,20,35,45)
x <- c(9,4,5,14,8,19,9,12,10,2)
y <- c(6,7,15,16,12,4,19,2,14,9)
df <- data.frame(tag, species, diameter, x, y)
species_map <- ppp(df$x, df$y, c(0,20), c(0,20))
marks(species_map) <- data.frame(m1 = df$species, m2=(df$diameter))
You need to choose four colours and fix the same range of diameters in
each plot and the do the loop (argumet bg is passed to symbols and
fills the background of the circles with this colour):
diamrange <- range(diameter)
cols <- c("black", "red", "green", "blue")
species_map_split <- split(species_map, reduce = TRUE)
plot(species_map_split[[1]], markrange = diamrange, markscale=.01,
main = "", cols = cols[1], bg = cols[1])
#> Warning: Interpretation of arguments maxsize and markscale has changed (in
#> spatstat version 1.37-0 and later). Size of a circle is now measured by its
#> diameter.
for(i in 2:4){
plot(species_map_split[[i]], markrange = diamrange, markscale=.01,
add = TRUE, col = cols[i], bg = cols[i])
}
Symbol maps for multiple columns of marks are not yet implemented in spatstat. So you'll need to do something like Ege suggests.
species <- c("A","A","A","A","B","B","B","C","C","D")
diameter <- c(50,20,55,30,30,45,15,20,35,45)
x <- c(9,4,5,14,8,19,9,12,10,2)
y <- c(6,7,15,16,12,4,19,2,14,9)
library(spatstat)
Dat <- data.frame(x,y,species, diameter)
X <- as.ppp(Dat,W=square(20))
marks(X)$species <- factor(marks(X)$species)
ccc <- c("red","green","blue","black")[as.numeric(marks(X)$species)]
plot(X,which.marks="diameter",maxsize=1,main="Elegant?")
plot(X,which.marks="diameter",maxsize=1,bg=ccc,add=TRUE)
#thanks to #RolfTurner for this!

How to plot deviation from mean

In R I have created a simple matrix of one column yielding a list of numbers with a set mean and a given standard deviation.
rnorm2 <- function(n,mean,sd) { mean+sd*scale(rnorm(n)) }
r <- rnorm2(100,4,1)
I now would like to plot how these numbers differ from the mean. I can do this in Excel as shown below:
But I would like to use ggplot2 to create a graph in R. in the Excel graph I have cheated by using a line graph but if I could do this as columns it would be better. I have tried using a scatter plot but I cant work out how to turn this into deviations from the mean.
Perhaps you want:
rnorm2 <- function(n,mean,sd) { mean+sd*scale(rnorm(n)) }
set.seed(101)
r <- rnorm2(100,4,1)
x <- seq_along(r) ## sets up a vector from 1 to length(r)
par(las=1,bty="l") ## cosmetic preferences
plot(x, r, col = "green", pch=16) ## draws the points
## if you don't want points at all, use
## plot(x, r, type="n")
## to set up the axes without drawing anything inside them
segments(x0=x, y0=4, x1=x, y1=r, col="green") ## connects them to the mean line
abline(h=4)
If you were plotting around 0 you could do this automatically with type="h":
plot(x,r-4,type="h", col="green")
To do this in ggplot2:
library("ggplot2")
theme_set(theme_bw()) ## my cosmetic preferences
ggplot(data.frame(x,r))+
geom_segment(aes(x=x,xend=x,y=mean(r),yend=r),colour="green")+
geom_hline(yintercept=mean(r))
Ben's answer using ggplot2 works great, but if you don't want to manually adjust the line width, you could do this:
# Half of Ben's data
rnorm2 <- function(n,mean,sd) { mean+sd*scale(rnorm(n)) }
set.seed(101)
r <- rnorm2(50,4,1)
x <- seq_along(r) ## sets up a vector from 1 to length(r)
# New variable for the difference between each value and the mean
value <- r - mean(r)
ggplot(data.frame(x, value)) +
# geom_bar anchors each bar at zero (which is the mean minus the mean)
geom_bar(aes(x, value), stat = "identity"
, position = "dodge", fill = "green") +
# but you can change the y-axis labels with a function, to add the mean back on
scale_y_continuous(labels = function(x) {x + mean(r)})
in base R it's quite simple, just do
plot(r, col = "green", type = "l")
abline(4, 0)
You also tagged ggplot2, so in that case it will be a bit more complicated, because ggplot requires creating a data frame and then melting it.
library(ggplot2)
library(reshape2)
df <- melt(data.frame(x = 1:100, mean = 4, r = r), 1)
ggplot(df, aes(x, value, color = variable)) +
geom_line()

Circular density plot using ggplot2

I'm working with circular data and I wanted to reproduce this kind of plot using ggplot2:
library(circular)
data1 <- rvonmises(1000, circular(0), 10, control.circular=list(units="radians")) ## sample
quantile.circular(data1,c(0.05,.95)) ## for interval
data2 <- mean(data1)
dens <- density(data1, bw=27)
p<-plot(dens, points.plot=TRUE, xlim=c(-1,2.1),ylim=c(-1.0,1.2),
main="Circular Density", ylab="", xlab="")
points(circular(0), plot.info=p, col="blue",type="o")
arrows.circular(c(5.7683795,0.5151433 )) ## confidence interval
arrows.circular(data2, lwd=3) ## circular mean
The thinest arrows are extremes of my interval
I suppose blue point is forecast
The third arrow is circular mean
I need circular density
I've been looking for something similar but I did not found anything.
Any suggestion?
Thanks
To avoid running in the wrong direction would you quickly check if this code goes in the right direction? The arrows can be added easily using +arrow(...) with appropriate loading.
EDIT: One remark to the complicated way of attaching density values - ggplot's geom_density does not seem to like coord_polar (at least the way I tried it).
#create some dummy radial data and wrap it in a dataframe
d1<-runif(100,min=0,max=120)
df = NULL
df$d1 <- d1
df <- as.data.frame(df)
#estimate kernel density and then derive an approximate function to attach density values to the radial values in the dataframe
data_density <- density(d1)
density_function <- with(data_density, approxfun(x, y, rule=1))
df$density <- density_function(df$d1)
#order dataframe to facilitate geom_line in polar coordinates
df <- df[order(df$density,df$d1),]
#ggplot object
require(ggplot2)
g = ggplot(df,aes(x=d1,y=density))
#Radial observations on unit circle
g = g + geom_point(aes(x=d1,y=min(df$density)))
#Density function
g = g + geom_line()
g = g + ylim(0,max(df$density))
g = g + xlim(0,360)
#polar coordinates
g = g + coord_polar()
g
Uniform random variables sampled from (0,120):

Plotting z as a color with R on a rGoogleMap

I have a function and I want to plot only x and y. z should be represented as a color. Is there a package that does the work for me ?
f = function(a,b){
dnorm(a^2+b^2)
}
x = seq(-2, 2, 0.1)
y = seq(-2, 2, 0.1)
z = outer(x, y, f)
persp(x, y, z)
I want to plot this function on a map generated with rGoogleMaps. Maybe there is a more specific package for this use?
Something like this?
library(ggmap) # loads ggplot2 as well
library(RgoogleMaps) # for getGeoCode
london.center <- getGeoCode("London")
london <- get_map("London", zoom=12)
x <- seq(-2,2,0.1)
df <- expand.grid(x=x,y=x)
df$z <- with(df,f(x,y))
df$x <- london.center[2]+df$x/20
df$y <- london.center[1]+df$y/20
ggp <- ggmap(london)+
geom_tile(data=df,aes(x=x,y=y,fill=z), alpha=0.2)+
scale_fill_gradientn(guide="none",colours=rev(heat.colors(10)))+
stat_contour(data=df, aes(x=x, y=y, z=z, color=..level..), geom="path", size=1)+
scale_color_gradientn(colours=rev(heat.colors(10)))
plot(ggp)
This solution uses ggplot. Perhaps someone else will show you how to do this using RgoogleMaps.
Basically, we load the map, using get_map(...) (which is just a wrapper for GetMap(...) in the RgoogleMaps package).
Then we create the sample data frame df, which contains three columns, x, y, and z, and one row for every combination of x and y (this is the format required by ggplot).
Then we create the map layers. First the map itself, using ggmap(...); then a layer of tiles "filled" based on the value of z, using geom_tile(...); then a set of contour lines colored using the value of z, using stat_contour(geom="path",...). The rest of the code sets the fill and line colors and renders the map.
Purists will tell you that you can render the filled contours directly using stat_contour(geom="polygon",...), instead of using tiles, but this has the unfortunate effect of clipping any contours not completely enclosed in the plot area.

plot with overlapping points

I have data in R with overlapping points.
x = c(4,4,4,7,3,7,3,8,6,8,9,1,1,1,8)
y = c(5,5,5,2,1,2,5,2,2,2,3,5,5,5,2)
plot(x,y)
How can I plot these points so that the points that are overlapped are proportionally larger than the points that are not. For example, if 3 points lie at (4,5), then the dot at position (4,5) should be three times as large as a dot with only one point.
Here's one way using ggplot2:
x = c(4,4,4,7,3,7,3,8,6,8,9,1,1,1,8)
y = c(5,5,5,2,1,2,5,2,2,2,3,5,5,5,2)
df <- data.frame(x = x,y = y)
ggplot(data = df,aes(x = x,y = y)) + stat_sum()
By default, stat_sum uses the proportion of instances. You can use raw counts instead by doing something like:
ggplot(data = df,aes(x = x,y = y)) + stat_sum(aes(size = ..n..))
Here's a simpler (I think) solution:
x <- c(4,4,4,7,3,7,3,8,6,8,9,1,1,1,8)
y <- c(5,5,5,2,1,2,5,2,2,2,3,5,5,5,2)
size <- sapply(1:length(x), function(i) { sum(x==x[i] & y==y[i]) })
plot(x,y, cex=size)
## Tabulate the number of occurrences of each cooordinate
df <- data.frame(x, y)
df2 <- cbind(unique(df), value = with(df, tapply(x, paste(x,y), length)))
## Use cex to set point size to some function of coordinate count
## (By using sqrt(value), the _area_ of each point will be proportional
## to the number of observations it represents)
plot(y ~ x, cex = sqrt(value), data = df2, pch = 16)
You didn't really ask for this approach but alpha may be another way to address this:
library(ggplot2)
ggplot(data.frame(x=x, y=y), aes(x, y)) + geom_point(alpha=.3, size = 3)
You need to add the parameter cex to your plot function. First what I would do is use the function as.data.frame and table to reduce your data to unique (x,y) pairs and their frequencies:
new.data = as.data.frame(table(x,y))
new.data = new.data[new.data$Freq != 0,] # Remove points with zero frequency
The only downside to this is that it converts numeric data to factors. So convert back to numeric, and plot!
plot(as.numeric(new.data$x), as.numeric(new.data$y), cex = as.numeric(new.data$Freq))
You may also want to try sunflowerplot.
sunflowerplot(x,y)
Let me propose alternatives to adjusting the size of the points. One of the drawbacks of using size (radius? area?) is that the reader's evaluation of spot size vs. the underlying numeric value is subjective.
So, option 1: plot each point with transparency --- ninja'd by Tyler!
option 2: use jitter to push your data around slightly so the plotted points don't overlap.
A solution using lattice and table ( similar to #R_User but no need to remove 0 since lattice do the job)
dt <- as.data.frame(table(x,y))
xyplot(dt$y~dt$x, cex = dt$Freq^2, col =dt$Freq)

Resources