График 3D данных в R - PullRequest
       59

График 3D данных в R

44 голосов
/ 17 июля 2011

У меня есть 3D-набор данных:

data = data.frame(
    x = rep( c(0.1, 0.2, 0.3, 0.4, 0.5), each=5),
    y = rep( c(1, 2, 3, 4, 5), 5)
)

data$z = runif(
    25,
    min = (data$x*data$y - 0.1 * (data$x*data$y)),
    max = (data$x*data$y + 0.1 * (data$x*data$y))
)

data
str(data)

И я хочу построить его, но встроенные функции R alwyas дают ошибку

увеличивая 'xОжидаемые значения 'и' y '

# ### 3D Plots ######################################################
# built-in function always give the error
#    "increasing 'x' and 'y' values expected"
demo(image)
image(x = data$x, y = data$y, z = data$z)

demo(persp)
persp(data$x,data$y,data$z)

contour(data$x,data$y,data$z)

Когда я искал в Интернете, я обнаружил, что это сообщение появляется, когда комбинации значений X и Y не являются уникальными.Но здесь они уникальны.

Я пробовал некоторые другие библиотеки, и там это работает без проблем.Но мне не нравится стиль графиков по умолчанию (встроенные функции должны соответствовать моим ожиданиям).

# ### 3D Scatterplot ######################################################
# Nice plots without surface maps?
install.packages("scatterplot3d", dependencies = TRUE)
library(scatterplot3d)
scatterplot3d(x = data$x, y = data$y, z = data$z)

# ### 3D Scatterplot ######################################################
# Only to play around?
install.packages("rgl", dependencies = TRUE)
library(rgl)
plot3d(x = data$x, y = data$y, z = data$z)
lines3d(x = data$x, y = data$y, z = data$z)
surface3d(x = data$x, y = data$y, z = data$z)

Почему мои наборы данных не принимаются встроенными функциями?

Ответы [ 5 ]

53 голосов
/ 17 июля 2011

Я использую пакет lattice почти для всего, что я рисую в R, и у него есть соответствующий график persp, называемый wireframe.Пусть data будет таким, каким его определил Свен.

wireframe(z ~ x * y, data=data)

wireframe plot

Или как насчет этого (модификация рисунка 6.3 в книге Дипаняна Саркара ):

p <- wireframe(z ~ x * y, data=data)
npanel <- c(4, 2)
rotx <- c(-50, -80)
rotz <- seq(30, 300, length = npanel[1]+1)
update(p[rep(1, prod(npanel))], layout = npanel,
    panel = function(..., screen) {
        panel.wireframe(..., screen = list(z = rotz[current.column()],
                                           x = rotx[current.row()]))
    })

Multiple wireframe plots using panel and update

Обновление: нанесение поверхностей на поверхность с помощью OpenGL

Поскольку этот пост продолжает привлекать внимание, я хочу добавить способ OpenGL для создания трехмерных графиковтоже (как предложено @tucson ниже).Сначала нам нужно переформатировать набор данных из xyz-tripplets в векторы оси x и y и матрицу z.

x <- 1:5/10
y <- 1:5
z <- x %o% y
z <- z + .2*z*runif(25) - .1*z

library(rgl)
persp3d(x, y, z, col="skyblue")

rgl::persp3d

Это изображение может бытьсвободно вращается и масштабируется с помощью мыши или изменяется с помощью дополнительных команд, а когда вам это нравится, вы сохраняете его с помощью rgl.snapshot.

rgl.snapshot("myplot.png")
23 голосов
/ 17 июля 2011

Если вы работаете с «реальными» данными, для которых интервалы и последовательность сетки не могут быть гарантированно увеличены или уникальны (возможно, комбинации (x,y,z) уникальны, по крайней мере, даже если эти тройки дублируются), я бы Рекомендовать пакет akima для интерполяции с нерегулярной сетки на обычную.

Используя ваше определение data:

library(akima)
im <- with(data,interp(x,y,z))
with(im,image(x,y,z))

enter image description here

И это должно работать не только с image, но и с подобными функциями.

Обратите внимание, что сетка по умолчанию, на которую ваши данные отображаются с помощью akima::interp, определяется 40 равными интервалами, охватывающими диапазон значений x и y:

> formals(akima::interp)[c("xo","yo")]
$xo
seq(min(x), max(x), length = 40)

$yo
seq(min(y), max(y), length = 40)

Но, конечно, это можно переопределить, передав аргументы xo и yo в akima::interp.

19 голосов
/ 06 октября 2016

В дополнение к решениям других, я хотел бы предложить использовать пакет plotly для R, так как для меня это сработало.

Ниже я использую предложенный выше переформатированный набор данных от xyz-триплетов до осевых векторов x и y и матрицы z:

x <- 1:5/10
y <- 1:5
z <- x %o% y
z <- z + .2*z*runif(25) - .1*z

library(plotly)
plot_ly(x=x,y=y,z=z, type="surface")

enter image description here

Визуализированная поверхность может вращаться и масштабироваться с помощью мыши. Это хорошо работает в RStudio.

Вы также можете попробовать его с помощью встроенного набора данных volcano из R:

plot_ly(z=volcano, type="surface")

enter image description here

8 голосов
/ 17 июля 2011

Я думаю, что следующий код близок к тому, что вы хотите

x    <- c(0.1, 0.2, 0.3, 0.4, 0.5)
y    <- c(1, 2, 3, 4, 5)
zfun <- function(a,b) {a*b * ( 0.9 + 0.2*runif(a*b) )}
z    <- outer(x, y, FUN="zfun")

Это дает такие данные (обратите внимание, что x и y оба увеличиваются)

> x
[1] 0.1 0.2 0.3 0.4 0.5
> y
[1] 1 2 3 4 5
> z
          [,1]      [,2]      [,3]      [,4]      [,5]
[1,] 0.1037159 0.2123455 0.3244514 0.4106079 0.4777380
[2,] 0.2144338 0.4109414 0.5586709 0.7623481 0.9683732
[3,] 0.3138063 0.6015035 0.8308649 1.2713930 1.5498939
[4,] 0.4023375 0.8500672 1.3052275 1.4541517 1.9398106
[5,] 0.5146506 1.0295172 1.5257186 2.1753611 2.5046223

и график типа

persp(x, y, z)

persp(x, y, z)

3 голосов
/ 22 января 2014

Не уверен, почему приведенный выше код не работает для библиотеки rgl, но по следующей ссылке есть отличный пример с той же библиотекой.Запустите код в R, и вы получите красивый 3D-график, который вы можете повернуть во всех ракурсах .

http://statisticsr.blogspot.de/2008/10/some-r-functions.html

########################################################################
## another example of 3d plot from my personal reserach, use rgl library
########################################################################
# 3D visualization device system

library(rgl);
data(volcano)
dim(volcano)

peak.height <- volcano;
ppm.index <- (1:nrow(volcano));
sample.index <- (1:ncol(volcano));

zlim <- range(peak.height)
zlen <- zlim[2] - zlim[1] + 1
colorlut <- terrain.colors(zlen) # height color lookup table
col <- colorlut[(peak.height-zlim[1]+1)] # assign colors to heights for each point
open3d()

ppm.index1 <- ppm.index*zlim[2]/max(ppm.index);
sample.index1 <- sample.index*zlim[2]/max(sample.index)

title.name <- paste("plot3d ", "volcano", sep = "");
surface3d(ppm.index1, sample.index1, peak.height, color=col, back="lines", main = title.name);
grid3d(c("x", "y+", "z"), n =20)

sample.name <- paste("col.", 1:ncol(volcano), sep="");
sample.label <- as.integer(seq(1, length(sample.name), length = 5));

axis3d('y+',at = sample.index1[sample.label], sample.name[sample.label], cex = 0.3);
axis3d('y',at = sample.index1[sample.label], sample.name[sample.label], cex = 0.3)
axis3d('z',pos=c(0, 0, NA))

ppm.label <- as.integer(seq(1, length(ppm.index), length = 10));
axes3d('x', at=c(ppm.index1[ppm.label], 0, 0), abs(round(ppm.index[ppm.label], 2)), cex = 0.3);

title3d(main = title.name, sub = "test", xlab = "ppm", ylab = "samples", zlab = "peak")
rgl.bringtotop();
...