Any help with this would be greatly appreciated. I am optimising parameters of a lognormal distribution so that the proportion of estimates matches a set of target values (distances). The proportions are calculated using the following functions:
adj_sumifs <- function(sum_array, condition_array, f, m=1){
n <- length(condition_array)
sm = 0
if (n == length(condition_array)){
fun <- function(x,i){if (f (condition_array[i])){sum_array[i] + x}else{x} }
sm <- Reduce(fun,1:n,0)
}
ifelse(m <= 0, sm , sm/m)
}
and
estimate.inrange <- function(vals,dist,lower,upper,total){
n <- length(lower)
if (n == length(upper)){
sapply(1:n, function(i){ ifelse(i < n ,
adj_sumifs(vals,dist, (function(x) x >= lower[i] && x < upper[i]),total) ,
adj_sumifs(vals,dist, (function(x) x >= lower[i]) , total)
) }
)
}else{
# for a failure in the process
as.numeric()
}
}
And the function I would like to optimise is:
calculate_Det_ptns <- function(alpha, beta, pxa, low,up, distances, eF){
temp <- numeric()
if ( length(pxa) == length(distances) && length(low) == length(up) )
{
ln_values <- as.numeric(Map(function(pa,d) eF * pa * dlnorm(d, meanlog = alpha, sdlog = beta),pxa,distances))
temp <- estimate.inrange (ln_values,distances,low,up, total = sum(ln_values))
}
temp
}
Optimisation is done using the Levenberg-Marquardt algorithm
lnVals <- nlsLM(target ~ calculate_Det_ptns(alpha = a,beta = b, pxa = odab,low = low, up = up, distances = dist, eF = expF),
start = list(a = mu, b = sd ),
trace = T)
where up,low and target are extracted from the same data file, e,g,
low, up, target
1,2,0.1
2,3,0.4
3,4,0.6
4,5,0.6
5,6,0.9
while odab and distance are vectors of arbitrary lengths (usually much longer than target,etc). The process works well when the target file has anout 150 rows, and distances and odab have about 500000 values. However, for reasons I cannot fathom, is fails when the target file has about 16 rows. The error message is:
Error in model.frame.default(formula = ~target + odab + low + up + dist) :
variable lengths differ (found for 'odab')
which suggests that the function is not being evaluated in the formula. Can anyone suggest a solution or explanation? It is important that the proportions are re-estimated for every new mu and sd.
You could try surrounding the function with I(), which will evaluate it as is before evaluating the formula; however, I could not replicate your problem with the code provided because I am missing some of the referenced objects (a, b, odab, dist, expF, mu, sd) so I could not confirm whether or not this works.
nVals <- nlsLM(target ~ I(calculate_Det_ptns(alpha = a,beta = b, pxa = odab,low = low, up = up, distances = dist, eF = expF)), start = list(a = mu, b = sd ), trace = T)
Related
So I'm trying to process the data ("final_ts") in order to make a forecast for the time series using kernel functions and doing parallel computing. The analysis is made this way: 1) analyse the empirical time series, 2) run a standard variable
selection to subset only those variables and their time-lags that provide the best validation error. 3) Run the same analysis described above to choose the optimal regularization parameters and kernel function of the regularized algorithm. As a result I show the RMSE (root mean squared error).
The code is not mine, and I'm trying to set it without problems, but as I do not have so much experience, I can't understand how to solve (I've spent 2 days trying to find the solution so if you help me I would be so grateful) the problem which occured: "The object '....' not found".
So the MAIN CODE looks like this (I'll try to make explanation on what happens, please don't judge me):
#download the libraries:
rm(list=ls(all=TRUE))
suppressMessages(library(Matrix))
suppressMessages(library(quantreg))
suppressMessages(library(parallel))
suppressMessages(library(compiler))
suppressMessages(library(lars))
suppressMessages(library(elasticnet))
suppressMessages(library(caret))
options(warn=-1)
#####################################################################################
#Download the sources (I'll show them in the end as a code)
###########Take the file###
ShowPlot = FALSE
lags = TRUE
ModelName = 'final_ts'
FileName = paste(ModelName, '.txt', sep = '')
########Calculate the logspace and the standard error functions #################
logspace <- function(d1, d2, n) exp(log(10)*seq(d1, d2, length.out=n))
std_err <- function(x) sd(x)/sqrt(length(x))
############# Choose the kernel function: we have 4 of them, but we try the second one #####
Kernel.Options = c('Exponential.Kernel', 'Epanechnikov.Kernel', 'TriCubic.Kernel', 'Matern.Kernel')
Regression.Kernel = Kernel.Options[1]
############# Choose the parameters for cross validation ####
lambda = logspace(-3,0,15)
if(Regression.Kernel == 'Exponential.Kernel'){
tht = seq(from = 0., to = 10, length = 30)
}else{
tht = seq(from = 0.1, to = 3, length = 20)
}
parameters_on_grid = expand.grid(tht, lambda)
### Read Time series
d = as.matrix(read.table(FileName, header= T))
######################
original.Embedding = c('Pro', 'Syn','Piceu')
original.TargetList = original.Embedding
d = d[, original.Embedding]
#### Here you take combinations of lags (best lag are 1 - 2) #####
x.lag = 1; y.lag = 2; z.lag = 1
sp.lag.selection = c(x.lag, y.lag, z.lag)
lagged.time.series = make.lagged.ts(d, sp.lag.selection)
d = lagged.time.series$time.series
original.col = lagged.time.series$original.variables
if(lags == TRUE){ var.sel = original.col; }else{ var.sel = colnames(d)}
##### Names and embedding in the laged dataset
if(lags == TRUE){ colnames(d) = Embedding = TargetList = LETTERS[1:ncol(d)]}else{
Embedding = TargetList = original.Embedding
}
##### length of training and test set (2 points for testing, 28 - for training)
length.testing = 2
length.training = nrow(d) - length.testing
#### Preserve training for the interactions
ts.train.preserved = d[1:length.training, var.sel]
std.ts.train = Standardizza(ts.train.preserved)
#### Preserve testing for the test (you want your algorithm to learn the real structure of the model)
ts.test.preserved = d[(length.training + 1):nrow(d), var.sel]
#### Training set:
d.training = Standardizza(d[1:length.training, ])
#### You now need to standardize the test set using mean and sd of the training set
d.testing = Standardizza.test(ts.test.preserved,ts.train.preserved)
############## Prepare for parallel computing
Lavoratori = detectCores() - 2
cl <- parallel::makeCluster(Lavoratori, setup_strategy = "sequential")
####
RegressionType = ELNET_fit_
alpha = 0.85
### should you compute all the variables or not?
BestModel = BestModelLOOCV(cl, d.training, TargetList, Embedding, parameters_on_grid, RegressionType,alpha)
I also found a fourmula for calculation of kernel function (it may be put in a SPECIAL r (SOURCE 1)):
Exponential.Kernel <- function(dst, theta){
dbar <- mean(dst)
krnl <- exp(-theta*dst/dbar)
return(krnl)
}
Exponential.Kernel <- cmpfun(Exponential.Kernel)
The formulas for finding the best leave-one-out cross-validation parameters is below (SOURCE 2):
########################### Cross Validation (Leave-one-out) ##################################
BestModelLOOCV <- function(cl, X, TargetList, Embedding, grid, RegressionType,alpha){
mine_output = Jacobian_(cl, X, TargetList, Embedding, grid, RegressionType,alpha)
theta_opt = mine_output$th
lambda_opt = mine_output$lm
mine_c0 = mine_output$c0
mine_output = mine_output$J
J_ = list()
C0_ = do.call(cbind, lapply(1:ncol(X), function(x, M) unlist(M[[x]]), mine_c0))
colnames(C0_) = sapply(TargetList,function(x) paste("c0_", x, sep = ""))
for(k in 1:(nrow(X) - 1)){
J_[[k]] = do.call(rbind, lapply(1:ncol(X), function(x, M, i) unlist(M[[x]][i,]), mine_output, k))
rownames(J_[[k]]) = Embedding
colnames(J_[[k]]) = Embedding
}
BestCoefficients = list()
BestCoefficients$J = J_
BestCoefficients$c0 = C0_
BestParameters = list()
BestParameters$BestTH = theta_opt
BestParameters$BestLM = lambda_opt
return(list(BestCoefficients = BestCoefficients, BestParameters = BestParameters))
}
#####Compute the jacobian
Jacobian_ <- function(cl, X, TargetList, Embedding, grid, RegressionType,alpha){
J = c0 = list()
th = lm = c()
n_ = 1
FUN = match.fun(RegressionType)
for(trg in TargetList){
RegularizedParameters <- LOOCrossValidation(cl, X, trg, Embedding, grid, RegressionType,alpha)
########## Now compute the optimum regularized coefficients
J[[n_]] = FUN(X, trg, Embedding, RegularizedParameters$BestTH, RegularizedParameters$BestLM,alpha)
th = c(th, RegularizedParameters$BestTH)
lm = c(lm, RegularizedParameters$BestLM)
c0[[n_]] = J[[n_]]$c0
J[[n_]] = J[[n_]][-1]
n_ = n_ + 1
}
return(list(J = J, c0 = c0, th = th, lm = lm))
}
In order to compute the elastic-net regularization function you may use this formula (SOURCE 3):
ELNET_fit_ <- function(time.series, targ_col, Embedding, theta, lambda,alp){
Edim <- length(Embedding)
coeff_names <- sapply(colnames(time.series),function(x) paste("d", targ_col, "d", x, sep = ""))
block <- cbind(time.series[2:dim(time.series)[1],targ_col],time.series[1:(dim(time.series)[1]-1),])
block <- as.data.frame(apply(block, 2, function(x) (x-mean(x))/sd(x)))
lib <- 1:dim(block)[1]
pred <- 1:dim(block)[1]
coeff <- array(0,dim=c(length(pred),Edim + 1))
colnames(coeff) <- c('c0', coeff_names)
coeff <- as.data.frame(coeff)
for (ipred in 1:length(pred)){
libs = lib[-pred[ipred]]
q <- matrix(as.numeric(block[pred[ipred],2:dim(block)[2]]),
ncol=Edim, nrow=length(libs), byrow = T)
distances <- sqrt(rowSums((block[libs,2:dim(block)[2]] - q)^2))
### Kernel
Krnl = match.fun(Regression.Kernel)
Ws = Krnl(distances, theta)
############ Fit function
x = as.matrix(block[libs,2:dim(block)[2]])
y = as.matrix(block[libs,1])
x = x[seq_along(y), ]
y = y[seq_along(y)]
Ws = Ws[seq_along(y)]
x = Ws * cbind(1, x)
y = Ws * y
fit <- enet(x, y, lambda = lambda, normalize = TRUE, intercept = FALSE)
coeff[ipred,] <- predict(fit, s = alp, type="coefficients", mode="fraction")$coefficients
}
return(coeff)
}
ELNET_fit_ <- cmpfun(ELNET_fit_)
The auxiliary formulas for computation are as follows (SOURCE 4):
TakeLag <- function(X, species.to.lag, num.lag){
tmp = matrix(0, nrow(X), num.lag)
tmp[,1] = X[,species.to.lag]
tmp[1, 1] = NA
tmp[2:nrow(X), 1] = X[1:(nrow(X) - 1), species.to.lag]
if(num.lag > 1){
for(lag. in 2:num.lag){
tmp[,lag.] = X[,species.to.lag]
tmp[1, lag.] = NA
tmp[2:nrow(X), lag.] = tmp[1:(nrow(tmp) - 1), lag.-1]
}
}
tmp
}
make.lagged.ts <- function(X,sp.lag.selection ){
### X = time series
### sp.lag is a vector whose entry are the lags of each variable
### e.g., sp.lag = c(x.lag, y.lag, ..., u.lag)
s = list()
for(i in 1:length(sp.lag.selection)){
Lag.sp = TakeLag(X, original.Embedding[i], sp.lag.selection[i])
s[[i]] = cbind(X[,original.Embedding[i]], Lag.sp)
}
X = do.call(cbind,s)
### Remove the NA
X = X[-c(1:max(sp.lag.selection)),]
### Save the position of the unlagged variables
original.col = c()
for(k in 1:length(sp.lag.selection)){
if(k == 1){ original.col = c(original.col, 1)}else{
num.lags = sum(unlist(lapply(1:(k-1), function(x,X) X[x], sp.lag.selection)))
original.col = c(original.col, k + num.lags )
}
}
return(list(time.series = X, original.variables = original.col))
}
take.coeff <- function(X, col.to.extract, original.emb){
### To use when prediction are made using lagged variables
### Take as input the sequence X of Jacobian along the attractor
### and the species to look at
### return a new sequence of Jacobian of the interaction among those species
m = lapply(1:length(X$J), function(t, M, specie) M$J[[t]][specie,specie],
X, col.to.extract)
for(i in 1:length(m)){
colnames(m[[i]]) = rownames(m[[i]]) =original.emb
}
return(m)
}
Standardizza <- function(X){
### This return y = (x-meanx)/stdx
for(i in 1:ncol(X)){
X[,i] = (X[,i]- mean(X[,i]))/sd(X[,i])
}
return(X)
}
Standardizza.test <- function(X, Y){
### X = test set
### Y = training set
### This return y = (x-meanY)/stdY
for(i in 1:ncol(X)){
X[,i] = (X[,i]- mean(Y[,i]))/sd(Y[,i])
}
return(X)
}
###########################
#### Here you compute the quality of the forecast as mean correlation coefficient
The problem in the main code sounds like: object 'Regression.Kernel' not found, but I see it in the code, it's written. Maybe the problem is connected with the type of it? But if I take away the quotes in order to make it a "closure", I cannot impose the function restrictions.
Please, help me if you can as I don't know how to solve.
The original dataset ("final_ts.txt"):
decy Temp CTD_S OxFix Pro Syn Piceu Naneu
2011.74221 27.60333 36.20700 27.26667 58638.33333 13107.00000 799.66667 117.66667
2011.74401 26.97950 36.13400 27.05000 71392.50000 13228.50000 1149.00000 116.50000
2011.74617 24.99750 35.34450 24.80000 264292.00000 27514.00000 2434.50000 132.50000
2011.74692 24.78400 35.25800 25.82500 208996.50000 39284.00000 3761.75000 220.75000
2011.74774 27.34225 35.86800 27.82500 114617.25000 23115.00000 2337.00000 139.75000
2011.74950 26.47875 36.18175 27.20000 97008.00000 9775.75000 855.50000 77.50000
2011.75583 26.86500 36.14575 27.47500 76255.00000 10226.75000 783.00000 99.50000
2011.75654 27.04550 36.04950 27.60000 95017.75000 10546.25000 915.25000 77.75000
2011.75962 27.06567 36.46367 26.56667 75750.00000 10194.33333 687.00000 44.00000
2011.76101 27.44700 36.48150 27.90000 38556.50000 8204.75000 791.25000 118.75000
2011.76169 27.32325 36.50075 27.80000 29848.50000 8995.50000 727.00000 159.25000
2011.76245 26.87050 36.57350 26.40000 36323.50000 10897.00000 792.00000 87.50000
2011.76349 27.43900 36.89325 27.90000 17308.50000 9678.50000 559.00000 149.00000
2011.77171 26.74050 36.90550 26.10000 20976.50000 7516.00000 489.50000 41.50000
2011.77224 26.53500 36.77500 27.22500 27229.00000 7578.00000 606.75000 159.50000
2011.77288 26.65450 36.78500 27.32500 37897.50000 10493.50000 1008.75000 209.50000
2011.77444 27.24150 36.73800 26.80000 15551.00000 8159.50000 479.00000 70.50000
2011.77505 26.67560 36.74240 27.30000 27887.80000 5290.80000 510.00000 101.20000
2011.77568 27.65125 36.69225 28.10000 12850.00000 9944.75000 640.75000 120.00000
2011.77693 28.11500 36.32750 27.85000 5694.00000 10288.50000 507.00000 32.00000
2011.77751 28.61950 36.26325 28.72500 20486.75000 10465.00000 430.50000 82.75000
2011.77814 28.60425 36.23100 28.70000 27974.50000 6977.25000 554.00000 80.50000
2011.77968 28.47200 35.69000 28.40000 126778.00000 2840.00000 537.00000 27.00000
2011.78087 28.89400 35.60650 28.35000 49250.00000 5533.00000 1004.00000 5.50000
2011.78190 28.74100 35.46200 28.80000 35698.00000 1298.00000 308.00000 23.00000
2011.78713 28.80500 35.50100 28.70000 99450.00000 5410.00000 637.50000 50.50000
2011.78887 28.39250 35.90900 28.25000 116562.00000 3758.50000 582.50000 60.00000
2011.79078 28.10550 36.40150 28.20000 13403.00000 11285.00000 472.00000 73.50000
2011.79261 27.25650 36.78350 27.45000 11205.00000 10576.00000 630.00000 74.00000
Please, help if you have any guess as I don't have an idea what has gone wrong.
I have an R programm for a regression that somehow gives me an error message that I do not understand. The regression model takes as input heat input heat data (Q_htg) and the corresponding temperature data (T_amb) and then builds a linear regression for those two variables. Afterwards I want to use the trained regression model to predict some outputs. Here is the code:
dalinearPowerScaling2.function <-
function(Dataset,
numberOfDaysForAggregation,
normOutsideTemperature) {
heatingPower <- Dataset$Q_htg
outSideTemperature <- Dataset$T_amb
aggregationLevel <- numberOfDaysForAggregation * 1440
index <- 0
meanValuesOutsideTemperature <-
vector(, length(outSideTemperature) / aggregationLevel)
for (i in seq(1, length(outSideTemperature), aggregationLevel)) {
sum <- 0
for (j in seq(i, i + aggregationLevel - 1, 1)) {
sum <- sum + outSideTemperature[j]
}
index <- index + 1
meanValuesOutsideTemperature[index] <- sum / aggregationLevel
}
index <- 0
meanValuesHeatingDemand <-
vector(, length(heatingPower) / aggregationLevel)
for (i in seq(1, length(heatingPower), aggregationLevel)) {
sum <- 0
for (j in seq(i, i + aggregationLevel - 1, 1)) {
sum <- sum + heatingPower[j]
}
index <- index + 1
meanValuesHeatingDemand[index] <- sum / aggregationLevel
}
linearModel <-
lm(meanValuesHeatingDemand ~ meanValuesOutsideTemperature)
abline(linearModel, col = "red")
pred <- predict(linearModel, data.frame(meanValuesOutsideTemperature = c(normOutsideTemperature)))
List<-list(meanValuesHeatingDemand, meanValuesOutsideTemperature)
List2 <- vector("list", length(heatingPower)/aggregationLevel)
for (i in seq(1, length(meanValuesHeatingDemand),1)){
List2 [[i]]<-c(meanValuesHeatingDemand[i], meanValuesOutsideTemperature[i])
}
List3<-List2[order(sapply(List2, function(x) x[1], simplify=TRUE), decreasing=FALSE)]
firstTemperatureWithHeatingDemand<-0
firstHeatingDemand<-0
for (i in seq(1, length(List3), 1)) {
if(List3[[i]][1]>0) {
firstTemperatureWithHeatingDemand<-List3[[i]][2]
firstHeatingDemand<-List3[[i]][1]
break}
}
regression2ValuesX <- vector(, 5)
regression2ValuesY <- vector(, 5)
regression2ValuesX [1] <- firstTemperatureWithHeatingDemand
regression2ValuesY [1] <-firstHeatingDemand
List3<-List2[order(sapply(List2, function(x) x[1], simplify=TRUE), decreasing=TRUE)]
for (i in seq(1, length(regression2ValuesX) - 1, 1)) {
regression2ValuesX[i + 1]<-List3[[i]][2]
regression2ValuesY[i + 1]<-List3[[i]][1]
}
plot(regression2ValuesX, regression2ValuesY)
linearModel2 <-
lm(regression2ValuesY ~ regression2ValuesX)
abline(linearModel2, col = "blue")
pred <- predict(linearModel2, data.frame(regression2ValuesX = c(normOutsideTemperature)))
paste("Predicted heating demand:", round(pred))
}
When I run with the command
linearPowerScaling2.function(data_heat_test, 1, -12)
I get the error message:
Error in int_abline(a = a, b = b, h = h, v = v, untf = untf, ...) :
plot.new has not been called yet
3.
int_abline(a = a, b = b, h = h, v = v, untf = untf, ...)
2.
abline(linearModel, col = "red") at LinearPowerScaling2_Function.R#33
1.
linearPowerScaling2.function(data_heat_test, 1, -12)
The data itself should be okay. Can anyone tell me, what the problem is?
Without reproducible minimal example it's hard to test if this solves it, but the error message tells you that you are calling abline() before calling plot().
That's exactly what happens on line 33...
Hope this helps.
Check here to see how to make a minimal reproducible example.
#calculate NMI(c,t) c : cluster assignment , t : ground truth
NMI <- function(c,t){
n <- length(c) # = length(t)
r <- length(unique(c))
g <- length(unique(t))
N <- matrix(0,nrow = r , ncol = g)
for(i in 1:r){
for (j in 1:g){
N[i,j] = sum(t[c == i] == j)
}
}
N_t <- colSums(N)
N_c <- rowSums(N)
B <- (1/n)*log(t( t( (n*N) / N_c ) / N_t))
W <- B*N
I <- sum(W,na.rm = T)
H_c <- sum((1/n)*(N_c * log(N_c/n)) , na.rm = T)
H_t <- sum((1/n)*(N_t * log(N_t/n)) , na.rm = T)
nmi <- I/sqrt(H_c * H_t)
return (nmi)
}
Running this on some clustering benchmarks here gives me a value of the Normalized Mutual information . But , when I compare it with values of NMI obtained from the aricode library , I get values of NMI that generally differ in the second decimal place .
I will be grateful if someone is able to pin-point any possible error that has creeped into this code .
I am including a test case for this using a synthetic case :
library(aricode)
c <- c(1,1,2,2,2,3,3,3,3,4,4,4)
t <- c(1,2,2,2,3,4,3,3,3,4,4,2)
print(aricode::NMI(c , t)) #0.489574
print(NMI(c,t)) #0.5030771
This might be very late for an answer but for the sake of posterity:
The difference is in the way you and the aricode package normalise the index. You divide by sqrt() whereas aricode offers the following options:
function (c1, c2, variant = c("max", "min", "sqrt", "sum", "joint"))
so if you select variant = sqrt you should hopefully get the same answer.
The NMI package uses sum.
So, I have these functions:
funk1 <- function(a,x,l,r) {
x^2*exp(-(l*(1-exp(-r*a))/r))}
funk2 <- function(x,l,r) {
sapply(x, function (s) {
integrate(funk1, lower = 0, upper = s, x=s, l=l, r=r)$value })}
which are used to explain the data y in,
z <- data.frame(ts = 1:100,
y = funk2(1:100, l = 1, r = 1) + rpois(100, 1:100))
I wish to use optim to maximise the likelihood, so I defined a likelihood function:
LL_funk <- function(l,r) {
n=nrow(z)
R = sum((funk2(ts,l,r) - y)^2)
logl = -((n/2)*log(R))
return(-logl)
}
and I tried to fit using optim
fit <- optim(par=c(0.5,0.5), fn= LL_funk, method="Nelder-Mead")
But I get an error:
Error in integrate(funk1, lower = 0, upper = s, x = s, l = l, r = r) :
a limit is missing
I am not sure why? I could run nls fitting funk2(x,l,r) to y
nls(y ~ funk2(ts,l,r), data = z, start = list(l = 0.5, r = 0.5))
That means funk2 is working. I guess its the problem with LL function that I have designed, which I cant figure out!! Please Help!
Yup! There were two problems with your function. This worked for me:
LL_funk <- function(params) {
n=nrow(z)
l = params[1]
r = params[2]
R = sum((funk2(z$ts,l,r) - z$y)^2)
logl = -((n/2)*log(R))
return(-logl)
}
Previous issues:
LL_funk only takes 1 argument, which is the vector of parameters.
In LHS of the assignment of R, ts and y were not actually referring to columns in your dataset.
There is my data (x and y columns are relevant):
https://www.dropbox.com/s/b61a7enhoa0p57p/Simple1.csv
What I need is to fit the data with the polyline. Matlab code that does this is:
spline_fit.m:
function [score, params] = spline_fit (points, x, y)
min_f = min(x)-1;
max_f = max(x);
points = [min_f points max_f];
params = zeros(length(points)-1, 2);
score = 0;
for i = 1:length(points)-1
in = (x > points(i)) & (x <= points(i+1));
if sum(in) > 2
p = polyfit(x(in), y(in), 1);
pred = p(1)*x(in) + p(2);
score = score + norm(pred - y(in));
params(i, :) = p;
else
params(i, :) = nan;
end
end
test.m:
%Find the parameters
r = [100,250,400];
p = fminsearch('spline_fit', r, [], x, y)
[score, param] = spline_fit(p, x, y)
%Plot the result
y1 = zeros(size(x));
p1 = [-inf, p, inf];
for i = 1:size(param, 1)
in = (x > p1(i)) & (x <= p1(i+1));
y1(in) = x(in)*param(i,1) + param(i,2);
end
[x1, I] = sort(x);
y1 = y1(I);
plot(x,y,'x',x1,y1,'k','LineWidth', 2)
And this does work fine, producing following optimization: [102.9842, 191.0006, 421.9912]
I've implemented the same idea in R:
library(pracma);
spline_fit <- function(x, xx, yy) {
min_f = min(xx)-1;
max_f = max(xx);
points = c(min_f, x, max_f)
params = array(0, c(length(points)-1, 2));
score = 0;
for( i in 1:length(points)-1)
{
inn <- (xx > points[i]) & (xx <= points[i+1]);
if (sum(inn) > 2)
{
p <- polyfit(xx[inn], yy[inn], 1);
pred <- p[1]*xx[inn] + p[2];
score <- score + norm(as.matrix(pred - yy[inn]),"F");
params[i,] <- p;
}
else
params[i,] <- NA;
}
score
}
But I get very bad results:
> fminsearch(spline_fit,c(100,250,400), xx = Simple1$x, yy = Simple1$y)
$xval
[1] 100.1667 250.0000 400.0000
$fval
[1] 4452.761
$niter
[1] 2
As you can see, it stops after 2 iterations and doesn't produce good points.
I'll be very glad for any help in resolving this issue.
Also, if anyone knows how to implement this in C# using any free library, it will be even better. I know whereto get polyfit, but not fminsearch.
The problem here is that the likelihood surface is very badly behaved -- there are both multiple minima and discontinuous jumps -- which will make the results you get with different optimizers almost arbitrary. I will admit that MATLAB's optimizers are remarkably robust, but I would say that it's pretty much a matter of chance (and where you start) whether an optimizer will get to the global minimum for this case, unless you use some form of stochastic global optimization such as simulated annealing.
I chose to use R's built-in optimizer (which uses Nelder-Mead by default) rather than fminsearch from the pracma package.
spline_fit <- function(x, xx = Simple1$x, yy=Simple1$y) {
min_f = min(xx)-1
max_f = max(xx)
points = c(min_f, x, max_f)
params = array(0, c(length(points)-1, 2))
score = 0
for( i in 1:(length(points)-1))
{
inn <- (xx > points[i]) & (xx <= points[i+1]);
if (sum(inn) > 2)
{
p <- polyfit(xx[inn], yy[inn], 1);
pred <- p[1]*xx[inn] + p[2];
score <- score + norm(as.matrix(pred - yy[inn]),"F");
params[i,] <- p;
}
else
params[i,] <- NA;
}
score
}
library(pracma) ## for polyfit
Simple1 <- read.csv("Simple1.csv")
opt1 <- optim(fn=spline_fit,c(100,250,400), xx = Simple1$x, yy = Simple1$y)
## [1] 102.4365 201.5835 422.2503
This is better than the fminsearch results, but still different from the MATLAB results, and worse than them:
## Matlab results:
matlab_fit <- c(102.9842, 191.0006, 421.9912)
spline_fit(matlab_fit, xx = Simple1$x, yy = Simple1$y)
## 3724.3
opt1$val
## 3755.5 (worse)
The bbmle package offers an experimental/not very well documented set of tools for exploring optimization surfaces:
library(bbmle)
ss <- slice2D(fun=spline_fit,opt1$par,nt=51)
library(lattice)
A 2D "slice" around the optim-estimated parameters. The circles show the optim fit (solid) and the minimum value within each slice (open).
png("splom1.png")
print(splom(ss))
dev.off()
A 'slice' between the matlab and optim fits shows that the surface is quite rugged:
ss2 <- bbmle:::slicetrans(matlab_fit,opt1$par,spline_fit)
png("slice1.png")
print(plot(ss2))
dev.off()