nlm с несколькими переменными в R

Я пытаюсь использовать nlm(), чтобы минимизировать SSE в функции. У меня проблемы с синтаксисом и получением nlm() оценок для обеих переменных. В конце концов, t_1, t_2 и t_3 будут значениями, извлеченными из data.frame, но я просто присваивал им номера, пока не смог заставить nlm() работать. Я пробовал решения из этих потоков там и там, без везения:

t_1 <- 1.91
t_2 <- 3.23
t_3 <- 4.20

fun <- function(s,y){
  (10 - s*(t_1-y + y*exp(-t_1/y)))^2+
    (20 - s*(t_2-y + y*exp(-t_2/y)))^2+
    (30 - s*(t_3-y + y*exp(-t_3/y)))^2
}

## Testing the function
fun(9.57,1.13)
[1] 0.9342627

Я пробовал несколько подходов для моего синтаксиса nlm. Я считаю, что с двумя переменными мне нужно вставить массив для p, но когда я пробовал, это не сработало. Ни одно из приведенных ниже решений не сработало:

# Attempt 1
p = array(c( 1,0), dim=c(2,1) ) 
ans <- nlm(fun, p)

# Attempt 2
ans <- nlm(fun, c( 0.1, 0.1))

# The first two return a "Error in f(x, ...) : argument "y" is missing, with no default"
# Attempt 3 returns a "invalid function value in 'nlm' optimizer" error

ans <- nlm(fun, c( 0.1, 0.1), y = c(1,1))

Я уверен, что в моем коде есть несколько ошибок, но я не уверен, где именно. Эта задача сложнее, чем я пытался раньше, поскольку я относительно новичок в R.


person VickiB    schedule 21.12.2019    source источник


Ответы (1)


Если вы внимательно посмотрите на функцию nlm. Он запрашивает только один аргумент. Одно решение:

fun <- function(x){
  s <- x[1]
  y <- x[2]
  (10 - s*(t_1-y + y*exp(-t_1/y)))^2+
    (20 - s*(t_2-y + y*exp(-t_2/y)))^2+
    (30 - s*(t_3-y + y*exp(-t_3/y)))^2
}

p <- array(c(0.4, 0.4), dim = c(2, 1))
# p <- c(0.4, 0.4)
ans <- nlm(f = fun, p = p)

И vector, и array работают, однако вы не можете указать два аргумента, как вы это сделали.

ИЗМЕНИТЬ

В численной оптимизации очень важна начальная точка. Я советую вам использовать функцию optim, которая менее чувствительна к неверному указанию начальной точки.

Одна идея состоит в том, чтобы сделать так: вы создаете сетку из множества начальных точек и выбираете ту, которая дает наилучший результат:

initialisation <- expand.grid(seq(1, 3, 0.5),
                              seq(1, 3, 0.5))
res <- data.frame(optim = rep(0, nrow(initialisation)),
                  nlm = rep(0, nrow(initialisation)))

for(i in 1:nrow(initialisation)){
  res[i, 1] <- optim(as.numeric(initialisation[i, ]), fun)$value
  res[i, 2] <- try(nlm(f = fun, p = as.numeric(initialisation[i, ]))$minimum, silent = T)
}
res

Я настаиваю на том, что в приведенном выше примере функция optim действительно более стабильна. Я советую вам использовать его, если у вас нет других ограничений.

Вы можете проверить параметры функции благодаря ?nlm.

Я надеюсь, что это помогает.

ИЗМЕНИТЬ 2

fun <- function(x){
  s <- x[1]
  y <- x[2]
  (10 - s*(t_1-y + y*exp (-t_1/y)))^2+
    (20 - s*(t_2-y + y*exp(-t_2/y)))^2+
    (30 - s*(t_3-y + y*exp(-t_3/y)))^2
}

Я выбираю эту начальную точку, потому что она кажется более близкой к оптимальной.

p <- c(10, 1)

ans <- nlm(f = fun, p = p)

Вы можете получить два параметра следующим образом: s is:

s <- ans$estimate[1]

y is :

y <- ans$estimate[2]

У вас также есть оптимальное значение, которое:

ans$minimum :
0.9337047
fun(c(s, y)) :
0.9337047

Мой второй пост, редактирование просто для того, чтобы подчеркнуть тот факт, что оптимизация с помощью функции nlm немного сложна, потому что вам нужно тщательно выбирать начальное значение.

optim также функция оптимизации для R более стабильна, как в примере, который я привожу со многими точками инициализации.

Функция expand.grid полезна для получения такой сетки:

initialisation <- expand.grid(s = seq(2, 3, 0.5),
                                y = seq(2, 3, 0.5))

initialisation :

   s   y
1 2.0 2.0
2 2.5 2.0
3 3.0 2.0
4 2.0 2.5
5 2.5 2.5
6 3.0 2.5
7 2.0 3.0
8 2.5 3.0
9 3.0 3.0

res data.frame дает вам минимальный результат с разными значениями инициалов. Вы видите, что первые значения инициалов не дают хорошего результата для nlm, но относительно стабильного для optim.

res <- data.frame(optim = rep(0, nrow(initialisation)),
                  nlm = rep(0, nrow(initialisation)))

for(i in 1:nrow(initialisation)){
  res[i, 1] <- optim(as.numeric(initialisation[i, ]), fun)$value
  res[i, 2] <- if(is.numeric(try(nlm(f = fun, p = as.numeric(initialisation[i, ]))$minimum, silent = T)) == T){
    round(nlm(f = fun, p = as.numeric(initialisation[i, ]))$minimum, 8)
  }else{
    NA
  }
}

Функция try предназначена только для того, чтобы избежать разрыва цикла. if должен поставить NA в нужное место.

res :
optim         nlm
1 0.9337094        <NA>
2 0.9337058  0.93370468
3 0.9337054        <NA>
4 0.9337101  0.93370468
5 0.9337125 61.18166446
6 0.9337057  0.93370468
7 0.9337120  0.93370468
8 0.9337080  0.93370468
9 0.9337114  0.93370468

Когда есть NA значений, это означает, что nlm плохо работает из-за инициализации. Я советую вам выбрать optim, если вам не нужна действительно точная оптимизация из-за его стабильности.

Подробное обсуждение optim и nlm вы можете посмотреть их. В вашем конкретном случае optim кажется лучшим выбором. Я не знаю, можем ли мы немного обобщить.

person Rémi Coulaud    schedule 21.12.2019
comment
Спасибо за ответ. Я попробовал первое решение, и оно выдало мне сообщения об ошибках. Во втором решении для оценки (6.6) вместо 2 было указано одно значение. Есть предложения? - person VickiB; 21.12.2019
comment
Я просто перезапустил его с вашими правками, и, похоже, он работает. Какова ваша стратегия при выборе начальных значений массива? Повлияет ли неправильный выбор на результаты оценок? - person VickiB; 21.12.2019
comment
Честно говоря, я не уверен! Я запускаю ваш код и получаю вывод для «nlm» и «optim». Но я не уверен, как эти значения соответствуют моему первоначальному вопросу об оптимальных значениях s и y. Первое решение в вашем текущем ответе дает мне две оценки, когда я запускаю nlm. Извините за дальнейшие вопросы, но это сложнее, чем мои нынешние навыки r! Просто пытаюсь понять, что вы предоставляете. - person VickiB; 21.12.2019
comment
Да, это очень помогло, спасибо! Я думаю, что есть еще некоторые настройки, которые я должен внести в функцию. Я воссоздаю лист Excel Solver, который выполняет эту задачу. Для этих значений t_1, t_2 и t_3 решатель дает минимальную SSE при оценках `s=9,57; у = 1,13'. Я буду продолжать играть с рабочим кодом, который вы мне дали. Еще раз спасибо! - person VickiB; 21.12.2019