在for循环中优化add_trace()?

时间:2016-07-03 11:47:57

标签: r plotly

我在 for 循环中使用 add_trace()函数在plotly的scatter3d模式下为3d网络图创建线条。每个add_trace在网络中的两个节点之间绘制单独的行。该方法正在运行,但是由于循环次数很多,单个循环的速度似乎很快就会减慢。

示例数据可在此处下载:https://gist.github.com/pravj/9168fe52823c1702a07b

library(igraph)
library(plotly)

G <- read.graph("karate.gml", format = c("gml"))
L <- layout.circle(G)

vs <- V(G)
es <- as.data.frame(get.edgelist(G))

Nv <- length(vs)
Ne <- length(es[1]$V1)

Xn <- L[,1]
Yn <- L[,2]

network <- plot_ly(type = "scatter3d", x = Xn, y = Yn, z = rep(0, Ne), mode = "markers", text = vs$label, hoverinfo = "text", showlegend = F)

for(i in 1:Ne) {
  v0 <- es[i,]$V1
  v1 <- es[i,]$V2

  x0 <-  Xn[v0]
  y0 <-  Yn[v0]
  x1 <-  Xn[v1]
  y1 <-  Yn[v1]

  df <-  data.frame(x = c(x0, x1), y = c(y0, y1), z = c(0, 0))
  network <- add_trace(network, data = df, x = x, y = y, z = z, type = "scatter3d", mode = "lines", showlegend = F, 
                       marker = list(color = '#030303'), line = list(width = 0.5))
}

这个例子相当快,但是当我包含几百个或更多边时,各个循环的执行开始从根本上减慢。我尝试了不同的优化方法(矢量化等),但似乎没有解决add_trace函数本身的缓慢问题。

有什么建议吗?

1 个答案:

答案 0 :(得分:0)

在plotly中添加许多线段的最有效方法不是每个都是单独的跟踪,而是仅使用包含所有线段的单个跟踪。您可以通过构建一个数据框来实现这一点,该数据框包含要连接的每个节点的x,y坐标,并在每个线段之间穿插NA。然后使用connectgaps=FALSE将跟踪分解为每个NA的单独段。您可以看到此方法的另一个示例,适用于this answer中的意大利面条图。

es$breaks <- NA
lines <- data.frame(node=as.vector(t(es)), x=NA, y=NA, z=0)
lines[which(!is.na(lines$node)),]$x <- Xn[lines[which(!is.na(lines$node)),]$node]
lines[which(!is.na(lines$node)),]$y <- Yn[lines[which(!is.na(lines$node)),]$node]

network <- plot_ly(type = "scatter3d", x = Xn, y = Yn, z = rep(0, Ne), 
                   mode = "markers", text = vs$label, hoverinfo = "text", 
                   showlegend = F) %>% 
  add_trace(data=lines, x=x, y=y, z=z, showlegend = FALSE,
                      type = 'scatter3d', mode = 'lines+markers',
                      marker = list(color = '#030303'), line = list(width = 0.5),
                      connectgaps=FALSE)

enter image description here

此问题的可重现数据

为方便起见,以下是此问题的数据。 OP需要从github下载.gml文件,并安装库(igraph)将数据处理成这些文件。

es <- structure(list(
  V1 = c(1, 1, 2, 1, 2, 3, 1, 1, 1, 5, 6, 1, 2, 3, 4, 1, 3, 3, 1, 5, 6, 1, 1, 4, 1, 2, 3, 4, 6, 7, 1, 2, 1, 2, 
    1, 2, 24, 25, 3, 24, 25, 3, 24, 27, 2, 9, 1, 25, 26, 29, 3, 9, 15, 16, 19, 21, 23, 24, 30, 31, 32, 9, 10, 14, 15, 16, 19, 20, 
    21, 23, 24, 27, 28, 29, 30, 31, 32, 33), 
  V2 = c(2, 3, 3, 4, 4, 4, 5, 6, 7, 7, 7, 8, 8, 8, 8, 9, 9, 10, 11, 11, 11, 12, 13, 13, 
    14, 14, 14, 14, 17, 17, 18, 18, 20, 20, 22, 22, 26, 26, 28, 28, 28, 29, 30, 30, 31, 31, 32, 32, 32, 32, 33, 33, 33, 33, 33, 33, 
    33, 33, 33, 33, 33, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34)), 
  .Names = c("V1", "V2"), row.names = c(NA, -78L), class = "data.frame")

theta <- seq(0,2,length.out=35)[1:34]
Xn <- cospi(theta)
Yn <- sinpi(theta)

Nv <- NROW(Xn)
Ne <- NROW(es)
vs <- data.frame(label = as.character(1:Nv))