首页 > 解决方案 > 在 Shiny 中保持运行值

问题描述

我是一名环境科学家,试图为牡蛎创建一个收获模拟。我希望模拟显示两张地图,一张用于显示当前的牡蛎种群,另一张用于显示禁捕区(保护区)。单击地图上的一个图(每个图都有一个不可见的标记)应该根据显示的地图执行不同的操作。单击牡蛎种群地图应导致种群随着单击图中发生的牡蛎收获而更新。点击保护区地图应该会导致点击的地块改变为打开或关闭的指定。

据我所知,问题是每次单击输入时所有这些值都会重置。例如,之前显示的地图无关紧要,“showSanctuary”变量,应该显示 F 是人口地图上升,T 如果保护区地图上升,总是设置为 False,它的起始值,每当单击新输入时。牡蛎种群向量和保护区向量似乎做同样的事情。如何防止这些变量重置为初始值?

另外,我对这个论坛很陌生,我不确定礼仪。我将在下面发布我的所有代码,而不是存储我的函数的两个脚本(我确定它们不是问题),但这是一个相当冗长的程序。这个问题与第 70 行和第 152 行之间的代码有关。我将首先发布这些行,下面是完整的脚本。如果这不是典型的礼仪,再次抱歉。

发生问题的行(这是在 Server 函数内部):

#Make Reactive Values
  offLim <- reactiveValues()
  offLim = 0
  
  showSanctuary <- reactiveValues()
  showSanctuary = F
  
  myOutputs <- reactiveValues()
  myOutputs$outHarvTime = 0
  myOutputs$outSacksTaken = 0
  myOutputs$outAvgSize = 0
  
  #React to click event
  observeEvent(input$map_marker_click, {
    click<-input$map_marker_click
    if(is.null(click))
      return()
    
    xClk=trunc(click$lng*1000)/1000
    yClk=trunc(click$lat*1000)/1000
    
    xCor = which(coord$x == xClk)
    yCor = which(coord$y == yClk)
    myPlot = intersect(xCor, yCor)
    
    if(showSanctuary == T){
      if(offLim[myPlot]==1){
        offLim[myPlot]=0
      }else if(offLim[myPlot]==0){
        offLim[myPlot]=1
      }
      myMap <- makeSanctuaryMap(offLim)
      myOutputs$finalMap = myMap
    }
    else{
      myHarvLim = input$amount
      myMaxTime = input$effort
      myMinSize = input$size
      returnShells = input$shell
      param = c(myHarvLim, myMaxTime, myMinSize, myPlot, returnShells)
    
      newOysters = oysters
      newDens = myDens
      newShell = myShell
    
      outVar<-localUpdate(param, newOysters, myMaxDens, newDens, newShell, nplts)
      oysters = outVar$oysters
      myDens = outVar$myDens
      myShell = outVar$myShell
    
      myOutputs$outAvgSize = outVar$avgSize
      myOutputs$outHarvTime = outVar$harvTime
      myOutputs$outSacksTaken = outVar$sacksTaken
      myOutputs$finalMap = outVar$myMap
    }
  })
  
  observeEvent(input$update,{
    #Make sanctuary variables. Needs to be reactive to be global
    showSanctuary = F #Whether the Map Currently Displays Sanctuary Areas
    offLim = vector(length=nplts) #1 if plot is a Sanctuary or 0 if not
    offLim[] = 0
    offLim[!cond]<-NA
    
    myOutputs$outAvgSize = NA
    myOutputs$outHarvTime = NA
    myOutputs$outSacksTaken = NA
    myOutputs$finalMap = myMap
  })
  
  observeEvent(input$sanctuaryMap,{
    print(showSanctuary)
    showSanctuary = T
    myMap <- makeSanctuaryMap(offLim)
    myOutputs$finalMap = myMap
  })
  
  observeEvent(input$harvestMap,{
    print(showSanctuary)
    showSanctuary = F
    myMap <- updateMap(heatVec)
    myOutputs$finalMap = myMap
  })

完整脚本:

library(shiny)
library('leaflet')
library(raster)
library('sf')
library(rgdal)
source('updateFunctions.R')

effortLbl = "What is the maximum number of hours that you are willing to spend harvesting each day?"
amountLbl = "What is the maximum number of sacks of oysters you would harvest in one day?"
sizeLbl = "Select a minimum size for legal harvest (inches)"
shellLbl="Check to require culling on site"

ui<-fluidPage(
  numericInput(inputId="effort", label=effortLbl, value=8, min=1, max=16, step=1),
  numericInput(inputId="amount", label=amountLbl, value=4, min=1, max=40, step=1),
  numericInput(inputId="size", label=sizeLbl, value=3, min=1, max=6, step=1),
  checkboxInput(inputId="shell", label=shellLbl, value = FALSE),
  actionButton(inputId="update", label="Begin"),
  actionButton(inputId="sanctuaryMap", label="Set Sanctuaries"),
  actionButton(inputId="harvestMap", label="Choose Harvest Area"),
  leafletOutput("map"),
  textOutput("time"),
  textOutput("sacks"),
  textOutput("size")
)

server<-function(input, output, session){
  #Generate list of clickable coordinates
  cedKey <- readOGR(dsn=path.expand("shapefile"), layer="LC_10_Area") #Imports Cedar Key shape file
  ckCrd <- spTransform(cedKey, CRSobj = CRS("+init=epsg:4326")) #Converts shape file coordinate to longitude/latitude
  matCrd=expand.grid(x=seq(from=-83.1164,to=-83.06251,length.out=moveRow), #Generates a series of coordinates within range
                     y=seq(from=29.2169,to=29.26528,length.out=moveRow))
  df = data.frame(x = matCrd$x, y = matCrd$y)
  s = SpatialPixelsDataFrame(df[,c('x', 'y')], data = df, proj4string = crs(ckCrd))
  clp <- over(s[,c("x", "y")], ckCrd)
  cond <- !is.na(clp$Id)
  spNew<-s[cond,]
  spDf = as.data.frame(spNew)
  coord = data.frame(x=(trunc(df$x*1000)/1000), y=(trunc(df$y*1000)/1000))
  
  #Initialize oyster population variables
  nplts = 1600 #Total number of plots
  nsize = 7 #Number of size classes (including larva)
  oysters = matrix(0, nplts,nsize)
  myDens = vector(length=nplts) #Total number of oysters weighted by size
  myShell = vector(length=nplts) #The amount of dead shell (or other non-living hard substrate)
  myMaxDens = 1000 #The maximum capacity of every plot
  moveRow = sqrt(nplts) #The number of plots in a row
  
  #Initialize oyster population with randomization
  for(i in 1:nplts){
    initMin = c(20,20,5,5,0,0) #Minimum number of oysters of each size at game start
    initMax = c(60,40,30,20,10,5) #Maximum number of oysters of each size at game start
    oysters[i,1:6]=runif(6, initMin, initMax)
    oysters[i,7]=sum(oysters[i,1:6]*4)
  }
  oysters[!cond,]<-NA
  for(i in 1:nplts){
    myDens[i]=sum(oysters[i,1:6]*c(1:6))
    myShell[i]=0.2*myDens[i]
  }
  
  #Set values of outputs before initial update
  heatVec = vector(length=nplts)
  for(i in 1:nplts){
    heatVec[i] = (100*myDens[i])/myMaxDens
  }
  myMap = updateMap(heatVec)
  
  #Make Reactive Values
  offLim <- reactiveValues()
  offLim = 0
  
  showSanctuary <- reactiveValues()
  showSanctuary = F
  
  myOutputs <- reactiveValues()
  myOutputs$outHarvTime = 0
  myOutputs$outSacksTaken = 0
  myOutputs$outAvgSize = 0
  
  #React to click event
  observeEvent(input$map_marker_click, {
    click<-input$map_marker_click
    if(is.null(click))
      return()
    
    xClk=trunc(click$lng*1000)/1000
    yClk=trunc(click$lat*1000)/1000
    
    xCor = which(coord$x == xClk)
    yCor = which(coord$y == yClk)
    myPlot = intersect(xCor, yCor)
    
    if(showSanctuary == T){
      if(offLim[myPlot]==1){
        offLim[myPlot]=0
      }else if(offLim[myPlot]==0){
        offLim[myPlot]=1
      }
      myMap <- makeSanctuaryMap(offLim)
      myOutputs$finalMap = myMap
    }
    else{
      myHarvLim = input$amount
      myMaxTime = input$effort
      myMinSize = input$size
      returnShells = input$shell
      param = c(myHarvLim, myMaxTime, myMinSize, myPlot, returnShells)
    
      newOysters = oysters
      newDens = myDens
      newShell = myShell
    
      outVar<-localUpdate(param, newOysters, myMaxDens, newDens, newShell, nplts)
      oysters = outVar$oysters
      myDens = outVar$myDens
      myShell = outVar$myShell
    
      myOutputs$outAvgSize = outVar$avgSize
      myOutputs$outHarvTime = outVar$harvTime
      myOutputs$outSacksTaken = outVar$sacksTaken
      myOutputs$finalMap = outVar$myMap
    }
  })
  
  observeEvent(input$update,{
    #Make sanctuary variables. Needs to be reactive to be global
    showSanctuary = F #Whether the Map Currently Displays Sanctuary Areas
    offLim = vector(length=nplts) #1 if plot is a Sanctuary or 0 if not
    offLim[] = 0
    offLim[!cond]<-NA
    
    myOutputs$outAvgSize = NA
    myOutputs$outHarvTime = NA
    myOutputs$outSacksTaken = NA
    myOutputs$finalMap = myMap
  })
  
  observeEvent(input$sanctuaryMap,{
    print(showSanctuary)
    showSanctuary = T
    myMap <- makeSanctuaryMap(offLim)
    myOutputs$finalMap = myMap
  })
  
  observeEvent(input$harvestMap,{
    print(showSanctuary)
    showSanctuary = F
    myMap <- updateMap(heatVec)
    myOutputs$finalMap = myMap
  })
  
  localUpdate <- function(param, locOyster, myMaxDens, locDens, locShell, nplts){
    myUpdate<-updateFunction(locOyster, myMaxDens, locDens, locShell, param, nplts) #All updates done in separate script
    
    #Set oyster pop, dens, and dead shell according to updates
    oysters =  myUpdate$oysters
    for(i in 1:nplts){
      myDens[i] = sum(oysters[i,1:6]*c(1:6))
    }
    myShell = myUpdate$shell
    
    #Calculate heatmap values based on density (biomass)
    for(i in 1:nplts){
      heatVec[i] = (100*myDens[i])/myMaxDens
    }
    myMap <- updateMap(heatVec) #Create Map
    
    return(list(avgSize = myUpdate$avgSize, harvTime = myUpdate$harvTime, sacksTaken = myUpdate$sacksTaken, 
                myMap = myMap, myShell = myShell, myDens = myDens, oysters = oysters))
  }
  
  #Assemble and Display Outputs
  output$map <- renderLeaflet({
    input$map_marker_click #Makes output dependent on map or button click (via isolate)
    input$sanctuaryMap
    input$harvestMap
    input$update
    isolate(myOutputs$finalMap)})
  output$time <- renderText({
    input$map_marker_click
    input$update
    timeString <- isolate(c("Time Spent Harvesting Each Day: ", 
                            toString(trunc(myOutputs$outHarvTime*100)/100), " hours"))
    timeString})
  output$sacks <- renderText({
    input$map_marker_click
    input$update
    sacksString<-isolate(c("Average Number of Sacks Harvested per Day: ", 
                           toString(trunc(myOutputs$outSacksTaken*100)/100), " sacks"))
    sacksString})
  output$size <- renderText({
    input$map_marker_click
    input$update
    sizeString<-isolate(c("Average Size of Harvested Oysters this Year: ", 
      toString(trunc(myOutputs$outAvgSize*100)/100), " inches"))
    sizeString})
}

shinyApp(ui = ui, server = server)

标签: shinyglobal-variablesreactive-programming

解决方案


对于初学者,您没有reactiveValues正确使用。它会是这样的:

my_reactives <- reactiveValues()
my_reactives$offLim <- 0
my_reactives$showSanctuary <- F

推荐阅读