mcsm-benchs: Benchmarks with personalized noise-generating functions

[1]:
import numpy as np
from numpy import pi as pi
import pandas as pd
import scipy.signal as sg
from matplotlib import pyplot as plt
from mcsm_benchs.Benchmark import Benchmark
from mcsm_benchs.ResultsInterpreter import ResultsInterpreter
from utils import spectrogram_thresholding, get_stft

Creating a dummy dictionary of methods

First let us define a dummy method for testing. Methods should receive a numpy array with shape (N,) where N is the number of time samples of the signal. Additionally, they can receive any number of positional or keyword arguments to allow testing different combinations of input parameters. The shape of the output depends on the task (signal denoising or detection). So the recommended signature of a method should be the following:

output = a_method(noisy_signal, *args, **kwargs).

If one set task='denoising', output shoud be a (N,) numpy array, i.e. the same shape as the input parameter noisy_signal, whereas if task='detection', the output should be boolean (0 or False for no signal, and 1 or True otherwise).

After this, we need to create a dictionary of methods to pass the Benchmark object at the moment of instantiation.

[2]:
def method_1(noisy_signal, *args, **kwargs):
    # If additional input parameters are needed, they can be passed in a tuple using
    # *args or **kwargs and then parsed.
    xr = spectrogram_thresholding(noisy_signal,1.0,fun='hard')
    return xr

def method_2(noisy_signal, *args, **kwargs):
    # If additional input parameters are needed, they can be passed in a tuple using
    # *args or **kwargs and then parsed.
    xr = spectrogram_thresholding(noisy_signal,2.0,fun='soft')
    return xr

# Create a dictionary of the methods to test.
my_methods = {
    'Method 1': method_1,
    'Method 2': method_2,
    }

Benchmarks with different kinds of noise

Now we are ready to instantiate a Benchmark object and run a benchmark using the proposed methods.

The Benchmark constructor receives a number of input parameters. The parameter complex_noise can be True or False indicating if the simulations are to be done using white complex Gaussian noise or not. However, if a function is passed instead of a bool variable, the function is called in order to obtain a different realization of noise. The noise generator function must receive an int variable indicating the length of the time series to generate: noise = noise_generator_fun(N,).

Real White Gaussian noise (default)

[3]:
# Benchmark class uses np.random.randn(N,) to generate noise realizations
n = np.random.randn(1024)

f, Pxx_den = sg.welch(n)
plt.semilogy(f, Pxx_den)
plt.xlabel('frequency')
plt.ylabel('PSD')
plt.title('White (Gaussian) Noise')
plt.show()
../_images/notebooks_demo_noise_function_7_0.png
[4]:
benchmark = Benchmark(task = 'denoising', # defines the default performance function
                        methods = my_methods, # dictionary of methods
                        N = 256,  # Length of the signals
                        SNRin = [40, 50], # SNRs to use during the test
                        repetitions = 3, # Number of noise realizations to use
                        signal_ids=['LinearChirp', 'CosChirp',], # Signals to use
                        complex_noise=False # Real white Gaussian Noise
                        )

benchmark.run() # Run the test.
results_df = benchmark.get_results_as_df() # This formats the results on a DataFrame
results_df
Running benchmark...
- Signal LinearChirp
  0%|          | 0/2 [00:00<?, ?it/s]100%|██████████| 2/2 [00:00<00:00, 418.51it/s]
- Signal CosChirp
100%|██████████| 2/2 [00:00<00:00, 354.07it/s]
The test has finished.

[4]:
Method Parameter Signal_id Repetition 40 50
6 Method 1 ((), {}) CosChirp 0 35.810596 35.848604
7 Method 1 ((), {}) CosChirp 1 35.076937 36.675817
8 Method 1 ((), {}) CosChirp 2 34.878397 35.250035
0 Method 1 ((), {}) LinearChirp 0 40.716980 45.830269
1 Method 1 ((), {}) LinearChirp 1 40.761751 44.879363
2 Method 1 ((), {}) LinearChirp 2 40.506275 44.715266
9 Method 2 ((), {}) CosChirp 0 22.325833 21.801351
10 Method 2 ((), {}) CosChirp 1 20.850857 21.708549
11 Method 2 ((), {}) CosChirp 2 21.080309 21.317084
3 Method 2 ((), {}) LinearChirp 0 35.733726 37.497704
4 Method 2 ((), {}) LinearChirp 1 35.193784 36.680307
5 Method 2 ((), {}) LinearChirp 2 36.448123 37.518342

Pink noise

Let’s first import a function to generate colored noise, and later pass it as a parameter to the Benchmark class constructor.

[5]:
from utils import voss

#Generates pink noise using the Voss-McCartney algorithm.
noise_fun = lambda N : voss(N,)
n = noise_fun(1024)

f, Pxx_den = sg.welch(n)
plt.semilogy(f, Pxx_den)
plt.xlabel('frequency')
plt.ylabel('PSD')
plt.title('"Pink" Noise')
plt.show()
../_images/notebooks_demo_noise_function_10_0.png
[6]:
benchmark = Benchmark(task = 'denoising', # defines the default performance function
                        methods = my_methods, # dictionary of methods
                        N = 256,  # Length of the signals
                        SNRin = [40, 50], # SNRs to use during the test
                        repetitions = 3, # Number of noise realizations to use
                        signal_ids=['LinearChirp', 'CosChirp',], # Signals to use
                        complex_noise=noise_fun, # "Pink" Noise
                        verbosity=0,
                        )

benchmark.run() # Run the test.
results_df = benchmark.get_results_as_df() # This formats the results on a DataFrame
results_df
Running benchmark...
100%|██████████| 2/2 [00:00<00:00, 326.96it/s]
100%|██████████| 2/2 [00:00<00:00, 187.86it/s]
[6]:
Method Parameter Signal_id Repetition 40 50
6 Method 1 ((), {}) CosChirp 0 35.191254 35.957057
7 Method 1 ((), {}) CosChirp 1 35.210970 35.960973
8 Method 1 ((), {}) CosChirp 2 36.105207 36.592537
0 Method 1 ((), {}) LinearChirp 0 40.903732 46.124983
1 Method 1 ((), {}) LinearChirp 1 41.337536 46.151734
2 Method 1 ((), {}) LinearChirp 2 40.517168 45.670656
9 Method 2 ((), {}) CosChirp 0 21.463604 21.583236
10 Method 2 ((), {}) CosChirp 1 21.182747 21.645561
11 Method 2 ((), {}) CosChirp 2 21.755696 21.611189
3 Method 2 ((), {}) LinearChirp 0 34.107279 37.318161
4 Method 2 ((), {}) LinearChirp 1 35.669359 37.793854
5 Method 2 ((), {}) LinearChirp 2 35.873981 37.461903