सही तरीके से एक वक्र को कैसे चिकना करें?


200

मान लें कि हमारे पास एक डेटासेट है, जिसे लगभग दिया जा सकता है

import numpy as np
x = np.linspace(0,2*np.pi,100)
y = np.sin(x) + np.random.random(100) * 0.2

इसलिए हमारे पास 20% डेटासेट की विविधता है। मेरा पहला विचार यह था कि यूनीवेरटेस्पलाइन फ़ंक्शन का उपयोग स्कैपी के लिए किया जाता है, लेकिन समस्या यह है कि यह छोटे शोर को अच्छे तरीके से नहीं मानता है। यदि आप आवृत्तियों पर विचार करते हैं, तो पृष्ठभूमि सिग्नल की तुलना में बहुत छोटा है, इसलिए केवल कटऑफ की एक सीमा एक विचार हो सकती है, लेकिन इसमें पीछे और आगे फूरियर परिवर्तन शामिल होगा, जिसके परिणामस्वरूप बुरा व्यवहार हो सकता है। एक और तरीका एक चलती औसत होगा, लेकिन इसमें देरी के सही विकल्प की भी आवश्यकता होगी।

कोई संकेत / किताबें या लिंक कैसे इस समस्या से निपटने के लिए?

उदाहरण


1
क्या आपका संकेत हमेशा एक साइन लहर होगा, या आप केवल एक उदाहरण के लिए उपयोग कर रहे थे?
मार्क रैनसम

नहीं, मेरे अलग-अलग संकेत होंगे, इस आसान उदाहरण में भी यह स्पष्ट है कि मेरे तरीके पर्याप्त नहीं हैं
varantir

कलमन फ़िल्टरिंग इस मामले के लिए इष्टतम है। और pykalman python पैकेज अच्छी गुणवत्ता का है।
toine

हो सकता है कि जब मैंने थोड़ा और समय दिया हो, तो मैं इसका पूरा जवाब दूंगा, लेकिन एक शक्तिशाली प्रतिगमन विधि जिसका अभी तक उल्लेख नहीं किया गया है वह है जीपी (गॉसियन प्रोसेस) प्रतिगमन।
Ori5678

जवाबों:


262

मैं एक सविट्ज़की-गोले फ़िल्टर पसंद करता हूं । यह एक बहुपद पर अपने डेटा की एक छोटी खिड़की को पुनः प्राप्त करने के लिए कम से कम वर्गों का उपयोग करता है, फिर खिड़की के केंद्र में बिंदु का अनुमान लगाने के लिए बहुपद का उपयोग करता है। अंत में विंडो को एक डेटा बिंदु द्वारा आगे स्थानांतरित किया जाता है और प्रक्रिया दोहराती है। यह तब तक जारी रहता है जब तक कि प्रत्येक बिंदु को अपने पड़ोसियों के सापेक्ष बेहतर रूप से समायोजित नहीं किया जाता है। यह गैर-आवधिक और गैर-रैखिक स्रोतों से शोर के नमूनों के साथ भी बढ़िया काम करता है।

यहाँ एक पूरी तरह से रसोई की किताब का उदाहरण है । उपयोग करने में कितना आसान है, इसका अंदाजा लगाने के लिए नीचे मेरा कोड देखें। नोट: मैंने savitzky_golay()फ़ंक्शन को परिभाषित करने के लिए कोड छोड़ दिया है क्योंकि आप सचमुच रसोई की किताब के उदाहरण से कॉपी / पेस्ट कर सकते हैं जिसे मैंने ऊपर जोड़ा था।

import numpy as np
import matplotlib.pyplot as plt

x = np.linspace(0,2*np.pi,100)
y = np.sin(x) + np.random.random(100) * 0.2
yhat = savitzky_golay(y, 51, 3) # window size 51, polynomial order 3

plt.plot(x,y)
plt.plot(x,yhat, color='red')
plt.show()

एक शोर साइनस को आसानी से चौरसाई करना

अद्यतन: यह मेरे ध्यान में आया है कि मैंने जिस रसोई की किताब को जोड़ा है, वह नीचे दी गई है। सौभाग्य से, सावित्स्की-गोले फ़िल्टर को SciPy लाइब्रेरी में शामिल किया गया है , जैसा कि @dodohjk द्वारा बताया गया है । SciPy स्रोत का उपयोग करके उपरोक्त कोड को अनुकूलित करने के लिए, टाइप करें:

from scipy.signal import savgol_filter
yhat = savgol_filter(y, 51, 3) # window size 51, polynomial order 3

मुझे त्रुटि Traceback (सबसे हाल ही में अंतिम कॉल) मिली: फ़ाइल "hp.py", पंक्ति 79, में <मॉड्यूल> ysm2 = savitzky_golay (y_data, 51,3) फ़ाइल "hp.py", पंक्ति 42, savitzky_golay प्रथम अंतराल में = y [०] - np.abs (y [१: half_window + १] [:: - १] - y [०])
मार्च हो


14
Savitzky-Golay फ़िल्टर शुरू करने के लिए धन्यवाद! तो मूल रूप से यह एक नियमित "मूविंग एवरेज" फिल्टर की तरह है, लेकिन केवल औसत की गणना करने के बजाय, हर बिंदु के लिए एक बहुपद (आमतौर पर 2 या 4 वां क्रम) फिट किया जाता है, और केवल "मध्य" बिंदु चुना जाता है। चूंकि 2nd (या 4th) ऑर्डर की जानकारी हर बिंदु पर संबंधित है, स्थानीय मैक्सिमा या मिनीमा में "मूविंग एवरेज" दृष्टिकोण में पेश किए गए पूर्वाग्रह को दरकिनार किया जाता है। वास्तव में सुरुचिपूर्ण।
एनपी n

2
बस इसके लिए आपको धन्यवाद कहना चाहता हूं, मैं पागल हो गया हूं कि स्मूथ डेटा प्राप्त करने के लिए वेवलेट डिकम्पोजिशन का पता लगाने की कोशिश कर रहा हूं, और यह बहुत अच्छा है।
एल्डर एम।

5
यदि x डेटा नियमित रूप से नहीं फैलाया जाता है, तो आप फ़िल्टर को x के साथ भी लागू करना चाह सकते हैं savgol_filter((x, y), ...):।
टिम कूइपर

127

चलती औसत बॉक्स (कनवल्शन द्वारा) के आधार पर, मेरे द्वारा उपयोग किए जाने वाले डेटा को सुचारू करने का एक त्वरित और गंदा तरीका:

x = np.linspace(0,2*np.pi,100)
y = np.sin(x) + np.random.random(100) * 0.8

def smooth(y, box_pts):
    box = np.ones(box_pts)/box_pts
    y_smooth = np.convolve(y, box, mode='same')
    return y_smooth

plot(x, y,'o')
plot(x, smooth(y,3), 'r-', lw=2)
plot(x, smooth(y,19), 'g-', lw=2)

यहां छवि विवरण दर्ज करें


9
इसके कुछ अच्छे फायदे हैं: (1) किसी भी फ़ंक्शन के लिए काम करता है, न कि केवल आवधिक, और (2) कोई निर्भरता या बड़े कार्यों को कॉपी-पेस्ट करने के लिए नहीं। आप इसे शुद्ध Numpy के साथ ठीक कर सकते हैं। इसके अलावा, यह बहुत गंदा नहीं है - यह ऊपर वर्णित कुछ अन्य तरीकों में से सबसे सरल मामला है (जैसे LOWESS लेकिन कर्नेल एक तेज अंतराल है और Savitzky-Golay की तरह है लेकिन बहुपद डिग्री शून्य है)।
जिम पीवार्स्की

2
चलती औसत के साथ एकमात्र समस्या यह है कि यह डेटा से पीछे है। आप इसे सबसे स्पष्ट रूप से अंत में देख सकते हैं जहां शीर्ष पर अधिक डॉट्स हैं और निचले हिस्से में कम है, लेकिन वर्तमान में हरे रंग की वक्र औसत से नीचे है क्योंकि विंडो फ़ंक्शन को उन खातों को लेने के लिए आगे बढ़ना है।
6

1
और यह एनडी सरणी पर काम नहीं करता है, केवल 1 डी। scipy.ndimage.filters.convolve1d()आपको फ़िल्टर करने के लिए nd-array का एक अक्ष निर्दिष्ट करने की अनुमति देता है। लेकिन मुझे लगता है कि दोनों नकाबपोश मूल्यों में कुछ मुद्दों से पीड़ित हैं।
जेसन

1
@nurettin मुझे लगता है कि आप जो वर्णन कर रहे हैं वह बढ़त प्रभाव है। सामान्य तौर पर, जब तक कन्वेक्शन कर्नेल सिग्नल के भीतर अपनी सीमा को कवर करने में सक्षम होता है, तब तक यह "पीछे नहीं" होता है जैसा कि आप कहते हैं। अंत में, हालांकि, औसत में शामिल करने के लिए 6 से परे कोई मूल्य नहीं हैं, इसलिए कर्नेल के केवल "बाएं" भाग का उपयोग किया जा रहा है। हर स्मूदिंग कर्नेल में एज इफेक्ट मौजूद होते हैं और इन्हें अलग से हैंडल किया जाना चाहिए।
जॉन

4
@nurettin नहीं, मैं इसे पढ़ने वाले अन्य लोगों के लिए स्पष्ट करने की कोशिश कर रहा था कि आपकी टिप्पणी "चलती औसत के साथ एकमात्र समस्या यह है कि यह डेटा के पीछे पिछड़ जाता है" भ्रामक है। कोई भी विंडो-फ़िल्टर विधि इस समस्या से ग्रस्त है, न कि केवल चलती औसत। सवित्स्की-गोले इस समस्या से भी ग्रस्त हैं। इसलिए आपका कथन "मैं जो वर्णन कर रहा हूं वह अनुमान के अनुसार एक प्रकार की मछली समझती है" केवल गलत है। या तो चौरसाई विधि को किनारों को संभालने का एक तरीका चाहिए जो कि चौरसाई विधि से स्वतंत्र हो।
जॉन

79

यदि आप एक संकेत के "चिकनी" संस्करण में रुचि रखते हैं जो आवधिक है (आपके उदाहरण की तरह), तो एक एफएफटी जाने का सही तरीका है। फूरियर रूपांतरण करें और कम योगदान वाली आवृत्तियों को घटाएं:

import numpy as np
import scipy.fftpack

N = 100
x = np.linspace(0,2*np.pi,N)
y = np.sin(x) + np.random.random(N) * 0.2

w = scipy.fftpack.rfft(y)
f = scipy.fftpack.rfftfreq(N, x[1]-x[0])
spectrum = w**2

cutoff_idx = spectrum < (spectrum.max()/5)
w2 = w.copy()
w2[cutoff_idx] = 0

y2 = scipy.fftpack.irfft(w2)

यहां छवि विवरण दर्ज करें

यहां तक ​​कि अगर आपका संकेत पूरी तरह से आवधिक नहीं है, तो यह सफेद शोर को घटाने का एक बड़ा काम करेगा। उपयोग करने के लिए कई प्रकार के फिल्टर (हाई-पास, लो-पास, आदि ...), उपयुक्त वही है जो आप देख रहे हैं पर निर्भर है।


कौन सा प्लाट किस चर के लिए है? मैं एक रैली में टेनिस बॉल के लिए निर्देशांक को सुचारू करने की कोशिश कर रहा हूं, अर्थात। मेरे प्लॉट पर थोड़ा
परवल

44

आपके डेटा पर एक मूविंग एवरेज फिट करने से शोर शांत हो जाएगा, इस उत्तर को देखें कि यह कैसे करना है।

यदि आप अपने डेटा को फिट करने के लिए LOWESS का उपयोग करना चाहते हैं (यह एक चलती औसत लेकिन अधिक परिष्कृत के समान है), तो आप यह कर सकते हैं कि आप डेटास्मोडेल लाइब्रेरी का उपयोग कर सकते हैं :

import numpy as np
import pylab as plt
import statsmodels.api as sm

x = np.linspace(0,2*np.pi,100)
y = np.sin(x) + np.random.random(100) * 0.2
lowess = sm.nonparametric.lowess(y, x, frac=0.1)

plt.plot(x, y, '+')
plt.plot(lowess[:, 0], lowess[:, 1])
plt.show()

अंत में, यदि आप अपने सिग्नल के कार्यात्मक रूप को जानते हैं, तो आप अपने डेटा के लिए एक वक्र फिट कर सकते हैं, जो संभवत: सबसे अच्छी बात होगी।


अगर केवल loessलागू किया था ।
छानबीन

18

एक अन्य विकल्प स्टैटमोडेल में कर्नेल रीग का उपयोग करना है :

from statsmodels.nonparametric.kernel_regression import KernelReg
import numpy as np
import matplotlib.pyplot as plt

x = np.linspace(0,2*np.pi,100)
y = np.sin(x) + np.random.random(100) * 0.2

# The third parameter specifies the type of the variable x;
# 'c' stands for continuous
kr = KernelReg(y,x,'c')
plt.plot(x, y, '+')
y_pred, y_std = kr.fit(x)

plt.plot(x, y_pred)
plt.show()

7

इसकी जांच करें! 1 डी सिग्नल को चौरसाई करने की स्पष्ट परिभाषा है।

http://scipy-cookbook.readthedocs.io/items/SignalSmooth.html

छोटा रास्ता:

import numpy

def smooth(x,window_len=11,window='hanning'):
    """smooth the data using a window with requested size.

    This method is based on the convolution of a scaled window with the signal.
    The signal is prepared by introducing reflected copies of the signal 
    (with the window size) in both ends so that transient parts are minimized
    in the begining and end part of the output signal.

    input:
        x: the input signal 
        window_len: the dimension of the smoothing window; should be an odd integer
        window: the type of window from 'flat', 'hanning', 'hamming', 'bartlett', 'blackman'
            flat window will produce a moving average smoothing.

    output:
        the smoothed signal

    example:

    t=linspace(-2,2,0.1)
    x=sin(t)+randn(len(t))*0.1
    y=smooth(x)

    see also: 

    numpy.hanning, numpy.hamming, numpy.bartlett, numpy.blackman, numpy.convolve
    scipy.signal.lfilter

    TODO: the window parameter could be the window itself if an array instead of a string
    NOTE: length(output) != length(input), to correct this: return y[(window_len/2-1):-(window_len/2)] instead of just y.
    """

    if x.ndim != 1:
        raise ValueError, "smooth only accepts 1 dimension arrays."

    if x.size < window_len:
        raise ValueError, "Input vector needs to be bigger than window size."


    if window_len<3:
        return x


    if not window in ['flat', 'hanning', 'hamming', 'bartlett', 'blackman']:
        raise ValueError, "Window is on of 'flat', 'hanning', 'hamming', 'bartlett', 'blackman'"


    s=numpy.r_[x[window_len-1:0:-1],x,x[-2:-window_len-1:-1]]
    #print(len(s))
    if window == 'flat': #moving average
        w=numpy.ones(window_len,'d')
    else:
        w=eval('numpy.'+window+'(window_len)')

    y=numpy.convolve(w/w.sum(),s,mode='valid')
    return y




from numpy import *
from pylab import *

def smooth_demo():

    t=linspace(-4,4,100)
    x=sin(t)
    xn=x+randn(len(t))*0.1
    y=smooth(x)

    ws=31

    subplot(211)
    plot(ones(ws))

    windows=['flat', 'hanning', 'hamming', 'bartlett', 'blackman']

    hold(True)
    for w in windows[1:]:
        eval('plot('+w+'(ws) )')

    axis([0,30,0,1.1])

    legend(windows)
    title("The smoothing windows")
    subplot(212)
    plot(x)
    plot(xn)
    for w in windows:
        plot(smooth(xn,10,w))
    l=['original signal', 'signal with noise']
    l.extend(windows)

    legend(l)
    title("Smoothing a noisy signal")
    show()


if __name__=='__main__':
    smooth_demo()

3
किसी समाधान का लिंक स्वागत योग्य है, लेकिन कृपया सुनिश्चित करें कि आपका उत्तर इसके बिना उपयोगी है: लिंक के चारों ओर संदर्भ जोड़ें ताकि आपके साथी उपयोगकर्ताओं को कुछ पता चले कि यह क्या है और यह क्यों है, तो पृष्ठ के सबसे प्रासंगिक हिस्से को उद्धृत करें ' लक्ष्य पृष्ठ अनुपलब्ध होने की स्थिति में पुनः लिंक करना। एक लिंक से थोड़ा अधिक उत्तर देने वाले उत्तर हटाए जा सकते हैं।
श्री

-4

यदि आप समय श्रृंखला ग्राफ की साजिश रच रहे हैं और यदि आपने रेखांकन के लिए mtplotlib का उपयोग किया है तो ग्राफ को सुचारू करने के लिए माध्यिका विधि का उपयोग करें

smotDeriv = timeseries.rolling(window=20, min_periods=5, center=True).median()

जहाँ timeseriesआपका डेटा सेट किया गया है आप windowsizeअधिक सुचारू रूप से बदल सकते हैं ।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.