2014-09-09 2 views
2

У меня есть массив Python, содержащий дат, представляющих количество явлений явления в конкретный год. Этот вектор содержит 200 разных дат, повторяющихся определенное количество раз каждый. Повторения - это количество явлений явления. Мне удалось вычислить и построить накопленную сумму с matplotlib с в следующем фрагменте кода:Параметры сигмоидальной регрессии в Python + scipy

counts = arange(0, len(list_of_dates)) 
# Add the cumulative sum to the plot (list_of_dates contains repetitions) 
plt.plot(list_of_dates, counts, linewidth=3.0) 

Cumulative sum (in blue) per date

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

  1. Сначала я пытался использовать LogisticRegression из Scikit учиться, но потом я понял, что они, кажется, использует эту модель для машинного обучения classification (и другие вещи), так , который не является то, что я хочу.

  2. Тогда я подумал, что могу перейти непосредственно к определению логистической функции и попытаться построить ее самостоятельно. Я нашел this thread, где для расчета кривой рекомендуется использовать scipy.special.expit. Кажется, эта функция уже реализована, поэтому я решил ее использовать. Так что я сделал это:

    target_vector = dictionary.values() Y = expit(target_vector) plt.plot(list_of_dates, y, linewidth=3.0)

Я получил вектор обратно с 209 элементами (так же, как target_vector), которые выглядят следующим образом: [ 1. 0.98201379 0.95257413 0.73105858 ... 0.98201379 1. ]. Тем не менее, графический вывод выглядит, если ребенок царапал бумагу, а не как хорошую сигмовидную кривую, как на картинке.

Я также проверил другие потоки переполнения стека (this, this), но я думаю, что мне нужно сделать только игрушечный пример по сравнению с ними. Мне нужна математическая формула для вычисления некоторых быстрых и простых параметров.

Есть ли способ сделать это и получить математическое представление сигмоидальной функции?

спасибо!

+1

Логистическая регрессия действительно является проблемой классификации. Я думаю, вы ищете обобщенную линейную модель с функцией логической ссылки. Я никогда не делал этого в python, но 'statsmodels' предлагает реализации GLM для ряда различных функций ссылок. Я уверен, что вы найдёте модель для регрессии логита. – cel

+0

Я проверяю пакет, который вы упоминаете в категории «Логистическая регрессия», и на мои (плохие) знания, кажется, больше ориентирован на «классификацию машинного обучения», больше, чем на проблему «кривой подгонки», которая до сих пор, я думаю то, что мне нужно: математическое описание кривой. Возможно, я неправильно использовал условия моего первоначального сообщения. :) – iamgin

ответ

0

Использование this post и комментарии, опубликованные вчера, я придумал следующий код:

from scipy.optimize import curve_fit 
import matplotlib.pyplot as plt 
import numpy as np 
from sklearn.preprocessing import normalize # Added this new line 

# This is how I normalized the vector. "ydata" looked like this: 
# original_ ydata = [ 1, 3, 8, 14, 12, 27, 33, 36, 87, 136, 77, 57, 32, 31, 28, 24, 12, 2 ] 
# The curve was NOT fitting using this values, so I found a function in 
# scikit-learn that normalizes (multidim) arrays: [normalize][2] 

# m = [] 
# m.append(original_ydata) 
# ydata = normalize(m, norm='l2') * 10 

# Why 10? This function is converting my original values in a range 
# going from [0.00014, ..., 0.002 ] or something similar. So "curve_fit" 
# couldn't find anything but a horizontal line crossing y = 1. 
# I tried multiplying by 5, 6, ..., 12, and I realized that 10 is 
# the maximum value that lets the maximum value of my array below 1.00, like 0.97599. 

# Length of both arrays is 209 
# Y-axis data has been normalized BUT then multiplied by 10 
ydata = array([ 5.09124776e-04, 1.01824955e-03, ... , 9.75992196e-01]) 
xdata = array(range(0,len(ydata),1)) 

def sigmoid(x, x0, k): 
    y = 1/(1+ np.exp(-k*(x-x0))) 
    return y 

popt, pcov = curve_fit(sigmoid, xdata, ydata) 

x = np.linspace(0, 250, 250) 
y = sigmoid(x, *popt) 

plt.plot(xdata, ydata, 'o', label='data') 
plt.plot(x,y, linewidth=3.0, label='fit') 
plt.ylim(0, 1.25) 
plt.legend(loc='best') 

# This (m, b, C) parameters not sure on where they are... popt, pcov? 
# y = C * sigmoid(m*x + b) 

Эта программа создает сюжет вы можете увидеть ниже. Как вы можете видеть, это справедливая корректировка, но я думаю, если я изменил определение Y в сигмоидной функции, добавив C, умножив первый 1, вероятно, я бы получил лучшую настройку. Все еще на этом.

Sigmoid curve fitting

кажется нормализующих данных (как это было предложено Бен Куна в комментариях) является необходимым шагом, в противном случае кривая не создается. Однако, если ваши значения нормализованы до очень низких значений (близких к нулю), кривая также не выводится. Поэтому я умножил нормированный вектор на 10, чтобы масштабировать его до более крупных единиц. Тогда программа просто нашла кривую. Я не могу объяснить, почему, поскольку я - полный новичок в этом. Пожалуйста, обратите внимание, что это только мой личный опыт, я не говорю, что это правило.

Если я печатаю popt и pcov я получаю:

#> print popt 
[ 8.56332788e+01 6.53678132e-02] 

#> print pcov 
[[ 1.65450283e-01 1.27146184e-07] 
[ 1.27146184e-07 2.34426866e-06]] 

И documentation on curve_fit говорит, что эти параметры содержат «Оптимальные значения параметров так, чтобы сумма квадратов ошибок сведена к минимуму» и covariance предыдущего параметра.

Является ли какой-либо из этих 6 значений параметрами, характеризующими сигмовидную кривую? Потому что если так, то вопрос очень близок к решению! :-)

Большое спасибо!

+1

Параметры находятся в 'popt'. Вы строите кривую с помощью 'y = sigmoid (x, * popt)', которая вызывает 'sigmoid' те значения для двух последних параметров. – chthonicdaemon

+0

Итак, первый - это наклон (x0), а второй - перехват (k)? Благодаря! :-) – iamgin

1

Есть несколько причин, по которым упомянутый сюжет может выглядеть плохо.

Первый - это потому, что dictionary.values() возвращает значения в неупорядоченном порядке. Что произойдет, если вы делаете что-то вроде (непроверенные, потому что у меня нет вашего словаря):

target_pairs = sorted(dictionary.iteritems()) #should be a sorted list of (date, count) 
target_vector = [count for (date, count) in target_pairs] 

и посмотреть на полученный target_vector? Теперь он должен увеличиваться.

Получение оттуда к логистической функции требует немного больше работы: вам необходимо нормализовать target_vector, чтобы значения лежали в [0, 1], затем применили scipy.special.logit (который превращает сигмоид в [0, 1] в прямая линия), а затем вы можете найти подходящую для этого линию.Затем вы можете восстановить параметры вашей логистической модели:

y = C * sigmoid(m*x + b) 

Где m и b являются наклон и перехват от вашей линейной регрессии на преобразованных данных и C является вещь, которую вы разделены, когда вы нормализовал данные.

+0

Вы указали на очень важную деталь: заказ. В самом деле, я этого не думал, поэтому я модифицировал свой код с вашим предложением. Я также не знал, что требуется нормализовать данные по оси Y, но это имеет большой смысл, поэтому я сделал это. Однако мне не ясно, как получить параметры логистической модели (наклон, перехват и это число делителей). Я обновляю в новом посте свою текущую ситуацию. Благодаря! – iamgin

+1

О, хорошо, вы использовали 'curve_fit', который был другим методом, чем я ожидал, но может работать лучше. Значение 'popt' будет массивом' [x0, k] ', что делает' sigmoid (x [i], x0, k) 'максимально приближенным к' y [i] '- так это два параметра сигмовидной кривой. (Стандартная сигмовидная кривая возвращает числа в [0,1], хотя - поэтому, чтобы восстановить исходные данные, вам нужно умножить их на все, что вы разделили, чтобы нормализовать.Нормализация WRT заключается в масштабировании ваших данных, так что минимум равен 0, а максимум равен 1 - i.e., Делите 'y' на' y.max() '. Это то, что вы сделали?) –

+0

Итак, я понимаю, что x0 - это наклон, а k - перехват. И те 2 - это те, которые характеризуют кривую, которая наилучшим образом соответствует моим данным. Правильно ли это утверждение? :-) Что касается нормализации, я добавил несколько строк кода, чтобы вы могли видеть, как я выполнил эту операцию. Я чувствовал себя немного ленивым (T_T), чтобы начать делать оптимизацию самостоятельно, и я обнаружил функцию sklearn.preprocessing.normalize, которая, по-видимому, делает трюк. Поэтому я НЕ разделил намеренно, но мне пришлось масштабировать мои значения, потому что они были слишком малы для «curve_fit». Как вы думаете? Имеет ли это смысл? Большое спасибо! – iamgin

Смежные вопросы