Source code for pisa.utils.spline_smooth
"""
Smooth an array by splining it and resampling from the spline
"""
from __future__ import absolute_import
import numpy as np
from pisa.utils.log import logging
from scipy.interpolate import splrep, splev, interp1d
__author__ = 'P. Eller'
__license__ = '''Copyright (c) 2014-2017, The IceCube Collaboration
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.'''
[docs]
def spline_smooth(array, spline_binning, eval_binning, axis=0, smooth_factor=5, k=3, errors=None):
"""Fuction for spline-smoothing arrays
It is smoothing in slices along one axis, assuming 2d arrays
The smoothing is done by splines
Parameters
----------
array : 2d-array
array to be smoothed
spline_binning : OneDimBinning
Binning of axis on which to construct the spline
Must corrspond to the array dimension
axis : int
Index of the axis along which to smooth
eval_binning : OneDimBinning
Binning on which to evaluate the constructed spline
smooth_factor : float
smoothing factor for spline
k : int
spline degree
errors : 2d-array or None
uncertainties on the array
Notes
-----
could be expanded to nd arrays to generalize it
"""
# only working for 2d right now!
if array.ndim != 2:
raise ValueError('cannot do other dimensions than 2 right now, sorry')
# points at which to evaluate splines
spline_points = spline_binning.midpoints
if axis == 1:
# always smooth along axis=0
array = array.T
if errors is not None:
errors = errors.T
smoothed_slices = []
interp_errors = None if errors is None else []
for index in range(array.shape[1]):
# take slice
h_slice = array[:, index]
if errors is None:
weights = None
else:
h_errors = errors[:, index]
#replace zeros errors minimum avrage value along other axis
for i in range(len(h_errors)):
if h_errors[i] == 0:
if np.sum(errors[i, :]) == 0:
h_errors[i] = 0
logging.warning('Detected row in array with all zero values, this can be problematic for spline smoothing!')
else:
h_errors[i] = np.min(errors[i, :][errors[i, :] != 0])
weights = 1./h_errors
# Fit spline to slices
slice_spline = splrep(
spline_points, h_slice, weights,
k=k, s=smooth_factor,
)
# eval spline over midpoints
smoothed_slice = splev(eval_binning.midpoints, slice_spline)
# Assert that there are no nan or inf values in smoothed cz-slice
assert np.all(np.isfinite(smoothed_slice))
smoothed_slices.append(smoothed_slice)
if errors is not None:
erf = interp1d(spline_points, 1./weights, fill_value="extrapolate")
new_errors = erf(eval_binning.midpoints)
interp_errors.append(new_errors)
# Convert list of slices to array
smoothed_array = np.array(smoothed_slices)
if errors is not None:
interp_errors = np.array(interp_errors)
# swap axes back if necessary
if axis == 0:
smoothed_array = smoothed_array.T
if errors is not None:
interp_errors = interp_errors.T
return smoothed_array, interp_errors