Hoe een curve op de juiste manier gladstrijken?

Laten we aannemen dat we een dataset hebben die ongeveer door

wordt gegeven

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

Daarom hebben we een variatie van 20% van de dataset. Mijn eerste idee was om de UnivariatesPline-functie van Scipy te gebruiken, maar het probleem is dat dit op een goede manier niet het kleine geluid beschouwt. Als u de frequenties beschouwt, is de achtergrond veel kleiner dan het signaal, dus een spline alleen van de cutoff is misschien een idee, maar dat zou een terug en vierde vierierse transformatie omvatten, wat kan leiden tot slecht gedrag.
Een andere manier zou een bewegend gemiddelde zijn, maar dit zou ook de juiste keuze van de vertraging nodig hebben.

Alle hints / boeken of links Hoe dit probleem aan te pakken?


1, Autoriteit 100%

Ik geef de voorkeur aan een >Savitzky-golay filter . Het gebruikt het minst vierkanten om een ​​klein venster van uw gegevens op een polynoom te regresseren en gebruikt vervolgens het polynoom om het punt in het midden van het venster te schatten. Eindelijk wordt het venster naar voren gebleven door één gegevenspunt en het proces herhaalt. Dit gaat door tot elk punt optimaal is aangepast ten opzichte van zijn buren. Het werkt geweldig, zelfs met lawaaierige monsters van niet-periodieke en niet-lineaire bronnen.

Hier is een grondig kookboekvoorbeeld. Zie mijn code hieronder om een idee te krijgen van hoe gemakkelijk het is om te gebruiken. Opmerking: ik heb de code voor het definiëren van de functie savitzky_golay()weggelaten, omdat je deze letterlijk kunt kopiëren en plakken uit het kookboekvoorbeeld dat ik hierboven heb gelinkt.

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()

UPDATE:Het is mij opgevallen dat het kookboekvoorbeeld waarnaar ik linkte, is verwijderd. Gelukkig is het Savitzky-Golay-filter in de SciPy-bibliotheek, zoals aangegeven door @dodohjk(bedankt @bicarlsenvoor de bijgewerkte link).
Om de bovenstaande code aan te passen met behulp van de SciPy-bron, typt u:

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

Antwoord 2, autoriteit 47%

EDIT: kijk naar ditantwoord. Het gebruik van np.cumsumis veel sneller dan np.convolve

Een snelle en vuile manier om gegevens die ik gebruik soepel te laten verlopen, op basis van een voortschrijdend gemiddelde (door convolutie):

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)


Antwoord 3, autoriteit 27%

Als u geïnteresseerd bent in een “soepele” versie van een signaal dat periodiek is (zoals uw voorbeeld), dan is een FFT de juiste keuze. Neem de fouriertransformatie en trek de lage bijdragende frequenties af:

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)

Zelfs als je signaal niet volledig periodiek is, zal dit een uitstekende manier zijn om witte ruis weg te nemen. Er zijn veel soorten filters om te gebruiken (high-pass, low-pass, enz…), de juiste is afhankelijk van wat u zoekt.


Antwoord 4, autoriteit 17%

Het aanpassen van een voortschrijdend gemiddelde aan uw gegevens zou de ruis verminderen, zie dit dit antwoordvoor informatie over hoe u dat doet.

Als u LOWESSwilt gebruiken om uw gegevens aan te passen (het is vergelijkbaar met een gemiddeld maar geavanceerder), kunt u dat doen met behulp van de bibliotheek statsmodels:

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()

Ten slotte, als u de functionele vorm van uw signaal kent, kunt u een curve aan uw gegevens aanpassen, wat waarschijnlijk het beste is om te doen.


5, Autoriteit 3%

Controleer dit! Er is een duidelijke definitie van het gladstrijken van een 1D-signaal.

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

Snelkoppeling:

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()

Antwoord 6

Voor een project van mij moest ik intervallen maken voor het modelleren van tijdreeksen, en om de procedure efficiënter te maken heb ik tsmoothie: een python-bibliotheek voor het afvlakken van tijdreeksen en detectie van uitschieters op een gevectoriseerde manier.

Het biedt verschillende afvlakkingsalgoritmen samen met de mogelijkheid om intervallen te berekenen.

Hier gebruik ik een ConvolutionSmoothermaar je kunt het ook anderen testen.

import numpy as np
import matplotlib.pyplot as plt
from tsmoothie.smoother import *
x = np.linspace(0,2*np.pi,100)
y = np.sin(x) + np.random.random(100) * 0.2
# operate smoothing
smoother = ConvolutionSmoother(window_len=5, window_type='ones')
smoother.smooth(y)
# generate intervals
low, up = smoother.get_intervals('sigma_interval', n_sigma=2)
# plot the smoothed timeseries with intervals
plt.figure(figsize=(11,6))
plt.plot(smoother.smooth_data[0], linewidth=3, color='blue')
plt.plot(smoother.data[0], '.k')
plt.fill_between(range(len(smoother.data[0])), low[0], up[0], alpha=0.3)

Ik wijs ook op dat TSMoothie het glading van meerdere tijdserieën op een vectorized manier kan uitvoeren


7

Een bewegend gemiddelde, een snelle manier gebruiken (die ook werkt voor niet-biologische functies) is

def smoothen(x, winsize=5):
    return np.array(pd.Series(x).rolling(winsize).mean())[winsize-1:]

Deze code is gebaseerd op Https://towardsDatascience.com/Data-smatching-for-data-cient-visualisatie-the-goldilocks-trio-part-1-867765050615 . Daar worden ook geavanceerde oplossingen besproken.


8

Als u de grafiek van de tijdreeks plakt en als u MTPLotlib voor tekengrafieken hebt gebruikt, gebruikt u dan
Mediaan-methode om-en de grafiek

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

Waar timeseriesis uw Set Gegevens doorgegeven, kunt u wijzigen windowsizevoor meer smoothron.

Other episodes