Skip to content

Commit

Permalink
Deprecate unit, specify samples as string (#53)
Browse files Browse the repository at this point in the history
  • Loading branch information
hagenw authored Jun 24, 2022
1 parent 54b59f0 commit 52551b1
Show file tree
Hide file tree
Showing 5 changed files with 238 additions and 77 deletions.
120 changes: 82 additions & 38 deletions audinterface/core/feature.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,15 +60,24 @@ class Feature:
sampling_rate: sampling rate in Hz.
If ``None`` it will call ``process_func`` with the actual
sampling rate of the signal
win_dur: window size in ``unit``,
if features are extracted with a sliding window
hop_dur: hop size in ``unit``,
win_dur: window size,
if features are extracted with a sliding window.
If value is as a float or integer
it is treated as seconds.
To specify a unit provide as string,
e.g. ``'2ms'``.
To specify in samples provide as string without unit,
e.g. ``'2000'``
hop_dur: hop size,
if features are extracted with a sliding window.
This defines the shift between two windows.
Defaults to ``win_dur / 2``.
unit: unit of ``win_dur`` and ``hop_dur``.
Can be ``'samples'``,
or any unit supported by :func:`pandas.to_timedelta`
If value is as a float or integer
it is treated as seconds.
To specify a unit provide as string,
e.g. ``'2ms'``.
To specify in samples provide a string without unit,
e.g. ``'2000'``.
Defaults to ``win_dur / 2``
resample: if ``True`` enforces given sampling rate by resampling
channels: channel selection, see :func:`audresample.remix`
mixdown: apply mono mix-down on selection
Expand All @@ -86,8 +95,8 @@ class Feature:
verbose: show debug messages
Raises:
ValueError: if ``unit == 'samples'``, ``sampling_rate is None``
and ``win_dur is not None``
ValueError: if ``win_dur`` or ``hop_dur`` are given in samples
and ``sampling_rate is None``
ValueError: if ``hop_dur`` is specified, but not ``win_dur``
Example:
Expand Down Expand Up @@ -127,9 +136,8 @@ def __init__(
process_func_args: typing.Dict[str, typing.Any] = None,
process_func_is_mono: bool = False,
sampling_rate: int = None,
win_dur: typing.Union[int, float] = None,
hop_dur: typing.Union[int, float] = None,
unit: str = 'seconds',
win_dur: Timestamp = None,
hop_dur: Timestamp = None,
resample: bool = False,
channels: typing.Union[int, typing.Sequence[int]] = 0,
mixdown: bool = False,
Expand All @@ -142,6 +150,33 @@ def __init__(
):
feature_names = audeer.to_list(feature_names)

# ------
# Handle deprecated 'unit' keyword argument
def add_unit(dur, unit):
if unit == 'samples':
return str(dur)
else:
return f'{dur}{unit}'

if 'unit' in kwargs:
message = (
"'unit' argument is deprecated "
"and will be removed with version '1.2.0'."
"The unit can now directly specified "
"within the 'win_dur' and 'hop_dur' arguments."
)
warnings.warn(
message,
category=UserWarning,
stacklevel=2,
)
unit = kwargs.pop('unit')
if win_dur is not None:
win_dur = add_unit(win_dur, unit)
if hop_dur is not None:
hop_dur = add_unit(hop_dur, unit)
# ------

process_func_args = process_func_args or {}
if kwargs:
warnings.warn(
Expand All @@ -156,11 +191,6 @@ def __init__(
raise ValueError(
"You have to specify 'win_dur' if 'hop_dur' is given."
)
if unit == 'samples' and sampling_rate is None and win_dur is not None:
raise ValueError(
"You have specified 'samples' as unit, "
"but haven't provided a sampling rate."
)

if process_func is None:
def process_func(signal, _):
Expand Down Expand Up @@ -214,9 +244,8 @@ def process_func(signal, _):
self.hop_dur = hop_dur
r"""Hop duration."""
if win_dur is not None and hop_dur is None:
self.hop_dur = win_dur // 2 if unit == 'samples' else win_dur / 2
self.unit = unit
r"""Unit of ``win_dur`` and ``hop dur``"""
win_dur = utils.to_timedelta(win_dur, sampling_rate)
self.hop_dur = win_dur / 2
self.verbose = verbose
r"""Show debug messages."""

Expand All @@ -233,9 +262,17 @@ def process_file(
Args:
file: file path
start: start processing at this position.
If value is as a float or integer it is treated as seconds
If value is as a float or integer it is treated as seconds.
To specify a unit provide as string,
e.g. ``'2ms'``.
To specify in samples provide as string without unit,
e.g. ``'2000'``
end: end processing at this position.
If value is as a float or integer it is treated as seconds
If value is as a float or integer it is treated as seconds.
To specify a unit provide as string,
e.g. ``'2ms'``.
To specify in samples provide as string without unit,
e.g. ``'2000'``
root: root folder to expand relative file path
Raises:
Expand Down Expand Up @@ -267,9 +304,17 @@ def process_files(
files: list of file paths
starts: segment start positions.
Time values given as float or integers are treated as seconds.
To specify a unit provide as string,
e.g. ``'2ms'``.
To specify in samples provide as string without unit,
e.g. ``'2000'``.
If a scalar is given, it is applied to all files
ends: segment end positions.
Time values given as float or integers are treated as seconds
To specify a unit provide as string,
e.g. ``'2ms'``.
To specify in samples provide as string without unit,
e.g. ``'2000'``.
If a scalar is given, it is applied to all files
root: root folder to expand relative file paths
Expand Down Expand Up @@ -395,9 +440,17 @@ def process_signal(
sampling_rate: sampling rate in Hz
file: file path
start: start processing at this position.
If value is as a float or integer it is treated as seconds
If value is as a float or integer it is treated as seconds.
To specify a unit provide as string,
e.g. ``'2ms'``.
To specify in samples provide as string without unit,
e.g. ``'2000'``
end: end processing at this position.
If value is as a float or integer it is treated as seconds
If value is as a float or integer it is treated as seconds.
To specify a unit provide as string,
e.g. ``'2ms'``.
To specify in samples provide as string without unit,
e.g. ``'2000'``
Raises:
RuntimeError: if sampling rates do not match
Expand Down Expand Up @@ -585,20 +638,11 @@ def _values_to_frame(
# [n_features, n_frames]
# [n_features]

if self.win_dur is not None:
if self.unit == 'samples':
win_dur = pd.to_timedelta(
self.win_dur / self.process.sampling_rate, unit='seconds',
)
hop_dur = pd.to_timedelta(
self.hop_dur / self.process.sampling_rate, unit='seconds',
)
else:
win_dur = pd.to_timedelta(self.win_dur, unit=self.unit)
hop_dur = pd.to_timedelta(self.hop_dur, unit=self.unit)
else:
win_dur = None
hop_dur = None
win_dur = self.win_dur
hop_dur = self.hop_dur
if win_dur is not None:
win_dur = utils.to_timedelta(win_dur, self.process.sampling_rate)
hop_dur = utils.to_timedelta(hop_dur, self.process.sampling_rate)

features = self._reshape_3d(features)
n_channels, n_features, n_frames = features.shape
Expand Down
43 changes: 32 additions & 11 deletions audinterface/core/process.py
Original file line number Diff line number Diff line change
Expand Up @@ -183,9 +183,17 @@ def process_file(
Args:
file: file path
start: start processing at this position.
If value is as a float or integer it is treated as seconds
If value is as a float or integer it is treated as seconds.
To specify a unit provide as string,
e.g. ``'2ms'``.
To specify in samples provide as string without unit,
e.g. ``'2000'``
end: end processing at this position.
If value is as a float or integer it is treated as seconds
If value is as a float or integer it is treated as seconds.
To specify a unit provide as string,
e.g. ``'2ms'``.
To specify in samples provide as string without unit,
e.g. ``'2000'``
root: root folder to expand relative file path
Returns:
Expand All @@ -198,8 +206,6 @@ def process_file(
.. _audformat: https://audeering.github.io/audformat/data-format.html
"""
start = utils.to_timedelta(start)
end = utils.to_timedelta(end)
if self.segment is not None:
index = self.segment.process_file(
file,
Expand All @@ -209,6 +215,8 @@ def process_file(
)
return self._process_index_wo_segment(index, root)
else:
start = utils.to_timedelta(start, self.sampling_rate)
end = utils.to_timedelta(end, self.sampling_rate)
return self._process_file(file, start=start, end=end, root=root)

def process_files(
Expand All @@ -225,9 +233,17 @@ def process_files(
files: list of file paths
starts: segment start positions.
Time values given as float or integers are treated as seconds.
To specify a unit provide as string,
e.g. ``'2ms'``.
To specify in samples provide as string without unit,
e.g. ``'2000'``.
If a scalar is given, it is applied to all files
ends: segment end positions.
Time values given as float or integers are treated as seconds
To specify a unit provide as string,
e.g. ``'2ms'``.
To specify in samples provide as string without unit,
e.g. ``'2000'``.
If a scalar is given, it is applied to all files
root: root folder to expand relative file paths
Expand All @@ -249,9 +265,6 @@ def process_files(
if isinstance(ends, (type(None), float, int, str, pd.Timedelta)):
ends = [ends] * len(files)

starts = utils.to_timedelta(starts)
ends = utils.to_timedelta(ends)

params = [
(
(file, ),
Expand Down Expand Up @@ -449,9 +462,17 @@ def process_signal(
sampling_rate: sampling rate in Hz
file: file path
start: start processing at this position.
If value is as a float or integer it is treated as seconds
If value is as a float or integer it is treated as seconds.
To specify a unit provide as string,
e.g. ``'2ms'``.
To specify in samples provide as string without unit,
e.g. ``'2000'``
end: end processing at this position.
If value is as a float or integer it is treated as seconds
To specify a unit provide as string,
e.g. ``'2ms'``.
To specify in samples provide as string without unit,
e.g. ``'2000'``
If value is as a float or integer it is treated as seconds.
Returns:
Series with processed signal conform to audformat_
Expand All @@ -463,8 +484,6 @@ def process_signal(
.. _audformat: https://audeering.github.io/audformat/data-format.html
"""
start = utils.to_timedelta(start)
end = utils.to_timedelta(end)
if self.segment is not None:
index = self.segment.process_signal(
signal,
Expand All @@ -479,6 +498,8 @@ def process_signal(
index,
)
else:
start = utils.to_timedelta(start, sampling_rate)
end = utils.to_timedelta(end, sampling_rate)
return self._process_signal(
signal,
sampling_rate,
Expand Down
32 changes: 28 additions & 4 deletions audinterface/core/segment.py
Original file line number Diff line number Diff line change
Expand Up @@ -229,9 +229,17 @@ def process_file(
Args:
file: file path
start: start processing at this position.
If value is as a float or integer it is treated as seconds
If value is as a float or integer it is treated as seconds.
To specify a unit provide as string,
e.g. ``'2ms'``.
To specify in samples provide as string without unit,
e.g. ``'2000'``
end: end processing at this position.
If value is as a float or integer it is treated as seconds
If value is as a float or integer it is treated as seconds.
To specify a unit provide as string,
e.g. ``'2ms'``.
To specify in samples provide as string without unit,
e.g. ``'2000'``
root: root folder to expand relative file path
Returns:
Expand Down Expand Up @@ -280,9 +288,17 @@ def process_files(
files: list of file paths
starts: segment start positions.
Time values given as float or integers are treated as seconds.
To specify a unit provide as string,
e.g. ``'2ms'``.
To specify in samples provide as string without unit,
e.g. ``'2000'``.
If a scalar is given, it is applied to all files
ends: segment end positions.
Time values given as float or integers are treated as seconds
To specify a unit provide as string,
e.g. ``'2ms'``.
To specify in samples provide as string without unit,
e.g. ``'2000'``.
If a scalar is given, it is applied to all files
root: root folder to expand relative file paths
Expand Down Expand Up @@ -413,9 +429,17 @@ def process_signal(
sampling_rate: sampling rate in Hz
file: file path
start: start processing at this position.
If value is as a float or integer it is treated as seconds
If value is as a float or integer it is treated as seconds.
To specify a unit provide as string,
e.g. ``'2ms'``.
To specify in samples provide as string without unit,
e.g. ``'2000'``
end: end processing at this position.
If value is as a float or integer it is treated as seconds
If value is as a float or integer it is treated as seconds.
To specify a unit provide as string,
e.g. ``'2ms'``.
To specify in samples provide as string without unit,
e.g. ``'2000'``
Returns:
Segmented index conform to audformat_
Expand Down
Loading

0 comments on commit 52551b1

Please sign in to comment.