Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow to specify filter freqs as args, Add different filter types #3294

Merged
merged 4 commits into from
May 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ Changes:
(see #3251)
* inventory: additional formatting tweaks to expanded channel information
(see #3261)
* allow to specify Trace and Stream filter frequencies as arguments
(see #3294)
- obspy.clients.filesystem:
* update syntax for SQLAlchemy 2.0 compatibility (see #3269)
- obspy.clients.fdsn
Expand All @@ -35,6 +37,8 @@ Changes:
* all butterworth filters: correct zero-phase filtering of 2-d arrays and
filtering along non-default axis of 2-d arrays (see #3291)
* fix naming of input args in function "rotate_rt_ne()" (see #3383)
* add support for Chebyshev I/II, elliptic and Bessel filters alongside
the default Butterworth filters (see #3294)

maintenance_1.4.x
=================
Expand Down
9 changes: 6 additions & 3 deletions obspy/core/stream.py
Original file line number Diff line number Diff line change
Expand Up @@ -2181,15 +2181,18 @@ def simulate(self, paz_remove=None, paz_simulate=None,
return self

@raise_if_masked
def filter(self, type, **options):
def filter(self, type, *args, **options):
"""
Filter the data of all traces in the Stream.

:type type: str
:param type: String that specifies which filter is applied (e.g.
``"bandpass"``). See the `Supported Filter`_ section below for
further details.
:param options: Necessary keyword arguments for the respective filter
:param args: Only filter frequency/frequencies can be specified
as argument(s). Alternatively filter frequencies can be specified
as keyword arguments.
:param options: Keyword arguments for the respective filter
that will be passed on. (e.g. ``freqmin=1.0``, ``freqmax=20.0`` for
``"bandpass"``)

Expand Down Expand Up @@ -2242,7 +2245,7 @@ def filter(self, type, **options):
st.plot()
"""
for tr in self:
tr.filter(type, **options)
tr.filter(type, *args, **options)
return self

def trigger(self, type, **options):
Expand Down
22 changes: 22 additions & 0 deletions obspy/core/tests/test_stream.py
Original file line number Diff line number Diff line change
Expand Up @@ -2835,3 +2835,25 @@ def test_stream_trim_slice_same_length(self):
n1 = len(st.slice(None, utc)[0])
n2 = len(st.trim(None, utc)[0])
assert n1 == n2

def test_filter_ftype(self):
st = read()
ftypes = ['butter', 'cheby1', 'cheby2', 'ellip', 'bessel']
streams = [st.copy().filter('bandpass', 5, 10,
ftype=ftype, rp=10, rs=100)
for ftype in ftypes]
for st in streams[1:]:
assert not streams_almost_equal(st, streams[0])

def test_filter_freq_args(self):
st = read()
for filtert, freq in [('lowpass', 5,),
('highpass', 5)]:
stf1 = st.copy().filter(filtert, freq=freq)
stf2 = st.copy().filter(filtert, freq)
assert streams_almost_equal(stf2, stf1)
for filtert, freqmin, freqmax in [('bandpass', 1, 5),
('bandstop', 1, 5)]:
stf1 = st.copy().filter(filtert, freqmin=freqmin, freqmax=freqmax)
stf2 = st.copy().filter(filtert, freqmin, freqmax)
assert streams_almost_equal(stf2, stf1)
13 changes: 10 additions & 3 deletions obspy/core/trace.py
Original file line number Diff line number Diff line change
Expand Up @@ -1481,15 +1481,18 @@ def simulate(self, paz_remove=None, paz_simulate=None,

@_add_processing_info
@raise_if_masked
def filter(self, type, **options):
def filter(self, type, *args, **options):
"""
Filter the data of the current trace.

:type type: str
:param type: String that specifies which filter is applied (e.g.
``"bandpass"``). See the `Supported Filter`_ section below for
further details.
:param options: Necessary keyword arguments for the respective filter
:param args: Only filter frequency/frequencies can be specified
as argument(s). Alternatively filter frequencies can be specified
as keyword arguments.
:param options: Keyword arguments for the respective filter
that will be passed on. (e.g. ``freqmin=1.0``, ``freqmax=20.0`` for
``"bandpass"``)

Expand Down Expand Up @@ -1533,6 +1536,9 @@ def filter(self, type, **options):
>>> tr = st[0]
>>> tr.filter("highpass", freq=1.0) # doctest: +ELLIPSIS
<...Trace object at 0x...>
>>> tr2 = st[1]
>>> tr2.filter("lowpass", 1.0) # doctest: +ELLIPSIS
<...Trace object at 0x...>
>>> tr.plot() # doctest: +SKIP

.. plot::
Expand All @@ -1549,7 +1555,8 @@ def filter(self, type, **options):
# filtering
# the options dictionary is passed as kwargs to the function that is
# mapped according to the filter_functions dictionary
self.data = func(self.data, df=self.stats.sampling_rate, **options)
self.data = func(self.data, *args,
df=self.stats.sampling_rate, **options)
return self

@_add_processing_info
Expand Down
88 changes: 72 additions & 16 deletions obspy/signal/filter.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,11 @@
remez, sosfilt)


def _filter(data, freqs, df, btype='band', ftype='butter',
corners=4, zerophase=False, axis=-1):
def _filter(data, freqs, df, rp=None, rs=None, btype='band', ftype='butter',
corners=4, zerophase=False, axis=-1, **kwargs):
fe = 0.5 * df
normalized_freqs = [f/fe for f in freqs]
sos = iirfilter(corners, normalized_freqs, btype=btype,
sos = iirfilter(corners, normalized_freqs, rp=rp, rs=rs, btype=btype,
ftype=ftype, output='sos')
if zerophase:
firstpass = np.flip(sosfilt(sos, data, axis=axis), axis=axis)
Expand All @@ -38,9 +38,9 @@ def _filter(data, freqs, df, btype='band', ftype='butter',


def bandpass(data, freqmin, freqmax, df, corners=4, zerophase=False,
axis=-1):
rp=None, rs=None, ftype='butter', axis=-1):
megies marked this conversation as resolved.
Show resolved Hide resolved
"""
Butterworth-Bandpass Filter.
Bandpass Filter.

Filter data from ``freqmin`` to ``freqmax`` using ``corners``
corners.
Expand All @@ -56,6 +56,19 @@ def bandpass(data, freqmin, freqmax, df, corners=4, zerophase=False,
:param zerophase: If True, apply filter once forwards and once backwards.
This results in twice the filter order but zero phase shift in
the resulting filtered trace.
:param rp:
For Chebyshev and elliptic filters, provides the maximum ripple
in the passband. (dB)
:param rs:
For Chebyshev and elliptic filters, provides the minimum attenuation
in the stop band. (dB)
:param ftype:
The type of filter
- Butterworth : 'butter' (default)
- Chebyshev I : 'cheby1'
- Chebyshev II : 'cheby2'
- Cauer/elliptic: 'ellip'
- Bessel/Thomson: 'bessel'
:param axis: The axis of the input data array along which to apply the
linear filter. The filter is applied to each subarray along this axis.
Default is -1.
Expand All @@ -71,18 +84,19 @@ def bandpass(data, freqmin, freqmax, df, corners=4, zerophase=False,
freqmax, fe)
warnings.warn(msg)
return highpass(data, freq=freqmin, df=df, corners=corners,
zerophase=zerophase)
ftype=ftype, zerophase=zerophase)
if low > 1:
msg = "Selected low corner frequency is above Nyquist."
raise ValueError(msg)
return _filter(data, (freqmin, freqmax), df, btype='band',
return _filter(data, (freqmin, freqmax), df, rp=rp, rs=rs,
btype='band', ftype=ftype,
corners=corners, zerophase=zerophase, axis=axis)


def bandstop(data, freqmin, freqmax, df, corners=4, zerophase=False,
axis=-1):
rp=None, rs=None, ftype='butter', axis=-1):
"""
Butterworth-Bandstop Filter.
Bandstop Filter.

Filter data removing data between frequencies ``freqmin`` and ``freqmax``
using ``corners`` corners.
Expand All @@ -98,6 +112,19 @@ def bandstop(data, freqmin, freqmax, df, corners=4, zerophase=False,
:param zerophase: If True, apply filter once forwards and once backwards.
This results in twice the number of corners but zero phase shift in
the resulting filtered trace.
:param rp:
For Chebyshev and elliptic filters, provides the maximum ripple
in the passband. (dB)
:param rs:
For Chebyshev and elliptic filters, provides the minimum attenuation
in the stop band. (dB)
:param ftype:
The type of filter
- Butterworth : 'butter' (default)
- Chebyshev I : 'cheby1'
- Chebyshev II : 'cheby2'
- Cauer/elliptic: 'ellip'
- Bessel/Thomson: 'bessel'
:param axis: The axis of the input data array along which to apply the
linear filter. The filter is applied to each subarray along this axis.
Default is -1.
Expand All @@ -115,14 +142,15 @@ def bandstop(data, freqmin, freqmax, df, corners=4, zerophase=False,
if low > 1:
msg = "Selected low corner frequency is above Nyquist."
raise ValueError(msg)
return _filter(data, (freqmin, freqmax), df, btype='bandstop',
return _filter(data, (freqmin, freqmax), df, rp=rp, rs=rs,
btype='bandstop', ftype=ftype,
corners=corners, zerophase=zerophase, axis=axis)


def lowpass(data, freq, df, corners=4, zerophase=False,
axis=-1):
rp=None, rs=None, ftype='butter', axis=-1):
"""
Butterworth-Lowpass Filter.
Lowpass Filter.

Filter data removing data over certain frequency ``freq`` using ``corners``
corners.
Expand All @@ -137,6 +165,19 @@ def lowpass(data, freq, df, corners=4, zerophase=False,
:param zerophase: If True, apply filter once forwards and once backwards.
This results in twice the number of corners but zero phase shift in
the resulting filtered trace.
:param rp:
For Chebyshev and elliptic filters, provides the maximum ripple
in the passband. (dB)
:param rs:
For Chebyshev and elliptic filters, provides the minimum attenuation
in the stop band. (dB)
:param ftype:
The type of filter
- Butterworth : 'butter' (default)
- Chebyshev I : 'cheby1'
- Chebyshev II : 'cheby2'
- Cauer/elliptic: 'ellip'
- Bessel/Thomson: 'bessel'
:param axis: The axis of the input data array along which to apply the
linear filter. The filter is applied to each subarray along this axis.
Default is -1.
Expand All @@ -150,14 +191,15 @@ def lowpass(data, freq, df, corners=4, zerophase=False,
msg = "Selected corner frequency is above Nyquist. " + \
"Setting Nyquist as high corner."
warnings.warn(msg)
return _filter(data, (freq,), df, btype='lowpass',
return _filter(data, (freq,), df, rp=rp, rs=rs,
btype='lowpass', ftype=ftype,
corners=corners, zerophase=zerophase, axis=axis)


def highpass(data, freq, df, corners=4, zerophase=False,
axis=-1):
rp=None, rs=None, ftype='butter', axis=-1):
"""
Butterworth-Highpass Filter.
Highpass Filter.

Filter data removing data below certain frequency ``freq`` using
``corners`` corners.
Expand All @@ -172,6 +214,19 @@ def highpass(data, freq, df, corners=4, zerophase=False,
:param zerophase: If True, apply filter once forwards and once backwards.
This results in twice the number of corners but zero phase shift in
the resulting filtered trace.
:param rp:
For Chebyshev and elliptic filters, provides the maximum ripple
in the passband. (dB)
:param rs:
For Chebyshev and elliptic filters, provides the minimum attenuation
in the stop band. (dB)
:param ftype:
The type of filter
- Butterworth : 'butter' (default)
- Chebyshev I : 'cheby1'
- Chebyshev II : 'cheby2'
- Cauer/elliptic: 'ellip'
- Bessel/Thomson: 'bessel'
:param axis: The axis of the input data array along which to apply the
linear filter. The filter is applied to each subarray along this axis.
Default is -1.
Expand All @@ -183,7 +238,8 @@ def highpass(data, freq, df, corners=4, zerophase=False,
if f > 1:
msg = "Selected corner frequency is above Nyquist."
raise ValueError(msg)
return _filter(data, (freq,), df, btype='highpass',
return _filter(data, (freq,), df, rp=rp, rs=rs,
btype='highpass', ftype=ftype,
corners=corners, zerophase=zerophase, axis=axis)


Expand Down