Source code for gigaanalysis.mfunc_trans

"""**Transformation Functions**

These are all functions that are applied to a :class:`.Data` class and 
return a similar object. These are either simple filters, transforms, or 
differentiation or intergeneration functions.
"""

from .data import *

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

from scipy.interpolate import interp1d  # For invert_x
from scipy.signal import savgol_filter  # For loess and deriv
from scipy.integrate import cumulative_trapezoid  # For integrate


[docs]def invert_x(data): """Inverts the x values and re-interpolates them. This is useful for quantum oscillations because the periodicity in inverse magnetic field. Parameters ---------- data : Data The data to invert Returns ------- inverted_data : Data A data object with points evenly separated in the new inverted x values. """ if not isinstance(data, Data): raise TypeError( f"data need to be a Data object but instead was {type(data)}.") elif not np.all(data.x[:-1] <= data.x[1:]): raise ValueError('Array to invert not sorted!') interp = interp1d(*data.both, bounds_error=False, fill_value=(data.y[0], data.y[1])) new_x = np.linspace(1./data.x.max(), 1./data.x.min(), len(data)) return Data(new_x, interp(1/new_x))
[docs]def loess(data, x_window, polyorder, **kwargs): """This applies a LOESS filter to the data. The LOESS filter is applied using :func:`scipy.signal.savgol_filter`. This fits a polynomial to many sections of the data and uses the central value as the value of the transformed data. Keyword arguments can be given and will be passed to :func:`scipy.signal.savgol_filter`. Parameters ---------- data_set : Data The data to be smoothed. The points must be evenly spaced in x for this to be applied. x_window : float The length of the window to apply in the same units as the x values. polyorder : int The order of the polynomial to apply. Returns ------- smoothed_data : Data A data object after the data has been smoothed with LOESS filter. """ if not isinstance(data, Data): raise TypeError( f"data need to be a Data object but instead was {type(data)}.") elif (np.max(data.x) - np.min(data.x)) < x_window: raise ValueError( "The LOESS window is longer than the given data range") spacing = data.x[1] - data.x[0] if not np.isclose(spacing, np.diff(data.x)).all(): raise ValueError( "The data needs to be evenly spaced to smooth.") smooth_data = savgol_filter(data.y, 2*int(round(0.5*x_window/spacing)) + 1, polyorder=polyorder, **kwargs) return Data(data.x, smooth_data)
[docs]def poly_reg(data, polyorder, sigma=None): """This applied a polynomial fit to data and returns the fit curve. This uses :func:`numpy.polyfit` and filters the data down to a simple polynomial. This can be used for subtracting polynomial background from data sets. Parameters ---------- data : Data The data to apply the fit too. polyorder : int The order of the polynomial to use in the fit. sigma : numpy.ndarray, optional The standard divation of the points of the data. Needs to be a :class:`numpy.ndarray` the same length as the data. Default is None where every point is evenly weighted. Returns ------- poly_smoothed_data : Data The polynomial which was the best fit to the data with the original x values and the generated values. """ if not isinstance(data, Data): raise TypeError( f"data need to be a Data object but instead was {type(data)}.") if sigma is not None: weight = 1/np.array(sigma) if weight.size != data.x.size: raise ValueError( f"The sigma values need to be an array the same size as the " f"data object. Was length {weight.size} where the data was " f"of length {data.x.size}.") else: weight = None fit = np.polyfit(*data.both, deg=polyorder, w=weight) y_vals = 0. for n, p in enumerate(fit): y_vals += p*np.power(data.x, polyorder-n) return Data(data.x, y_vals)
[docs]def deriv(data, x_window, deriv_order, polyorder=None, **kwargs): """This applies a LOESS filter to the data. The LOESS filter is applied using :func:`scipy.signal.savgol_filter`. This fits a polynomial to many sections of the data and uses the central value as the value of the transformed data. Keyword arguments can be given and will be passed to :func:`scipy.signal.savgol_filter`. Parameters ---------- data_set : Data The data to be smoothed. The points must be evenly spaced in x for this to be applied. x_window : float The length of the window to apply in the same units as the x values. polyorder : int The order of the polynomial to apply. Returns ------- smoothed_data : Data A data object after the data has been smoothed with LOESS filter. """ if not isinstance(data, Data): raise TypeError( f"data need to be a Data object but instead was {type(data)}.") elif (np.max(data.x) - np.min(data.x)) < x_window: raise ValueError( "The x window is longer than the given data range") spacing = data.x[1] - data.x[0] if not np.isclose(spacing, np.diff(data.x)).all(): raise ValueError( "The data needs to be evenly spaced to smooth.") if not isinstance(deriv_order, (int, np.int_)) or deriv_order < 0: raise TypeError( f"deriv_order must be an non negative integer but was of type " f"{type(deriv_order)}.") if polyorder is not None: if not isinstance(polyorder, (int, np.int_)): raise TypeError( f"polyorder needs to be an integer but was of type " f"{type(polyorder)}") elif deriv_order > polyorder: raise ValueError( f"polyorder if given needs to be bigger than the " f"deriv_order value. deriv_order was {deriv_order} and " f"polyorder was {polyorder}") else: polyorder = deriv_order smooth_data = savgol_filter(data.y, 2*int(round(0.5*x_window/spacing)) + 1, polyorder=polyorder, deriv=deriv_order, delta=spacing, **kwargs) return Data(data.x, smooth_data)
[docs]def integrate(data): """Integrate a :class:`.Data` object cumulatively. This uses :func:`scipy.integrate.cumulative_trapezoid` to preform a cumulative integration over the data set. This will sort the data set in the process. Parameters ---------- data : Data The :class:`.Data` object to preform the integration on. Returns ------- integral : Data The integral the of the data and will be the same size. """ if not isinstance(data, Data): raise TypeError( f"data need to be a Data object but instead was {type(data)}.") d_sort = data.sort() y_int = cumulative_trapezoid(d_sort.y, d_sort.x, initial=0.) return Data(d_sort.x, y_int)