TL;DR 有很多选择。下面解释了三个,其中一个是 OP 要求的plotly。 plotly 可能不是最好的。可能需要一些数据准备,就像 OP 和answer by @rar 给出的示例数据一样。
免责声明:不幸的是,3d 曲面图在 R 中有点麻烦。Python 是一种可能的替代方案;与 R 中的所有选项相比,我发现 python-matplotlib 是 extremely easy, straightforward, and intuitive。
数据准备一:实际应该有3d数据
将 2d 数据绘制为 3d 是一个常见错误。例如,OP 中的数据在 3d 中形成近乎线性的曲线。虽然可以将这些点视为 xy 平面中特定位置的 z 轴值的独立观测值,并从中插值 3d 表面,但我怀疑生成的表面图是否非常有用或信息丰富。
在绘制 3d 表面的任何情况下,您几乎总是需要在 x-y 平面上进行观察网格。要在 R 中插入此类网格所需的值,您可以像这样使用akima::interp
library(akima)
df <- data.frame(matrix(rnorm(60), nrow=20))
plot_data <- interp(df$X1,df$X2,df$X3)
但是,akima::interp 相当脆弱,在某些情况下会导致您的 R 解释器 shell 崩溃(例如下面的 data_grid 变量示例)。
从好的方面来说,它为您创建了一个包含两个向量和一个矩阵的数据结构,就像 R 中的所有 (?) 3d 曲面绘图函数都期望数据一样。
数据准备2:不能有长格式的数据
假设您在 3x3 网格中有 9 个点的数据(为了让这个例子简单):
data_grid <- data.frame(data_col = c(45.62151, 60.30996, 66.01667, 45.48701,
60.39519, 65.42441, 45.48208, 60.39041, 65.52165),
axis_one=c(10000, 10000, 10000, 1000000, 1000000, 1000000, 100000000,
100000000, 100000000),
axis_two=c(1, 100, 10000,1, 100, 10000,1, 100, 10000))
data_grid
# data_col axis_one axis_two
#1 45.62151 1e+04 1
#2 60.30996 1e+04 100
#3 66.01667 1e+04 10000
#4 45.48701 1e+06 1
#5 60.39519 1e+06 100
#6 65.42441 1e+06 10000
#7 45.48208 1e+08 1
#8 60.39041 1e+08 100
#9 65.52165 1e+08 10000
我在 R 中找不到任何可以接受这种数据结构的 3d 绘图包或函数。大多数关于 stackoverflow 上 R 中的 3d 绘图的回复都假设了这一点,可能是因为在大多数情况下,假设这不是经验数据,而是从 3d 函数中提取的值。示例here 和here 和here 或(用于3d 绘图示例)经常使用的volcano 数据集包含在基础R 中。
那么这些包的期望是什么?他们需要两个向量(用于基准平面中的网格坐标)和一个矩阵,其条目对应于由这两个向量为 z 轴创建的网格网格。换句话说,您需要将任何长格式数据重新排列为宽格式,即将 x 和 y 坐标作为行和列索引/标签的矩阵。
这可以通过reshape2::acast来完成:
library(reshape2)
plot_matrix <- t(acast(data_grid, axis_one~axis_two, value.var="data_col"))
plot_matrix
# 10000 1e+06 1e+08
#1 45.62151 45.48701 45.48208
#100 60.30996 60.39519 60.39041
#10000 66.01667 65.42441 65.52165
如果您已经拥有宽格式数据:太棒了,您不必这样做。
请注意,另一个 answer by @rar 似乎使用了以长格式结构化的示例数据,但绘制方式错误,因此 R 将列 cs 和 w 解释为不是基本的普通坐标,而是作为附加观察。很难判断这是否确实是一个错误,或者答案是否有意如此;不幸的是,它包含的文字很少。
绘图选项 1:用 persp 绘图
绘图很简单。不幸的是,我们现在也修改了 x 和 y 数据。所以我们必须将这些从矩阵的列名和行名中提取出来。在将数据转换为宽格式矩阵之前,可能还有其他方法可以保存这些数据,但如果不同轴的数据取自不同对象,这可能会导致错误。
persp(x = as.numeric(colnames(plot_matrix)),
y = as.numeric(rownames(plot_matrix)),
z = plot_matrix,
xlab = "Axis one",
ylab = "Axis two",
zlab = "Data",
ticktype ='detailed',
theta = 310,
phi = 20,
col = "green", shade = 0.5)
persp 的一个主要缺点是它似乎没有对数刻度轴。这很不方便;所以我们必须手动完成。
persp(x = log(as.numeric(colnames(plot_matrix))),
y = log(as.numeric(rownames(plot_matrix))),
z = plot_matrix,
xlab = "log Axis one",
ylab = "log Axis two",
zlab = "Data",
ticktype ='detailed',
theta = 310,
phi = 20,
col = "green", shade = 0.5)
我们像往常一样保存绘图(尽管与 Python 相比,这很违反直觉)
pdf(file="out.pdf", width=5, height=5)
persp(x = log(as.numeric(colnames(plot_matrix))),
y = log(as.numeric(rownames(plot_matrix))),
z = plot_matrix,
xlab = "log Axis one",
ylab = "log Axis two",
zlab = "Data",
ticktype ='detailed',
theta = 310,
phi = 20,
col = "green", shade = 0.5)
dev.off()
...或等效于 png 等。
绘图选项 2:用 plotly 绘图
OP 改为询问情节。所以我们开始:
Plotly 具有更多功能并且看起来更好,但保留了一些相同的缺点(需要宽格式)。
library(plotly)
plot_ly(
x = as.numeric(colnames(plot_matrix)),
y = as.numeric(rownames(plot_matrix)),
z = plot_matrix
) %>%
add_surface() %>%
layout(
title = "",
scene = list(
xaxis = list(type = "log", title = "Total observations"),
yaxis = list(type = "log", title = "Firm size"),
zaxis = list(title = "Median"),
camera = list(eye = list(x = 1.95, y = -1.25, z = 1.25))
))
然而,使用 plotly 可能几乎不可能的事情是保存图形。过去,plotly 似乎需要订阅付费服务才能在其服务器上使用plotly::export 在线创建人物。今天它推荐使用一个名为orca 的包,它必须被安装。安装选项在orca's github repository 上进行了描述,并且总是要求您通过安装不受信任的第三方包或第二个包管理器conda 来破解您的系统,后者可能还需要 root 访问权限或整个第二个 Python 发行版。
如果您已经在使用他们的工具堆栈,例如,如果您有 Python 左右的 Anaconda 发行版,您可以安装 orca 而不会影响系统的完整性。否则,不要打扰。在这种情况下,您可以通过这样做来保存绘图(显然;我无法测试它)
plotly_figure <- plot_ly(
x = as.numeric(colnames(plot_matrix)),
y = as.numeric(rownames(plot_matrix)),
z = plot_matrix
) %>%
add_surface() %>%
layout(
title = "",
scene = list(
xaxis = list(type = "log", title = "Total observations"),
yaxis = list(type = "log", title = "Firm size"),
zaxis = list(title = "Median"),
camera = list(eye = list(x = 1.95, y = -1.25, z = 1.25))
))
orca(plotly_figure, file="out.pdf")
附带说明一下,他们的网站使用第三方脚本会破坏浏览器插件(尤其是隐私增强插件)也是一个危险信号。
这个数字当然更像是一个屏幕截图,因为它不允许我保存任何东西。
绘图选项 3+:其他包
还有一些包。例如。 plot3D,它有一些功能,包括plot3D::persp3D,这里的那个是合适的。与persp 相比,这看起来更花哨,并添加了突出表面高度的颜色。
plot3D::persp3D(x = log(as.numeric(colnames(plot_matrix))),
y = log(as.numeric(rownames(plot_matrix))),
z = plot_matrix,
xlab = "log Axis one",
ylab = "log Axis two",
zlab = "Data",
ticktype ='detailed',
theta = 310,
phi = 20)
对于更密集的数据,例如上面使用akima::interp 创建的数据,它看起来更有趣:
plot3D::persp3D(x = plot_data$x,
y = plot_data$y,
z = plot_data$z,
xlab = "log Axis one",
ylab = "log Axis two",
zlab = "Data",
ticktype ='detailed',
theta = 310,
phi = 20)
保存数字应该像上面的persp 一样工作。
还有一个rgl:plot3d;如果我理解正确,这需要非常密集的数据和otherwise falls back to a 3d scatter plot。