Подгонка кривой к конкретным данным

У меня есть следующие данные в моей диссертации:

28 45
91 14
102 11
393 5
4492 1.77

Мне нужно вписать кривую в это. Если я нарисую это, то это то, что я получаю.

введите здесь описание изображения

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


person The Flying Dutchman    schedule 07.01.2013    source источник
comment
Я бы предложил начать с построения данных в логарифмической шкале, по крайней мере, по оси Y и, возможно, по оси X. Это должно сделать ваши данные намного ближе к прямой линии. Если прямая линия в логарифмических масштабах является разумной аппроксимацией, это облегчит интерпретацию неопределенностей в параметрах и т. д., чем аппроксимирующие кривые.   -  person Simon    schedule 25.02.2013


Ответы (2)


На всякий случай, если R является вариантом, вот схема двух методов, которые вы можете использовать.

Первый метод: оценить соответствие набора моделей-кандидатов.

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

# read in the data
dat <- read.table(text= "x y 
28 45
91 14
102 11
393 5
4492 1.77", header = TRUE)

# quick visual inspection
plot(dat); lines(dat)

введите здесь описание изображения

# a smattering of possible models... just made up on the spot
# with more effort some better candidates should be added
# a smattering of possible models...
models <- list(lm(y ~ x, data = dat), 
               lm(y ~ I(1 / x), data = dat),
               lm(y ~ log(x), data = dat),
               nls(y ~ I(1 / x * a) + b * x, data = dat, start = list(a = 1, b = 1)), 
               nls(y ~ (a + b * log(x)), data = dat, start = setNames(coef(lm(y ~ log(x), data = dat)), c("a", "b"))),
               nls(y ~ I(exp(1) ^ (a + b * x)), data = dat, start = list(a = 0,b = 0)),
               nls(y ~ I(1 / x * a) + b, data = dat, start = list(a = 1,b = 1))
)

# have a quick look at the visual fit of these models
library(ggplot2)
ggplot(dat, aes(x, y)) + geom_point(size = 5) +
    stat_smooth(method = lm, formula = as.formula(models[[1]]), size = 1, se = FALSE, color = "black") + 
    stat_smooth(method = lm, formula = as.formula(models[[2]]), size = 1, se = FALSE, color = "blue") + 
    stat_smooth(method = lm, formula = as.formula(models[[3]]), size = 1, se = FALSE, color = "yellow") + 
    stat_smooth(method = nls, formula = as.formula(models[[4]]), data = dat, method.args = list(start = list(a = 0,b = 0)), size = 1, se = FALSE, color = "red", linetype = 2) + 
    stat_smooth(method = nls, formula = as.formula(models[[5]]), data = dat, method.args = list(start = setNames(coef(lm(y ~ log(x), data = dat)), c("a", "b"))), size = 1, se = FALSE, color = "green", linetype = 2) +
    stat_smooth(method = nls, formula = as.formula(models[[6]]), data = dat, method.args = list(start = list(a = 0,b = 0)), size = 1, se = FALSE, color = "violet") +
    stat_smooth(method = nls, formula = as.formula(models[[7]]), data = dat, method.args = list(start = list(a = 0,b = 0)), size = 1, se = FALSE, color = "orange", linetype = 2)

введите описание изображения здесь

Оранжевая кривая выглядит довольно хорошо. Давайте посмотрим, как это ранжируется, когда мы измеряем относительное соответствие этих моделей...

# calculate the AIC and AICc (for small samples) for each 
# model to see which one is best, ie has the lowest AIC
library(AICcmodavg); library(plyr); library(stringr)
ldply(models, function(mod){ data.frame(AICc = AICc(mod), AIC = AIC(mod), model = deparse(formula(mod))) })

      AICc      AIC                     model
1 70.23024 46.23024                     y ~ x
2 44.37075 20.37075                y ~ I(1/x)
3 67.00075 43.00075                y ~ log(x)
4 43.82083 19.82083    y ~ I(1/x * a) + b * x
5 67.00075 43.00075      y ~ (a + b * log(x))
6 52.75748 28.75748 y ~ I(exp(1)^(a + b * x))
7 44.37075 20.37075        y ~ I(1/x * a) + b

# y ~ I(1/x * a) + b * x is the best model of those tried here for this curve
# it fits nicely on the plot and has the best goodness of fit statistic
# no doubt with a better understanding of nls and the data a better fitting
# function could be found. Perhaps the optimisation method here might be
# useful also: http://stats.stackexchange.com/a/21098/7744

Второй метод: использовать генетическое программирование для поиска огромного количества моделей.

Это кажется чем-то вроде дикого выстрела в темном подходе к подгонке кривой. Вам не нужно указывать много в начале, хотя, возможно, я делаю это неправильно...

# symbolic regression using Genetic Programming
# http://rsymbolic.org/projects/rgp/wiki/Symbolic_Regression
library(rgp)
# this will probably take some time and throw
# a lot of warnings...
result1 <- symbolicRegression(y ~ x, 
             data=dat, functionSet=mathFunctionSet,
             stopCondition=makeStepsStopCondition(2000))
# inspect results, they'll be different every time...
(symbreg <- result1$population[[which.min(sapply(result1$population, result1$fitnessFunction))]])

function (x) 
tan((x - x + tan(x)) * x) 
# quite bizarre...

# inspect visual fit
ggplot() + geom_point(data=dat, aes(x,y), size = 3) +
  geom_line(data=data.frame(symbx=dat$x, symby=sapply(dat$x, symbreg)), aes(symbx, symby), colour = "red")

введите здесь описание изображения

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

Кредиты: Подгонка кривой, ответ 1, подгонка кривой ответ 2 от G. Гротендик.

person Ben    schedule 24.02.2013
comment
Я бы не стал возиться с последним из-за 5 точек данных (!) Не могли бы вы прокомментировать, как вы угадали poly(1/x,3) в качестве модели-кандидата??? - person Ben Bolker; 25.02.2013
comment
Согласен, все это немного неразумно, учитывая небольшое количество данных, но для меня это было полезным упражнением. Что касается poly, то это точно, предположение. Читая немного больше об этом (ваша книга была полезной), я вижу, что многочлен третьего порядка для столь малого количества степеней свободы бесполезен для большинства целей (хотя он проводит красивую линию через точки!). Я заменил его несколькими менее странными кандидатами, спасибо за ваш комментарий. - person Ben; 26.02.2013
comment
какую заглавную букву I() вы используете в подходящих моделях? например, lm(y~I(1/x), data=dat) - person Kristof Pal; 24.01.2017
comment
help(I) должен дать вам понять. Тем не менее, вот так он включает значения 1/x как x. - person Masclins; 27.04.2017

Знаете ли вы какую-то аналитическую функцию, которой должны соответствовать данные? Если это так, это может помочь вам выбрать форму функции, соответствующую данным.

В противном случае, поскольку данные выглядят как экспоненциальное затухание, попробуйте что-то подобное в gnuplot, где к данным подгоняется функция с двумя свободными параметрами:

 f(x) = exp(-x*c)*b
 fit f(x) "data.dat" u 1:2 via b,c
 plot "data.dat" w p, f(x)

Gnuplot будет варьировать параметры, названные в соответствии с предложением «via», для наилучшего соответствия. Статистика выводится на стандартный вывод, а также в файл с именем «fit.log» в текущем рабочем каталоге.

Переменная c будет определять кривизну (затухание), а переменная b линейно масштабирует все значения, чтобы получить правильную величину данных.

Дополнительные сведения см. в разделе Подбор кривой в документации Gnuplot.

person Anders Damsgaard    schedule 09.01.2013
comment
+1, знаете ли вы какую-нибудь аналитическую функцию, которой должны придерживаться данные? Выбор подходящей функции на основе априорного знания функциональной формы, которая должна соответствовать данным, всегда лучше, чем подбор какой-либо произвольной функции. - person Simon; 25.02.2013