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 like0.1700000000001or0.1699999999999. Sincerecodedoes 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(), ...))wheredatensatz_patientis already reactive). This creates unnecessary complexity and can trigger unexpected re-renders. - Syntax & Parameter Mistakes: The
as.numeric = TRUEin yourapplycall is invalid (themeanfunction 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:
Handle Floating-Point Matching:
Instead of matching exact decimals, either round your calculated means to a fixed number of decimal places first, or usecase_when(from dplyr) to handle matches more robustly. Rounding eliminates the tiny precision errors that break exact matches.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.Fix Syntax Errors:
Close all missing parentheses, and replace the inefficientapplycall withrowMeans(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
rowMeansis way more efficient thanapply(..., 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 withas.numericensures your mean calculations are correct. case_whenis a better choice thanrecodeif you ever need to handle ranges (e.g., "values between 0 and 0.2 get 50") instead of exact matches.
内容的提问来源于stack exchange,提问作者lderlu

