Source code for ess.powder.smoothing
# SPDX-License-Identifier: BSD-3-Clause
# Copyright (c) 2023 Scipp contributors (https://github.com/scipp)
"""
Smoothing arrays data.
"""
import scipp as sc
from scipp.scipy.signal import butter
from .logging import get_logger
def _ensure_no_variances(var: sc.DataArray) -> sc.DataArray:
if var.variances is not None:
get_logger().warning(
'Tried to smoothen data with uncertainties. '
'This is not supported because the results would be highly correlated.\n'
'Instead, the variances are ignored and the output '
'will be returned without any!'
'\n--------------------------------------------------\n'
'If you know a good solution for handling uncertainties in such a case, '
'please contact the scipp developers! (e.g. via https://github.com/scipp)'
'\n--------------------------------------------------\n'
)
return sc.values(var)
return var
[docs]
def lowpass(
da: sc.DataArray, *, dim: str, N: int, Wn: sc.Variable, coord: str | None = None
) -> sc.DataArray:
"""
Smooth data using a lowpass frequency filter.
Applies a lowpass Butterworth filter to `da.data` based on the sampling rate
defined by `coord`.
See :py:func:`scipp.signal.butter` for information on filter design.
Important
---------
If `coord` is bin-edges, it is first converted to bin-centers using
:func:`scipp.midpoints`.
This is only valid for linearly-spaced edges.
Parameters
----------
da:
Data to smoothen.
dim:
Dimension along which to smooth.
coord:
Name of the coordinate that defines the sampling frequency.
Defaults to `dim`.
N:
Order of the lowpass filter.
Wn:
Critical frequency of the filter.
Returns
-------
:
Smoothed `da`.
See Also
--------
scipp.signal.butter scipp.signal.sosfiltfilt
Examples
--------
>>> from ess.powder.smoothing import lowpass
>>> x = sc.linspace(dim='x', start=1.1, stop=4.0, num=1000, unit='m')
>>> y = sc.sin(x * sc.scalar(1.0, unit='rad/m'))
>>> y += sc.sin(x * sc.scalar(400.0, unit='rad/m'))
>>> noisy = sc.DataArray(data=y, coords={'x': x})
>>> smooth = lowpass(noisy, dim='x', N=4, Wn=20 / x.unit)
"""
da = _ensure_no_variances(da)
coord = dim if coord is None else coord
if da.coords[coord].sizes[dim] == da.sizes[dim] + 1:
da = da.copy(deep=False)
da.coords[coord] = sc.midpoints(da.coords[coord], dim)
return butter(da.coords[coord], N=N, Wn=Wn).filtfilt(da, dim)