Related
I have a sitation in which I generate data from simulation and then would like to plot (heat map/contour/3d plot etc); however, for these, data needs to be interpolated using functions like interp. Here is the sample dataset.
Here is the piece of code I tried...
library(akima)
library(GA) # for persp3D; there exists another package for same function "fields"
data <- read.table(commandArgs()[3], header=T,sep="\t")
data <- na.omit(data)
qmax = max(data$q)
kmax = max(data$k)
x <- data$k_bike/kmax
y <- data$k_car/kmax
z <- data$q/qmax
matrix = interp(x,y,z)
persp3D(matrix ,nlevels=30, asp=1, xlim=c(0,1), ylim=c(0,1), color.palette=colorRampPalette(c("green3","yellow", "red"),space = "rgb") )
so the result is --
Now, due to interpolation, there are many points, which have red/orange color instead of green or so. For e.g, if I use levelplot of lattice
levelplot(z~x*y, xlim=c(0,1), ylim=c(0,1), col.regions=colorRampPalette(c("green3","yellow", "red"),space = "rgb") )
The outcome is --
Now, it is clearly visible that there are very few data points having zero (or almost zero) zvalue. Now, the problem is, with levelplot, I get artefacts (white color for missing data points) and I would like to have a better interpolation. Is there any other function to perform this?
I also tried contour plots as follows:
scale <- (qmax+10) / qmax * c(0.000, 0.01, 0.05, 0.10, 0.25, 0.5, 0.75, 1.0)
filled.contour(matrix, nlevels=30, asp=1, xlim=c(0,1), ylim=c(0,1), levels=scale,color.palette=colorRampPalette(c("green3","yellow", "red"),space = "rgb") )
and result is again (kind of wrong color indication).
In short -- I would like to have contour plot or 3d plot but with a
clear (or correct) color indication of zero (about zero) zvalue data
points similar to level plot.
I approached your question with deldir and rgl packages (they allow plotting of surfaces defined by irregular collections of points).
library(deldir); library(rgl)
# Below two lines require time depending on the machine power, be careful
dxyz <- deldir(x, y, z = z) # do Delaunay triangulation
mxyz <- as.mesh3d(dxyz) # convert it to triangle.mesh3d.obj
bgyr <- colorRampPalette(c("blue", "green", "yellow", "red")) # colour func
# use z values for colouring
plot3d(mxyz, col=bgyr(256)[cut(mxyz$vb[3,], 256)][mxyz$it], type="shade")
light3d() # if you want vivit colours
# another approach
# you can solve it by just increasing interp()'s arguments, nx and ny.
library(akima); library(lattice); library(dplyr)
df <- interp(x,y,z, nx=150, ny=150) %>% interp2xyz() %>% data.frame()
levelplot(z ~ x * y, df, xlim=c(0,1), ylim=c(0,1),
col.regions = colorRampPalette(c("green3", "yellow", "red"), space = "rgb"))
It is known that we'll use two dotted lines to express two lines are perpendicular (like the picture).
Is there any argument to express this symbol such as symbol="perpendicular"?
So far, I plot this symbol manually like code below.
plot(rnorm(10),type="n",xlim=c(-1,1),ylim=c(-1,1))
abline(h=0,v=0)
lines(c(0,0.1),c(0.1,0.1),lty=2)
lines(c(0.1,0.1),c(0,0.1),lty=2)
Custom function:
#Cutom function
myfunction <- function(x, y){
for(i in seq_along(x)){
lines(c(x[i], x[i]), c(0,y[i]), lty = 2)
lines(c(0, x[i]), c(y[i], y[i]), lty = 2)
}
}
plot(rnorm(10), type="n", xlim = c(-1,1), ylim = c(-1,1))
abline(h = 0, v = 0)
myfunction(x = c(0.1, 0.5, -0.3, -0.9),
y = c(0.5, -0.3, -0.9, 0.7))
First, thanks zx8754's answer. I enjoy the idea using custom function. However, in my real case the two lines are not parallel to x axis and y axis. Hence, I write another custom function,called perpendicular, instead. BTW, any recommend will be appreciated. The code and the result are below:
# Make an axuliarry line,perpendicular, from A to line OC
plot(rnorm(1),xlim=c(-7,7),ylim=c(-3,3),type="n",las=1,axes=F,xlab="",ylab="")
abline(0,-7/15,col="red",lwd=3)
arrows(0,0,-15/4,-1/4,lwd=2,col="red")
arrows(0,0,18,0,col="gray");arrows(0,0,-9,0,col="gray")
arrows(0,0,0,10,col="gray");arrows(0,0,0,-9,col="gray")
lines(c(-15/4,-218*15/1096),c(-1/4,218*7/1096),col="red",lty=3,lwd=3)
text(-15/4,-1/4,"A",cex=2)
text(-218*15/1096,218*7/1096,"C",cex=2)
text(0,0,"O",cex=2)
#make an perpendicilar symbol to emphasis that it is perpendicular
perpendicular(-218*15/1096,218*7/1096,-15/4,-1/4,0,0,0.3)
# Given any three points,connected with two lines, perpendicualr function can draw an shape in the oppisite direction.
# Note: When two lines are perpendicular,the output will be an perpendicular symbol!
# Definition of notation:
# (x,y) is the turning points of the shape.
# (x1,y1) and (x2,y2) are the other two points.
# k is the length, depending on how large shape you want.
perpendicular<-function(x,y,x1,y1,x2,y2,k){
#points(x,y,col="red",pch=16);points(x1,y1,pch=16);points(x2,y2,pch=16)
m1<-c(x1-x,y1-y);m2<-c(x2-x,y2-y) #two vector of the line
m1<-m1/sqrt(sum(m1^2));m2<-m2/sqrt(sum(m2^2)) #standardlized the vector
#construct the shape I want
xx1<-c(x,y)+k*m1
xx2<-c(x,y)+k*m2
xx3<-c(x,y)+k*(m1+m2)
lines(c(xx1[1],xx3[1]),c(xx1[2],xx3[2]),lty=2)
lines(c(xx2[1],xx3[1]),c(xx2[2],xx3[2]),lty=2)
}
I find it challenging to create aesthetically pleasing 3D surfaces in R. I am familiar with the solutions (persp, image, wireframe, lattice, rgl and several other solutions in other questions in SO), but the results are not nice.
Is it possible to create 3D surface plots in R like in MATLAB?
Here is the MATLAB code
% Create a grid of x and y points
points = linspace(-2, 0, 20);
[X, Y] = meshgrid(points, -points);
% Define the function Z = f(X,Y)
Z = 2./exp((X-.5).^2+Y.^2)-2./exp((X+.5).^2+Y.^2);
% "phong" lighting is good for curved, interpolated surfaces. "gouraud"
% is also good for curved surfaces
surf(X, Y, Z); view(30, 30);
shading interp;
light;
lighting phong;
title('lighting phong', 'FontName', 'Courier', 'FontSize', 14);
The plot is modern, colorful, aesthetically pleasing, the code syntax is very readable.
Is this possible in base R?
jet.colors is the R-answer to one of hte Matlab color palettes:
points = seq(-2, 0, length=20)
#create a grid
XY = expand.grid(X=points,Y=-points)
# A z-function
Zf <- function(X,Y){
2./exp((X-.5)^2+Y^2)-2./exp((X+.5)^2+Y^2);
}
# populate a surface
Z <- Zf(XY$X, XY$Y)
zlim <- range(Z)
zlen <- zlim[2] - zlim[1] + 1
jet.colors <- # function from grDevices package
colorRampPalette(c("#00007F", "blue", "#007FFF", "cyan",
"#7FFF7F", "yellow", "#FF7F00", "red", "#7F0000"))
colorzjet <- jet.colors(100) # 100 separate color
require(rgl)
open3d()
rgl.surface(x=points, y=matrix(Z,20),
coords=c(1,3,2),z=-points,
color=colorzjet[ findInterval(Z, seq(min(Z), max(Z), length=100))] )
axes3d()
rgl.snapshot("copyMatlabstyle.png")
I will admit that getting the colors to line up with the "Z-axis" (which is actually the rgl y-axis) seemed very unintuitive. If you want the shiny, specular effect that Matlab delivers you can play with the angle of illumination.
You can also add or remove lighting:
clear3d(type = "lights")
light3d(theta=0, phi=0)
light3d(theta=0, phi=0) # twice as much light.
After:
grid3d("x")
grid3d("y")
grid3d("z")
rgl.snapshot("copyMatlabstyle3.png")
You could have put the y-grid "behind" the surface with:
grid3d("y+")
Similar tweaks to the axes3d or axis3d calls could move the location of the scales.
For further examples, look at http://rgm3.lab.nig.ac.jp/RGM/R_image_list and search for 'plot3d' which brings up examples of the R2BayesX::plot3d function, Look at Karline Soetaert's plot3D package vignette, "50 ways to plot a volcano"
This may well not do everything you want, but I'm posting it in hopes of attracting better answers.
X <- Y <- seq(-2, 0, length.out= 20)
Z <- outer(X,Y,
function(X,Y) 2/exp((X-.5)^2+Y^2)-2/exp((X+.5)^2+Y^2))
cc <- colorRamp(rev(rainbow(10)))
Zsc <- (Z-min(Z))/diff(range(Z))
rgbvec2col <- function(x) do.call(rgb,c(as.list(x),list(max=255)))
colvec <- apply(cc(Zsc),1,rgbvec2col)
library(rgl)
surface3d(X,Y,Z,col=colvec)
bbox3d(color=c("white","black"))
I need to use black and white color for my boxplots in R. I would like to colorfill the boxplot with lines and dots. For an example:
I imagine ggplot2 could do that but I can't find any way to do it.
Thank you in advance for your help!
I thought this was a great question and pondered if it was possible to do this in base R and to obtain the checkered look. So I put together some code that relies on boxplot.stats and polygon (which can draw angled lines). Here's the solution, which is really not ready for primetime, but is a solution that could be tinkered with to make more general.
boxpattern <-
function(y, xcenter, boxwidth, angle=NULL, angle.density=10, ...) {
# draw an individual box
bstats <- boxplot.stats(y)
bxmin <- bstats$stats[1]
bxq2 <- bstats$stats[2]
bxmedian <- bstats$stats[3]
bxq4 <- bstats$stats[4]
bxmax <- bstats$stats[5]
bleft <- xcenter-(boxwidth/2)
bright <- xcenter+(boxwidth/2)
# boxplot
polygon(c(bleft,bright,bright,bleft,bleft),
c(bxq2,bxq2,bxq4,bxq4,bxq2), angle=angle[1], density=angle.density)
polygon(c(bleft,bright,bright,bleft,bleft),
c(bxq2,bxq2,bxq4,bxq4,bxq2), angle=angle[2], density=angle.density)
# lines
segments(bleft,bxmedian,bright,bxmedian,lwd=3) # median
segments(bleft,bxmin,bright,bxmin,lwd=1) # min
segments(xcenter,bxmin,xcenter,bxq2,lwd=1)
segments(bleft,bxmax,bright,bxmax,lwd=1) # max
segments(xcenter,bxq4,xcenter,bxmax,lwd=1)
# outliers
if(length(bstats$out)>0){
for(i in 1:length(bstats$out))
points(xcenter,bstats$out[i])
}
}
drawboxplots <- function(y, x, boxwidth=1, angle=NULL, ...){
# figure out all the boxes and start the plot
groups <- split(y,as.factor(x))
len <- length(groups)
bxylim <- c((min(y)-0.04*abs(min(y))),(max(y)+0.04*max(y)))
xcenters <- seq(1,max(2,(len*(1.4))),length.out=len)
if(is.null(angle)){
angle <- seq(-90,75,length.out=len)
angle <- lapply(angle,function(x) c(x,x))
}
else if(!length(angle)==len)
stop("angle must be a vector or list of two-element vectors")
else if(!is.list(angle))
angle <- lapply(angle,function(x) c(x,x))
# draw plot area
plot(0, xlim=c(.97*(min(xcenters)-1), 1.04*(max(xcenters)+1)),
ylim=bxylim,
xlab="", xaxt="n",
ylab=names(y),
col="white", las=1)
axis(1, at=xcenters, labels=names(groups))
# draw boxplots
plots <- mapply(boxpattern, y=groups, xcenter=xcenters,
boxwidth=boxwidth, angle=angle, ...)
}
Some examples in action:
mydat <- data.frame(y=c(rnorm(200,1,4),rnorm(200,2,2)),
x=sort(rep(1:2,200)))
drawboxplots(mydat$y, mydat$x)
mydat <- data.frame(y=c(rnorm(200,1,4),rnorm(200,2,2),
rnorm(200,3,3),rnorm(400,-2,8)),
x=sort(rep(1:5,200)))
drawboxplots(mydat$y, mydat$x)
drawboxplots(mydat$y, mydat$x, boxwidth=.5, angle.density=30)
drawboxplots(mydat$y, mydat$x, # specify list of two-element angle parameters
angle=list(c(0,0),c(90,90),c(45,45),c(45,-45),c(0,90)))
EDIT: I wanted to add that one could also obtain dots as a fill by basically drawing a pattern of dots, then covering them a "donut"-shaped polygon, like so:
x <- rep(1:10,10)
y <- sort(x)
plot(y~x, xlim=c(0,11), ylim=c(0,11), pch=20)
outerbox.x <- c(2.5,0.5,10.5,10.5,0.5,0.5,2.5,7.5,7.5,2.5)
outerbox.y <- c(2.5,0.5,0.5,10.5,10.5,0.5,2.5,2.5,7.5,7.5)
polygon(outerbox.x,outerbox.y, col="white", border="white") # donut
polygon(c(2.5,2.5,7.5,7.5,2.5),c(2.5,2.5,2.5,7.5,7.5)) # inner box
But mixing that with angled lines in a single plotting function would be a bit difficult, and is generally a bit more challenging, but it starts to get you there.
I think it is hard to do this with ggplot2 since it dont use shading polygon(gris limitatipn). But you can use shading line feature in base plot, paramtered by density and angle arguments in some plot functions ( ploygon, barplot,..).
The problem that boxplot don't use this feature. So I hack it , or rather I hack bxp internally used by boxplot. The hack consist in adding 2 arguments (angle and density) to bxp function and add them internally in the call of xypolygon function ( This occurs in 2 lines).
my.bxp <- function (all.bxp.argument,angle,density, ...) {
.....#### bxp code
xypolygon(xx, yy, lty = boxlty[i], lwd = boxlwd[i],
border = boxcol[i],angle[i],density[i])
.......## bxp code after
xypolygon(xx, yy, lty = "blank", col = boxfill[i],angle[i],density[i])
......
}
Here an example. It should be noted that it is entirely the responsibility of the user to ensure
that the legend corresponds to the plot. So I add some code to rearrange the legend an the boxplot code.
require(stats)
set.seed(753)
(bx.p <- boxplot(split(rt(100, 4), gl(5, 20))))
layout(matrix(c(1,2),nrow=1),
width=c(4,1))
angles=c(60,30,40,50,60)
densities=c(50,30,40,50,30)
par(mar=c(5,4,4,0)) #Get rid of the margin on the right side
my.bxp(bx.p,angle=angles,density=densities)
par(mar=c(5,0,4,2)) #No margin on the left side
plot(c(0,1),type="n", axes=F, xlab="", ylab="")
legend("top", paste("region", 1:5),
angle=angles,density=densities)
Refer to the above plot. I have drawn the equations in excel and then shaded by hand. You can see it is not very neat. You can see there are six zones, each bounded by two or more equations. What is the easiest way to draw inequalities and shade the regions using hatched patterns ?
To build up on #agstudy's answer, here's a quick-and-dirty way to represent inequalities in R:
plot(NA,xlim=c(0,1),ylim=c(0,1), xaxs="i",yaxs="i") # Empty plot
a <- curve(x^2, add = TRUE) # First curve
b <- curve(2*x^2-0.2, add = TRUE) # Second curve
names(a) <- c('xA','yA')
names(b) <- c('xB','yB')
with(as.list(c(b,a)),{
id <- yB<=yA
# b<a area
polygon(x = c(xB[id], rev(xA[id])),
y = c(yB[id], rev(yA[id])),
density=10, angle=0, border=NULL)
# a>b area
polygon(x = c(xB[!id], rev(xA[!id])),
y = c(yB[!id], rev(yA[!id])),
density=10, angle=90, border=NULL)
})
If the area in question is surrounded by more than 2 equations, just add more conditions:
plot(NA,xlim=c(0,1),ylim=c(0,1), xaxs="i",yaxs="i") # Empty plot
a <- curve(x^2, add = TRUE) # First curve
b <- curve(2*x^2-0.2, add = TRUE) # Second curve
d <- curve(0.5*x^2+0.2, add = TRUE) # Third curve
names(a) <- c('xA','yA')
names(b) <- c('xB','yB')
names(d) <- c('xD','yD')
with(as.list(c(a,b,d)),{
# Basically you have three conditions:
# curve a is below curve b, curve b is below curve d and curve d is above curve a
# assign to each curve coordinates the two conditions that concerns it.
idA <- yA<=yD & yA<=yB
idB <- yB>=yA & yB<=yD
idD <- yD<=yB & yD>=yA
polygon(x = c(xB[idB], xD[idD], rev(xA[idA])),
y = c(yB[idB], yD[idD], rev(yA[idA])),
density=10, angle=0, border=NULL)
})
In R, there is only limited support for fill patterns and they can only be
applied to rectangles and polygons.This is and only within the traditional graphics, no ggplot2 or lattice.
It is possible to fill a rectangle or polygon with a set of lines drawn
at a certain angle, with a specific separation between the lines. A density
argument controls the separation between the lines (in terms of lines per inch)
and an angle argument controls the angle of the lines.
here an example from the help:
plot(c(1, 9), 1:2, type = "n")
polygon(1:9, c(2,1,2,1,NA,2,1,2,1),
density = c(10, 20), angle = c(-45, 45))
EDIT
Another option is to use alpha blending to differentiate between regions. Here using #plannapus example and gridBase package to superpose polygons, you can do something like this :
library(gridBase)
vps <- baseViewports()
pushViewport(vps$figure,vps$plot)
with(as.list(c(a,b,d)),{
grid.polygon(x = xA, y = yA,gp =gpar(fill='red',lty=1,alpha=0.2))
grid.polygon(x = xB, y = yB,gp =gpar(fill='green',lty=2,alpha=0.2))
grid.polygon(x = xD, y = yD,gp =gpar(fill='blue',lty=3,alpha=0.2))
}
)
upViewport(2)
There are several submissions on the MATLAB Central File Exchange that will produce hatched plots in various ways for you.
I think a tool that will come handy for you here is gnuplot.
Take a look at the following demos:
feelbetween
statistics
some tricks