Подтвердить что ты не робот

Сигмоидальная регрессия с 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?

4b9b3361

Ответ 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 более общую кривую рутина. Если вам нужно катиться самостоятельно, возможно, эта статья в Логистическая регрессия даст вам несколько идей.