Сплайны внутри нелинейного метода наименьших квадратов в R

Рассмотрим нелинейную модель наименьших квадратов в R, например, следующего вида):

 y ~ theta / ( 1 + exp( -( alpha + beta * x) ) )

(моя реальная проблема имеет несколько переменных, а внешняя функция не логистическая, а немного более сложная; эта проще, но я думаю, что если я смогу это сделать, мой случай должен следовать почти сразу)

Я хотел бы заменить термин «альфа + бета * х» (скажем) на натуральный кубический сплайн.

вот некоторый код для создания примера данных с нелинейной функцией внутри логистики:

set.seed(438572L)
x <- seq(1,10,by=.25)
y <- 8.6/(1+exp( -(-3+x/4.4+sqrt(x*1.1)*(1.-sin(1.+x/2.9))) )) + rnorm(x, s=0.2 )

Без необходимости в логистике вокруг этого, если бы я был в lm, я мог бы легко заменить линейный термин сплайновым; поэтому линейная модель примерно такая:

 lm( y ~ x ) 

затем становится

 library("splines")
 lm( y ~ ns( x, df = 5 ) )

генерировать подобранные значения просто, а получение прогнозируемых значений с помощью (например) пакета rms кажется достаточно простым.

Действительно, подгонка исходных данных под этот сплайн на основе lm не так уж плоха, но есть причина, по которой мне это нужно внутри логистической функции (или, скорее, эквивалент в моей задаче).

Проблема с nls заключается в том, что мне нужно указать имена для всех параметров (я вполне доволен тем, что называю их, скажем, (b1,..., b5) для одного сплайна (и скажем, c1,..., c6 для другой переменной - Мне нужно будет сделать несколько из них).

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

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

(изменить для уточнения) Для этой небольшой задачи я, конечно, могу сделать это вручную - написать выражение для внутреннего произведения каждой переменной в матрице, сгенерированной ns, умножить на вектор параметров. Но тогда я должен снова выписывать все это почленно для каждого сплайна в каждой другой переменной, и снова каждый раз, когда я изменяю df в любом из сплайнов, и снова, если я хочу использовать cs вместо ns. И затем, когда я хочу попытаться сделать какой-то прогноз (/ интерполяцию), мы получаем целый ряд новых проблем, с которыми нужно иметь дело. Мне нужно продолжать делать это снова и снова, и потенциально для значительно большего количества узлов и нескольких переменных, для анализа за анализом - и я задался вопросом, есть ли более аккуратный и простой способ, чем выписывание каждого отдельного термина, без необходимости писать много кода. Я вижу довольно тупой способ сделать это, который потребовал бы немалого количества кода, чтобы все было правильно, но, будучи R, я подозреваю, что есть гораздо более аккуратный способ (или, что более вероятно, 3 или 4 более аккуратных способа), который просто ускользает от меня. Отсюда вопрос.

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

[Более конкретно, я обычно хотел бы иметь возможность попробовать подогнать любой из нескольких различных сплайнов в каждой переменной — попробовать пару возможностей — чтобы посмотреть, смогу ли я найти простую модель, но все же ту, где подходит подходит для этой цели (шум действительно довольно низок; некоторая погрешность в подгонке подходит для достижения хорошего гладкого результата, но только до определенного момента). Это скорее «найти красивую, интерпретируемую, но адекватную подходящую функцию», чем что-либо, приближающееся к выводу, и интеллектуальный анализ данных на самом деле не является проблемой для этой проблемы.]

В качестве альтернативы, если это было бы намного проще, скажем, в gnm или ASSIST или в одном из других пакетов, это было бы полезным знанием, но тогда не помешали бы некоторые подсказки о том, как действовать с ними над игрушечной проблемой.


person Glen_b    schedule 04.02.2012    source источник


Ответы (2)


ns фактически генерирует матрицу предикторов. Что вы можете сделать, так это разделить эту матрицу на отдельные переменные и передать их nls.

m <- ns(x, df=5)
df <- data.frame(y, m)  # X-variables will be named X1, ... X5
# starting values should be set as appropriate for your data
nls(y ~ theta * plogis(alpha + b1*X1 + b2*X2 + b3*X3 + b4*X4 + b5*X5), data=df,
        start=list(theta=1, alpha=0, b1=1, b2=1, b3=1, b4=1, b5=1))

ETA: попробуйте автоматизировать это для разных значений df. Это строит формулу, используя преобразование текста, а затем использует do.call для вызова nls. Предостережение: не проверено.

my.nls <- function(x, y, df)
{
    m <- ns(x, df=df)
    xn <- colnames(m)
    b <- paste("b", seq_along(xn), sep="")
    fm <- formula(paste("y ~ theta * plogis(1 + alpha + ", paste(b, xn, sep="*",
          collapse=" + "), ")", sep=""))
    start <- c(1, 1, rep(1, length=length(b)))
    names(start) <- c("theta", "alpha", b)
    do.call(nls, list(fm, data=data.frame(y, m), start=start))
}
person Hong Ooi    schedule 04.02.2012

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

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

Трюк Хонга Ооя с использованием data.frame в матрице, сгенерированной ns, для автоматического присвоения имен столбцам довольно симпатичный, и я использовал его ниже. Я, вероятно, буду использовать пасту для их создания в целом, потому что у меня есть несколько переменных для игры.

Предполагая настройку данных, указанную в вопросе -

lin.expr <- function(p,xn) {
  pn<-paste(p, 1:length(xn), sep = "")
  paste(paste(pn,xn,sep=" * "),collapse=" + ")
  }


m <- ns(x, df=3)
mydf <- data.frame(y, m)  # X-variables will be named X1, X2, ... 
xn <- names(mydf)[2:dim(mydf)[2]]

nspb <- lin.expr("b",xn)

c.form <- paste("y ~ theta * plogis( a + ",nspb,")",sep="")
stl <- list(theta=2, a=-5,b1=10, b2=10, b3=10)
nls( c.form, data=mydf, start= stl)

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

редактировать: добавление Хонга Оои (которое было опубликовано, когда я печатал свое, и использует аналогичные идеи, но добавило пару приятных дополнений) в значительной степени делает это; это приемлемый ответ, поэтому я его проверил.

person Glen_b    schedule 05.02.2012