Skip to content

Instantly share code, notes, and snippets.

@13steinj
Last active October 29, 2017 01:54
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save 13steinj/3775b363816b861b57afa79dcd027761 to your computer and use it in GitHub Desktop.
Save 13steinj/3775b363816b861b57afa79dcd027761 to your computer and use it in GitHub Desktop.
Some functions that are used to fit functions, yo
"""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