Сигмоидальная регрессия с scipy, numpy, python и т.д.
У меня есть две переменные (x и y), которые имеют несколько сигмоидальное отношение друг к другу, и мне нужно найти какое-то уравнение предсказания, которое позволит мне предсказать значение y при любом значении x. Мое уравнение предсказания должно показать несколько сигмоидальное соотношение между двумя переменными. Поэтому я не могу согласиться на уравнение линейной регрессии, которое создает линию. Мне нужно увидеть постепенное криволинейное изменение наклона, которое происходит как справа, так и слева от графика двух переменных.
Я начал использовать numpy.polyfit после криволинейной регрессии в googling и python, но это дало мне ужасные результаты, которые вы можете увидеть, если вы запустите код ниже. Может ли кто-нибудь показать мне, как переписать код ниже, чтобы получить тип сигмоидального уравнения регрессии, который я хочу?
Если вы запустите код ниже, вы увидите, что он дает параболу, направленную вниз, и это не так, как должно выглядеть отношение между моими переменными. Вместо этого должно быть больше сигмоидальной связи между моими двумя переменными, но с плотной подгонкой данных, которые я использую в приведенном ниже коде. Данные в приведенном ниже коде являются средством из исследования с большой выборкой, поэтому они собирают больше статистической мощности, чем могут предположить их пять точек данных. У меня нет фактических данных из исследования с большой выборкой, но у меня есть средства ниже и их стандартные отклонения (которые я не показываю). Я бы предпочел просто построить простую функцию со средними данными, перечисленными ниже, но код мог бы стать более сложным, если бы сложность обеспечила существенные улучшения.
Как я могу изменить свой код, чтобы наилучшим образом соответствовать сигмоидальной функции, предпочтительно используя scipy, numpy и python? Вот текущая версия моего кода, которая должна быть исправлена:
import numpy as np
import matplotlib.pyplot as plt
# Create numpy data arrays
x = np.array([821,576,473,377,326])
y = np.array([255,235,208,166,157])
# Use polyfit and poly1d to create the regression equation
z = np.polyfit(x, y, 3)
p = np.poly1d(z)
xp = np.linspace(100, 1600, 1500)
pxp=p(xp)
# Plot the results
plt.plot(x, y, '.', xp, pxp, '-')
plt.ylim(140,310)
plt.xlabel('x')
plt.ylabel('y')
plt.grid(True)
plt.show()
ИЗМЕНИТЬ НИЖЕ: (Подтвердил вопрос)
Ваш ответ и его скорость очень впечатляют. Благодарю вас, unutbu.
Но для того, чтобы получить более достоверные результаты, мне нужно переопределить мои значения данных. Это означает перебронирование значений x в процентах от значения max x, при повторном литье значений y в процентах от значений x в исходных данных. Я попытался сделать это с помощью вашего кода и придумал следующее:
import numpy as np
import matplotlib.pyplot as plt
import scipy.optimize
# Create numpy data arrays
'''
# Comment out original data
#x = np.array([821,576,473,377,326])
#y = np.array([255,235,208,166,157])
'''
# Re-calculate x values as a percentage of the first (maximum)
# original x value above
x = np.array([1.000,0.702,0.576,0.459,0.397])
# Recalculate y values as a percentage of their respective x values
# from original data above
y = np.array([0.311,0.408,0.440,0.440,0.482])
def sigmoid(p,x):
x0,y0,c,k=p
y = c / (1 + np.exp(-k*(x-x0))) + y0
return y
def residuals(p,x,y):
return y - sigmoid(p,x)
p_guess=(600,200,100,0.01)
(p,
cov,
infodict,
mesg,
ier)=scipy.optimize.leastsq(residuals,p_guess,args=(x,y),full_output=1,warning=True)
'''
# comment out original xp to allow for better scaling of
# new values
#xp = np.linspace(100, 1600, 1500)
'''
xp = np.linspace(0, 1.1, 1100)
pxp=sigmoid(p,xp)
x0,y0,c,k=p
print('''\
x0 = {x0}
y0 = {y0}
c = {c}
k = {k}
'''.format(x0=x0,y0=y0,c=c,k=k))
# Plot the results
plt.plot(x, y, '.', xp, pxp, '-')
plt.ylim(0,1)
plt.xlabel('x')
plt.ylabel('y')
plt.grid(True)
plt.show()
Можете ли вы показать мне, как исправить этот исправленный код?
ПРИМЕЧАНИЕ. Переливая данные, я по существу повернул сигмоид 2d (x, y) относительно оси z на 180 градусов. Кроме того, 1.000 не является максимальным значением x. Вместо этого 1.000 представляет собой среднее значение диапазона значений от разных участников теста в максимальном состоянии теста.
ВТОРАЯ РЕДАКТИРОВАТЬ НИЖЕ:
Спасибо, ubuntu. Я внимательно прочитал ваш код и рассмотрел его аспекты в скудной документации. Поскольку ваше имя, похоже, появляется как автор скудной документации, я надеюсь, что вы ответите на следующие вопросы:
1.) Идет ли lesssq() остатки(), которые затем возвращают разницу между входным вектором y и y-вектором, возвращаемым функцией sigmoid()? Если да, то как это объясняет разницу в длинах входного y-вектора и y-вектора, возвращаемого функцией sigmoid()?
2.) Похоже, что я могу назвать lesssq() для любого математического уравнения, пока я получаю доступ к этому математическому уравнению через функцию остатков, которая, в свою очередь, вызывает математическую функцию. Это правда?
3.) Кроме того, я замечаю, что p_guess имеет такое же количество элементов, что и p. Означает ли это, что четыре элемента p_guess соответствуют, соответственно, значениям, возвращаемым x0, y0, c и k?
4.) Является ли p, который отправляется как аргумент функции residuals() и sigmoid(), тот же p, который будет выводиться с помощью наименьших квадратов(), а функция lesssq() использует это p внутри, прежде чем возвращать это?
5.) Могут ли p и p_guess иметь любое количество элементов в зависимости от сложности используемого уравнения в качестве модели, если число элементов в p равно числу элементов в p_guess?
Ответы
Ответ 1
Использование scipy.optimize.leastsq:
import numpy as np
import matplotlib.pyplot as plt
import scipy.optimize
def sigmoid(p,x):
x0,y0,c,k=p
y = c / (1 + np.exp(-k*(x-x0))) + y0
return y
def residuals(p,x,y):
return y - sigmoid(p,x)
def resize(arr,lower=0.0,upper=1.0):
arr=arr.copy()
if lower>upper: lower,upper=upper,lower
arr -= arr.min()
arr *= (upper-lower)/arr.max()
arr += lower
return arr
# raw data
x = np.array([821,576,473,377,326],dtype='float')
y = np.array([255,235,208,166,157],dtype='float')
x=resize(-x,lower=0.3)
y=resize(y,lower=0.3)
print(x)
print(y)
p_guess=(np.median(x),np.median(y),1.0,1.0)
p, cov, infodict, mesg, ier = scipy.optimize.leastsq(
residuals,p_guess,args=(x,y),full_output=1,warning=True)
x0,y0,c,k=p
print('''\
x0 = {x0}
y0 = {y0}
c = {c}
k = {k}
'''.format(x0=x0,y0=y0,c=c,k=k))
xp = np.linspace(0, 1.1, 1500)
pxp=sigmoid(p,xp)
# Plot the results
plt.plot(x, y, '.', xp, pxp, '-')
plt.xlabel('x')
plt.ylabel('y',rotation='horizontal')
plt.grid(True)
plt.show()
дает
![alt text]()
с сигмовидными параметрами
x0 = 0.826964424481
y0 = 0.151506745435
c = 0.848564826467
k = -9.54442292022
Обратите внимание, что для более новых версий scipy (например, 0.9) есть функция scipy.optimize.curve_fit, которая проще в использовании, чем leastsq
, Соответствующее обсуждение сигмоидов, использующих curve_fit
, можно найти здесь.
Изменить: добавлена функция resize
, чтобы исходные данные можно было масштабировать и сдвигать, чтобы они соответствовали любому требуемому ограничивающему прямоугольнику.
"ваше имя появляется как писатель от скудной документации"
ОТКАЗ ОТ ОТВЕТСТВЕННОСТИ: Я не писатель скупой документации. Я просто пользователь, и новичок в этом. Большая часть того, что я знаю о leastsq
, происходит от чтения этого учебника, написанного Трэвисом Олифантом.
1.) Вызывает ли lesssq() остатки(), которые затем возвращают разницу между входным вектором y и y-вектор, возвращаемый сигмоидом() функция?
Да! точно.
Если да, то как это объясняет разница в длинах ввода y-вектора и y-вектора, возвращаемого функция sigmoid()?
Длины одинаковы:
In [138]: x
Out[138]: array([821, 576, 473, 377, 326])
In [139]: y
Out[139]: array([255, 235, 208, 166, 157])
In [140]: p=(600,200,100,0.01)
In [141]: sigmoid(p,x)
Out[141]:
array([ 290.11439268, 244.02863507, 221.92572521, 209.7088641 ,
206.06539033])
Одна из замечательных вещей о Numpy заключается в том, что она позволяет писать "векторные" уравнения, которые работают на всех массивах.
y = c / (1 + np.exp(-k*(x-x0))) + y0
может выглядеть так, как будто он работает с float (на самом деле это было бы), но если вы сделаете x
массив numpy и c
, k
, x0
, y0
float, то уравнение определяет y
, чтобы быть массивом numpy той же формы, что и x
. Таким образом, sigmoid(p,x)
возвращает массив numpy. Более полное объяснение того, как это работает в numpybook (требуется чтение для серьезных пользователей numpy).
2.) Похоже, я могу назвать lesssq() для любого математического уравнения, если я доступ к этому математическому уравнению через функция остатков, которая, в свою очередь, вызывает математическую функцию. Это правда?
True. leastsq
пытается минимизировать сумму квадратов остатков (разностей). Он ищет пространство параметров (все возможные значения p
), ища p
, который минимизирует эту сумму квадратов. x
и y
, отправленные в residuals
, являются вашими необработанными значениями данных. Они исправлены. Они не меняются. Это p
(параметры в сигмоидной функции), которые leastsq
пытается свести к минимуму.
3.) Кроме того, я замечаю, что p_guess имеет такое же количество элементов, что и p. Есть ли это означает, что четыре элемента p_guess соответствуют по порядку, соответственно, с возвращенными значениями через x0, y0, c и k?
Точно так! Как и метод Ньютона, leastsq
требует начального предположения для p
. Вы указываете его как p_guess
. Когда вы видите
scipy.optimize.leastsq(residuals,p_guess,args=(x,y))
вы можете думать, что как часть алгоритма наименьшего квадрата (действительно алгоритм Левенбурга-Марквардта) в качестве первого прохода, наименьший вызов вызывает residuals(p_guess,x,y)
.
Обратите внимание на визуальное сходство между
(residuals,p_guess,args=(x,y))
и
residuals(p_guess,x,y)
Это может помочь вам запомнить порядок и смысл аргументов leastsq
.
residuals
, например sigmoid
возвращает массив numpy. Значения в массиве квадратичны, а затем суммируются. Это номер, который нужно бить. p_guess
изменяется тогда как leastsq
ищет набор значений, который минимизирует residuals(p_guess,x,y)
.
4.) Является ли p, который отправляется как аргумент остаткам() и sigmoid() выполняет ту же функцию p, что будет выводиться с помощью наименьших квадратов(), а Функция minsq() использует это значение p внутренне, прежде чем возвращать его?
Ну, не совсем. Как вы знаете, p_guess
изменяется, поскольку leastsq
выполняет поиск значения p
, которое минимизирует residuals(p,x,y)
. p
(er, p_guess
), который отправляется на leastsq
, имеет ту же форму, что и p
, которая возвращается leastsq
. Очевидно, что значения должны быть разными, если вы не догадываетесь:)
5.) Может p и p_guess иметь любое количество элементов, в зависимости от сложность используемого уравнения в качестве модели, если число элементы в p равны числу элементов в p_guess?
Да. Я не испытывал стресс-тестов leastsq
для очень большого количества параметров, но это захватывающий инструмент.
Ответ 2
Я не думаю, что вы получите хорошие результаты с полиномиальным подходом любой степени - поскольку
все многочлены уходят в бесконечность при достаточно больших и малых X, но сигмоидальная кривая асимптотически приближается к некоторому конечному значению в каждом направлении.
Я не программист на Python, поэтому я не знаю, имеет ли numpy более общую кривую
рутина. Если вам нужно катиться самостоятельно, возможно, эта статья в Логистическая регрессия даст вам несколько идей.
Ответ 3
Для логистической регрессии в Python scikits-learn предоставляет высокопроизводительный код подстановки:
http://scikit-learn.sourceforge.net/modules/linear_model.html#logistic-regression