Sine fitting


In [1]:
# Enable autoreloading of modules
%load_ext autoreload
%autoreload 2

In [2]:
import numpy as np
import scipy.optimize
import pandas as pd

import holoviews as hv
from holoviews import opts
hv.extension('bokeh', 'matplotlib')



In [3]:
# Set default figure size on curves
opts.defaults(opts.Curve( height=400, width=900 ,show_grid=True))

Fitting function


In [18]:
def fit_sin(tt, yy):
    """
    Fit sin to the input time sequence, and return fitting parameters "amp",
    "omega", "phase", "offset", "freq", "period" and "fitfunc"
    
    @param tt: independent variable, raw. List/array.
    @param yy: dependent variable, raw. List/array.
    @return: Struct of results. Dict.
    
    Source: https://stackoverflow.com/questions/16716302/how-do-i-fit-a-sine-curve-to-my-data-with-pylab-and-numpy
    
    """
    
    tt = np.array(tt)
    yy = np.array(yy)
    ff = np.fft.fftfreq(len(tt), (tt[1]-tt[0]))   # assume uniform spacing
    Fyy = np.fft.fft(yy)
    Fyy_phase = np.angle(Fyy)
    Fyy_mag = abs(Fyy)
    peak_index = np.argmax(Fyy_mag[1:])+1
    guess_freq = abs(ff[peak_index])  # exclude the zero freq "peak"
    guess_phase = Fyy_phase[peak_index]
    guess_amp = np.std(yy) * 2.**0.5
    guess_offset = np.mean(yy)
    guess = np.array([guess_amp, 2.*np.pi*guess_freq, guess_phase, guess_offset])
    
    def sinfunc(t, A, w, p, c):  return A * np.sin(w*t + p) + c
    popt, pcov = scipy.optimize.curve_fit(sinfunc, tt, yy, p0=guess)
    A, w, p, c = popt
    f = w/(2.*np.pi)
    fitfunc = lambda t: A * np.sin(w*t + p) + c
    return {"amp": A, "omega": w, "phase": p, "offset": c, "freq": f,
            "period": 1./f, "fitfunc": fitfunc, "maxcov": np.max(pcov),
            "rawres": (guess,popt,pcov)}

Test fit 1

Clean fit curve to fit


In [19]:
f1 = 2.5
phi1 = 0
A1 = 1
offs1 = 0

fs = 100
dt = 1/fs
N = 512

t = np.arange(0,N)*dt
y1 = A1*np.sin(2*np.pi*f1*t + phi1) + offs1

pFunc1 = hv.Curve((t,y1),('t','Time'),('y','amplitude'),label='Func1')

fit1 = fit_sin(t,y1)
pfit1 = hv.Curve((t,fit1['fitfunc'](t)),('t','Time'),('y','amplitude'),label='Fit1')

(pfit1*pFunc1).opts(legend_position='right',title='Fitting clean sine wave with no phase offset')


Out[19]:

Test fit 2

Sine wave with phase offset


In [20]:
f1 = 2.5
phi1 =  180*np.pi/180
A1 = 1
offs1 = 0

fs = 100
dt = 1/fs
N = 512

t = np.arange(0,N)*dt
y1 = A1*np.sin(2*np.pi*f1*t + phi1) + offs1

pFunc1 = hv.Curve((t,y1),('t','Time'),('y','amplitude'),label='Func1')

fit1 = fit_sin(t,y1)
pfit1 = hv.Curve((t,fit1['fitfunc'](t)),('t','Time'),('y','amplitude'),label='Fit1')

(pfit1*pFunc1).opts(legend_position='right',title='Fitting clean sine wave with phase offset')


Out[20]:

Test fit 3

Sine wave with phase offset and amplitude offset


In [22]:
f1 = 2.5
phi1 = 35
A1 = 1
offs1 = 0.7

fs = 100
dt = 1/fs
N = 512

t = np.arange(0,N)*dt
y1 = A1*np.sin(2*np.pi*f1*t + phi1) + offs1

pFunc1 = hv.Curve((t,y1),('t','Time'),('y','amplitude'),label='Func1')

fit1 = fit_sin(t,y1)
pfit1 = hv.Curve((t,fit1['fitfunc'](t)),('t','Time'),('y','amplitude'),label='Fit1')

(pfit1*pFunc1).opts(legend_position='right',title='Fitting clean sine wave with phase and amplitude offset')


Out[22]:

Test fit 4

Sine wave with phase offset, amplitude offset and amplitude noise


In [23]:
f1 = 2.5
phi1 = 35
A1 = 1
offs1 = 0.7

fs = 100
dt = 1/fs
N = 512

Amp = A1 + np.random.rand(N)*A1*0.2

t = np.arange(0,N)*dt
y1 = Amp*np.sin(2*np.pi*f1*t + phi1) + offs1

pFunc1 = hv.Curve((t,y1),('t','Time'),('y','amplitude'),label='Func1')

fit1 = fit_sin(t,y1)
pfit1 = hv.Curve((t,fit1['fitfunc'](t)),('t','Time'),('y','amplitude'),label='Fit1')

(pfit1*pFunc1).opts(legend_position='right',title='Fitting sine wave with phase & amplitude offset, amplitude noise')


Out[23]:

Test fit 5

Sine wave with

  • phase offset,
  • amplitude offset,
  • amplitude noise
  • offset noise

In [24]:
f1 = 2.5
phi1 = 35
A1 = 1
offs1 = 0.7

fs = 100
dt = 1/fs
N = 512

Amp = A1 + np.random.rand(N)*A1*0.2
offset = offs1 + np.random.rand(N)*offs1*0.2

t = np.arange(0,N)*dt
y1 = Amp*np.sin(2*np.pi*f1*t + phi1) + offset

pFunc1 = hv.Curve((t,y1),('t','Time'),('y','amplitude'),label='Func1')

fit1 = fit_sin(t,y1)
pfit1 = hv.Curve((t,fit1['fitfunc'](t)),('t','Time'),('y','amplitude'),label='Fit1')

(pfit1*pFunc1).opts(legend_position='right',title='Fitting sine wave with phase & amplitude offset, amplitude & offset noise ')


Out[24]:

Test fit 6

Sine wave with

  • phase offset,
  • amplitude offset,
  • amplitude noise
  • offset noise
  • phase offset noise

In [22]:
f1 = 2.5
phi1 = 180*np.pi/180
A1 = 1
offs1 = 0.7

fs = 100
dt = 1/fs
N = 512

Amp = A1 + np.random.rand(N)*A1*0.4
offset = offs1 + np.random.rand(N)*offs1*0.2
ph_offset = phi1 + np.random.rand(N)*phi1*0.3

t = np.arange(0,N)*dt
y1 = Amp*np.sin(2*np.pi*f1*t + ph_offset) + offset

pFunc1 = hv.Curve((t,y1),('t','Time'),('y','amplitude'),label='Func1')

fit1 = fit_sin(t,y1)
pfit1 = hv.Curve((t,fit1['fitfunc'](t)),('t','Time'),('y','amplitude'),label='Fit1')

(pfit1*pFunc1).opts(legend_position='right',title='Fitting sine wave with phase & amplitude offset, amplitude, offset & phase noise ')


Out[22]:

In [ ]: