R语言Shiny应用新增数据库无对应日期的绘图条件判断逻辑
问题说明
如下Shiny代码目前可针对数据库date2字段包含的日期(即2021-07-01、2021-07-02、2021-07-04)生成对应图表,当前日历组件虽已配置禁用不存在日期的逻辑,但存在配置范围错误导致禁用失效的问题,现需新增兜底判断逻辑:当所选日期不存在于数据库中时,执行指定代码段的逻辑,生成仅含m值对应水平线、无数据点的图表。
要求执行的逻辑代码
if (nrow(datas)<=2){ abline(h=m,lwd=2) points(0, m, col = "red", pch = 19, cex = 2, xpd = TRUE) text(.1,m+ .5, round(m,1), cex=1.1,pos=4,offset =1,col="black") }
解决方案
需要修改两个核心位置:
- 修正日历组件的禁用日期范围错误,避免可选到不在
date2范围内的日期 - 在绘图函数
f1最开头新增日期存在性判断,作为兜底逻辑,不存在时直接生成仅含水平线的空图表
修改后完整可运行代码
library(shiny) library(shinythemes) library(dplyr) library(tidyverse) library(lubridate) library(stringr) function.test<-function(){ df1 <- structure( list(date1= c("2021-06-28","2021-06-28","2021-06-28"), date2 = c("2021-07-01","2021-07-02","2021-07-04"), Category = c("ABC","ABC","ABC"), Week= c("Wednesday","Wednesday","Wednesday"), DR1 = c(4,1,0), DR01 = c(4,1,0), DR02= c(4,2,0),DR03= c(9,5,0), DR04 = c(5,4,0),DR05 = c(5,4,0),DR06 = c(5,4,0),DR07 = c(5,4,0),DR08 = c(5,4,0)), class = "data.frame", row.names = c(NA, -3L)) return(df1) } f1 <- function(df1, dmda, CategoryChosse) { # 新增:判断所选日期是否存在于数据库中 target_date <- ymd(dmda) exist_dates <- ymd(df1$date2) if (!target_date %in% exist_dates) { # 获取所选日期对应的星期 target_week <- weekdays(target_date) # 计算对应Category和星期的m值 m <- df1 %>% group_by(Category,Week) %>% summarize(across(starts_with("DR1"), mean),.groups = "drop") %>% filter(Week == target_week, Category == CategoryChosse) %>% pull(DR1) # 绘制空画布 plot(1, type = "n", xlim = c(0, 10), ylim = c(0, max(m + 10, 35)), xaxs = 'i', xlab = "Days", ylab = "Numbers", main = paste0(dmda, "-", CategoryChosse, " (无匹配数据)")) # 执行要求的水平线逻辑 abline(h=m,lwd=2) points(0, m, col = "red", pch = 19, cex = 2, xpd = TRUE) text(.1,m+ .5, round(m,1), cex=1.1,pos=4,offset =1,col="black") return() } x<-df1 %>% select(starts_with("DR0")) x<-cbind(df1, setNames(df1$DR1 - x, paste0(names(x), "_PV"))) PV<-select(x, date2,Week, Category, DR1, ends_with("PV")) med<-PV %>% group_by(Category,Week) %>% summarize(across(ends_with("PV"), median),.groups = "drop") SPV<-df1%>% inner_join(med, by = c('Category', 'Week')) %>% mutate(across(matches("^DR0\\d+$"), ~.x + get(paste0(cur_column(), '_PV')), .names = '{col}_{col}_PV')) %>% select(date1:Category, DR01_DR01_PV:last_col()) SPV<-data.frame(SPV) mat1 <- df1 %>% filter(date2 == dmda, Category == CategoryChosse) %>% select(starts_with("DR0")) %>% pivot_longer(cols = everything()) %>% arrange(desc(row_number())) %>% mutate(cs = cumsum(value)) %>% filter(cs == 0) %>% pull(name) dropnames <- paste0(mat1,"_",mat1, "_PV") SPV <- SPV %>% filter(date2 == dmda, Category == CategoryChosse) %>% select(-any_of(dropnames)) if(length(grep("DR0", names(SPV))) == 0) { SPV[head(mat1,10)] <- NA_real_ } datas <-SPV %>% filter(date2 == ymd(dmda)) %>% group_by(Category) %>% summarize(across(starts_with("DR0"), sum),.groups = "drop") %>% pivot_longer(cols= -Category, names_pattern = "DR0(.+)", values_to = "val") %>% mutate(name = readr::parse_number(name)) colnames(datas)[-1]<-c("Days","Numbers") datas <- datas %>% group_by(Category) %>% slice((as.Date(dmda) - min(as.Date(df1$date1) [ df1$Category == first(Category)])):max(Days)+1) %>% ungroup m<-df1 %>% group_by(Category,Week) %>% summarize(across(starts_with("DR1"), mean),.groups = "drop") m<-subset(m, Week == df1$Week[match(ymd(dmda), ymd(df1$date2))] & Category == CategoryChosse)$DR1 maxrange <- range(min(0, datas$Numbers, na.rm = TRUE), na.rm = TRUE) maxrange[2] <- maxrange[2] - (maxrange[2] %%10) + 35 max<-max(0, datas$Days, na.rm = TRUE)+1 plot(Numbers ~ Days, xlim= c(0,max), ylim= c(0,maxrange[2]), xaxs='i',data = datas,main = paste0(dmda, "-", CategoryChosse)) if (nrow(datas)<=2){ abline(h=m,lwd=2) points(0, m, col = "red", pch = 19, cex = 2, xpd = TRUE) text(.1,m+ .5, round(m,1), cex=1.1,pos=4,offset =1,col="black")} else if(any(table(datas$Numbers) >= 3) & length(unique(datas$Numbers)) == 1){ yz <- unique(datas$Numbers) lines(c(0,datas$Days), c(yz, datas$Numbers), lwd = 2) points(0, yz, col = "red", pch = 19, cex = 2, xpd = TRUE) text(.1,yz+ .5,round(yz,1), cex=1.1,pos=4,offset =1,col="black")} else{ mod <- nls(Numbers ~ b1*Days^2+b2,start = list(b1 = 0,b2 = 0),data = datas, algorithm = "port") new.data <- data.frame(Days = with(datas, seq(min(Days),max(Days),len = 45))) new.data <- rbind(0, new.data) lines(new.data$Days,predict(mod,newdata = new.data),lwd=2) coef<-coef(mod)[2] points(0, coef, col="red",pch=19,cex = 2,xpd=TRUE) text(.99,coef + 1,max(0, round(coef,1)), cex=1.1,pos=4,offset =1,col="black") } } ui <- fluidPage( shiny::navbarPage(theme = shinytheme("flatly"), collapsible = TRUE, br(), tabPanel("", sidebarLayout( sidebarPanel( uiOutput("date"), uiOutput("mycode"), br(), ), mainPanel( tabsetPanel( tabPanel("", plotOutput("graph",width = "100%", height = "600") ) ), )) ))) server <- function(input, output,session) { data <- reactive(function.test()) output$date <- renderUI({ req(data()) # 修正:日期范围改为覆盖date2的实际范围,避免禁用逻辑失效 all_dates <- seq(min(ymd(data()$date2))-3, max(ymd(data()$date2))+3, by = "day") disabled <- as.Date(setdiff(all_dates, as.Date(data()$date2)), origin = "1970-01-01") dateInput(input = "date2", label = h4("Data"), min = min(data()$date2), value = min(data()$date2), format = "dd-mm-yyyy", datesdisabled = disabled) }) output$mycode <- renderUI({ req(input$date2) df1 <- data() # 新增:如果所选日期不存在,也显示可选Category,避免下拉框为空 df2 <- df1[as.Date(df1$date2) %in% input$date2,] if(nrow(df2) == 0) { choices = unique(df1$Category) } else { choices = unique(df2$Category) } selectInput("code", label = h4("Category"),choices=choices) }) output$graph <- renderPlot({ req(input$date2,input$code) f1(data(),as.character(input$date2),as.character(input$code)) }) } shinyApp(ui = ui, server = server)
修改说明
- 修正了原代码中日历组件日期范围设置错误的问题,原范围设置为2021年1月,和实际date2的7月范围不匹配,导致禁用逻辑完全失效
- 在f1函数开头新增了日期存在性兜底判断,即使后续数据库更新或者前端逻辑有漏洞,只要所选日期不存在就会直接生成仅含m值水平线的图表
- 优化了Category下拉框的逻辑,日期不存在时也会显示所有可选的Category,保证界面交互正常
- 新增了无数据时图表标题的提示,方便用户识别当前状态
内容的提问来源于stack exchange,提问作者Antonio
相关产品推荐
相关产品推荐

