scipy.interpolate.UnivariateSpline не сглаживается независимо от параметров

У меня возникли проблемы с получением scipy.interpolate.UnivariateSpline для использования любого сглаживания при интерполяции. Основываясь на странице функции, а также на некоторых предыдущих сообщениях , я считаю, что она должна обеспечивать сглаживание с помощью параметра s .

Вот мой код:

 # Imports import scipy import pylab # Set up and plot actual data x = [0, 5024.2059124920379, 7933.1645067836089, 7990.4664106277542, 9879.9717114947653, 13738.60563208926, 15113.277958924193] y = [0.0, 3072.5653360000988, 5477.2689107965398, 5851.6866463790966, 6056.3852496014106, 7895.2332350173638, 9154.2956175610598] pylab.plot(x, y, "o", label="Actual") # Plot estimates using splines with a range of degrees for k in range(1, 4): mySpline = scipy.interpolate.UnivariateSpline(x=x, y=y, k=k, s=2) xi = range(0, 15100, 20) yi = mySpline(xi) pylab.plot(xi, yi, label="Predicted k=%d" % k) # Show the plot pylab.grid(True) pylab.xticks(rotation=45) pylab.legend( loc="lower right" ) pylab.show() 

Вот результат:

Сплайны без сглаживания

Я пробовал это с диапазоном значений s (0,01, 0,1, 1, 2, 5, 50), а также явными весами, установленными либо одинаково (1.0), либо рандомизированными. Я все еще не могу сгладить, и количество узлов всегда совпадает с количеством точек данных. В частности, я искал выбросы, подобные этому 4-му пункту (7990.4664106277542, 5851.6866463790966), чтобы сглаживаться.

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

    Короткий ответ: вам нужно выбрать значение для s более тщательно.

    В документации для UnivariateSpline указано, что:

     Positive smoothing factor used to choose the number of knots. Number of knots will be increased until the smoothing condition is satisfied: sum((w[i]*(y[i]-s(x[i])))**2,axis=0) <= s 

    Из этого можно вывести, что «разумные» значения для сглаживания, если вы не проходите в явных весах, находятся вокруг s = m * v где m – количество точек данных и v – дисперсия данных. В этом случае s_good ~ 5e7 .

    EDIT : разумные значения для s зависят, конечно, от уровня шума в данных. Документы, похоже, рекомендуют выбирать s в диапазоне (m - sqrt(2*m)) * std**2 <= s <= (m + sqrt(2*m)) * std**2 где std является стандартным отклонение, связанное с «шумом», который вы хотите сгладить.

    @ Ответ Женя на ручные настройки узлов между точками данных был слишком грубым, чтобы обеспечить хорошие результаты в шумных данных, не будучи выборочным о том, как применяется этот метод. Однако, воодушевленный его предложением, у меня был успех с кластеризацией Mean-Shift из пакета scikit-learn. Он выполняет автоматическое определение количества кластеров и, как представляется, выполняет довольно хорошую работу сглаживания (на самом деле очень гладкая).

     # Imports import numpy import pylab import scipy import sklearn.cluster # Set up original data - note that it's monotonically increasing by X value! data = {} data['original'] = {} data['original']['x'] = [0, 5024.2059124920379, 7933.1645067836089, 7990.4664106277542, 9879.9717114947653, 13738.60563208926, 15113.277958924193] data['original']['y'] = [0.0, 3072.5653360000988, 5477.2689107965398, 5851.6866463790966, 6056.3852496014106, 7895.2332350173638, 9154.2956175610598] # Cluster data, sort it and and save inputNumpy = numpy.array([[data['original']['x'][i], data['original']['y'][i]] for i in range(0, len(data['original']['x']))]) meanShift = sklearn.cluster.MeanShift() meanShift.fit(inputNumpy) clusteredData = [[pair[0], pair[1]] for pair in meanShift.cluster_centers_] clusteredData.sort(lambda pair1, pair2: cmp(pair1[0],pair2[0])) data['clustered'] = {} data['clustered']['x'] = [pair[0] for pair in clusteredData] data['clustered']['y'] = [pair[1] for pair in clusteredData] # Build a spline using the clustered data and predict mySpline = scipy.interpolate.UnivariateSpline(x=data['clustered']['x'], y=data['clustered']['y'], k=1) xi = range(0, round(max(data['original']['x']), -3) + 3000, 20) yi = mySpline(xi) # Plot the datapoints pylab.plot(data['clustered']['x'], data['clustered']['y'], "D", label="Datapoints (%s)" % 'clustered') pylab.plot(xi, yi, label="Predicted (%s)" % 'clustered') pylab.plot(data['original']['x'], data['original']['y'], "o", label="Datapoints (%s)" % 'original') # Show the plot pylab.grid(True) pylab.xticks(rotation=45) pylab.legend( loc="lower right" ) pylab.show() 

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

    Хотя я не знаю какой-либо библиотеки, которая будет делать это для вас, я бы попробовал немного более DIY-подход: я бы начал с создания сплайна с узлами между необработанными точками данных, как в x и в y . В вашем конкретном примере, имея единственный узел между 4-м и 5-м точками, должен сделать трюк, так как он удалит огромную производную около x=8000 .