如何在R中将多个3D曲面图展示在同一页面?
问题描述
我写了一段R代码,用来生成变量p3取不同值时的3D曲面图,代码及遇到的问题如下:
定义核心函数
library(plotly) library(dplyr) calculate_b_values <- function(p1, p2, p3, weights) { grid <- expand.grid(p1 = p1, p2 = p2) grid$B <- (weights$w4 / weights$w2) * weights$w3 * grid$p1 * grid$p2 * p3 matrix(grid$B, nrow = length(p1), ncol = length(p2)) } create_surface_plot <- function(p1, p2, z_matrix, p3_value) { plot_ly() %>% add_surface( z = z_matrix, x = p1, y = p2, colorscale = "Viridis", opacity = 0.9 ) %>% layout( scene = list( xaxis = list(title = "p1"), yaxis = list(title = "p2"), zaxis = list(title = "B"), aspectratio = list(x = 1, y = 1, z = 0.7) ), title = paste("Surface Plot with p3 =", p3_value) ) } generate_surface_plots <- function(p_range = seq(0.1, 1, length.out = 20), p3_values = c(0.1, 0.4, 0.7, 1.0), weights = list(w1 = 10, w2 = 5, w3 = 3, w4 = 8)) { plot_list <- list() for (i in seq_along(p3_values)) { p3_fixed <- p3_values[i] z_matrix <- calculate_b_values(p_range, p_range, p3_fixed, weights) plot_list[[i]] <- create_surface_plot(p_range, p_range, z_matrix, p3_fixed) } names(plot_list) <- paste0("p3_", p3_values) return(plot_list) }
生成单个图表
weights <- list(w1 = 10, w2 = 5, w3 = 3, w4 = 8) p_vals <- seq(0.1, 1, length.out = 20) p3_values <- c(0.1, 0.4, 0.7, 1.0) plots <- generate_surface_plots(p_vals, p3_values, weights) plots[[1]] plots[[2]] plots[[3]] plots[[4]]
合并图表失败尝试
我想用subplot将4个图表放在同一页面,代码如下:
plots <- generate_surface_plots(p_vals, p3_values, weights) combined_plot <- subplot( plots[[1]], plots[[2]], plots[[3]], plots[[4]], nrows = 2, titleX = TRUE, titleY = TRUE, margin = 0.05 ) combined_plot <- combined_plot %>% layout( title = list( text = "Surface Plots at Different p3 Values", font = list(size = 16) ) ) combined_plot
但运行后所有图表叠加在同一个绘图区域,我需要实现4个独立3D图分2行2列排列的布局,类似Python的3D子图效果。
解决方案
问题根源是所有3D图默认共用同一个scene对象,导致叠加。需给每个子图分配唯一的scene标识,并在整体布局中指定每个scene的位置。
修改后的完整代码如下:
1. 更新绘图函数
新增scene_name参数,为每个图绑定独立的scene:
create_surface_plot <- function(p1, p2, z_matrix, p3_value, scene_name = "scene") { plot_ly() %>% add_surface( z = z_matrix, x = p1, y = p2, colorscale = "Viridis", opacity = 0.9 ) %>% layout( !!scene_name := list( xaxis = list(title = "p1"), yaxis = list(title = "p2"), zaxis = list(title = "B"), aspectratio = list(x = 1, y = 1, z = 0.7) ) ) }
2. 更新批量生成函数
为每个图指定唯一的scene名称:
generate_surface_plots <- function(p_range = seq(0.1, 1, length.out = 20), p3_values = c(0.1, 0.4, 0.7, 1.0), weights = list(w1 = 10, w2 = 5, w3 = 3, w4 = 8)) { plot_list <- list() scene_names <- paste0("scene", seq_along(p3_values)) for (i in seq_along(p3_values)) { p3_fixed <- p3_values[i] z_matrix <- calculate_b_values(p_range, p_range, p3_fixed, weights) plot_list[[i]] <- create_surface_plot(p_range, p_range, z_matrix, p3_fixed, scene_names[i]) } names(plot_list) <- paste0("p3_", p3_values) return(plot_list) }
3. 合并并布局子图
通过domain参数指定每个scene在画布上的位置,实现2行2列排列:
plots <- generate_surface_plots(p_vals, p3_values, weights) combined_plot <- subplot( plots[[1]], plots[[2]], plots[[3]], plots[[4]], nrows = 2, shareX = FALSE, shareY = FALSE, margin = 0.1 ) %>% layout( title = list(text = "Surface Plots at Different p3 Values", font = list(size = 16)), scene1 = list(domain = list(x = c(0, 0.45), y = c(0.55, 1))), scene2 = list(domain = list(x = c(0.55, 1), y = c(0.55, 1))), scene3 = list(domain = list(x = c(0, 0.45), y = c(0, 0.45))), scene4 = list(domain = list(x = c(0.55, 1), y = c(0, 0.45))) ) combined_plot
关键说明
- 每个3D图必须绑定唯一的
scene对象,避免共用导致叠加; domain参数用0-1的比例值指定每个scene在画布上的坐标范围,实现2行2列的排列;- 设置
shareX = FALSE, shareY = FALSE保证每个子图的坐标轴独立。
内容的提问来源于stack exchange,提问作者stats_noob
相关产品推荐
相关产品推荐

