R: Построение 3D-поверхности из x, y, z
Представьте, что у меня есть матрица из 3 столбцов
x, y, z
где z - функция от x и y.
Я знаю, как построить "график рассеяния" этих точек с
plot3d(x,y,z)
Но если я хочу поверхность вместо этого, я должен использовать другие команды, такие как surface3d
Проблема в том, что он не принимает те же самые входы, что и plot3d
кажется, нужна матрица с
(nº elements of z) = (n of elements of x) * (n of elements of x)
Как я могу получить эту матрицу?
Я пробовал с помощью команды interp, как и в случае необходимости использовать контурные графики.
Как я могу построить поверхность непосредственно из x, y, z без вычисления этой матрицы?
Если бы у меня было слишком много точек, эта матрица была бы слишком большой.
веселит
Ответы
Ответ 1
Если ваши координаты x и y не находятся на сетке, вам необходимо интерполировать поверхность x, y, z на одну. Вы можете сделать это с помощью кригинга с использованием любого из пакетов геостатистики (geoR, gstat, others) или более простых методов, таких как взвешивание обратного расстояния.
Я предполагаю, что функция "interp", о которой вы говорите, относится к пакету akima. Обратите внимание, что выходная матрица не зависит от размера ваших входных точек. Вы можете иметь 10000 точек на вашем входе и интерполировать их на сетку 10x10, если хотите. По умолчанию akima:: interp делает это на сетке 40x40:
> require(akima) ; require(rgl)
> x=runif(1000)
> y=runif(1000)
> z=rnorm(1000)
> s=interp(x,y,z)
> dim(s$z)
[1] 40 40
> surface3d(s$x,s$y,s$z)
Это будет выглядеть шипучим и мусорным, потому что его случайные данные. Надеюсь, ваши данные не будут!
Ответ 2
Вы можете использовать функцию outer()
для ее создания.
Посмотрите демоверсию функции persp()
, которая является базовой графической функцией для рисования перспективных графиков для поверхностей.
Вот их первый пример:
x <- seq(-10, 10, length.out = 50)
y <- x
rotsinc <- function(x,y) {
sinc <- function(x) { y <- sin(x)/x ; y[is.na(y)] <- 1; y }
10 * sinc( sqrt(x^2+y^2) )
}
z <- outer(x, y, rotsinc)
persp(x, y, z)
То же самое относится к surface3d()
:
require(rgl)
surface3d(x, y, z)
Ответ 3
Вы можете посмотреть на использование решетки. В этом примере я определил сетку, над которой я хочу построить z ~ x, y. Это выглядит примерно так. Обратите внимание, что большая часть кода просто создает трехмерную форму, которую я рисую с использованием функции каркаса.
Переменные "b" и "s" могут быть x или y.
require(lattice)
# begin generating my 3D shape
b <- seq(from=0, to=20,by=0.5)
s <- seq(from=0, to=20,by=0.5)
payoff <- expand.grid(b=b,s=s)
payoff$payoff <- payoff$b - payoff$s
payoff$payoff[payoff$payoff < -1] <- -1
# end generating my 3D shape
wireframe(payoff ~ s * b, payoff, shade = TRUE, aspect = c(1, 1),
light.source = c(10,10,10), main = "Study 1",
scales = list(z.ticks=5,arrows=FALSE, col="black", font=10, tck=0.5),
screen = list(z = 40, x = -75, y = 0))
Ответ 4
rgl
отлично, но требует немного экспериментов, чтобы получить правильные оси.
Если у вас много очков, почему бы не взять произвольный образец из них, а затем нарисовать полученную поверхность. Вы можете добавить несколько поверхностей на основе выборок из одних и тех же данных, чтобы убедиться, что процесс выборки ужасно влияет на ваши данные.
Итак, вот довольно ужасная функция, но она делает то, что я думаю, вы хотите, чтобы она делала (но без выборки). Учитывая матрицу (x, y, z), где z - высоты, она будет изображать как точки, так и поверхность. Ограничения заключаются в том, что для каждой пары (x, y) может быть только одна z. Таким образом, самолеты, которые обходят себя, вызовут проблемы.
plot_points = T
будет отображать отдельные точки, из которых производится поверхность - это полезно для проверки того, что поверхность и точки фактически встречаются. plot_contour = T
построит график 2d контура ниже 3D-визуализации. Установите цвет в rainbow
, чтобы дать красивые цвета, все остальное установит его в серый цвет, но затем вы можете изменить функцию, чтобы создать пользовательскую палитру. В любом случае, это трюк для меня, но я уверен, что его можно убрать и оптимизировать. verbose = T
печатает много выходных данных, которые я использую для отладки функции как и когда она ломается.
plot_rgl_model_a <- function(fdata, plot_contour = T, plot_points = T,
verbose = F, colour = "rainbow", smoother = F){
## takes a model in long form, in the format
## 1st column x
## 2nd is y,
## 3rd is z (height)
## and draws an rgl model
## includes a contour plot below and plots the points in blue
## if these are set to TRUE
# note that x has to be ascending, followed by y
if (verbose) print(head(fdata))
fdata <- fdata[order(fdata[, 1], fdata[, 2]), ]
if (verbose) print(head(fdata))
##
require(reshape2)
require(rgl)
orig_names <- colnames(fdata)
colnames(fdata) <- c("x", "y", "z")
fdata <- as.data.frame(fdata)
## work out the min and max of x,y,z
xlimits <- c(min(fdata$x, na.rm = T), max(fdata$x, na.rm = T))
ylimits <- c(min(fdata$y, na.rm = T), max(fdata$y, na.rm = T))
zlimits <- c(min(fdata$z, na.rm = T), max(fdata$z, na.rm = T))
l <- list (x = xlimits, y = ylimits, z = zlimits)
xyz <- do.call(expand.grid, l)
if (verbose) print(xyz)
x_boundaries <- xyz$x
if (verbose) print(class(xyz$x))
y_boundaries <- xyz$y
if (verbose) print(class(xyz$y))
z_boundaries <- xyz$z
if (verbose) print(class(xyz$z))
if (verbose) print(paste(x_boundaries, y_boundaries, z_boundaries, sep = ";"))
# now turn fdata into a wide format for use with the rgl.surface
fdata[, 2] <- as.character(fdata[, 2])
fdata[, 3] <- as.character(fdata[, 3])
#if (verbose) print(class(fdata[, 2]))
wide_form <- dcast(fdata, y ~ x, value_var = "z")
if (verbose) print(head(wide_form))
wide_form_values <- as.matrix(wide_form[, 2:ncol(wide_form)])
if (verbose) print(wide_form_values)
x_values <- as.numeric(colnames(wide_form[2:ncol(wide_form)]))
y_values <- as.numeric(wide_form[, 1])
if (verbose) print(x_values)
if (verbose) print(y_values)
wide_form_values <- wide_form_values[order(y_values), order(x_values)]
wide_form_values <- as.numeric(wide_form_values)
x_values <- x_values[order(x_values)]
y_values <- y_values[order(y_values)]
if (verbose) print(x_values)
if (verbose) print(y_values)
if (verbose) print(dim(wide_form_values))
if (verbose) print(length(x_values))
if (verbose) print(length(y_values))
zlim <- range(wide_form_values)
if (verbose) print(zlim)
zlen <- zlim[2] - zlim[1] + 1
if (verbose) print(zlen)
if (colour == "rainbow"){
colourut <- rainbow(zlen, alpha = 0)
if (verbose) print(colourut)
col <- colourut[ wide_form_values - zlim[1] + 1]
# if (verbose) print(col)
} else {
col <- "grey"
if (verbose) print(table(col2))
}
open3d()
plot3d(x_boundaries, y_boundaries, z_boundaries,
box = T, col = "black", xlab = orig_names[1],
ylab = orig_names[2], zlab = orig_names[3])
rgl.surface(z = x_values, ## these are all different because
x = y_values, ## of the confusing way that
y = wide_form_values, ## rgl.surface works! - y is the height!
coords = c(2,3,1),
color = col,
alpha = 1.0,
lit = F,
smooth = smoother)
if (plot_points){
# plot points in red just to be on the safe side!
points3d(fdata, col = "blue")
}
if (plot_contour){
# plot the plane underneath
flat_matrix <- wide_form_values
if (verbose) print(flat_matrix)
y_intercept <- (zlim[2] - zlim[1]) * (-2/3) # put the flat matrix 1/2 the distance below the lower height
flat_matrix[which(flat_matrix != y_intercept)] <- y_intercept
if (verbose) print(flat_matrix)
rgl.surface(z = x_values, ## these are all different because
x = y_values, ## of the confusing way that
y = flat_matrix, ## rgl.surface works! - y is the height!
coords = c(2,3,1),
color = col,
alpha = 1.0,
smooth = smoother)
}
}
add_rgl_model
выполняет ту же работу без параметров, но накладывает поверхность на существующий 3dplot.
add_rgl_model <- function(fdata){
## takes a model in long form, in the format
## 1st column x
## 2nd is y,
## 3rd is z (height)
## and draws an rgl model
##
# note that x has to be ascending, followed by y
print(head(fdata))
fdata <- fdata[order(fdata[, 1], fdata[, 2]), ]
print(head(fdata))
##
require(reshape2)
require(rgl)
orig_names <- colnames(fdata)
#print(head(fdata))
colnames(fdata) <- c("x", "y", "z")
fdata <- as.data.frame(fdata)
## work out the min and max of x,y,z
xlimits <- c(min(fdata$x, na.rm = T), max(fdata$x, na.rm = T))
ylimits <- c(min(fdata$y, na.rm = T), max(fdata$y, na.rm = T))
zlimits <- c(min(fdata$z, na.rm = T), max(fdata$z, na.rm = T))
l <- list (x = xlimits, y = ylimits, z = zlimits)
xyz <- do.call(expand.grid, l)
#print(xyz)
x_boundaries <- xyz$x
#print(class(xyz$x))
y_boundaries <- xyz$y
#print(class(xyz$y))
z_boundaries <- xyz$z
#print(class(xyz$z))
# now turn fdata into a wide format for use with the rgl.surface
fdata[, 2] <- as.character(fdata[, 2])
fdata[, 3] <- as.character(fdata[, 3])
#print(class(fdata[, 2]))
wide_form <- dcast(fdata, y ~ x, value_var = "z")
print(head(wide_form))
wide_form_values <- as.matrix(wide_form[, 2:ncol(wide_form)])
x_values <- as.numeric(colnames(wide_form[2:ncol(wide_form)]))
y_values <- as.numeric(wide_form[, 1])
print(x_values)
print(y_values)
wide_form_values <- wide_form_values[order(y_values), order(x_values)]
x_values <- x_values[order(x_values)]
y_values <- y_values[order(y_values)]
print(x_values)
print(y_values)
print(dim(wide_form_values))
print(length(x_values))
print(length(y_values))
rgl.surface(z = x_values, ## these are all different because
x = y_values, ## of the confusing way that
y = wide_form_values, ## rgl.surface works!
coords = c(2,3,1),
alpha = .8)
# plot points in red just to be on the safe side!
points3d(fdata, col = "red")
}
Таким образом, мой подход будет состоять в том, чтобы попытаться сделать это со всеми вашими данными (я легко рисую поверхности, созданные из ~ 15k точек). Если это не сработает, возьмите несколько меньших образцов и запишите их все сразу, используя эти функции.
Ответ 5
Может быть, сейчас поздно, но после Spacedman вы пытались дублировать = "полоса" или любой другой вариант?
x=runif(1000)
y=runif(1000)
z=rnorm(1000)
s=interp(x,y,z,duplicate="strip")
surface3d(s$x,s$y,s$z,color="blue")
points3d(s)