Estou tentando ajustar uma função de decaimento exponencial aos valores y que se tornam negativos em valores x altos, mas não consigo configurar minha nls
função corretamente.
Alvo
Estou interessado na inclinação da função decay ( acordo com algumas fontes ). Como obtenho essa inclinação não é importante, mas o modelo deve ajustar meus dados da melhor maneira possível (por exemplo, linearizar o problema é aceitável , se o ajuste for bom; consulte "linearização"). No entanto, trabalhos anteriores sobre esse tópico usaram a seguinte função de decaimento exponencial ( artigo de acesso fechado de Stedmon et al., Equação 3 ):
onde S
está a inclinação em que estou interessado, K
o fator de correção para permitir valores negativos e a
o valor inicial para x
(ou seja, interceptação).
Preciso fazer isso em R, pois estou escrevendo uma função que converte medidas brutas da matéria orgânica dissolvida cromofórica (CDOM) em valores nos quais os pesquisadores estão interessados.
Dados de exemplo
Devido à natureza dos dados, tive que usar o PasteBin. Os dados de exemplo estão disponíveis aqui .
Escreva dt <-
e copie o código do PasteBin no seu console R. Ou seja,
dt <- structure(list(x = ...
Os dados são assim:
library(ggplot2)
ggplot(dt, aes(x = x, y = y)) + geom_point()
Valores y negativos ocorrem quando .
Tentando encontrar solução usando nls
A tentativa inicial de usar nls
produz uma singularidade, o que não deve ser uma surpresa, pois acabei de observar os valores iniciais dos parâmetros:
nls(y ~ a * exp(-S * x) + K, data = dt, start = list(a = 0.5, S = 0.1, K = -0.1))
# Error in nlsModel(formula, mf, start, wts) :
# singular gradient matrix at initial parameter estimates
Após esta resposta , posso tentar definir parâmetros de início melhores para ajudar a nls
função:
K0 <- min(dt$y)/2
mod0 <- lm(log(y - K0) ~ x, data = dt) # produces NaNs due to the negative values
start <- list(a = exp(coef(mod0)[1]), S = coef(mod0)[2], K = K0)
nls(y ~ a * exp(-S * x) + K, data = dt, start = start)
# Error in nls(y ~ a * exp(-S * x) + K, data = dt, start = start) :
# number of iterations exceeded maximum of 50
A função parece não conseguir encontrar uma solução com o número padrão de iterações. Vamos aumentar o número de iterações:
nls(y ~ a * exp(-S * x) + K, data = dt, start = start, nls.control(maxiter = 1000))
# Error in nls(y ~ a * exp(-S * x) + K, data = dt, start = start, nls.control(maxiter = 1000)) :
# step factor 0.000488281 reduced below 'minFactor' of 0.000976562
Mais erros. Chuck it! Vamos forçar a função a nos dar uma solução:
mod <- nls(y ~ a * exp(-S * x) + K, data = dt, start = start, nls.control(maxiter = 1000, warnOnly = TRUE))
mod.dat <- data.frame(x = dt$x, y = predict(mod, list(wavelength = dt$x)))
ggplot(dt, aes(x = x, y = y)) + geom_point() +
geom_line(data = mod.dat, aes(x = x, y = y), color = "red")
Bem, essa definitivamente não era uma boa solução ...
Linearizando o problema
Muitas pessoas linearizaram suas funções de decaimento exponencial com sucesso (fontes: 1 , 2 , 3 ). Nesse caso, precisamos garantir que nenhum valor de y seja negativo ou 0. Vamos tornar o valor mínimo de y o mais próximo possível de 0 dentro dos limites de ponto flutuante dos computadores :
K <- abs(min(dt$y))
dt$y <- dt$y + K*(1+10^-15)
fit <- lm(log(y) ~ x, data=dt)
ggplot(dt, aes(x = x, y = y)) + geom_point() +
geom_line(aes(x=x, y=exp(fit$fitted.values)), color = "red")
Muito melhor, mas o modelo não rastreia os valores y perfeitamente em valores x baixos.
Observe que a nls
função ainda não conseguiu ajustar o decaimento exponencial:
K0 <- min(dt$y)/2
mod0 <- lm(log(y - K0) ~ x, data = dt) # produces NaNs due to the negative values
start <- list(a = exp(coef(mod0)[1]), S = coef(mod0)[2], K = K0)
nls(y ~ a * exp(-S * x) + K, data = dt, start = start)
# Error in nlsModel(formula, mf, start, wts) :
# singular gradient matrix at initial parameter estimates
Os valores negativos são importantes?
Os valores negativos são obviamente um erro de medição, pois os coeficientes de absorção não podem ser negativos. E se eu fizer os valores y generosamente positivos? É no declive que me interessa. Se a adição não afeta o declive, eu devo decidir:
dt$y <- dt$y + 0.1
fit <- lm(log(y) ~ x, data=dt)
ggplot(dt, aes(x = x, y = y)) + geom_point() + geom_line(aes(x=x, y=exp(fit$fitted.values)), color = "red")
Bem, isso não foi tão bem ... Valores altos de x devem obviamente ser o mais próximo possível de zero.
A questão
Obviamente, estou fazendo algo errado aqui. Qual é a maneira mais precisa de estimar a inclinação para uma função de decaimento exponencial ajustada em dados que possuem valores y negativos usando R?
fonte
nls
convergiu para mim usando os valores iniciais . Alternativamente, você pode usar a função de auto-partida: . Isso converge para mim também.nls(y~SSasymp(x, Asym, r0, lrc), data = dt)
Respostas:
Use uma função de auto-inicialização:
No entanto, eu consideraria seriamente se o seu conhecimento de domínio não justifica definir a assíntota como zero. Eu acredito que sim e o modelo acima não discorda (veja o erro padrão / valor p do coeficiente).
fonte
SSasymp
função. Obrigado! Acredito que os pesquisadores querem se referir ao artigo que citei na pergunta e usar oK
termo, mas vou sugerir que modifiquem sua equação. Eu acho que eles querem manter oK
, porque valores negativos significam que o instrumento não se comportou como o esperado, mas eles estão interessados na inclinação. A remoção da assíntota negativa pode afetar a inclinação em alguns casos.Esta pergunta tem relações com várias outras perguntas
Tenho três observações adicionais sobre alguns pontos desta questão.
1: Por que o modelo linearizado não se ajusta bem aos grandes valores dey
O ajuste linearizado não está minimizando os mesmos resíduos. Na escala logarítmica, os resíduos para valores menores serão maiores. A imagem abaixo mostra a comparação plotando o eixo y em uma escala de log na imagem correta:
Quando necessário, você pode adicionar pesos à função de perda de mínimos quadrados.
2: Usando ajuste linearizado como valores iniciais
Depois de obter estimativas com seu ajuste linearizado, você poderá usá-las como ponto de partida para o ajuste não linear. *
3: Usando um método mais geral para obter o ponto de partida
Se você tiver pontos suficientes, também poderá obter a inclinação sem se preocupar com o valor assintótico e os valores negativos (não é necessário o cálculo de um logaritmo).
Você pode fazer isso integrando os pontos de dados. Em seguida, com e é possível utilizar um modelo linear para se obter o valor de , descrevendo como uma combinação linear dos vetores , e um intercepto:y=aesx+k Y=asesx+kx+Const s y Y x
A vantagem desse método (consulte Tittelbach e Helmrich 1993 "Um método de integração para a análise de sinais transitórios multiexponenciais" ) é que você pode estendê-lo a mais do que um único componente decadente exponencialmente (adicionando mais integrais).
Nota de rodapé: * Esse uso da inclinação no problema linearizado é exatamente o que a
SSasymp
função de inicialização automática faz. Primeiro, estima a assíntotae depois a inclinação (subtraindo o valor da assíntota e obtendo os valores do log)
Observe a linha
start = list(lrc = log(-coef(lm(log(abs(y - rt)) ~ x, data = xy))[[2L]]))
Nota: No caso especial de você pode usarK=0
que modela o parâmetro observado comoy
fonte