【问题标题】:Creating Surface 3D Plot of 3 Numeric variables in R在 R 中创建 3 个数值变量的曲面 3D 图
【发布时间】:2018-12-27 03:01:03
【问题描述】:
> plot_data
      cs    w          u
1     0  0.0 1.00000000
2   125  0.5 1.23818786
3   250  1.0 4.15500984
4   375  1.5 1.41931096
5   500  2.0 0.51660657
6   625  2.5 0.29800493
7   750  3.0 0.20846944
8   875  3.5 0.16441816
9  1000  4.0 0.14116564
10 1125  4.5 0.12890978

#Scatter PLot 
 p <- plot_ly(x=plot_data$cs,y=plot_data$w,z=plot_data$u)
 p

plot_data 是具有三个变量的数据框 .. 使用 plot_ly 函数可以获得 3d 散点图 .. 如何为与曲面图查找矩阵相同的变量进行曲面图以及如何创建该矩阵。

【问题讨论】:

    标签: r plot ggplot2 scatter-plot r-plotly


    【解决方案1】:

    TL;DR 有很多选择。下面解释了三个,其中一个是 OP 要求的plotlyplotly 可能不是最好的。可能需要一些数据准备,就像 OP 和answer by @rar 给出的示例数据一样。

    免责声明:不幸的是,3d 曲面图在 R 中有点麻烦。Python 是一种可能的替代方案;与 R 中的所有选项相比,我发现 python-matplotlibextremely 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 函数中提取的值。示例hereherehere 或(用于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 将列 csw 解释为不是基本的普通坐标,而是作为附加观察。很难判断这是否确实是一个错误,或者答案是否有意如此;不幸的是,它包含的文字很少。

    绘图选项 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

    【讨论】:

    • 感谢您的详细解答。关于“...@rar 的其他答案...以错误的方式绘制它,... cs 和 w 不是作为基本的普通坐标而是作为额外的观察结果。”,您能否详细说明原因这是错误的,它如何影响情节?
    • @Dunois 对不起,我很久以前回答过这个问题,我不记得细节了。我目前只有纯 R 可用,而不是 RStudio,并且无法使用纯 R 进行绘图。因此我目前无法检查。但请注意,在 rar 的答案中提供的图中,x 和 y 坐标似乎不是来自 cs 和 w 列的值。
    • 感谢您的回复。我很惊讶plotly 不适用于纯 R!嗯,看着那个情节(并亲自尝试过),我同意@rar 的解决方案是不完整的。我猜y 轴很好,但x 轴是从一些自动排序中获取的(df 有 3 列,所以可能是 0 索引的列号?)。无论如何,我认为不需要将数据推送到matrix;他们本可以做类似plot_ly(data = df_sample, x = ~cs, y = ~w, z = ~u, type = "mesh3d") 的事情,它会给 OP 他们想要的东西。
    • @Dunois 是的,您的代码对我来说很合适。关于纯 R 中的情节:我怀疑如果我花一些时间弄清楚如何正确设置环境(浏览器等),它会起作用。
    • 我明白了!从来没想过,也许有一天我会尝试从终端运行plot_ly()。感谢您富有成果的讨论!
    【解决方案2】:
    library(plotly)    
    # df_sample could be your plot_data    
    > df_sample 
             cs w     u
        1  21.0 6 160.0
        2  21.0 6 160.0
        3  22.8 4 108.0
        4  21.4 6 258.0
        5  18.7 8 360.0
        6  18.1 6 225.0
        7  14.3 8 360.0
        8  24.4 4 146.7
        9  22.8 4 140.8
        10 19.2 6 167.6
    > df<-as.matrix(df_sample, rownames.force = NA)
    > plot_ly(z=~df) %>% add_surface()
    

    如果要更改 x、y 和 z 轴。然后在绘图时重新排列列。

    plot_ly(z=~df[,c(3,1,2)])%>%add_surface()
    

    【讨论】:

    • 需要 plotly 包?
    • 是的。它将需要情节包。现在已将其包含在答案中。
    • 如何改变x、y、z轴
    • 你能告诉我 x,y 和 z 轴的列顺序吗?我想要 xaxis - w,yaxis=cs,zaxis=u
    • @premon 试试plot_ly(data = df_sample, x = ~cs, y = ~w, z = ~u, type = "mesh3d")(或类似的东西),以防你还在处理这个问题。
    猜你喜欢
    • 2020-06-02
    • 1970-01-01
    • 2020-09-23
    • 1970-01-01
    • 2020-06-07
    • 2021-12-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多