You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何构建R Shiny应用展示USGS流量数据的图表与表格

解决R Shiny中USGS流量数据图表与表格共用反应式数据的问题

问题核心

你的代码中,反应式函数df()每次合并站点数据时,都将流量列命名为Flow,导致最终数据框中所有站点的流量列重名。绘图时每个子图都调用~Flow,实际只会读取最后一列的流量数据;表格展示的也是一堆重复列名的混乱数据。

修复方案

1. 重构反应式数据获取逻辑

给每个站点的流量列赋予唯一的站点名称,避免列名冲突,同时让表格展示更直观。

2. 调整绘图逻辑

循环绘图时,根据站点名称匹配对应的数据列,确保每个子图展示对应站点的流量数据。

修改后的完整代码

library(shiny)
library(shinythemes)
library(dplyr)
library(dataRetrieval)
library(plotly)
library(lubridate)
library(DT)

C_site_numbers <- c("10308200", "10309000")
C_site_names <- c("Markleeville (cfs)", "Gardnerville (cfs)")

ui <- fluidPage(theme = shinytheme("yeti"),
                titlePanel("USGS Streamflow Data"),
                
                fluidRow(
                  column(4,
                         wellPanel(dateInput("start_date", "Start Date:", value = Sys.Date() - 7), width = 10)),
                  column(4, 
                         wellPanel(dateInput("end_date", "End Date:", value = Sys.Date()), width = 10)),
                  mainPanel(
                    tabsetPanel(
                      tabPanel("Carson Basin", plotlyOutput("C_plot_panel", width = "100%", height = ceiling(length(C_site_numbers)) * 400)),
                      tabPanel("Carson Tabular", dataTableOutput("Data_Tables", width = "100%"))
                    )
                  )
                ))

server <- function(input, output) {
  
  # 重构反应式数据:给每个站点的流量列赋予唯一名称
  df <- reactive({
    start_date <- ymd(input$start_date)
    end_date <- ymd(input$end_date)
    
    # 创建时间序列框架(5分钟间隔)
    df <- tibble(dateTime = seq(from = start_date, to = end_date, by = "5 mins"))
    
    # 循环获取每个站点数据并合并
    for (i in seq_along(C_site_numbers)) {
      site_num <- C_site_numbers[i]
      site_name <- C_site_names[i]
      # 根据站点名称选择参数代码(保留原逻辑)
      para_code <- if (site_name == 'Lahontan (acre-ft)') {'00054'} else {'00060'}
      
      # 获取USGS数据
      streamflow <- readNWISuv(siteNumbers = site_num, 
                               parameterCd = para_code, 
                               startDate = start_date, 
                               endDate = end_date)
      attr(streamflow$dateTime, 'tzone') <- "America/Los_Angeles"
      
      # 整理数据:用站点名称作为流量列名
      streamflow_df <- tibble(
        dateTime = as.POSIXct(streamflow$dateTime, tz = "America/Los_Angeles"),
        !!site_name := if (site_name == 'Lahontan (acre-ft)') {streamflow$X_00054_00000} else {streamflow$X_00060_00000}
      )
      
      # 合并到主数据框
      df <- df %>% left_join(streamflow_df, by = "dateTime")
    }
    
    df
  })
  
  # 生成多站点子图面板
  output$C_plot_panel <- renderPlotly({
    C_plots <- list()
    
    for (i in seq_along(C_site_names)) {
      site_name <- C_site_names[i]
      plot_data <- df()
      
      # 绘制对应站点的流量图
      plot <- plot_ly(data = plot_data, 
                      x = ~dateTime, 
                      y = ~.data[[site_name]],  # 用站点名称匹配列
                      type = "scatter", 
                      mode = "lines") %>%
        layout(yaxis = list(title = site_name),
               hovermode = "x unified", 
               plot_bgcolor = 'rgb(212,213,214)', 
               showlegend = FALSE)
      C_plots[[i]] <- plot
    }
    
    # 组合子图
    subplot(C_plots, nrows = ceiling(length(C_site_names)), titleY = TRUE, margin = 0.07)
  })
  
  # 展示数据表格
  output$Data_Tables <- renderDataTable({
    df() %>% 
      datatable(options = list(scrollX = TRUE, pageLength = 10),
                rownames = FALSE)
  })
}

shinyApp(ui = ui, server = server)

关键修改说明

  • 数据列命名:使用!!site_name :=将流量列命名为站点名称,避免重名,表格中能直接区分各站点数据。
  • 绘图列匹配:用.data[[site_name]]动态调用对应站点的列,确保每个子图展示正确的流量数据。
  • 数据结构优化:用tibble替代data.frame,结合left_join让数据合并更简洁可靠。
  • 表格美化:添加scrollX = TRUE解决宽表格横向滚动问题,隐藏行号提升可读性。

内容的提问来源于stack exchange,提问作者Koda

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 06:13:11