Source code for VolterraBasis.basis._basis_features

"""
This the main estimator module
"""
import numpy as np

import scipy.interpolate
import scipy.stats
from ._data_describe import quick_describe, minimal_describe

from sklearn.base import TransformerMixin


[docs]class LinearFeatures(TransformerMixin): """ Linear function """ def __init__(self, to_center=False): """""" self.centered = to_center self.const_removed = False def fit(self, describe_result): if isinstance(describe_result, np.ndarray): describe_result = minimal_describe(describe_result) self.n_output_features_ = describe_result.mean.shape[0] self.dim_out_basis = 1 if self.centered: self.mean_ = describe_result.mean else: self.mean_ = np.zeros((self.n_output_features_,)) return self def basis(self, X): return X - self.mean_ def deriv(self, X, deriv_order=1): nsamples, dim = X.shape grad = np.zeros((nsamples, dim) + (dim,) * deriv_order) if deriv_order == 1: for i in range(dim): grad[..., i, i] = 1.0 return grad def hessian(self, X): return self.deriv(X, deriv_order=2) def antiderivative(self, X, order=1): return 0.5 * np.power(X, 2)
[docs]class PolynomialFeatures(TransformerMixin): """ Wrapper for numpy polynomial series. """
[docs] def __init__(self, deg=1, polynom=np.polynomial.Polynomial, remove_const=True): """ Providing a numpy polynomial class via polynom keyword allow to change polynomial type. """ self.degree = deg + 1 self.polynom = polynom self.const_removed = remove_const
def fit(self, describe_result): if isinstance(describe_result, np.ndarray): describe_result = quick_describe(describe_result) self.n_output_features_ = describe_result.mean.shape[0] * self.degree self.dim_out_basis = 1 return self def basis(self, X): nsamples, dim = X.shape features = np.zeros((nsamples, dim * self.degree)) for n in range(0, self.degree): istart = n * dim iend = (n + 1) * dim features[:, istart:iend] = self.polynom.basis(n)(X) # print("Basis", X.shape, features.shape) return features def deriv(self, X, deriv_order=1): nsamples, dim = X.shape with_const = int(self.const_removed) features = np.zeros((nsamples, dim * (self.degree - with_const)) + (dim,) * deriv_order) for n in range(with_const, self.degree): istart = (n - with_const) * dim for i in range(dim): features[(Ellipsis, slice(istart + i, istart + i + 1)) + (i,) * deriv_order] = self.polynom.basis(n).deriv(deriv_order)(X[:, slice(i, i + 1)]) return features def hessian(self, X): return self.deriv(X, deriv_order=2) def antiderivative(self, X, order=1): nsamples, dim = X.shape features = np.zeros((nsamples, dim * self.degree)) for n in range(0, self.degree): istart = n * dim iend = (n + 1) * dim features[:, istart:iend] = self.polynom.basis(n).integ(order)(X) return features
[docs]class FourierFeatures(TransformerMixin): """ Fourier series. """
[docs] def __init__(self, order=1, freq=1.0, remove_const=True): """ Parameters ---------- order : int Order of the Fourier series freq: float Base frequency """ self.order = 2 * order + 1 self.freq = freq self.const_removed = remove_const
def fit(self, describe_result): if isinstance(describe_result, np.ndarray): describe_result = quick_describe(describe_result) self.n_output_features_ = describe_result.mean.shape[0] * self.order self.dim_out_basis = 1 return self def basis(self, X): nsamples, dim = X.shape features = np.zeros((nsamples, dim * self.order)) for n in range(0, self.order): istart = n * dim iend = (n + 1) * dim if n == 0: features[:, istart:iend] = np.ones_like(X) / np.sqrt(2 * np.pi) elif n % 2 == 0: # print(n / 2) features[:, istart:iend] = np.cos(n / 2 * X * self.freq) / np.sqrt(np.pi) else: # print((n + 1) / 2) features[:, istart:iend] = np.sin((n + 1) / 2 * X * self.freq) / np.sqrt(np.pi) return features def deriv(self, X, deriv_order=1): if deriv_order == 2: return self.hessian(X) nsamples, dim = X.shape with_const = int(self.const_removed) features = np.zeros((nsamples, dim * (self.order - with_const)) + (dim,) * deriv_order) for n in range(with_const, self.order): istart = (n - with_const) * dim for i in range(dim): if n % 2 == 0: features[(Ellipsis, slice(istart + i, istart + i + 1)) + (i,) * deriv_order] = -n / 2 * self.freq * np.sin(n / 2 * self.freq * X[:, slice(i, i + 1)]) / np.sqrt(np.pi) else: features[(Ellipsis, slice(istart + i, istart + i + 1)) + (i,) * deriv_order] = (n + 1) / 2 * self.freq * np.cos((n + 1) / 2 * self.freq * X[:, slice(i, i + 1)]) / np.sqrt(np.pi) return features def hessian(self, X): nsamples, dim = X.shape with_const = int(self.const_removed) features = np.zeros((nsamples, dim * (self.order - with_const)) + (dim,) * 2) for n in range(with_const, self.order): istart = (n - with_const) * dim for i in range(dim): if n % 2 == 0: features[(Ellipsis, slice(istart + i, istart + i + 1)) + (i,) * 2] = -(((n / 2) * self.freq) ** 2) * np.cos(n / 2 * self.freq * X[:, slice(i, i + 1)]) / np.sqrt(np.pi) else: features[(Ellipsis, slice(istart + i, istart + i + 1)) + (i,) * 2] = -(((n + 1) / 2 * self.freq) ** 2) * np.sin((n + 1) / 2 * self.freq * X[:, slice(i, i + 1)]) / np.sqrt(np.pi) return features def antiderivative(self, X, order=1): if order > 1: raise NotImplementedError nsamples, dim = X.shape features = np.zeros((nsamples, dim * self.order)) for n in range(0, self.order): istart = n * dim iend = (n + 1) * dim if n == 0: features[:, istart:iend] = X / np.sqrt(2 * np.pi) elif n % 2 == 0: # print(n / 2) features[:, istart:iend] = np.sin(n / 2 * X * self.freq) / (np.sqrt(np.pi) * n / 2 * self.freq) else: # print((n + 1) / 2) features[:, istart:iend] = -1 * np.cos((n + 1) / 2 * X * self.freq) / (np.sqrt(np.pi) * (n + 1) / 2 * self.freq) return features
[docs]class SplineFctFeatures(TransformerMixin): """ A single basis function that is given from splines fit of data """
[docs] def __init__(self, knots, coeffs, k=3, periodic=False): self.periodic = periodic self.k = k self.t = knots # knots are position along the axis of the knots self.c = coeffs self.const_removed = False self.dim_out_basis = 1
def fit(self, describe_result): if isinstance(describe_result, np.ndarray): describe_result = quick_describe(describe_result) self.spl_ = scipy.interpolate.BSpline(self.t, self.c, self.k) self.n_output_features_ = describe_result.mean.shape[0] return self def basis(self, X): return self.spl_(X) def deriv(self, X, deriv_order=1): nsamples, dim = X.shape grad = np.zeros((nsamples, dim) + (dim,) * deriv_order) for i in range(dim): grad[(Ellipsis, slice(i, i + 1)) + (i,) * (deriv_order)] = self.spl_.derivative(deriv_order)(X[:, slice(i, i + 1)]) return grad def hessian(self, X): return self.deriv(X, deriv_order=2) def antiderivative(self, X, order=1): return self.spl_.antiderivative(order)(X)
[docs]class FeaturesCombiner(TransformerMixin): """ Allow to combine features to build composite basis """
[docs] def __init__(self, *basis): self.basis_set = basis self.const_removed = np.any([b.const_removed for b in self.basis_set]) # Check if one of the basis set have the constant removed
def fit(self, describe_result): if isinstance(describe_result, np.ndarray): describe_result = scipy.stats.describe(describe_result) for b in self.basis_set: b.fit(describe_result) self.n_output_features_ = np.sum([b.n_output_features_ for b in self.basis_set]) self.dim_out_basis = 1 return self def basis(self, X): features = self.basis_set[0].basis(X) for b in self.basis_set[1:]: features = np.concatenate((features, b.basis(X)), axis=1) return features def deriv(self, X, deriv_order=1): grad = self.basis_set[0].deriv(X, deriv_order=deriv_order) for b in self.basis_set[1:]: # print(grad.shape, b.deriv(X, deriv_order=deriv_order).shape) features = np.concatenate((grad, b.deriv(X, deriv_order=deriv_order)), axis=1) return features def hessian(self, X): return self.deriv(X, deriv_order=2) def antiderivative(self, X, order=1): raise NotImplementedError("Don't try this")
# class SplineFctWithLinFeatures(TransformerMixin): # """ # Combine a basis function that is given from splines fit of data with linear function # """ # # def __init__(self, knots, coeffs, k=3, periodic=False): # self.periodic = periodic # self.k = k # self.t = knots # knots are position along the axis of the knots # self.c = coeffs # # def fit(self, X, y=None): # nsamples, dim = X.shape # self.spl_ = scipy.interpolate.BSpline(self.t, self.c, self.k) # self.n_output_features_ = 2 * dim # return self # # def basis(self, X): # return np.concatenate((X, self.spl_(X)), axis=1) # # def deriv(self, X, deriv_order=1): # if deriv_order == 1: # lin_deriv = np.ones_like(X) # else: # lin_deriv = np.zeros_like(X) # return np.concatenate((lin_deriv, self.spl_.derivative(deriv_order)(X)), axis=1) # # def hessian(self, X): # return self.deriv(X, deriv_order=2) # # def antiderivative(self, X, order=1): # return self.spl_.antiderivative(order)(X) if __name__ == "__main__": # pragma: no cover import matplotlib.pyplot as plt from scipy.stats import describe x_range = np.linspace(-10, 10, 30).reshape(-1, 2) # b2 = LinearFeatures() basis = FourierFeatures(order=3, freq=1.0) # b1 = SplineFctFeatures(knots=np.linspace(-1, 1, 8), coeffs=np.logspace(1, 2, 8), k=2) # basis = FeaturesCombiner(b1, b2) basis.fit(describe(x_range)) print(x_range.shape) print("Basis") print(basis.basis(x_range).shape) print("Deriv") print(basis.deriv(x_range).shape) print(basis.deriv(x_range)[0, :, :]) print("Hessian") print(basis.hessian(x_range).shape) # Plot basis x_range = np.linspace(-2, 2, 50).reshape(-1, 1) basis = basis.fit(x_range) # basis = LinearFeatures().fit(x_range) y = basis.basis(x_range) plt.grid() for n in range(y.shape[1]): plt.plot(x_range[:, 0], y[:, n]) plt.show()