Reputation: 67
I'm trying to find a method in R which allows the parameters (a, b, c) of the general equation a*b^x + c which provides the best fit to 3 constrained random coordinates/points (p1, p2, p3 - with coordinates x1/y1, x2/y2 and x3/y3 respectively).
The constraints to these coordinates are:
I want to find a method which is able to generate values for a, b, and c which produces a line close to p1, p2 and p3. This is simply using desmos (see here for an example - https://www.desmos.com/calculator/4lmgazmrko) but I haven't been able to find a solution in R. I've tried the following:
x <- c(0, 0.7, 0.9)
y <- c(0.9, 0.8, 0)
df_test <- as.data.frame(cbind(x, y))
predict_y_nonlinearly <- function(beta, x){
beta[1]*(beta[2]^x) + beta[3]
}
a_nonlinearmodel <- function(beta, x, y){
y_hat <- predict_y_nonlinearly(beta, x)
sum((y-y_hat)^2)
}
beta <- optim(rnorm(3), a_nonlinearmodel, method = "SANN",
y = df_test$y, x = df_test$x)$par
predict_y_nonlinearly(beta, df_test$x)
But the optimisation function appears to get stuck in local minima, and rarely produces the correct solution (even when a different 'method' setting is used). I'm aware of the nls function, but this requires starting values to be chosen - I'd prefer a method which does not require manual input at this stage (as the desmos method is able to achieve).
Thanks
Upvotes: 0
Views: 201
Reputation: 226162
Given the two zero constraints, we can reduce this analytically to a one-parameter problem:
x1 = 0 → y1 = a + c → c = y1-a
y3 = 0 → 0 = a*b^x3 + (y1-a)
→ a*(b^x3 - 1) = -y1
→ a = y1/(1-b^x3)
So we have a one-parameter function that predicts y
, incorporating the x1 = y3 = 0
constraints:
predfun <- function(b = 1, x, y) {
a <- y[1]/(1-b^x[3])
c <- y[1] - a
a*b^x +c
}
A sum-of-squares target function:
target <- Vectorize(function(b) sum((y - predfun(b, x, y))^2))
Visualize:
curve(target, from = -10000, to = 100000, log = "y")
Now use optimize()
for 1D optimization (we still need to specify a starting interval, although not a specific starting point).
optimize(target, c(-10000, 1000000))
Results:
$minimum
[1] 58928.93
$objective
[1] 2.066598e-20
Upvotes: 2