Пример #1
0
def test_manually_generated_spectrograms():
    """Tests the concatenation behavior with manually generated spectrograms."""
    spectrogram1 = sumpf.Spectrogram(channels=numpy.array([
        ([1.11, 1.12, 1.13], [1.21j, 1.22j, 1.23j], [1.31, 1.32, 1.33])
    ]),
                                     resolution=1.0,
                                     sampling_rate=2.0,
                                     offset=1,
                                     labels=("s1c1", ))
    spectrogram2 = sumpf.Spectrogram(channels=numpy.array([
        ([2.11, 2.12, 2.13, 2.14], [2.21j, 2.22j, 2.23j, 2.24j]),
        ([3.11, 3.12, 3.13, 3.14], [3.21j, 3.22j, 3.23j, 3.24j])
    ]),
                                     resolution=3.0,
                                     sampling_rate=4.0,
                                     offset=-4,
                                     labels=("s2c1", "s2c2"))
    spectrogram12 = sumpf.Spectrogram(channels=numpy.array([
        ([2.11, 3.23, 3.25, 3.27], [2.21j, 3.43j, 3.45j,
                                    3.47j], [0.0, 1.31, 1.32, 1.33]),
        ([3.11, 3.12, 3.13, 3.14], [3.21j, 3.22j, 3.23j,
                                    3.24j], [0.0, 0.0, 0.0, 0.0])
    ]),
                                      resolution=1.0,
                                      sampling_rate=2.0,
                                      offset=0,
                                      labels=("Concatenation 1",
                                              "Concatenation 2"))
    spectrogram21 = sumpf.Spectrogram(channels=numpy.array([
        ([2.11, 2.12, 2.13, 2.14, 0.0, 1.11, 1.12,
          1.13], [2.21j, 2.22j, 2.23j, 2.24j, 0.0j, 1.21j, 1.22j,
                  1.23j], [0.0, 0.0, 0.0, 0.0, 0.0, 1.31, 1.32, 1.33]),
        ([3.11, 3.12, 3.13, 3.14, 0.0, 0.0, 0.0,
          0.0], [3.21j, 3.22j, 3.23j, 3.24j, 0.0j, 0.0j, 0.0j,
                 0.0j], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0])
    ]),
                                      resolution=3.0,
                                      sampling_rate=4.0,
                                      offset=-4,
                                      labels=("Concatenation 1",
                                              "Concatenation 2"))
    assert tests.compare_spectrograms_approx(
        _concatenate_spectrograms([spectrogram1, spectrogram2]), spectrogram12)
    assert tests.compare_spectrograms_approx(
        _concatenate_spectrograms([spectrogram2, spectrogram1]), spectrogram21)
    assert tests.compare_spectrograms_approx(
        sumpf.Concatenate([spectrogram1, spectrogram2]).output(),
        spectrogram12)
    assert tests.compare_spectrograms_approx(
        sumpf.Concatenate([spectrogram2, spectrogram1]).output(),
        spectrogram21)
Пример #2
0
def _sanitize_offsets(data):
    """makes sure, that the offsets of the signals and spectrograms are not so
    large, that the concatenation requires excessive amounts of memory."""
    r = random.Random()
    r.seed(data[0].offset())
    # the first data set is returned with its original offset
    result = [data[0]]
    # the other data sets offset must not exceed their length, so the concatenation is at maximum twice as long as the original data sets combined
    for s in data[1:]:
        if abs(s.offset()) < s.length():
            result.append(s)
        else:
            if isinstance(s, sumpf.Signal):
                result.append(
                    sumpf.Signal(channels=s.channels(),
                                 sampling_rate=s.sampling_rate(),
                                 offset=random.randint(-s.length(),
                                                       s.length()),
                                 labels=s.labels()))
            else:
                result.append(
                    sumpf.Spectrogram(channels=s.channels(),
                                      resolution=s.resolution(),
                                      sampling_rate=s.sampling_rate(),
                                      offset=random.randint(
                                          -s.length(), s.length()),
                                      labels=s.labels()))
    return result
Пример #3
0
def _compare_merge_spectrograms_first_channel_first(spectrograms, merged):
    """test if the merged spectrogram's channels are ordered in a way that the first
    channels of all input spectrogram come first"""
    resolution = spectrograms[0].resolution()
    sampling_rate = spectrograms[0].sampling_rate()
    spectrograms = list(spectrograms)
    c = 0
    d = 0
    while spectrograms:
        to_remove = []
        for s in spectrograms:
            if d < len(s):
                spectrogram = sumpf.Spectrogram(channels=s[d].channels(),
                                                resolution=resolution,        # take the resolution from the first spectrogram
                                                sampling_rate=sampling_rate,  # take the sampling rate from the first spectrogram
                                                offset=s[d].offset(),
                                                labels=s[d].labels())            # the merged spectrogram has a label for each channel, which could be empty
                start = spectrogram.offset() - merged.offset()
                stop = start + spectrogram.length()
                max_f = spectrogram.number_of_frequencies()
                assert merged[c, 0:max_f, start:stop] == spectrogram
                c += 1
            else:
                to_remove.append(s)
        for s in to_remove:
            spectrograms.remove(s)
        d += 1
    assert c == len(merged)
Пример #4
0
def _sanitize_offsets(data):
    """makes sure, that the offsets of the signals and spectrograms are not so
    large, that the  merged signal requires excessive amounts of memory."""
    first_offset = data[0].offset()
    r = random.Random()
    r.seed(first_offset)
    # the first signal is returned with its original offset
    result = [data[0]]
    # the other signal's offset must not differ from the first signal's offset by more than their length
    for s in data[1:]:
        if abs(first_offset - s.offset()) < s.length():
            result.append(s)
        else:
            if isinstance(s, sumpf.Signal):
                result.append(sumpf.Signal(channels=s.channels(),
                                           sampling_rate=s.sampling_rate(),
                                           offset=random.randint(first_offset - s.length(),
                                                                 first_offset + s.length()),
                                           labels=s.labels()))
            else:  # s must be a spectrogram
                result.append(sumpf.Spectrogram(channels=s.channels(),
                                                resolution=s.resolution(),
                                                sampling_rate=s.sampling_rate(),
                                                offset=random.randint(first_offset - s.length(),
                                                                      first_offset + s.length()),
                                                labels=s.labels()))
    return result
Пример #5
0
def _concatenate_spectrograms(spectrograms):
    """A simple but inefficient implementation, that concatenates signals by adding them."""
    # test for the corner cases of zero or one signal
    if not spectrograms:
        raise RuntimeError("Nothing to concatenate")
    if len(spectrograms) == 1:
        return spectrograms[0]
    # add the signals
    sum_ = spectrograms[0]
    index = spectrograms[0].length() + spectrograms[0].offset()
    for s in spectrograms[1:]:
        # fill missing channels with zeros
        if len(s) < len(sum_):
            new_channels = numpy.zeros(shape=(len(sum_),
                                              s.number_of_frequencies(),
                                              s.length()),
                                       dtype=numpy.complex128)
            new_channels[0:len(s)] = s.channels()
            s = sumpf.Spectrogram(channels=new_channels, offset=s.offset())
        elif len(sum_) < len(s):
            new_channels = numpy.zeros(shape=(len(s),
                                              sum_.number_of_frequencies(),
                                              sum_.length()),
                                       dtype=numpy.complex128)
            new_channels[0:len(sum_)] = sum_.channels()
            sum_ = sumpf.Spectrogram(channels=new_channels,
                                     offset=sum_.offset())
        sum_ += s.shift(index)
        index += s.length() + s.offset()
    # return a signal with the correct labels
    return sumpf.Spectrogram(channels=sum_.channels(),
                             resolution=spectrograms[0].resolution(),
                             sampling_rate=spectrograms[0].sampling_rate(),
                             offset=sum_.offset(),
                             labels=tuple(
                                 tuple(f"Concatenation {i}"
                                       for i in range(1,
                                                      len(sum_) + 1))))
Пример #6
0
def _compare_merge_first_spectrogram_first(spectrograms, merged):
    """tests if the merged spectrogram's channels are ordered like the input spectrograms"""
    c = 0
    resolution = spectrograms[0].resolution()
    sampling_rate = spectrograms[0].sampling_rate()
    for s in spectrograms:
        spectrogram = sumpf.Spectrogram(channels=s.channels(),
                                        resolution=resolution,        # take the resolution from the first spectrogram
                                        sampling_rate=sampling_rate,  # take the sampling rate from the first spectrogram
                                        offset=s.offset(),
                                        labels=s.labels())            # the merged spectrogram has a label for each channel, which could be empty
        start = spectrogram.offset() - merged.offset()
        stop = start + spectrogram.length()
        max_f = s.number_of_frequencies()
        assert merged[c:c + len(spectrogram), 0:max_f, start:stop] == spectrogram
        c += len(spectrogram)
    assert c == len(merged)
Пример #7
0
from lad_harmonics import lad_training_harmonics
from lad import lad_training
from lad import lad_testing
import numpy as np
import sumpf
import sumpf_staging
import utilities
import matplotlib.pyplot as plt

signal = sumpf.SineWave(frequency=200.0, sampling_rate=2048, length=16384)
#for i in range(2, 2049, 2):
#signal = signal + sumpf.SineWave(frequency=i, sampling_rate=2048, length=16384)
spectrogram = signal.short_time_fourier_transform()
#lad_training_harmonics(spectrogram.magnitude())
lad_training(spectrogram.magnitude())
spectrogram2 = lad_testing(spectrogram.magnitude())
result_spectrogram = sumpf.Spectrogram(channels=spectrogram2)

plot = utilities.plot(spectrogram, log_frequency=False, log_magnitude=True)
plot = plot.plot(result_spectrogram)

#plot = plot.plot(added_signals)
#plot = plot.plot(spectrogram)
#plot[0].set_xlim(0, 5)
#plot[1].set_xlim(0, 5)
plot.show()
import sumpf
import sumpf_staging
import utilities
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches

result = []
resultFormula = []
#resultMLS = [0.53, 0.56, 0.56]
#inputMLS = [2, 3, 4]
inputs = []
input_wave = np.zeros((1, 9, 10000))
for c in range(2, 1000):
    for i in range(0, 10000, c):
        input_wave[0, 0:9, i] = 1
    spectrogram = sumpf.Spectrogram(channels=input_wave)
    res = lad_training(spectrogram.magnitude())
    result.append(res)
    resultFormula.append(pow(c - 1, 2) / (pow(c, 2) + 1))
    input_wave = np.zeros((1, 9, 10000))
    inputs.append(c)

yellow_patch = mpatches.Patch(color='yellow', label='LAD')
black_patch = mpatches.Patch(color='black', label='Formula')
red_patch = mpatches.Patch(color='red', label='MLS')
plt.plot(inputs, result, 'yo', inputs, resultFormula, 'k')
plt.title('Change in Time-Right Weight According to Consecutivity')
plt.ylabel('Time-Right Weight')
plt.xlabel('Consecutivity')
plt.legend(handles=[yellow_patch, black_patch])
plt.show()
Пример #9
0
    def output(
        self
    ):  # noqa: C901; it's either a complex method or a lot of duplicated code
        # pylint: disable=too-many-branches,too-many-statements;
        """Computes the concatenated signal and returns it

        :returns: a Signal instance
        """
        if not self.__data:
            raise RuntimeError("Nothing to concatenate")
        if len(self.__data) == 1:
            return next(iter(self.__data.values())
                        )  # simply return the first and only data set
        else:
            first = next(iter(self.__data.values()))
            # find the start and stop samples of the data sets
            index = 0
            indices = []
            number_of_channels = 0
            for s in self.__data.values():
                start = index + s.offset()
                index = start + s.length()
                indices.append((start, index, s.channels()))
                number_of_channels = max(number_of_channels, len(s))
            indices.sort(key=lambda t: (t[0], t[1]))
            # allocate an array for the concatenated channels and copy the first data set
            offset = min(i[0] for i in indices)
            length = max(i[1] for i in indices) - offset
            start, stop, dataset_channels = indices[0]
            start -= offset
            stop -= offset
            if len(dataset_channels.shape) == 2:
                signal = True
                channels = sumpf_internal.allocate_array(
                    shape=(number_of_channels, length),
                    dtype=dataset_channels.dtype)
                channels[0:len(dataset_channels),
                         start:stop] = dataset_channels
                if len(dataset_channels) < number_of_channels:
                    channels[len(dataset_channels):, start:stop] = 0.0
            else:
                signal = False
                number_of_frequencies = max(i[2].shape[1] for i in indices)
                channels = sumpf_internal.allocate_array(
                    shape=(number_of_channels, number_of_frequencies, length),
                    dtype=dataset_channels.dtype)
                l, f = dataset_channels.shape[0:2]
                channels[0:l, 0:f, start:stop] = dataset_channels
                if f < number_of_frequencies:
                    channels[0:l, f:, start:stop] = 0.0
                if l < number_of_channels:
                    channels[l:, :, start:stop] = 0.0
            # copy the other data sets
            last_index = 0  # the maximum sample index, at which there is already data in the channels array
            for index, previous in zip(indices[1:], indices[0:-1]):
                start, stop, dataset_channels = index
                start -= offset
                stop -= offset
                l, f = dataset_channels.shape[0:2]
                last_index = max(last_index, previous[1] - offset)
                if start < last_index:  # the current data set overlaps with the previous one, add the samples in the overlapping region
                    if last_index >= stop:  # pylint: disable=no-else-continue
                        stop = start + dataset_channels.shape[-1]
                        if signal:
                            channels[0:l, start:stop] += dataset_channels
                        else:
                            channels[0:l, 0:f, start:stop] += dataset_channels
                        continue
                    else:
                        if signal:
                            channels[
                                0:l, start:
                                last_index] += dataset_channels[:,
                                                                0:last_index -
                                                                start]
                            dataset_channels = dataset_channels[:, last_index -
                                                                start:]
                        else:
                            channels[
                                0:l, 0:f, start:
                                last_index] += dataset_channels[:, :,
                                                                0:last_index -
                                                                start]
                            dataset_channels = dataset_channels[:, :,
                                                                last_index -
                                                                start:]
                        start = last_index
                elif start > last_index:  # a gap between the current data set and the previous one, fill it with zeros
                    if signal:
                        channels[:, last_index:start] = 0.0
                    else:
                        channels[:, :, last_index:start] = 0.0
                if signal:
                    channels[0:l, start:stop] = dataset_channels
                    if len(
                            dataset_channels
                    ) < number_of_channels:  # if the current signal has less channels than the other, fill the missing channels with zeros
                        channels[l:, start:stop] = 0.0
                else:
                    channels[0:l, 0:f, start:stop] = dataset_channels
                    if f < number_of_frequencies:
                        channels[0:l, f:, start:stop] = 0.0
                    if len(
                            dataset_channels
                    ) < number_of_channels:  # if the current signal has less channels than the other, fill the missing channels with zeros
                        channels[l:, :, start:stop] = 0.0
            # create and return the result
            if signal:
                return sumpf.Signal(
                    channels=channels,
                    sampling_rate=first.sampling_rate(),
                    offset=offset,
                    labels=tuple(f"Concatenation {i}"
                                 for i in range(1, number_of_channels + 1)))
            else:
                return sumpf.Spectrogram(
                    channels=channels,
                    resolution=first.resolution(),
                    sampling_rate=first.sampling_rate(),
                    offset=offset,
                    labels=tuple(f"Concatenation {i}"
                                 for i in range(1, number_of_channels + 1)))
Пример #10
0
from lad import lad_training
from lad import lad_testing
import numpy as np
import sumpf
import sumpf_staging
import utilities

signal = sumpf.LinearSweep(length=2**18)
spectrogram = signal.short_time_fourier_transform()

lad_training(spectrogram.magnitude())
result = lad_testing(spectrogram.magnitude())

result_spectrogram = sumpf.Spectrogram(
    channels=result,
    resolution=spectrogram.resolution(),
    sampling_rate=spectrogram.sampling_rate())

plot = utilities.plot(result_spectrogram,
                      log_frequency=False,
                      log_magnitude=False)
plot = plot.plot(spectrogram)
plot.show()
Пример #11
0
def test_merge_spectrograms_manually():
    """Tests the merging behavior with manually generated spectrograms."""
    spectrogram1 = sumpf.Spectrogram(channels=numpy.array([([1.11j, 1.12, 1.13j],
                                                            [1.21j, 1.22, 1.23j],
                                                            [1.31j, 1.32, 1.33j])]),
                                     resolution=1.0,
                                     sampling_rate=2.0,
                                     offset=1,
                                     labels=("s1c1",))
    spectrogram2 = sumpf.Spectrogram(channels=numpy.array([([2.11, 2.12j, 2.13, 2.14j],
                                                            [2.21, 2.22j, 2.23, 2.24j]),
                                                           ([3.11, 3.12j, 3.13, 3.14j],
                                                            [3.21, 3.22j, 3.23, 3.24j])]),
                                     resolution=2.0,
                                     sampling_rate=3.0,
                                     offset=-4,
                                     labels=("s2c1", "s2c2"))
    spectrogram12 = sumpf.Spectrogram(channels=numpy.array([([0.0, 0.0, 0.0, 0.0, 0.0, 1.11j, 1.12, 1.13j],
                                                             [0.0, 0.0, 0.0, 0.0, 0.0, 1.21j, 1.22, 1.23j],
                                                             [0.0, 0.0, 0.0, 0.0, 0.0, 1.31j, 1.32, 1.33j]),
                                                            ([2.11, 2.12j, 2.13, 2.14j, 0.0, 0.0, 0.0, 0.0],
                                                             [2.21, 2.22j, 2.23, 2.24j, 0.0, 0.0, 0.0, 0.0],
                                                             [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]),
                                                            ([3.11, 3.12j, 3.13, 3.14j, 0.0, 0.0, 0.0, 0.0],
                                                             [3.21, 3.22j, 3.23, 3.24j, 0.0, 0.0, 0.0, 0.0],
                                                             [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0])]),
                                      resolution=1.0,
                                      sampling_rate=2.0,
                                      offset=-4,
                                      labels=("s1c1", "s2c1", "s2c2"))
    spectrogram21 = sumpf.Spectrogram(channels=numpy.array([([2.11, 2.12j, 2.13, 2.14j, 0.0, 0.0, 0.0, 0.0],
                                                             [2.21, 2.22j, 2.23, 2.24j, 0.0, 0.0, 0.0, 0.0],
                                                             [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]),
                                                            ([3.11, 3.12j, 3.13, 3.14j, 0.0, 0.0, 0.0, 0.0],
                                                             [3.21, 3.22j, 3.23, 3.24j, 0.0, 0.0, 0.0, 0.0],
                                                             [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]),
                                                            ([0.0, 0.0, 0.0, 0.0, 0.0, 1.11j, 1.12, 1.13j],
                                                             [0.0, 0.0, 0.0, 0.0, 0.0, 1.21j, 1.22, 1.23j],
                                                             [0.0, 0.0, 0.0, 0.0, 0.0, 1.31j, 1.32, 1.33j])]),
                                      resolution=2.0,
                                      sampling_rate=3.0,
                                      offset=-4,
                                      labels=("s2c1", "s2c2", "s1c1"))
    spectrogram21c = sumpf.Spectrogram(channels=numpy.array([([2.11, 2.12j, 2.13, 2.14j, 0.0, 0.0, 0.0, 0.0],
                                                              [2.21, 2.22j, 2.23, 2.24j, 0.0, 0.0, 0.0, 0.0],
                                                              [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]),
                                                             ([0.0, 0.0, 0.0, 0.0, 0.0, 1.11j, 1.12, 1.13j],
                                                              [0.0, 0.0, 0.0, 0.0, 0.0, 1.21j, 1.22, 1.23j],
                                                              [0.0, 0.0, 0.0, 0.0, 0.0, 1.31j, 1.32, 1.33j]),
                                                             ([3.11, 3.12j, 3.13, 3.14j, 0.0, 0.0, 0.0, 0.0],
                                                              [3.21, 3.22j, 3.23, 3.24j, 0.0, 0.0, 0.0, 0.0],
                                                              [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0])]),
                                       resolution=2.0,
                                       sampling_rate=3.0,
                                       offset=-4,
                                       labels=("s2c1", "s1c1", "s2c2"))
    FIRST_CHANNELS_FIRST = sumpf.Merge.modes.FIRST_CHANNELS_FIRST
    assert sumpf.Merge([spectrogram1, spectrogram2]).output() == spectrogram12
    assert sumpf.Merge([spectrogram2, spectrogram1]).output() == spectrogram21
    assert sumpf.Merge([spectrogram1, spectrogram2], mode=FIRST_CHANNELS_FIRST).output() == spectrogram12
    assert sumpf.Merge([spectrogram2, spectrogram1], mode=FIRST_CHANNELS_FIRST).output() == spectrogram21c
Пример #12
0
    def output(self):
        """Computes the merged data set and returns it.

        :returns: a data set, that contains all channels of the added data sets.
        """
        if not self.__data:
            raise RuntimeError("Nothing to merge")
        if len(self.__data) == 1:
            return next(iter(self.__data.values())
                        )  # simply return the first and only data set
        else:
            first = next(iter(self.__data.values()))
            number_of_channels = sum(len(s) for s in self.__data.values())
            labels = [""] * number_of_channels
            if isinstance(first, sumpf.Signal):
                merged_offset = min(s.offset() for s in self.__data.values())
                length = max(s.offset() + s.length()
                             for s in self.__data.values()) - merged_offset
                channels = sumpf_internal.allocate_array(
                    shape=(number_of_channels, length))
                for index, channel, offset, label in zip(
                        self.__indices(),
                    (c for d in self.__data.values() for c in d.channels()),
                    (d.offset() for d in self.__data.values()
                     for l in d.channels()),  # pylint: disable=line-too-long
                    (l for d in self.__data.values() for l in d.labels())):
                    start = offset - merged_offset
                    stop = start + len(channel)
                    channels[index, 0:start] = 0.0
                    channels[index, start:stop] = channel
                    channels[index, stop:] = 0.0
                    labels[index] = label
                return sumpf.Signal(channels=channels,
                                    sampling_rate=first.sampling_rate(),
                                    offset=merged_offset,
                                    labels=labels)
            elif isinstance(first, sumpf.Spectrum):
                length = max(s.length() for s in self.__data.values())
                channels = sumpf_internal.allocate_array(
                    shape=(number_of_channels, length), dtype=numpy.complex128)
                for index, channel, label in zip(
                        self.__indices(), (c for d in self.__data.values()
                                           for c in d.channels()),
                    (l for d in self.__data.values() for l in d.labels())):
                    channels[index, 0:len(channel)] = channel
                    channels[index, len(channel):] = 0.0
                    labels[index] = label
                return sumpf.Spectrum(channels=channels,
                                      resolution=first.resolution(),
                                      labels=labels)
            elif isinstance(first, sumpf.Spectrogram):
                merged_offset = min(s.offset() for s in self.__data.values())
                length = max(s.offset() + s.length()
                             for s in self.__data.values()) - merged_offset
                number_of_frequencies = max(s.number_of_frequencies()
                                            for s in self.__data.values())
                channels = sumpf_internal.allocate_array(
                    shape=(number_of_channels, number_of_frequencies, length),
                    dtype=numpy.complex128)
                channels[:] = 0.0
                for index, channel, offset, label in zip(
                        self.__indices(),
                    (c for d in self.__data.values() for c in d.channels()),
                    (d.offset() for d in self.__data.values()
                     for l in d.channels()),  # pylint: disable=line-too-long
                    (l for d in self.__data.values() for l in d.labels())):
                    channel_frequencies, channel_length = channel.shape
                    start = offset - merged_offset
                    stop = start + channel_length
                    channels[index, 0:channel_frequencies,
                             start:stop] = channel
                    labels[index] = label
                return sumpf.Spectrogram(channels=channels,
                                         resolution=first.resolution(),
                                         sampling_rate=first.sampling_rate(),
                                         offset=merged_offset,
                                         labels=labels)
            elif isinstance(first, sumpf.Filter):
                transfer_functions = [None] * number_of_channels
                for index, transfer_function, label in zip(
                        self.__indices(),
                    (tf for d in self.__data.values()
                     for tf in d.transfer_functions()),  # pylint: disable=line-too-long
                    (l for d in self.__data.values() for l in d.labels())):
                    transfer_functions[index] = transfer_function
                    labels[index] = label
                return sumpf.Filter(transfer_functions=transfer_functions,
                                    labels=labels)
            else:
                raise ValueError(
                    f"Cannot merge data sets of type {type(first)}")