Setting an image as the colour of a polygon - r

I borrowed this code from statmethods[dot]net. The result is a coloured area under a normal distribution.
mean=100; sd=15
lb=80; ub=120
x <- seq(-4,4,length=100)*sd + mean
hx <- dnorm(x,mean,sd)
plot(x, hx, type="n", xlab="IQ Values", ylab="Density",
main="Normal Distribution", axes=FALSE)
i <- x >= lb & x <= ub
lines(x, hx)
polygon(c(lb,x[i],ub), c(0,hx[i],0), col="red")
area <- pnorm(ub, mean, sd) - pnorm(lb, mean, sd)
result <- paste("P(",lb,"< IQ <",ub,") =",
signif(area, digits=3))
mtext(result,2)
I was wondering if it is possible to select an image as the colour of the red polygon?
Many thanks!

Working with your code, I basically suggest plotting an image (I used a random image below) using the png package (based on advice from In R, how to plot with a png as background?) and then plot your lines over that.
mean=100; sd=15
lb=80; ub=120
x <- seq(-4,4,length=100)*sd + mean
hx <- dnorm(x,mean,sd)
# load package and an image
library(png)
ima <- readPNG("Red_Hot_Sun.PNG")
# plot an empty plot with your labels, etc.
plot(1,xlim=c(min(x),max(x)), type="n", xlab="IQ Values", ylab="Density",
main="Normal Distribution", axes=FALSE)
# put in the image
lim <- par()
rasterImage(ima, lim$usr[1], lim$usr[3], lim$usr[2], lim$usr[4])
# add your plot
par(new=TRUE)
plot(x, hx, xlim=c(min(x),max(x)), type="l", xlab="", ylab="", axes=FALSE)
i <- x >= lb & x <= ub
lines(x, hx)
# add a polygon to cover the background above plot
polygon(c(x,180,180,20,20), c(hx,0,1,1,0), col="white")
# add polygons to cover the areas under the plot you don't want visible
polygon(c(-20,-20,x[x<=lb],lb), c(-10,min(hx),hx[x<=lb],-10), col="white")
polygon(c(ub,x[x>=ub],200,200), c(-1,hx[x>=ub],min(hx),-1), col="white")
# add your extra text
area <- pnorm(ub, mean, sd) - pnorm(lb, mean, sd)
result <- paste("P(",lb,"< IQ <",ub,") =",
signif(area, digits=3))
mtext(result,2)
Gives you:

Related

Kernel density scatter plot in R

I saw a beautiful plot and I'd like to recreate it. Here's an example showing what I've got so far:
# kernel density scatterplot
library(RColorBrewer)
library(MASS)
greyscale <- rev(brewer.pal(4, "Greys"))
x <- rnorm(20000, mean=5, sd=4.5); x <- x[x>0]
y <- x + rnorm(length(x), mean=.2, sd=.4)
z <- kde2d(x, y, n=100)
plot(x, y, pch=".", col="hotpink")
contour(z, drawlabels=FALSE, nlevels=4, col=greyscale, add=T)
abline(c(0,1), lty=1, lwd=2)
abline(lm(y~x), lty=2, lwd=2)
I'm struggling to fill the contours with colour. Is this a job for smoothScatter or another package? I suspect it might be down to my use of kde2d and, if so, can someone please explain this function or link me to a good tutorial?
Many thanks!
P.S. the final image should be greyscale
Seems like you want a filled contour rather than jus a contour. Perhaps
library(RColorBrewer)
library(MASS)
greyscale <-brewer.pal(5, "Greys")
x <- rnorm(20000, mean=5, sd=4.5); x <- x[x>0]
y <- x + rnorm(length(x), mean=.2, sd=.4)
z <- kde2d(x, y, n=100)
filled.contour(z, nlevels=4, col=greyscale, plot.axes = {
axis(1); axis(2)
#points(x, y, pch=".", col="hotpink")
abline(c(0,1), lty=1, lwd=2)
abline(lm(y~x), lty=2, lwd=2)
})
which gives

Bid Rent Curves - Plotting Circles of Projected Radii from Another Dimension

The goal is to reproduce this Bid-Rent graph in R:
The challenge is to draw the projected circles. So far I got:
The 2D part is created by the R code below with the traditional graphic system in base R:
#Distance
X <- seq(0,7,1)
#Bid Rent Curves: Commercial, Industrial, Residential
com <- -5*X + 10
ind <- -2*X + 7
res <- -0.75*X + 4
graph <- plot(X, com, type="l", col="green", ylim=c(0,10), xlab="", ylab="", axes=FALSE)
lines(X, ind, col="red")
lines(X, res, col="blue")
abline(v=0, h=0)
segments(1,0, 1,5, lty=2)
segments(2.5,0, 2.5,2, lty=2)
title(main="Bid Rent Curves", sub="Alonso Model",
xlab="Distance from CBD", ylab="Rent per m2")
text(2.5,7.5, "Commercial", col="green")
text(3.5,4, "Industrial", col="red")
text(5.5,2, "Residential", col="blue")
(Detail: Why the curves do not respect the ylim = 0 ?)
How make the projection and draw the semi-circles?
It is not exactly a 3D plot. I have looked into plot3D and rgl. I am not sure which packages or strategy to use from here.
I'm taking you at your word that you want circles, so you need to push the plot area into the upper right corner:
outHalfCirc <- function(r,colr) {opar=par(xpd=TRUE, new=TRUE) #plot ouside plot area
polygon(x=seq(r,-r,by=-0.1),
y= -sqrt(r^2 - seq(r,-r,by=-0.1)^2) , # solve r^2 = x^2 +y^2 for y
xlim =c(0,7 ), ylim=c(0,10), col=colr, # need xlim and ylim to match base plot ranges
yaxs="i", yaxt="n", xaxs="i") # yaxis off; x and y axes meet at origin
par(opar)}
Then push plot up and to the right: This will draw a colored half-circles (largest first so they overlap) below the y=0 line.
png() # send to image file; not needed for testing
opar <- par(mar=c(15, 15, 2,2) ) # default units are in widths of text-"line".
# the margins start at lower, then clockwise
# run your code
outHalfCirc(5.5, "blue")
outHalfCirc(2.5, "red")
outHalfCirc(1, "green")
dev.off() # complete image production
par(opar) # different than the 'opar' inside the function
Voila! Although not really circles because the aspect ratio is not 1. That can be fixed (or you could set the xlim and ylim to be equal.

Icons as x-axis labels in R

I would like to plot something like this (from this paper) where icons, in this case small graphs, are used as tick labels.
I get this far, where icons are more or less properly placed:
This is the code:
library(igraph)
npoints <- 15
y <- rexp(npoints)
x <- seq(npoints)
par(fig=c(0.05,1,0.3,1), new=FALSE)
plot(y, xlab=NA, xaxt='n', pch=15, cex=2, col="red")
lines(y, col='red', lwd=2)
xspan <- 0.9
xoffset <- (0.07+0.5/npoints)*xspan
for(i in 1:npoints){
x1 <- (xoffset+(i-1)/npoints)*xspan
x2 <- min(xspan*(xoffset+(i)/npoints),1)
par(fig=c(x1,x2,0,0.5), new=TRUE)
plot(graph.ring(i), vertex.label=NA)
}
However, if the number of points grows (e.g. npoints <- 15) it complains because there is no place for the icons:
Error in plot.new() : figure margins too large
I wonder wether there is a more natural way to do this so that it works for any (reasonable) number of points.
Any advice is welcome.
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')
# abline(v=1:npoints, xpd=NA)
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)
# uncomment to draw box around plot to verify proper alignment:
# box()
}

r xyplot "steps" centered on data points

the type argument to xyplot() can take "s" for "steps." From help(plot):
The two step types differ in their x-y preference: Going from
(x1,y1) to (x2,y2) with x1 < x2, 'type = "s"' moves first
horizontal, then vertical, whereas 'type = "S"' moves the other
way around.
i.e. if you use type="s", the horizontal part of the step has its left end attached to the data point, while type="S" has its right end attached to the data point.
library(lattice)
set.seed(12345)
num.points <- 10
my.df <- data.frame(x=sort(sample(1:100, num.points)),
y=sample(1:40, num.points, replace=TRUE))
xyplot(y~x, data=my.df, type=c("p","s"), col="blue", main='type="s"')
xyplot(y~x, data=my.df, type=c("p","S"), col="red", main='type="S"')
How could one achieve a "step" plot, where the vertical motion happens between data points points, i.e. at x1 + (x2-x1)/2, so that the horizontal part of the step is centered on the data point?
Edited to include some example code. better late than never I suppose.
I am using excellent #nico answer to give its lattice version. Even I am ok with #Dwin because the question don't supply a reproducible example, but customizing lattice panel is sometimes challenging.
The idea is to use panel.segments which is the equivalent of segments of base graphics.
library(lattice)
xyplot(y~x,
panel =function(...){
ll <- list(...)
x <- ll$x
y <- ll$y
x.start <- x - (c(0, diff(x)/2))
x.end <- x + (c(diff(x)/2, 0))
panel.segments(x.start, y, x.end, y, col="orange", lwd=2)
panel.segments(x.end[-length(x.end)], y[1:(length(y)-1)],
x.end[-length(x.end)], y[-1], col="orange", lwd=2)
## this is optional just to compare with type s
panel.xyplot(...,type='s')
## and type S
panel.xyplot(...,type='S')
})
This is a base graphics solution, as I am not too much of an expert in lattice.
Essentially you can use segments to draw first the horizontal, then the vertical steps, passing the shifted coordinates as a vector.
Here is an example:
set.seed(12345)
# Generate some data
num.points <- 10
x <- sort(sample(1:100, num.points))
y <- sample(1:40, num.points, replace=T)
# Plot the data with style = "s" and "S"
par(mfrow=c(1,3))
plot(x, y, "s", col="red", lwd=2, las=1,
main="Style: 's'", xlim=c(0, 100))
points(x, y, pch=19, col="red", cex=0.8)
plot(x, y, "S", col="blue", lwd=2, las=1,
main="Style: 'S'", xlim=c(0, 100))
points(x, y, pch=19, col="blue", cex=0.8)
# Now plot our points
plot(x, y, pch=19, col="orange", cex=0.8, las=1,
main="Centered steps", xlim=c(0, 100))
# Calculate the starting and ending points of the
# horizontal segments, by shifting the x coordinates
# by half the difference with the next point
# Note we leave the first and last point as starting and
# ending points
x.start <- x - (c(0, diff(x)/2))
x.end <- x + (c(diff(x)/2, 0))
# Now draw the horizontal segments
segments(x.start, y, x.end, y, col="orange", lwd=2)
# and the vertical ones (no need to draw the last one)
segments(x.end[-length(x.end)], y[1:(length(y)-1)],
x.end[-length(x.end)], y[-1], col="orange", lwd=2)
Here is the result:

layout add line R

In the 4th example of layout function, which can be generated by example(layout),
I want to overlay the line plot on the barplot of samples from a normal distriubtion.
I tried lines(), plot( , add=TRUE), but with no luck.
How can I do that? Or do I have to take a different route from using layout?
Here is one approach:
library(TeachingDemos)
x <- pmin(3, pmax(-3, stats::rnorm(50)))
y <- pmin(3, pmax(-3, stats::rnorm(50)))
xhist <- hist(x, breaks=seq(-3,3,0.5), plot=FALSE)
yhist <- hist(y, breaks=seq(-3,3,0.5), plot=FALSE)
top <- max(c(xhist$density, yhist$density))
xrange <- c(-3,3)
yrange <- c(-3,3)
nf <- layout(matrix(c(2,0,1,3),2,2,byrow=TRUE), c(3,1), c(1,3), TRUE)
layout.show(nf)
par(mar=c(3,3,1,1))
plot(x, y, xlim=xrange, ylim=yrange, xlab="", ylab="")
par(mar=c(0,3,1,1))
bx.out <- barplot(xhist$density, axes=FALSE, ylim=c(0, top), space=0)
updateusr( bx.out[1:2], 0:1, xhist$mids[1:2], 0:1 )
xdens <- density(x)
lines(xdens$x, xdens$y, col='blue')
par(mar=c(3,0,1,1))
by.out <- barplot(yhist$density, axes=FALSE, xlim=c(0, top), space=0, horiz=TRUE)
updateusr( 0:1, by.out[1:2], 0:1, yhist$mids[1:2] )
ydens <- density(y)
lines(ydens$y, ydens$x, col='blue')
Note the change from counts to density so that the "heights" of the bars will match with the density and the use of updateusr from the TeachingDemos package to match the coordinate systems. Instead of updateusr you could also specify width and xlim to the barplot function.

Resources