我一直在尝试构建一个应用程序,让我可以探索起点-目的地流数据。它接受一个 csv,其中包含起始区域编号、目标区域编号和一堆信息字段,以及区域的多边形形状文件,让我可以描绘流入或流出特定区域的流量。我实际上有一个可以运行的 Shiny 应用程序,但它要么效率太低,要么这不是适合这项工作的工具。我将粘贴下面的代码。
我的问题是它似乎太占用内存了。对于少数区域,它可以工作,但速度非常慢(最初加载需要很长时间,并且每次输入之一更改时都需要更新)。更大的问题是我通常使用大型区域系统,有数百甚至数千个区域,总共有数百万个顶点。当我尝试将其应用于其中一个大型区域系统时,它最初会加载,但在两到三个移动(缩放或平移)或仅一次输入更改后,RStudio 就会崩溃。
所以我想知道是否有任何方法可以提高效率,或者 RStudio/Shiny 是否根本不适合用于此类用途。请记住,我对优化几乎一无所知,所以如果您稍微简化一下解释,我将不胜感激。
这是有效的应用程序:
library(shiny)
library(rgdal)
library(sp)
library(leaflet)
library(RColorBrewer)
shape <- readOGR(dsn=".", layer="zones")
shape <- spTransform(shape, CRS("+init=EPSG:4326"))
读取 shapefile,然后读取数据。 “i”和“j”是出发地和目的地区域编号。我还需要要映射的可能字段列表,并且它必须由完全数字的字段组成。我还没有弄清楚如何在没有循环的情况下对其进行子集化。任何有关这方面的帮助将不胜感激。
values <- read.csv("values.csv")
zonelist <- sort(unique(c(values$i, values$j)))
zones <- length(zonelist)
fieldlist <- as.vector(character())
fields <- 0
for (valname in colnames(values)){
if (valname != "i" &
valname != "j" &
is.numeric(values[[valname]])
) {
fields <- fields + 1
fieldlist[fields] <- valname
}
}
我将它们放入矩阵 mm 中。再次强调,任何更有效的方法都将受到欢迎。
mm <- array(numeric(), dim=c(fields,zones,zones))
mm[,,] <- 0
valarray <- array(numeric(), dim=fields*zones*zones)
valarray[] <- 0
for (k in 1:length(values)){
if (colnames(values)[k] %in% fieldlist) {
valarray[(values$j - 1) * zones * fields
+ (values$i - 1) * fields
+ which(colnames(values)[k] == fieldlist)
] <- values[[k]]
}
}
mm[,,] <- valarray
界面很简单:选择从/到参考区域,选择参考区域,选择要映射的数值字段。显示流程的传单地图。
ui <- fluidPage(
titlePanel("Interzonal values mapping"),
sidebarLayout(
sidebarPanel(
selectInput(inputId = "ft", label = "From/to zone", choices = c("from", "to")),
selectInput(inputId = "zn", label = "Reference zone", choices = zonelist),
selectInput(inputId = "fl", label = "Field to map", choices = fieldlist),
width = 2
),
mainPanel(leafletOutput("mapped_zonelist", height = 800),
width = 10)
)
)
服务器根据输入从矩阵 mm 中选取要映射为向量 (vs) 的流量,并根据流量值格式化区域图。
server <- function(input, output) {
m <- reactive({
if (input$ft == "from"){
vs <- mm[which(input$fl == fieldlist), as.numeric(input$zn), ]
} else {
vs <- mm[which(input$fl == fieldlist), , as.numeric(input$zn)]
}
c_weight <- numeric()
c_weight[1:zones] <- 1
c_weight[as.numeric(input$zn)] <- 2
c_color <- character()
c_color[1:zones] <- "blue"
c_color[as.numeric(input$zn)] <- "black"
leaflet() %>%
addTiles() %>%
addPolygons(data=shape,
fillOpacity = 0.7,
opacity = 0.8,
weight = c_weight[shape@data$n],
color = c_color[shape@data$n],
fillColor = colorNumeric(palette = brewer.pal(10, "RdBu"), domain = vs)(vs[shape@data$n]),
popup = paste0("Zone ", input$zn, " to zone ", as.character(shape@data$n),
"<br>",
input$fl, " = ", vs)
)
})
output$mapped_zonelist <- renderLeaflet(m())
}
shinyApp(ui = ui, server = server)
根据您的形状文件的大小/复杂性,我要做的第一件事就是简化它。正常加载你的 shapefile,然后:
install.packages("rmapshaper")
shape_simp <- rmapshaper::ms_simplify(shape, keep = 0.05)
用
keep =
论证进行实验。数字越大,生成的 shapefile 的复杂性就越高。这可能无法完全解决您的问题,但这是一个开始。