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

Shiny中数值重编码异常:小数无法转换问题求助

问题分析与解决方案

Hey there, let's break down why your recoding isn't working for decimal values and fix it step by step:

Key Issues Causing the Problem

  • Floating-Point Precision Quirks: When you calculate the mean with apply, decimal values like 0.17 aren't stored as exact numbers under the hood—they might be something like 0.1700000000001 or 0.1699999999999. Since recode does exact matches only, it can't recognize these approximate values.
  • Nested Reactives: You're nesting reactive expressions inside other reactives (like BSI_dat <- reactive(subset(datensatz_patient(), ...)) where datensatz_patient is already reactive). This creates unnecessary complexity and can trigger unexpected re-renders.
  • Syntax & Parameter Mistakes: The as.numeric = TRUE in your apply call is invalid (the mean function doesn't take that argument), and your original code has unclosed parentheses that would crash Shiny before it even runs.

How to Fix It

Let's tackle each issue one by one:

  1. Handle Floating-Point Matching:
    Instead of matching exact decimals, either round your calculated means to a fixed number of decimal places first, or use case_when (from dplyr) to handle matches more robustly. Rounding eliminates the tiny precision errors that break exact matches.

  2. Clean Up Reactive Logic:
    Avoid nesting reactives. Each reactive should handle a single, clear step (read data, subset columns, calculate means, recode) to keep your code maintainable.

  3. Fix Syntax Errors:
    Close all missing parentheses, and replace the inefficient apply call with rowMeans (it's faster and designed specifically for row-wise mean calculations).

Corrected Full Code

library(shiny)
library(dplyr) # Required for recode/case_when

ui <- fluidPage(
  fileInput("datensatz", "Upload Your Data File"),
  tableOutput("test8.1"),
  tableOutput("test8.2")
)

server <- function(input, output) {
  # Step 1: Read uploaded data (only runs after file is uploaded)
  datensatz_patient <- reactive({
    req(input$datensatz) # Ensure file is uploaded before proceeding
    read.table(
      input$datensatz$datapath, 
      header = TRUE, 
      strip.white = TRUE, 
      stringsAsFactors = FALSE, 
      sep = ";",
      dec = ",", 
      na = -77
    )
  })
  
  # Step 2: Extract BSI columns
  BSI_dat <- reactive({
    req(datensatz_patient())
    subset(datensatz_patient(), select = c(Base_BSI_v1:Base_BSI_v53))
  })
  
  # Step 3: Extract somatization subscale columns
  BSI.sub_soma <- reactive({
    req(BSI_dat())
    subset(BSI_dat(), select = c(2, 7, 23, 29, 30, 33, 37))
  })
  
  # Step 4: Calculate row means (use rowMeans for efficiency)
  BSI.soma.SW <- reactive({
    req(BSI.sub_soma())
    # Ensure all columns are numeric first
    soma_numeric <- as.data.frame(lapply(BSI.sub_soma(), as.numeric))
    rowMeans(soma_numeric, na.rm = TRUE)
  })
  
  # Step 5: Recode values (fix floating-point issue with rounding)
  BSI.soma.T_m <- reactive({
    req(BSI.soma.SW())
    # Round to 2 decimal places to eliminate precision errors
    rounded_means <- round(BSI.soma.SW(), digits = 2)
    
    # Option 1: Using recode (works after rounding)
    recode(
      rounded_means,
      `0` = 41, 
      `0.17` = 50, 
      `1` = 60, 
      `1.5` = 70, 
      `2` = 80, 
      `3.71` = 112
    )
    
    # Option 2: Using case_when (more flexible for complex rules)
    # case_when(
    #   rounded_means == 0 ~ 41,
    #   rounded_means == 0.17 ~ 50,
    #   rounded_means == 1 ~ 60,
    #   rounded_means == 1.5 ~ 70,
    #   rounded_means == 2 ~ 80,
    #   rounded_means == 3.71 ~ 112,
    #   TRUE ~ rounded_means # Keep unmodified values as-is
    # )
  })
  
  # Output results as tables
  output$test8.1 <- renderTable(data.frame(Original_Mean = BSI.soma.SW()))
  output$test8.2 <- renderTable(data.frame(Recoded_Value = BSI.soma.T_m()))
}

shinyApp(ui, server)

Extra Tips

  • rowMeans is way more efficient than apply(..., 1, mean) for calculating row averages—always prefer it when possible.
  • If your input data might have non-numeric columns (even if you set stringsAsFactors = FALSE), explicitly converting to numeric with as.numeric ensures your mean calculations are correct.
  • case_when is a better choice than recode if you ever need to handle ranges (e.g., "values between 0 and 0.2 get 50") instead of exact matches.

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 04:04:13