R语言求解方程遇optimize函数报错:invalid function value排查
Hey there! Let's break down what's going wrong here and fix it step by step.
The Root Cause of Your Error
The "invalid function value in 'optimize'" error happens because optimize() expects your function f(x) to return a single scalar value (one number) every time it's called. But right now, your f(x) is calculating values for all 8016 rows of your data frame at once, returning a vector of 8016 numbers. The optimization function can't work with that—it needs a single value to evaluate how "good" the current x is.
The Fix: Process Rows One at a Time
Since each row in your data frame needs its own unique seuil value, you need to run the optimization individually for each row. Here's how to do that cleanly:
Step 1: Define a Row-Specific Function
First, create a function that takes a single row of your data, extracts the relevant variables, and runs the optimization for just that row. We'll also tweak the logic to prioritize finding the root (where your core expression equals 0) instead of minimizing the absolute value, since that's your actual goal:
find_seuil <- function(row) { # Pull variables from the current row sA1 <- row$shortA1 mA1 <- row$middleA1 tA1 <- row$timeA1 ms1 <- row$minshort1 mm1 <- row$minmiddle1 sB1 <- row$shortB1 mB1 <- row$middleB1 tB1 <- row$timeB1 # Define the core function we want to find the root of core_func <- function(x) { -(1 - sA1 - mA1) * (tA1)^(1 + x) - sA1 * (tA1 + ms1)^(1 + x) - mA1 * (tA1 + mm1)^(1 + x) + (1 - sB1 - mB1) * (tB1)^(1 + x) + sB1 * (tB1 + ms1)^(1 + x) + mB1 * (tB1 + mm1)^(1 + x) } # First try uniroot (more efficient if we can confirm a root exists in the interval) # Check if the function crosses zero in a reasonable interval (adjust if needed) low_val <- core_func(-10) high_val <- core_func(10) if (sign(low_val) != sign(high_val)) { # Use uniroot since we know there's a root in this range result <- uniroot(core_func, interval = c(-10, 10), tol = 1e-4) return(result$root) } else { # Fall back to optimize to find the minimum absolute value opt_result <- optimize(function(x) abs(core_func(x)), interval = c(-10, 10), maximum = FALSE, tol = 1e-4) return(opt_result$minimum) } }
Step 2: Apply the Function to Every Row
Now use apply() (base R) or pmap_dbl() (tidyverse) to run this function on each row of your data frame:
Base R Approach:
# Apply the function row-by-row (margin = 1 means "rows") d$seuil <- apply(d, 1, find_seuil)
Tidyverse Approach (if you use purrr):
library(purrr) # pmap_dbl passes each row's columns as arguments to find_seuil d$seuil <- pmap_dbl(d, find_seuil)
Key Notes to Avoid Future Issues
- Shrink Your Interval: Your original interval
c(-10000, 10000)is way too large—it can cause numerical overflow (e.g.,(small_number)^(1 + 10000)becomes 0,(large_number)^(1 + 10000)becomes infinity). Start with a reasonable range likec(-10, 10)and adjust only if some rows don't find a solution there. - Why
unirootis Better: Since you're looking for a solution where your expression equals 0,uniroot()is more efficient than minimizing the absolute value—if you can confirm the function crosses zero in your interval (which you said is guaranteed, so this should work for most rows). - Vectorization Pitfalls: R loves vectorization, but optimization functions like
optimize()anduniroot()are designed for scalar inputs/outputs. Always wrap them in a row-wise operation when working with data frames.
内容的提问来源于stack exchange,提问作者Timothée David

