Last active
October 29, 2017 01:54
-
-
Save 13steinj/3775b363816b861b57afa79dcd027761 to your computer and use it in GitHub Desktop.
Some functions that are used to fit functions, yo
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"""A calculation of functions using python. | |
Any mathematical asumptions need to be either stated, | |
or given as fact. To optimize this file, mathematical | |
expressions are used to simplify things. For example, | |
using manipulation on eulers formula, | |
cosh(x) = cos(ix), and cos(x) = cosh(ix). This would | |
greatly simplify the latter regression by induction. | |
""" | |
import numpy | |
import scipy.optimize | |
import scipy.stats | |
class models(object): | |
@staticmethod | |
def sin(t, A, B, C, D): | |
return A * numpy.sin(B * t + C) + D | |
@staticmethod | |
def cos(t, A, B, C, D): | |
return A * numpy.cos(B * t + C) + D | |
@staticmethod | |
def mono(t, A, B): | |
return A * t + B | |
class _regressor(object): | |
"""Base class for regression functions. | |
All regressors must return a dictionary with the keys [ | |
"model", | |
"fit_func", | |
"stderr", | |
] | |
Most Regressors will also have the following in the dict [ | |
"vertical stretch", | |
"horizontal stretch", | |
"horizontal shift", | |
"vertical shift", | |
] | |
Note: in some of the regressions, such as the monomial linear | |
regression, the vertical and horizontal components are | |
equal to each other by induction. | |
Note: Some other function specific keys may be returned. | |
These may be manipulations of, or aliases to, one of the | |
above seven keys. | |
""" | |
@classmethod | |
def fit(cls, model, t_data, y_data): | |
return scipy.optimize.curve_fit(model, t_data, y_data) | |
@classmethod | |
def fit_with_guess(cls, model, t_data, y_data, guess): | |
return scipy.optimize.curve_fit(model, t_data, y_data, p0=guess) | |
class trigonometric(_regressor): | |
"""Information and fitters for trigonometric regressions.""" | |
@classmethod | |
def _calculate_guess(cls, given_guess, use_guess, t_data, y_data, t_space): | |
if not use_guess: | |
return None | |
if given_guess is None or any(g is None for g in given_guess): | |
# perform a discrete fourier transform | |
ff = numpy.fft.fftfreq(len(t_data), t_space) | |
Fyy = abs(numpy.fft.fft(y_data)) | |
# exclude the zero frequency "peak", which is related to offset | |
guess_amp = given_guess[0] if given_guess and given_guess[0] is not None else \ | |
numpy.std(y_data) * 2.0 ** 0.5 | |
guess_horizontal_stretch = given_guess[1] if given_guess and given_guess[1] is not None else \ | |
2.0 * numpy.pi * abs(ff[numpy.argmax(Fyy[1:]) or 1]) | |
guess_phase_shift = given_guess[2] if given_guess and given_guess[2] is not None else \ | |
0.0 | |
guess_offset = given_guess[3] if given_guess and given_guess[3] is not None else \ | |
numpy.mean(y_data) | |
return numpy.array([guess_amp, guess_horizontal_stretch, | |
guess_phase_shift, guess_offset]) | |
@classmethod | |
def sin(cls, y_data, t_data=None, t_start=None, | |
t_end=None, guess=None, use_guess=True): | |
"""Fit sin to the input time sequence, and return fitting parameters. | |
:param y_data: The array-type of y coordinates. | |
:param t_data: The array of t coordinates. | |
Must be of the same length as y_data or None | |
:param t_start: The beginning t coordinate | |
:param t_end: The ending t coordinate | |
if t_data is None, it will be replaced with an array from | |
t_start to t_end, spaced by the length of y_data. | |
:param guess: an array of [guess amplitude, guess horizontal stretch, | |
guess horizontal shift, guess vertical shift] or None | |
Any of the parameters can also be None | |
:param use_guess: Boolean, default True. Whether or not to use the | |
guess in optimization. If True and guess is either incomplete | |
or None, calculate the guess. Only works if t_data is evenly spaced | |
""" | |
# make arrays of equal data amounts in time and price | |
if t_data is None: | |
t_data = numpy.linspace(t_start, t_end, len(y_data)) | |
t_even_space = t_data[1] - t_data[0] | |
else: | |
t_even_space = numpy.mean([ | |
t_data[i] - t_data[i-1] for i in range(1, len(t_data))]) | |
t_data = numpy.array(t_data) | |
y_data = numpy.array(y_data) | |
guess = cls._calculate_guess(guess, use_guess, t_data, y_data, t_even_space) | |
if use_guess: | |
popt, pcov = cls.fit_with_guess(models.sin, t_data, y_data, guess) | |
else: | |
popt, pcov = cls.fit(models.sin, t_data, y_data) | |
A, B, C, D = popt | |
f = B / (2. * numpy.pi) | |
return { | |
"model": models.sin, | |
"amplitude": A, | |
"vertical stretch": A, | |
"horizontal stretch": B, | |
"horizontal shift": C, | |
"vertical shift": D, | |
"freq": f, | |
"period": 1. / f, | |
"fit_func": lambda t: models.sin(t, A, B, C, D), | |
"stderr": numpy.sqrt(numpy.diag(pcov)), | |
} | |
@classmethod | |
def cos(cls, y_data, t_data=None, t_start=None, | |
t_end=None, guess=None, use_guess=True): | |
"""Fit cos to the input time sequence, and return fitting parameters. | |
:param y_data: The array-type of y coordinates. | |
:param t_data: The array of t coordinates. | |
Must be of the same length as y_data or None | |
:param t_start: The beginning t coordinate | |
:param t_end: The ending t coordinate | |
if t_data is None, it will be replaced with an array from | |
t_start to t_end, spaced by the length of y_data. | |
:param guess: an array of [guess amplitude, guess horizontal stretch, | |
guess horizontal shift, guess vertical shift] or None | |
Any of the parameters can also be None | |
:param use_guess: Boolean, default True. Whether or not to use the | |
guess in optimization. If True and guess is either incomplete | |
or None, calculate the guess. Only works if t_data is evenly spaced | |
""" | |
# make arrays of equal data amounts in time and price | |
if t_data is None: | |
t_data = numpy.linspace(t_start, t_end, len(y_data)) | |
t_even_space = t_data[1] - t_data[0] | |
else: | |
t_even_space = numpy.mean([ | |
t_data[i] - t_data[i-1] for i in range(1, len(t_data))]) | |
t_data = numpy.array(t_data) | |
y_data = numpy.array(y_data) | |
guess = cls._calculate_guess(guess, use_guess, t_data, y_data, t_even_space) | |
if use_guess: | |
popt, pcov = cls.fit_with_guess(models.cos, t_data, y_data, guess) | |
else: | |
popt, pcov = cls.fit(models.cos, t_data, y_data) | |
A, B, C, D = popt | |
f = B / (2. * numpy.pi) | |
return { | |
"model": models.cos, | |
"amplitude": A, | |
"vertical stretch": A, | |
"horizontal stretch": B, | |
"horizontal shift": C, | |
"vertical shift": D, | |
"freq": f, | |
"period": 1. / f, | |
"fit_func": lambda t: models.cos(t, A, B, C, D), | |
"stderr": numpy.sqrt(numpy.diag(pcov)), | |
} | |
class nomial(_regressor): | |
def mono(self, y_data, t_data): | |
"""Return a linear regression for t_data, y_data pairs. | |
:param y_data: An array-like value for y points | |
:param t_data: An array-like value for time points | |
Note: len(y_data) must == len(t_data) | |
""" | |
slope, intercept, r, p, stderr = scipy.stats.linregress(t_data, y_data) | |
return { | |
"model": models.mono, | |
"vertical stretch": slope, | |
"horizontal stretch": 1./slope, | |
"horizontal shift": -intercept, | |
"vertical shift": intercept, | |
"fit_func": lambda t: models.mono(t, slope, intercept), | |
"stderr": stderr, | |
"correlation_coefficient": r, | |
"2-sided P m>0": p | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment