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

Shiny App开发求助:实现选定日期当月累计雨量计算

问题描述

我在RStudio中开发了一款用于分析乳制品生产数据的Shiny App,应用内设有日期选择器,选择日期后会展示该日的各类数据。我希望在应用中新增「Total Rain for the Month」列(不修改原始数据集),用于计算选定日期所在月份从月初到该选定日期的Rain列数值总和(例如选定2020-08-07时,需计算2020-08-01至2020-08-07的Rain总和23.5)。但当前代码无法生成该列,现附上数据集示例及现有代码。

数据集示例:

d <- structure(list(date = c(
  "2020-08-01", "2020-08-02", "2020-08-03",
  "2020-08-04", "2020-08-05", "2020-08-06", "2020-08-07"
), Milk.sold = c(
  17396L,
  17715L, 18071L, 17950L, 17749L, 17821L, 17749L
), Rations = c(
  43L,
  27L, 48L, 55L, 32L, 45L, 48L
), Tot.milk = c(
  17439L, 17742L, 18119L,
  18005L, 17781L, 17866L, 17797L
), No.cows = c(
  1142L, 1158L, 1166L,
  1159L, 1166L, 1178L, 1187L
), Liters.cow = c(
  15.3, 15.3, 15.5,
  15.5, 15.2, 15.2, 15
), Rain = c(0, 0, 0, 0, 12, 9, 2.5), Weighted.Growth = c(
  0,
  0, 0, 12.2, 0, 0, 0
)), class = "data.frame", row.names = c(
  "1",
  "2", "3", "4", "5", "6", "7"
))

现有代码:

# Define the UI
ui <- fluidPage(
  h1("Glen Dye Dashboard"),
  sidebarLayout(
    sidebarPanel(
      dateInput("selected_date", "Select a Date:", value = as.Date("2021-01-12"))
    ),
    mainPanel(
      h3("Daily Figures"),
      tableOutput("selected_data")
    )
  )
)


# Define the server
server <- function(input, output) {
  # Render the selected data table
  output$selected_data <- renderTable({
    selected_date <- as.Date(input$selected_date)  # Convert selected date to Date format
    
    # Extract the year and month from the selected date
    selected_year <- lubridate::year(selected_date)
    selected_month <- lubridate::month(selected_date)
    
    # Filter the data for the selected month and up to the selected date
    filtered_data <- d %>% 
      filter(lubridate::year(as.Date(date)) == selected_year,
             lubridate::month(as.Date(date)) == selected_month,
             as.Date(date) <= selected_date)
    
    # Calculate the total rain for the selected month
    total_rain <- sum(filtered_data$Rain)
    
    # Create the new column "Total Rain for the Month"
    selected_data <- data.frame(filtered_data, "Total Rain for the Month" = total_rain)
    
    # Rename the column names
    colnames(selected_data) <- c("Milk sold", "Rations", "Total Milk", "Cows in milk", 
                                 "Liters/Cow", "Rain", "Weighted Growth", "Pasture Utilized",
                                 "Total Rain for the Month")  # Corrected column name
    
    selected_data
  })
  
  # Automatically update the table when a date is selected
  observeEvent(input$selected_date, {
    output$selected_data <- renderTable({
      selected_date <- input$selected_date
      selected_data <- d[d$date == selected_date, c("Milk.sold", "Rations", "Tot.milk", "No.cows", "Liters.cow", "Rain", "Weighted.Growth","Pasture.utilized")]
      colnames(selected_data) <- c("Milk sold", "Rations", "Total Milk", "Cows in milk", "Liters/Cow", "Rain","Weighted Growth","Pasture Utilized")
      selected_data
    })
  })
}

# Run the Shiny app
shinyApp(ui = ui, server = server)
解决方案

代码无法生成目标列的核心原因是**observeEvent中的renderTable完全覆盖了之前的逻辑**,每次选择日期时,都会执行observeEvent里的代码,直接返回当日数据,完全忽略了计算累计降雨的逻辑。此外还有几个小问题需要修正:

  • 原始数据中没有Pasture.utilized列,代码中引用会报错
  • 日期转换可以提前处理,避免重复转换
  • 累计降雨需要对应到选定的单日数据,而不是返回整个月的数据

修正后的完整代码如下:

library(shiny)
library(dplyr)
library(lubridate)

# 提前转换日期格式,避免在render中重复转换
d$date <- as.Date(d$date)

# Define the UI
ui <- fluidPage(
  h1("Glen Dye Dashboard"),
  sidebarLayout(
    sidebarPanel(
      # 限制日期选择范围为数据中的日期区间
      dateInput("selected_date", "Select a Date:", 
                value = d$date[1],
                min = min(d$date),
                max = max(d$date))
    ),
    mainPanel(
      h3("Daily Figures"),
      tableOutput("selected_data")
    )
  )
)


# Define the server
server <- function(input, output) {
  # 只保留一个renderTable逻辑,利用Shiny的响应式自动更新
  output$selected_data <- renderTable({
    selected_date <- as.Date(input$selected_date)
    
    # 计算选定日期所在月份从月初到该日的累计降雨
    total_rain <- d %>%
      filter(year(date) == year(selected_date),
             month(date) == month(selected_date),
             date <= selected_date) %>%
      pull(Rain) %>%
      sum()
    
    # 获取选定日期的单日数据
    selected_data <- d %>%
      filter(date == selected_date) %>%
      # 重命名列并新增累计降雨列
      rename(
        "Milk sold" = Milk.sold,
        "Total Milk" = Tot.milk,
        "Cows in milk" = No.cows,
        "Liters/Cow" = Liters.cow,
        "Weighted Growth" = Weighted.Growth
      ) %>%
      mutate("Total Rain for the Month" = total_rain)
    
    # 调整列顺序(可选,按你需要的展示顺序)
    selected_data <- selected_data %>%
      select("Milk sold", "Rations", "Total Milk", "Cows in milk", 
             "Liters/Cow", "Rain", "Weighted Growth", "Total Rain for the Month")
    
    selected_data
  })
}

# Run the Shiny app
shinyApp(ui = ui, server = server)

关键修正点说明

  • 移除冗余的observeEvent:Shiny的renderTable本身就是响应式的,当input$selected_date变化时会自动重新计算,不需要额外的observeEvent覆盖逻辑。
  • 提前处理日期格式:在服务器逻辑外把d$date转换为Date类型,避免在每次渲染时重复转换,提升效率。
  • 正确计算累计降雨:先筛选出选定月份内从月初到选定日期的所有数据,求和后添加到单日数据中。
  • 移除不存在的列引用:原始数据中没有Pasture.utilized,所以删除相关引用避免报错。
  • 优化日期选择器范围:设置min和max为数据中的日期区间,避免用户选择无效日期。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 16:14:57