Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 23 additions & 4 deletions audinterface/core/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -255,8 +255,8 @@ def signal_index(

index = pd.MultiIndex.from_arrays(
[
pd.TimedeltaIndex(to_timedelta(starts)),
pd.TimedeltaIndex(to_timedelta(ends)),
_to_timedelta_index(starts),
_to_timedelta_index(ends),
],
names=[
audformat.define.IndexField.START,
Expand Down Expand Up @@ -460,9 +460,28 @@ def duration_in_seconds(duration, sampling_rate):
# avoid converting Timedelta values to ensure precision
# https://github.com/audeering/audinterface/pull/137
if isinstance(durations, pd.Timedelta):
return durations
# Normalize to nanosecond resolution for pandas >=3.0
return durations.as_unit("ns")

durations = duration_in_seconds(durations, sampling_rate)
durations = pd.to_timedelta(durations, unit="s")
durations = pd.to_timedelta(durations, unit="s").as_unit("ns")

return durations


def _to_timedelta_index(
durations: Timestamps,
sampling_rate: int | None = None,
) -> pd.TimedeltaIndex:
r"""Return ``durations`` as ``pd.TimedeltaIndex`` with ``ns`` resolution.

Pandas >= 3 introduces variable-resolution
:class:`pandas.Timedelta` values
(``s``, ``ms``, ``us``, ``ns``).
Centralizes the normalization to ``ns`` required
to match the ``timedelta64[ns]`` convention used by
:func:`audformat.segmented_index`
and asserted by :func:`audinterface.core.utils.assert_index`.

"""
return pd.TimedeltaIndex(to_timedelta(durations, sampling_rate)).as_unit("ns")
3 changes: 2 additions & 1 deletion tests/test_process.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,10 @@ def __call__(self, signal, sampling_rate):


def segment(signal, sampling_rate):
duration = audinterface.utils.to_timedelta(signal.shape[1] / sampling_rate)
return audinterface.utils.signal_index(
pd.to_timedelta(0),
pd.to_timedelta(signal.shape[1] / sampling_rate, unit="s") / 2,
duration / 2,
)


Expand Down
20 changes: 8 additions & 12 deletions tests/test_segment.py
Original file line number Diff line number Diff line change
Expand Up @@ -211,9 +211,8 @@ def process_func(signal, sampling_rate):
table_df = table.copy()
table_df["string"] = audformat.Column()
table_df.set({"string": ["a", "b", "c"]})
expected_dataframe = pd.DataFrame(
table_df.get().values, index=expected, columns=["values", "string"]
)
expected_dataframe = table_df.get()
expected_dataframe.index = expected
result = segment.process_table(table_df.get())
pd.testing.assert_frame_equal(result, expected_dataframe)

Expand All @@ -237,9 +236,8 @@ def process_func(signal, sampling_rate):
table_df = table.copy()
table_df["string"] = audformat.Column()
table_df.set({"string": ["d"]})
expected_dataframe = pd.DataFrame(
table_df.get().values, index=expected, columns=["values", "string"]
)
expected_dataframe = table_df.get()
expected_dataframe.index = expected
result = segment.process_table(table_df.get())
pd.testing.assert_frame_equal(result, expected_dataframe)

Expand Down Expand Up @@ -271,9 +269,8 @@ def process_func(signal, sampling_rate):
table_df = table.copy()
table_df["string"] = audformat.Column()
table_df.set({"string": ["a", "b", "c"]})
expected_dataframe = pd.DataFrame(
table_df.get().values, index=expected, columns=["values", "string"]
)
expected_dataframe = table_df.get()
expected_dataframe.index = expected
result = segment.process_table(table_df.get(), root=root)
pd.testing.assert_frame_equal(result, expected_dataframe)

Expand All @@ -297,9 +294,8 @@ def process_func(signal, sampling_rate):
table_df = table.copy()
table_df["string"] = audformat.Column()
table_df.set({"string": ["d"]})
expected_dataframe = pd.DataFrame(
table_df.get().values, index=expected, columns=["values", "string"]
)
expected_dataframe = table_df.get()
expected_dataframe.index = expected
result = segment.process_table(table_df.get(), root=root)
pd.testing.assert_frame_equal(result, expected_dataframe)

Expand Down
6 changes: 3 additions & 3 deletions tests/test_segment_with_feature.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ def segment_non_zeros_with_mean_mono(signal, sampling_rate):
ends = [pd.to_timedelta(end / sampling_rate, unit="s") for end in end_indices]
starts = [pd.to_timedelta(start / sampling_rate, "s") for start in start_indices]
means = [frame.mean() for frame in frames]
index = pd.MultiIndex.from_tuples(zip(starts, ends), names=["start", "end"])
index = audinterface.utils.signal_index(starts, ends)
return pd.Series(data=means, dtype=signal.dtype, index=index)


Expand All @@ -71,7 +71,7 @@ def segment_with_mean(signal, sampling_rate, *, win_size=1.0, hop_size=1.0):
signal, sampling_rate, win_size, hop_size
)
means = frames.mean(axis=(0, 1))
index = pd.MultiIndex.from_tuples(zip(starts, ends), names=["start", "end"])
index = audinterface.utils.signal_index(starts, ends)
return pd.Series(data=means, index=index)


Expand All @@ -88,7 +88,7 @@ def segment_with_mean_std(signal, sampling_rate, *, win_size=1.0, hop_size=1.0):
)
means = frames.mean(axis=(0, 1))
stds = frames.std(axis=(0, 1))
index = pd.MultiIndex.from_tuples(zip(starts, ends), names=["start", "end"])
index = audinterface.utils.signal_index(starts, ends)
features = list(np.stack((means, stds), axis=-1))
return pd.Series(data=features, index=index)

Expand Down
20 changes: 14 additions & 6 deletions tests/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -174,8 +174,12 @@ def test_read_audio(tmpdir):
2,
pd.MultiIndex.from_arrays(
[
pd.TimedeltaIndex([pd.Timedelta("0 days 00:00:01")]),
pd.TimedeltaIndex([pd.Timedelta("0 days 00:00:02")]),
audinterface.core.utils._to_timedelta_index(
[pd.Timedelta("0 days 00:00:01")]
),
audinterface.core.utils._to_timedelta_index(
[pd.Timedelta("0 days 00:00:02")]
),
],
names=["start", "end"],
),
Expand All @@ -185,13 +189,13 @@ def test_read_audio(tmpdir):
[3, 4],
pd.MultiIndex.from_arrays(
[
pd.TimedeltaIndex(
audinterface.core.utils._to_timedelta_index(
[
pd.Timedelta("0 days 00:00:01"),
pd.Timedelta("0 days 00:00:02"),
]
),
pd.TimedeltaIndex(
audinterface.core.utils._to_timedelta_index(
[
pd.Timedelta("0 days 00:00:03"),
pd.Timedelta("0 days 00:00:04"),
Expand All @@ -206,8 +210,12 @@ def test_read_audio(tmpdir):
[36],
pd.MultiIndex.from_arrays(
[
pd.TimedeltaIndex([pd.Timedelta("0 days 00:00:35.511437999")]),
pd.TimedeltaIndex([pd.Timedelta("0 days 00:00:36")]),
audinterface.core.utils._to_timedelta_index(
[pd.Timedelta("0 days 00:00:35.511437999")]
),
audinterface.core.utils._to_timedelta_index(
[pd.Timedelta("0 days 00:00:36")]
),
],
names=["start", "end"],
),
Expand Down
Loading