【问题标题】:Shiny:How to avoid global variables and problem with ggplot one observation at a timeShiny:如何避免全局变量和 ggplot 一次观察的问题
【发布时间】:2020-07-10 12:01:24
【问题描述】:

我正在构建一个闪亮的应用程序,其中每 30 秒,reactivefilereader 读取新数据并通过附加到自应用程序开始运行以来积累的数据来处理它(数据处理函数将新数据附加到现有聚合数据并返回一个单行),然后ggplot 将在绘图上绘制此单个观察值。它会用一条线连续绘制观察结果。但是,我收到了此错误消息,并且在闪亮的应用程序上没有绘制任何内容。

geom_path: Each group consists of only one observation. Do you need to adjust the group aesthetic?

我的数据如下:

ts
                          Px
2020-03-13 17:15:19.000 23335.5

我在server 函数之外有全局变量。请注意,下面不包含任何数据,因为闪亮将每 30 秒读取一次数据。

ggprice   <-  ggplot() + scale_colour_manual(values=c(Px="black"))

并且绘图通过下面更新,ts 是数据。它将只包含一个观察结果。

ggprice   <<-    ggprice   + geom_line(aes(x=Index,y=Px,colour = "Px"),data=ts)

我该如何克服这个问题?

更新:根据要求提供可重现的示例。

我在下面有 2 个全局变量,我知道它们非常笨拙。

  1. 全局变量 1 - xts_bars_Agg 用于存储自应用开始运行以来所有处理过的数据
  2. 全局变量 2 - ggpricegeom_line(...) 将每个新的观察结果附加到这个全局变量上。

如何优化?这里可以避免全局变量吗?

# Global variables
xts_bars_Agg <-  NULL


#   --- Function: Data I/O ------------------------------------------------------
data_processing <- function(csv_file){

    df                              <-  read.csv(csv_file, header=T,stringsAsFactors = F,colClasses = c("character","double"))
        # convert String to timestamp, remove string timestamp
        df                          <-  (data.frame( Timestamp = strptime(df[,1],"%Y-%m-%d %H:%M:%OS"), df[,c(-1)]))
        df_xts                      <-  xts(x=df[,c(-1)], order.by=df$Timestamp)
        xts_bars_Agg                <<- rbind.xts(xts_bars_Agg,df_xts)
        # *** the reason I need to rbind the new df_xts to 
        # existing xts_bars_agg (xts object) 
        # because the computeMagicalVal function below needs 
        # both the previous aggregated xts data plus the current xts data
        # to compute a value.
        # This and the usage of global variable looks very clumsy and inefficient and stupid to me. 
        # Is there a way to optimise this ?
        df_xts_final                 <- computeMagicalVal(xts_bars_Agg)

    # return df_xts_final with only one row, 
    # whereas xts_bars_Agg contains all the xts data with many rows
    return(df_xts_final)
}

# second global variable
# global variable
ggprice      <-  ggplot() +
                scale_colour_manual(values=c(Price="black"))

ggplot_func <- function(ts){
    ggprice      <<-    ggprice      + geom_line(aes(x=Index,y=Px,colour = "Price"),data=ts)
    return(ggprice)
}

# UI
ui <- fluidPage(
    #ggplot
    mainPanel(
        plotOutput(outputId = 'ggp')
    )
)

# Define server logic
server <- function(input, output, session) {    
    df_update <- reactiveFileReader(intervalMillis = 10000,session=NULL,
                                    filePath = "output.csv",
                                    readFunc = data_processing)

    output$ggp <- renderPlot({
        ggplot_func(df_update())
    })
}

# Run the application 
shinyApp(ui = ui, server = server)

【问题讨论】:

  • 您能否提供一个可重现的闪亮应用示例?
  • 另外,不推荐在闪亮的应用中使用全局变量,尝试使用reactiveValues
  • 你介意把这个作为一个例子的答案吗?
  • 我想我还没有解决你的问题。如果您提供可重现的示例,也许有人会找到您问题的解决方案并将其作为答案。
  • 你是对的。我在上面包含了我的代码。全局变量在我看来效率低下且令人费解。请教我如何在这里做得更好。

标签: r ggplot2 shiny


【解决方案1】:

您好,您可以尝试使用这样的响应式值

#   --- Function: Data I/O ------------------------------------------------------
data_processing <- function(csv_file){

  df                              <-  read.csv(csv_file, header=T,stringsAsFactors = F,colClasses = c("character","double"))
  # convert String to timestamp, remove string timestamp
  df                          <-  (data.frame( Timestamp = strptime(df[,1],"%Y-%m-%d %H:%M:%OS"), df[,c(-1)]))
  df_xts                      <-  xts(x=df[,c(-1)], order.by=df$Timestamp)


  return(df_xts)
}

# UI
ui <- fluidPage(
  #ggplot
  mainPanel(
    plotOutput(outputId = 'ggp')
  )
)

# Define server logic
server <- function(input, output, session) {

  rective_values <- reactiveValues(
    xts_bars_Agg = tibble(),
    ggprice = ggplot() +
      scale_colour_manual(values=c(Price="black"))
  )

  # whereas xts_bars_Agg contains all the xts data with many rows
  observe({
    reactive_values$xts_bars_Agg = rbind.xts(xts_bars_Agg,df_update()) 
    # *** the reason I need to rbind the new df_xts to 
    # existing xts_bars_agg (xts object) 
    # because the computeMagicalVal function below needs 
    # both the previous aggregated xts data plus the current xts data
    # to compute a value.
    # This and the usage of global variable looks very clumsy and inefficient and stupid to me. 
    # Is there a way to optimise this ?
  })

  observeEvent(reactive_values$xts_bars_Agg,{
    df_xts_final = computeMagicalVal(reactive_values$xts_bars_Agg)
    reactive_values$ggprice = reactive_values$ggprice + 
      geom_line(aes(x=Index,y=Px,colour = "Price"),
                data= df_xts_final)
  })
  df_update <- reactiveFileReader(intervalMillis = 10000,session=NULL,
                                  filePath = "output.csv",
                                  readFunc = data_processing)



  output$ggp <- renderPlot({
    reactive_values$ggprice
  })
}

# Run the application 
shinyApp(ui = ui, server = server)

希望这会有所帮助!

【讨论】:

  • 这是一个很好的答案。有没有办法在我的代码中保留ggplot_func?因为我实际上有多个地块,我会在最后使用grid.arrange 将多个地块组合成一个。
  • 有没有办法将反应值作为参数传递给函数?你能给我一个例子吗?抱歉,我为此苦苦挣扎了一段时间。
  • 是的,您可以将反应值作为参数传递给您只需在反应上下文中执行的函数,例如:反应观察或渲染函数
  • 我会将执行 grid.arrange 的函数放在 renderPlot 函数中
  • 在这种情况下,你是否将 reactive_values 或 reactive_values$ggprice 作为参数传递给函数?或者没关系?
猜你喜欢
  • 2019-02-16
  • 2015-12-26
  • 1970-01-01
  • 2021-01-26
  • 2011-04-10
  • 2012-12-19
  • 2016-09-29
  • 1970-01-01
  • 2016-03-19
相关资源
最近更新 更多