Lets consider that I have five 2D-Matrices which describe the magnetic field at different z-Layers. A nice, smoothed version of a 2D-Surface plot can be obtained as follows:
data2_I<-matrix(c(1.0,1.0,0.6,0.6,0.7,0.9,0.9,0.5,0.5,0.5,0.7,0.9,0.9,0.6,0.3,0.4,0.7,0.9,0.9,0.7,0.5,0.5,0.6,0.9,0.9,0.7,0.6,0.6,1.0,1.0), nrow=5)
Z = as.vector(data2_I)
length(Z)
XY=data.frame(x=as.numeric(gl(5,1,30)),y=as.numeric(gl(5,6,30)))
t=Tps(XY,Z)
surface(t)
Now it would be great if I could get a 3D-plot where at different z-Positions these surfaces are plotted. Is there a possibility to do that?
I found an alternative approach: With the package rgl I and the function surface 3D I can stack several 3D-Surface plots within one open3d-window. Lets look at a small example:
library("rgl")
data2_I<-matrix(c(1.0,1.0,0.6,0.6,0.7,0.9,0.9,0.5,0.5,0.5,0.7,0.9,0.9,0.6,0.3,0.4,0.7,0.9,0.9,0.7,0.5,0.5,0.6,0.9,0.9,0.7,0.6,0.6,1.0,1.0), nrow=5)
data0_I<-matrix(c(1.0,1.0,0.6,0.6,0.7,0.9,0.9,0.5,0.5,0.5,0.7,0.9,0.9,0.6,0.3,0.4,0.7,0.9,0.9,0.7,0.5,0.5,0.6,0.9,0.9,0.7,0.6,0.6,1.0,1.0), nrow=5)
data1_I<-2*data0_I
data2_I<-1/data1_I
elv=0
offs=5*elv+1
z0 <- scale*data0_I
z1 <- scale*data1_I
z2 <- scale*data2_I
x <- 1:nrow(z0)
y <- 1:ncol(z0)
palette <- colorRampPalette(c("blue","green","yellow", "red"))
col.table <- palette(256)
open3d(windowRect=c(50,50,800,800))
surface3d(x, y, elv*z0, color = col.table[cut(z0, 256)], back = "lines")
surface3d(x, y, elv*z1+1*offs, color = col.table[cut(z1, 256)], back = "lines")
surface3d(x, y, elv*z2+2*offs, color = col.table[cut(z2, 256)], back = "lines")
axes3d()
aspect3d(1,1,2)
The variables offsand elv are included for cosmetic purposes: offs controls the space between two surface plots and elevation how the z-axes of the surface3d-plots should scale. As I wanted to have a 2D surface plot without any elevation I set it to zero.
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"))
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"))
Im drawing a knn-classification plot in R using plot to plot the samples and contour to plot the lines that classify the plane.
Here is my code:
k<-1
datax<-rbind(matrix(rnorm(30,-1,5.25),15,2),matrix(rnorm(36,1,5.25),18,2))
datay<-rbind(matrix(1,15,1),matrix(0,18,1))
plot(datax[,1], datax[,2],pch = datay+1,axes=FALSE,ann=FALSE)
box()
n <- 1000
xp <- seq(length=n, from = min(datax[,1]), to = max(datax[,1]))
yp <- seq(length=n,from = min(datax[,2]) ,to = max(datax[,2]))
gr <- expand.grid(xp, yp)
library(class)
z <- as.numeric(knn(datax, gr, datay,k))-1
zM <- matrix(z, n, n, byrow = FALSE)
contour(xp, yp, zM, xlab="x",ylab="",nlevels = 1 ,lwd=2, add=TRUE, drawlabels =FALSE)
My question is: How can i color the enclosed areas in the plot? I tried filled.contour but there is no add parameter. I simply want the area where the classifier is = 0 white and where it classifies = 1 in blue. How should i do this?
thanks
Instead of contour, you can use contourLines to keep the coordinates of the edges of the contour lines and plot them with polygon.
plot(datax[,1], datax[,2],axes=FALSE,ann=FALSE, type="n")
box()
cL <- contourLines(xp, yp, zM,nlevels = 1)
lapply(cL,function(x)polygon(x$x,x$y,col="red"))
points(datax[,1], datax[,2],pch = datay+1)
However it is not perfect with contour lines that reach the edges of the plot (see the left lower corner of the second plot), so it will need some hand-made tuning:
Edit: In the case of nested contour lines, I don't think there is an easy way to deal with it but here is one way:
library(splancs)
ord <- sapply(lapply(cL,function(x)datay[inout(datax,cbind(x$x,x$y))]),
median) #Check what values are present in the polygon and
#take the most common one
plot(datax[,1], datax[,2],axes=FALSE,ann=FALSE, type="n")
box()
lapply(cL[ord==1],function(x)polygon(x$x,x$y,col="blue"))
lapply(cL[ord==0],function(x)polygon(x$x,x$y,col="white"))
points(datax[,1], datax[,2],pch = datay+1)
2nd Edit: There is of course also the possibility of using function image in your case:
image(xp, yp, zM, col=c("transparent","blue"))
points(datax[,1], datax[,2],pch = datay+1)
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
See this example
This was created in matlab by making two scatter plots independently, creating images of each, then using the imagesc to draw them into the same figure and then finally setting the alpha of the top image to 0.5.
I would like to do this in R or matlab without using images, since creating an image does not preserve the axis scale information, nor can I overlay a grid (e.g. using 'grid on' in matlab). Ideally I wold like to do this properly in matlab, but would also be happy with a solution in R. It seems like it should be possible but I can't for the life of me figure it out.
So generally, I would like to be able to set the alpha of an entire plotted object (i.e. of a matlab plot handle in matlab parlance...)
Thanks,
Ben.
EDIT: The data in the above example is actually 2D. The plotted points are from a computer simulation. Each point represents 'amplitude' (y-axis) (an emergent property specific to the simulation I'm running), plotted against 'performance' (x-axis).
EDIT 2: There are 1796400 points in each data set.
Using ggplot2 you can add together two geom_point's and make them transparent using the alpha parameter. ggplot2 als adds up transparency, and I think this is what you want. This should work, although I haven't run this.
dat = data.frame(x = runif(1000), y = runif(1000), cat = rep(c("A","B"), each = 500))
ggplot(aes(x = x, y = y, color = cat), data = dat) + geom_point(alpha = 0.3)
ggplot2 is awesome!
This is an example of calculating and drawing a convex hull:
library(automap)
library(ggplot2)
library(plyr)
loadMeuse()
theme_set(theme_bw())
meuse = as.data.frame(meuse)
chull_per_soil = ddply(meuse, .(soil),
function(sub) sub[chull(sub$x, sub$y),c("x","y")])
ggplot(aes(x = x, y = y), data = meuse) +
geom_point(aes(size = log(zinc), color = ffreq)) +
geom_polygon(aes(color = soil), data = chull_per_soil, fill = NA) +
coord_equal()
which leads to the following illustration:
You could first export the two data sets as bitmap images, re-import them, add transparency:
library(grid)
N <- 1e7 # Warning: slow
d <- data.frame(x1=rnorm(N),
x2=rnorm(N, 0.8, 0.9),
y=rnorm(N, 0.8, 0.2),
z=rnorm(N, 0.2, 0.4))
v <- with(d, dataViewport(c(x1,x2),c(y, z)))
png("layer1.png", bg="transparent")
with(d, grid.points(x1,y, vp=v,default="native",pch=".",gp=gpar(col="blue")))
dev.off()
png("layer2.png", bg="transparent")
with(d, grid.points(x2,z, vp=v,default="native",pch=".",gp=gpar(col="red")))
dev.off()
library(png)
i1 <- readPNG("layer1.png", native=FALSE)
i2 <- readPNG("layer2.png", native=FALSE)
ghostize <- function(r, alpha=0.5)
matrix(adjustcolor(rgb(r[,,1],r[,,2],r[,,3],r[,,4]), alpha.f=alpha), nrow=dim(r)[1])
grid.newpage()
grid.rect(gp=gpar(fill="white"))
grid.raster(ghostize(i1))
grid.raster(ghostize(i2))
you can add these as layers in, say, ggplot2.
Use the transparency capability of color descriptions. You can define a color as a sequence of four 2-byte words: muddy <- "#888888FF" . The first three pairs set the RGB colors (00 to FF); the final pair sets the transparency level.
AFAIK, your best option with Matlab is to just make your own plot function. The scatter plot points unfortunately do not yet have a transparency attribute so you cannot affect it. However, if you create, say, most crudely, a bunch of loops which draw many tiny circles, you can then easily give them an alpha value and obtain a transparent set of data points.