library(ggplot2) mtcars$quart <- cut(mtcars$qsec, quantile(mtcars$qsec)) ggplot(mtcars, aes(x = wt, y = hp, color = factor(quart))) + geom_point(shape = 16, size = 5) + theme(legend.position = c(0.80, 0.85), legend.background = element_rect(colour = "black"), panel.background = element_rect(fill = "black")) + labs(x = "Weight (1,000lbs)", y = "Horsepower") + scale_colour_manual(values = c("#fdcc8a", "#fc8d59", "#e34a33", "#b30000"), name = "Quartiles of qsec", labels = c("14.5-16.9s", "17.0-17.7s", "17.8-18.9s", "19.0-22.9s"))
data.loess <- loess(qsec ~ wt * hp, data = mtcars)
# Create a sequence of incrementally increasing (by 0.3 units) values for both wt and hp xgrid <- seq(min(mtcars$wt), max(mtcars$wt), 0.3) ygrid <- seq(min(mtcars$hp), max(mtcars$hp), 0.3) # Generate a dataframe with every possible combination of wt and hp data.fit <- expand.grid(wt = xgrid, hp = ygrid) # Feed the dataframe into the loess model and receive a matrix output with estimates of # acceleration for each combination of wt and hp mtrx3d <- predict(data.loess, newdata = data.fit) # Abbreviated display of final matrix mtrx3d[1:4, 1:4]
contour(x = xgrid, y = ygrid, z = mtrx3d, xlab = "Weight (1,000lbs)", ylab = "Horsepower")
outer(x, x)
require(grDevices) # for colours x <- -6:16 op <- par(mfrow = c(2, 2)) contour(outer(x, x), method = "edge", vfont = c("sans serif", "plain")) # z <- outer(x, sqrt(abs(x)), FUN = "/") # # image(x, x, z) # contour(x, x, z, col = "pink", add = TRUE, method = "edge", # vfont = c("sans serif", "plain"))
contour(x, x, z, ylim = c(1, 6), method = "simple", labcex = 1, xlab = quote(x[1]), ylab = quote(x[2]))
contour(x, x, z, ylim = c(-6, 6), nlev = 20, lty = 2, method = "simple", main = "20 levels; \"simple\" labelling method") par(op)
## Persian Rug Art: x <- y <- seq(-4*pi, 4*pi, len = 27) r <- sqrt(outer(x^2, y^2, "+")) opar <- par(mfrow = c(2, 2), mar = rep(0, 4)) for(f in pi^(0:3)) contour(cos(r^2)*exp(-r/f), drawlabels = FALSE, axes = FALSE, frame = TRUE)
rx <- range(x <- 10*1:nrow(volcano)) ry <- range(y <- 10*1:ncol(volcano)) ry <- ry + c(-1, 1) * (diff(rx) - diff(ry))/2 tcol <- terrain.colors(12) par(opar); opar <- par(pty = "s", bg = "lightcyan") plot(x = 0, y = 0, type = "n", xlim = rx, ylim = ry, xlab = "", ylab = "") u <- par("usr") rect(u[1], u[3], u[2], u[4], col = tcol[8], border = "red") contour(x, y, volcano, col = tcol[2], lty = "solid", add = TRUE, vfont = c("sans serif", "plain")) title("A Topographic Map of Maunga Whau", font = 4) abline(h = 200*0:4, v = 200*0:4, col = "lightgray", lty = 2, lwd = 0.1) ## contourLines produces the same contour lines as contour plot(x = 0, y = 0, type = "n", xlim = rx, ylim = ry, xlab = "", ylab = "") u <- par("usr") rect(u[1], u[3], u[2], u[4], col = tcol[8], border = "red") contour(x, y, volcano, col = tcol[1], lty = "solid", add = TRUE, vfont = c("sans serif", "plain")) line.list <- contourLines(x, y, volcano) invisible(lapply(line.list, lines, lwd=3, col=adjustcolor(2, .3))) par(opar)
library(rsm) swiss2.lm <- lm(Fertility ~ poly(Agriculture, Education, degree=2), data=swiss) par(mfrow=c(1,3)) image(swiss2.lm, Education ~ Agriculture) contour(swiss2.lm, Education ~ Agriculture) persp(swiss2.lm, Education ~ Agriculture, zlab = "Fertility")
# http://www.stat.cmu.edu/~cshalizi/statcomp/12/lectures/10/lecture-10.R # Code for examples in Lecture 10, 36-350, Fall 2012 # See lecture slides for context # Load the data for the West et al. model once again gmp <- read.table("http://www.stat.cmu.edu/~cshalizi/statcomp/labs/02/gmp.dat") pop <- gmp$gmp/gmp$pcgmp gmp <- data.frame(gmp,pop=pop) # Define the MSE function # Calculate mean squared error of West et al. power-law scaling model # Inputs: scale factor (y0), scaling exponent (a), response variable (Y), # predictor variable (N) # Output: the MSE mse <- function(y0,a,Y=gmp$pcgmp,N=gmp$pop) { mean((Y - y0*(N^a))^2) } # Interaction of curve() and mse() # Next line won't work --- why? ###curve(mse(a=x,y0=6611),from=0.10,to=0.15) # Using sapply() to "vectorize" mse: sapply(seq(from=0.10,to=0.15,by=0.01),mse,y0=6611) # Check that it's doing what it should: mse(6611,0.10) # Define a vectorized version using sapply # You should work out what the inputs and outputs are here! mse.plottable <- function(a,...){sapply(a,mse,...)} # Make some plots curve(mse.plottable(a=x,y0=6611),from=0.10,to=0.15) curve(mse.plottable(a=x,y0=5100),from=0.10,to=0.20) # You should try varying Y and N in mse.plottable # Numerical gradients # This approach to numerical gradients is "for educational purposes only" # For serious work, see the numDeriv package on CRAN (or texts on numerical # analysis) --- slides mention some drawbacks of simple first differences # Clunky numerical gradient calculation, with a for loop # Take numerical gradient by first differences # Inputs: function (f), point at which to take derivative (x), increments for # first differences (deriv.steps), further arguments to f (...) # Output: vector of partial derivatives # Presumes: f takes a vector as argument and returns a numeric scalar # deriv.steps and x have the same number of components gradient <- function(f,x,deriv.steps,...) { p <- length(x) stopifnot(length(deriv.steps)==p) f.old <- f(x,...) gradient <- vector(length=p) for (coordinate in 1:p) { x.new <- x x.new[coordinate] <- x.new[coordinate]+deriv.steps[coordinate] f.new <- f(x.new,...) gradient[coordinate] <- (f.new - f.old)/deriv.steps[coordinate] } return(gradient) } # Better version, avoiding iteration # Take numerical gradient by first differences # Inputs: function (f), point at which to take derivative (x), increments for # first differences (deriv.steps), further arguments to f (...) # Output: vector of partial derivatives # Presumes: f takes a vector as argument and returns a numeric scalar # deriv.steps and x have the same number of components gradient <- function(f,x,deriv.steps,...) { p <- length(x) stopifnot(length(deriv.steps)==p) f.old <- f(x,...) x.new <- matrix(rep(x,times=p),nrow=p) + diag(deriv.steps,nrow=p) # columns of x.new are perturbations of x because of how matrix() fills in f.new <- apply(x.new,2,f,...) gradient <- (f.new - f.old)/deriv.steps # Recycling return(gradient) } # Minimize a function by gradient descent # Inputs: function (f), starting guess (x), number of steps to # take (max.iterations), factor by which to multiply gradient (step.scale), # size of negible derivatives (stopping.deriv), further arguments to # gradien or to f (...) # Outputs: list with final guess at minimum, final value of gradient, number # of iterations taken # Presumes: f takes a numeric vector and returns a numeric scalar # need to pass gradient any extra arguments via ... gradient.descent <- function(f,x,max.iterations,step.scale, stopping.deriv,...) { for (iteration in 1:max.iterations) { grad <- gradient(f,x,...) if(all(abs(grad) < stopping.deriv)) { break() } x <- x - step.scale*grad } fit <- list(argmin=x,final.gradient=grad,final.value=f(x), iterations=iteration) return(fit) } # Adapting mse() to gradient() and gradient.descent() # Option 1: write a wrapper # Version of MSE function taking a vector argument # Inputs: Vector of parameters (param), optional extra arguments to mse (...) # Output: The mean squared error mse.for.optimization <- function(param,...) { mse(y0=param[1],a=param[2],...) } # Try the following call (may have to wait a little) gradient.descent(f=mse.for.optimization,x=c(6611,0.15), max.iterations=1e5,step.scale=c(1e-3,1e-12),stopping.deriv=1e-2, deriv.step=c(1e-2,1e-7)) # Option 2: use an anonymous function gradient.descent(function(param,...) {mse(y0=param[1],a=param[2],...)}, x=c(6611,0.15),max.iterations=1e5,step.scale=c(1e-3,1e-12), stopping.deriv=1e-2,deriv.step=c(1e-2,1e-7)) # Results should be identical to previous call # Returning functions # Rather trivial example # Make circumference-calculators for non-standard values of pi # Inputs: effective value of pi (ratio.to.diameter) # Outputs: Function which calculates circumference from diameter make.noneuclidean <- function(ratio.to.diameter=pi) { circumference <- function(d) { return(ratio.to.diameter*d) } return(circumference) } # Next line will produce a ``not found'' error #### circumference(10) kings.i <- make.noneuclidean(3) kings.i(10) formals(kings.i) body(kings.i) environment(kings.i) #### circumference(10) # Still not found # Less trivial example # Make a linear predictor based on sample data # Inputs: vectors for independent and dependent variable (x and y) # Output: function which calculates expected response at new values of x make.linear.predictor <- function(x,y) { linear.fit <- lm(y~x) predictor <- function(x) { return(predict(object=linear.fit,newdata=data.frame(x=x))) } return(predictor) } independent.variable <- runif(10) dependent.variable <- 7 + 3*independent.variable # No noise my.predictor <- make.linear.predictor(x=independent.variable, y=dependent.variable) all.equal(my.predictor(independent.variable),dependent.variable) # Wouldn't work with noise in dependent variable rm(independent.variable,dependent.variable) ### independent.variable # Gives not-found error ### dependent.variable # Ditto my.predictor(5) # Should be 5*3+7=22 my.predictor(runif(10)) # Even less trivial example: build the gradient operator, instead # of just a function that evaluates the gradient at a point # Gradient operator # Inputs: function (f), optional extras (...) # Outputs: the function which calculates the gradient of f # Presumes: f takes a vector argument and is numeric-valued # our gradient() function from last lecture exists nabla <- function(f,...) { g <- function(x,...) { gradient(f=f,x=x,...) } return(g) } # Presume that our mse.for.optimization() function from last time is around mse.gradient <- nabla(mse.for.optimization) # Next two should match mse.gradient(c(6611,0.15),deriv.steps=c(1,1e-6)) gradient(mse.for.optimization,c(6611,0.15),c(1,1e-6)) # Check that ellipses are working properly by changing a default value gradient(mse.for.optimization,c(6611,0.15),c(1,1e-6),Y=2*gmp$pcgmp) mse.gradient(c(6611,0.15),deriv.steps=c(1,1e-6),Y=2*gmp$pcgmp) # As mentioned last time, the first-difference method for approximating # partial derivatives is not very good, the numDeriv package has # better approximation algorithms in its grad() function # Gradient operator # Inputs: function (f), optional extras (...) # Outputs: the function which calculates the gradient of f # Presumes: f takes a vector argument and is numeric-valued # the numDeriv package is on the system del <- function(f,...) { # If the numDeriv library isn't loaded, load it or die trying require(numDeriv) # Invoke its grad() function in our function definition g <- function(x,...) { grad(func=f,x=x, ...)} return(g) } # Long example: write surface() as an analog to curve(), using # the standard graphics function contour() to do actual plots # EXERCISE: Switch to using persp() to do perspective plots # First attempt # Plot contours of a two-argument function # Inputs: function (f), limits for x and y (from.x,to.x,from.y,to.y), number # of values to space between limits (n.x,n.y), optional extras for contour() # via ... # Outputs: Invisibly, a list with the x and y coordinates, and the matrix of # z coordinates # Presumes: f takes a vector (of length 2!) and returns a single number surface.0 <- function(f,from.x=0,to.x=1,from.y=0,to.y=1,n.x=101, n.y=101,...) { # Make sequences with the right limits and sizes x.seq <- seq(from=from.x,to=to.x,length.out=n.x) y.seq <- seq(from=from.y,to=to.y,length.out=n.y) # Make an (n.x*n.y) by 2 data frame with all combinations of x and y values plot.grid <- expand.grid(x=x.seq,y=y.seq) # Apply f to reach row of the plotting-grid dataframe z.values <- apply(plot.grid,1,f) # Reshape into an n.x by n.y matrix z.matrix <- matrix(z.values,nrow=n.x) # Make the contour plot contour(x=x.seq,y=y.seq,z=z.matrix,...) # Invisibly, return the values we calculated invisible(list(x=x.seq,y=y.seq,z=z.matrix)) } # First graphic # Note use of anonymous function surface.0(function(p){return(sum(p^3))},from.x=-1,from.y=-1) # Second attempt at surface() # Use substitute() and eval() to hold off on evaluating the # expression until the right values are assembled # idea taken from source to curve() # Plot contours of a two-argument function # Inputs: R expression (expr), limits for x and y (from.x,to.x,from.y,to.y), # number of values to space between limits (n.x,n.y), optional extras for # contour() via ... # Outputs: Invisibly, a list with the x and y coordinates, and the matrix of # z coordinates # Presumes: expr is a valid R expression involving x and y, any other names in # it can be found in the environment the function is called from surface.1 <- function(expr,from.x=0,to.x=1,from.y=0,to.y=1,n.x=101, n.y=101,...) { # Make sequences with the right limits and sizes x.seq <- seq(from=from.x,to=to.x,length.out=n.x) y.seq <- seq(from=from.y,to=to.y,length.out=n.y) # Make an (n.x*n.y) by 2 data frame with all combinations of x and y values plot.grid <- expand.grid(x=x.seq,y=y.seq) # Recover the unevaluated, but evaluable, R expression from the first # argument unevaluated.expression <- substitute(expr) # ... and evaluate it in the environment set up by plot.grid z.values <- eval(unevaluated.expression,envir=plot.grid) # Reshape the vector of values into a matrix z.matrix <- matrix(z.values,nrow=n.x) # Make the contour plot contour(x=x.seq,y=y.seq,z=z.matrix,...) # Return everything we calculated invisible(list(x=x.seq,y=y.seq,z=z.matrix)) } # Second figure surface.1(abs(x^3)+abs(y^3),from.x=-1,from.y=-1) # Third attempt, using function creation and outer # Plot contours of a two-argument function # Inputs: R expression (expr), limits for x and y (from.x,to.x,from.y,to.y), # number of values to space between limits (n.x,n.y), optional extras for # contour() via ... # Outputs: Invisibly, a list with the x and y coordinates, and the matrix of # z coordinates # Presumes: expr is a valid R expression involving x and y, any other names in # it can be found in the environment the function is called from surface.2 <- function(expr,from.x=0,to.x=1,from.y=0,to.y=1,n.x=101, n.y=101,...) { # Make appropriate coordinate sequences x.seq <- seq(from=from.x,to=to.x,length.out=n.x) y.seq <- seq(from=from.y,to=to.y,length.out=n.y) # Recover the actual R expression from the first argument unevaluated.expression <- substitute(expr) # Define a new function that evaluates that expression z <- function(x,y) { return(eval(unevaluated.expression,envir=list(x=x,y=y))) } # Run that function on all combinations of coordinates z.values <- outer(X=x.seq,Y=y.seq,FUN=z) # Reshape the vector of z.values into a matrix z.matrix <- matrix(z.values,nrow=n.x) # Make the contour plot contour(x=x.seq,y=y.seq,z=z.matrix,...) # Return all the numbers we calculated # How would you also return the function z as well? invisible(list(x=x.seq,y=y.seq,z=z.matrix)) } # Third plot surface.2(x^4-y^4,from.x=-1,from.y=-1)
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.