I have a number of points in three dimensions that I am plotting using plot3d from the rgl library. The point coordinates are stored in three vectors x, y and z. I would like to annotate each point in the plot with its coordinates, i. e. (x, y, z)
Using text3d I would have to create a vector of label strings from the coordinate vectors. The only solution I found so far is looping over the coordinate vectors:
library(rgl);
x = c(1, 2, 3)
y = c(4, 5, 6)
z = c(7, 8, 9)
label_vector = 1:3
for (i in 1:3) {
l = paste("(", x[i], y[i], z[i], ")", collapse=" ")
label_vector[i] = l
}
plot3d(x, y, z)
text3d(x, y, z, label_vector)
Is there a more elegant way to do this? Ideally, but not necessarily, in the (x, y, z) instead of the ( x y z ) format from my example.
paste works on vectors. from ?paste
If the arguments are vectors, they are concatenated term-by-term to
give a character vector result.
So, you can generate the label_vector by running just one line
label_vector = paste( "(" , x, ", " , y , ", " , z , ")", sep = "")
I think this will give you a lot cleaner output
library(rgl);
x = c(1, 2, 3)
y = c(4, 5, 6)
z = c(7, 8, 9)
label_vector <- paste0("[",x,",",y,",",z,"]")
plot3d(x, y, z, col= "red" , type ="s", radius=0.02)
text3d(x, y, z, label_vector,adj=c(-0.25,0))
Thank you
Related
I trying to use the function identify() on R, but it's not working.
Do you know if there is any package that I'm missing?
It doesn't work not even for a simple code:
x = 1:10
y = x^2
name = letters[1:10]
plot(x, y)
identify(x, y, labels = name, plot=TRUE, n = 2)
This way it works: it is the same code: With n=2 you have to click on two points:
x = 1:10
y = x^2
name = letters[1:10]
plot(x, y)
identify(x, y, labels = name, plot=TRUE, n = 2)
I'm looking to plot 3D functions using R. For example, take the elliptic paraboloid given by f(x,y) = (𝑥−2𝑦−1)^2 + (3𝑥+𝑦−2)^2. Here's what I've tried:
require(lattice)
x <- seq(-10, 10, by=0.5)
y <- seq(-10, 10, by=0.5)
g <- expand.grid(x = x, y = y)
g$z <- (x-2*y-1)^2 + (3*x-y-2)^2
wireframe(z ~ x * y, g, drape = TRUE,
aspect = c(1,1), colorkey = TRUE)`
And here's the output
However, here's the "true" graph of f:
I've tried changing the definitions of x and y, to no avail. I've also tried the curve3d() function from the emdbook package. It looks even worse.
You multiplied by the wrong x and y. You need to use the ones inside g:
g$z <- with(g, (x-2*y-1)^2 + (3*x-y-2)^2)
wireframe(z ~ x * y, g, drape = TRUE,
aspect = c(1,1), colorkey = TRUE)
I have got two vectors and a 2D-matrix, from which I want to create a 3D surface plot. I already have split my data into X and Y (vectors (time "t" and wavelength "w") and Z (matrix; absorbance "NIR" at time and wavelength) with the same number of rows/columns respectively:
t = matrix(1:456, ncol= 1)
w = matrix(1350:1650, nrow = 1)
NIR = as.matrix(read.table("NIR_alle_pur.txt", header = TRUE, dec =","))
colnames(NIR) = c(paste0("NIR.", 1350:1650))
dim(NIR)
# [1] 456 301
dput(NIR_example)
structure(c(60771.93, 57230.56, 56235.96, 41617.47, 41709.93,
57466.6, 59916.97, 63376.4, 41966.73, 41254.34, 65535, 61468.76,
65535, 41238.03, 42530.97, 56936.03, 65009.4, 65535, 40375.5,
41021.6, 62757, 65455.44, 63795.6, 41349.6, 41178.2), .Dim = c(5L,
5L), .Dimnames = list(NULL, c("NIR.Spectrum_1350.0000000", "NIR.Spectrum_1351.0000000",
"NIR.Spectrum_1352.0000000", "NIR.Spectrum_1353.0000000", "NIR.Spectrum_1354.0000000"
)))
I tried to insert those into the rgl.surface function, but I get the following error message:
Error in rgl.surface(x, y, z, coords = 1:3) : Bad dimension for rows
I've also tried to plot them with plotly, but my success was equally low.
Can someone give me an input how I can get my spectral data to look like the last ones (multiple surfaces) on this site, individually? I'll try the overlay of the surfaces with plotlylater on!
I am happy for every extra input and information on my level!
Thank you!
After looking at the source code, I'd guess the problem is that you stored your x and y vectors as matrices. If they are matrices, they need to be identical in shape to z.
As I mentioned in a comment, you should avoid using rgl.surface (and the other rgl.* functions in most cases), and use surface3d instead, or persp3d if you want axes.
The *3d functions are higher level functions that act more like other R functions, and they will lead to fewer problems in the long run.
You haven't posted any data, so I'll post a completely artificial example. Let's suppose z = x^2 + y^2 + a, where a is a different constant for each surface. Then you can plot it like this:
x <- seq(-2, 2, length = 7)
y <- seq(-3, 3, length = 5) # I've chosen different ranges
# and lengths just to illustrate.
z <- outer(x, y, function(x, y) x^2 + y^2)
colours <- heat.colors(100)
minval <- min(z)
maxval <- max(z) + 10
col <- colours[(z - minval)/(maxval - minval)*99 + 1]
persp3d(x, y, z, col = col) # get axes the first time
z <- outer(x, y, function(x, y) x^2 + y^2 + 5)
col <- colours[(z - minval)/(maxval - minval)*99 + 1]
surface3d(x, y, z, col = col)
z <- outer(x, y, function(x, y) x^2 + y^2 + 10)
col <- colours[(z - minval)/(maxval - minval)*99 + 1]
surface3d(x, y, z, col = col)
aspect3d(1, 1, 1) # Make axes all equal
That produces this plot:
I have a 30 x 16 x 9 matrix that I would like to visualize via a 4-D plot in R.
I have tried scatter3D() in packages plot3d and scatterplot3d.
x <- seq(10, 300, 10)
y <- seq(5.0, 20.0, 1.0)
z <- c(seq(0.5, 4, 0.5), 10)
scatter3D(x, y, z, colvar = data)
It always gives error saying that y should have same length as x.
How do I deal with this? Why do x, y, z have to be equal length? This is so inconvenient.
This happens because each point must have three values for this plot. You have 30 values for x, 16 values for y and 9 values for z. With this data, only the first 9 points will have the x-y-z value. In the comments of your question, eipi10 gives a very good explanation about this. An alternative, for example, is to interpolate the data to create the missing values. More about data interpolation.
library("plot3D")
number_of_points <- 50
xx <- seq(10, 300, 10)
yy <- seq(5.0, 20.0, 1.0)
zz <- c(seq(0.5, 4, 0.5), 10)
xx <- approx(x = xx, method="linear", n=number_of_points, ties = mean)$y
yy <- approx(x = yy, method="linear", n=number_of_points, ties = mean)$y
zz <- approx(x = zz, method="linear", n=number_of_points, ties = mean)$y
scatter3D(xx, yy, zz)
Hope it helps!
You say you have a matrix, but what you have is three vectors. You first need to create the "matrix" (here we will make a data.frame named df using expand.grid):
x <- seq(10, 300, 10)
y <- seq(5.0, 20.0, 1.0)
z <- c(seq(0.5, 4, 0.5), 10)
df <- expand.grid(x = x, y = y, z = z)
Then we can plot using the scatter3d function from the car package using either method:
car::scatter3d(x ~ y + z, data = df)
car::scatter3d(df$x, df$y, df$z)
I was wondering how I could use sapply and lapply simultaneously so that I could avoid writing my function called GG as it appears below?
GG = function(x, y) dnorm(250, mean = x, sd = y)*dnorm(265, mean = x, sd = y) *
dnorm(259, mean = x, sd = y)
P.S. I know if only x in my function above was varying, the following could work:
function(x) sapply(lapply(x, dnorm, x = c(250, 265, 259), 10), prod)
But in my case x and y both vary.
We can use Map with Reduce from base R. The reason for using Map is that functions can be applied on corresponding elements of the objects passed into it. Here, dnorm is the function which takes each corresponding element of 'x' and 'y' as the mean and sd arguments while it has a constant vector of "x" (c(250, 265, 259)). The output of Map is a list and we Reduce the corresponding elements of list to a single one by multiplying (*)
GG1 <- function(x, y) Reduce(`*`, Map(dnorm, x = c(250, 265, 259),
mean = list(x), sd = list(y)))
identical(GG(24, 12), GG1(24, 12))
#[1] TRUE
identical(GG(32, 15), GG1(32, 15))
#[1] TRUE
Based on the OP's comments,
x <- seq(10, 40, length= 30)
y <- x
z <- outer(x, y, GG1)
persp(x, y, z , theta = 0, phi = 20, expand = 0.5, col = 'pink')