# -*- coding: utf-8 -*-
"""
Unit tests for the spectral module.
:copyright: Copyright 2015 by the Elephant team, see AUTHORS.txt.
:license: Modified BSD, see LICENSE.txt for details.
"""
import unittest
import numpy as np
import scipy.signal as spsig
import quantities as pq
import neo.core as n
import elephant.spectral
[docs]class WelchPSDTestCase(unittest.TestCase):
[docs] def test_welch_psd_errors(self):
# generate a dummy data
data = n.AnalogSignal(np.zeros(5000), sampling_period=0.001*pq.s,
units='mV')
# check for invalid parameter values
# - length of segments
self.assertRaises(ValueError, elephant.spectral.welch_psd, data,
len_seg=0)
self.assertRaises(ValueError, elephant.spectral.welch_psd, data,
len_seg=data.shape[0] * 2)
# - number of segments
self.assertRaises(ValueError, elephant.spectral.welch_psd, data,
num_seg=0)
self.assertRaises(ValueError, elephant.spectral.welch_psd, data,
num_seg=data.shape[0] * 2)
# - frequency resolution
self.assertRaises(ValueError, elephant.spectral.welch_psd, data,
freq_res=-1)
self.assertRaises(ValueError, elephant.spectral.welch_psd, data,
freq_res=data.sampling_rate/(data.shape[0]+1))
# - overlap
self.assertRaises(ValueError, elephant.spectral.welch_psd, data,
overlap=-1.0)
self.assertRaises(ValueError, elephant.spectral.welch_psd, data,
overlap=1.1)
[docs] def test_welch_psd_behavior(self):
# generate data by adding white noise and a sinusoid
data_length = 5000
sampling_period = 0.001
signal_freq = 100.0
noise = np.random.normal(size=data_length)
signal = [np.sin(2*np.pi*signal_freq*t)
for t in np.arange(0, data_length*sampling_period,
sampling_period)]
data = n.AnalogSignal(np.array(signal+noise),
sampling_period=sampling_period*pq.s,
units='mV')
# consistency between different ways of specifying segment length
freqs1, psd1 = elephant.spectral.welch_psd(data, len_seg=data_length//5, overlap=0)
freqs2, psd2 = elephant.spectral.welch_psd(data, num_seg=5, overlap=0)
self.assertTrue((psd1==psd2).all() and (freqs1==freqs2).all())
# frequency resolution and consistency with data
freq_res = 1.0 * pq.Hz
freqs, psd = elephant.spectral.welch_psd(data, freq_res=freq_res)
self.assertAlmostEqual(freq_res, freqs[1]-freqs[0])
self.assertEqual(freqs[psd.argmax()], signal_freq)
freqs_np, psd_np = elephant.spectral.welch_psd(data.magnitude.flatten(), fs=1/sampling_period, freq_res=freq_res)
self.assertTrue((freqs==freqs_np).all() and (psd==psd_np).all())
# check of scipy.signal.welch() parameters
params = {'window': 'hamming', 'nfft': 1024, 'detrend': 'linear',
'return_onesided': False, 'scaling': 'spectrum'}
for key, val in params.items():
freqs, psd = elephant.spectral.welch_psd(data, len_seg=1000, overlap=0, **{key: val})
freqs_spsig, psd_spsig = spsig.welch(np.rollaxis(data, 0, len(data.shape)),
fs=1/sampling_period, nperseg=1000, noverlap=0, **{key: val})
self.assertTrue((freqs==freqs_spsig).all() and (psd==psd_spsig).all())
# - generate multidimensional data for check of parameter `axis`
num_channel = 4
data_length = 5000
data_multidim = np.random.normal(size=(num_channel, data_length))
freqs, psd = elephant.spectral.welch_psd(data_multidim)
freqs_T, psd_T = elephant.spectral.welch_psd(data_multidim.T, axis=0)
self.assertTrue(np.all(freqs==freqs_T))
self.assertTrue(np.all(psd==psd_T.T))
[docs] def test_welch_psd_input_types(self):
# generate a test data
sampling_period = 0.001
data = n.AnalogSignal(np.array(np.random.normal(size=5000)),
sampling_period=sampling_period*pq.s,
units='mV')
# outputs from AnalogSignal input are of Quantity type (standard usage)
freqs_neo, psd_neo = elephant.spectral.welch_psd(data)
self.assertTrue(isinstance(freqs_neo, pq.quantity.Quantity))
self.assertTrue(isinstance(psd_neo, pq.quantity.Quantity))
# outputs from Quantity array input are of Quantity type
freqs_pq, psd_pq = elephant.spectral.welch_psd(data.magnitude.flatten()*data.units, fs=1/sampling_period)
self.assertTrue(isinstance(freqs_pq, pq.quantity.Quantity))
self.assertTrue(isinstance(psd_pq, pq.quantity.Quantity))
# outputs from Numpy ndarray input are NOT of Quantity type
freqs_np, psd_np = elephant.spectral.welch_psd(data.magnitude.flatten(), fs=1/sampling_period)
self.assertFalse(isinstance(freqs_np, pq.quantity.Quantity))
self.assertFalse(isinstance(psd_np, pq.quantity.Quantity))
# check if the results from different input types are identical
self.assertTrue((freqs_neo==freqs_pq).all() and (psd_neo==psd_pq).all())
self.assertTrue((freqs_neo==freqs_np).all() and (psd_neo==psd_np).all())
[docs] def test_welch_psd_multidim_input(self):
# generate multidimensional data
num_channel = 4
data_length = 5000
sampling_period = 0.001
noise = np.random.normal(size=(num_channel, data_length))
data_np = np.array(noise)
# Since row-column order in AnalogSignal is different from the
# conventional one, `data_np` needs to be transposed when its used to
# define an AnalogSignal
data_neo = n.AnalogSignal(data_np.T,
sampling_period=sampling_period*pq.s,
units='mV')
data_neo_1dim = n.AnalogSignal(data_np[0],
sampling_period=sampling_period*pq.s,
units='mV')
# check if the results from different input types are identical
freqs_np, psd_np = elephant.spectral.welch_psd(data_np,
fs=1/sampling_period)
freqs_neo, psd_neo = elephant.spectral.welch_psd(data_neo)
freqs_neo_1dim, psd_neo_1dim = elephant.spectral.welch_psd(data_neo_1dim)
self.assertTrue(np.all(freqs_np==freqs_neo))
self.assertTrue(np.all(psd_np==psd_neo))
self.assertTrue(np.all(psd_neo_1dim==psd_neo[0]))
[docs]class WelchCohereTestCase(unittest.TestCase):
[docs] def test_welch_cohere_errors(self):
# generate a dummy data
x = n.AnalogSignal(np.zeros(5000), sampling_period=0.001*pq.s,
units='mV')
y = n.AnalogSignal(np.zeros(5000), sampling_period=0.001*pq.s,
units='mV')
# check for invalid parameter values
# - length of segments
self.assertRaises(ValueError, elephant.spectral.welch_cohere, x, y,
len_seg=0)
self.assertRaises(ValueError, elephant.spectral.welch_cohere, x, y,
len_seg=x.shape[0] * 2)
# - number of segments
self.assertRaises(ValueError, elephant.spectral.welch_cohere, x, y,
num_seg=0)
self.assertRaises(ValueError, elephant.spectral.welch_cohere, x, y,
num_seg=x.shape[0] * 2)
# - frequency resolution
self.assertRaises(ValueError, elephant.spectral.welch_cohere, x, y,
freq_res=-1)
self.assertRaises(ValueError, elephant.spectral.welch_cohere, x, y,
freq_res=x.sampling_rate/(x.shape[0]+1))
# - overlap
self.assertRaises(ValueError, elephant.spectral.welch_cohere, x, y,
overlap=-1.0)
self.assertRaises(ValueError, elephant.spectral.welch_cohere, x, y,
overlap=1.1)
[docs] def test_welch_cohere_behavior(self):
# generate data by adding white noise and a sinusoid
data_length = 5000
sampling_period = 0.001
signal_freq = 100.0
noise1 = np.random.normal(size=data_length) * 0.01
noise2 = np.random.normal(size=data_length) * 0.01
signal1 = [np.cos(2*np.pi*signal_freq*t)
for t in np.arange(0, data_length*sampling_period,
sampling_period)]
signal2 = [np.sin(2*np.pi*signal_freq*t)
for t in np.arange(0, data_length*sampling_period,
sampling_period)]
x = n.AnalogSignal(np.array(signal1+noise1), units='mV',
sampling_period=sampling_period*pq.s)
y = n.AnalogSignal(np.array(signal2+noise2), units='mV',
sampling_period=sampling_period*pq.s)
# consistency between different ways of specifying segment length
freqs1, coherency1, phase_lag1 = elephant.spectral.welch_cohere(x, y,
len_seg=data_length//5, overlap=0)
freqs2, coherency2, phase_lag2 = elephant.spectral.welch_cohere(x, y,
num_seg=5, overlap=0)
self.assertTrue((coherency1==coherency2).all() and
(phase_lag1==phase_lag2).all() and
(freqs1==freqs2).all())
# frequency resolution and consistency with data
freq_res = 1.0 * pq.Hz
freqs, coherency, phase_lag = elephant.spectral.welch_cohere(x, y,
freq_res=freq_res)
self.assertAlmostEqual(freq_res, freqs[1]-freqs[0])
self.assertAlmostEqual(freqs[coherency.argmax()], signal_freq,
places=2)
self.assertAlmostEqual(phase_lag[coherency.argmax()], np.pi/2,
places=2)
freqs_np, coherency_np, phase_lag_np =\
elephant.spectral.welch_cohere(x.magnitude.flatten(), y.magnitude.flatten(),
fs=1/sampling_period, freq_res=freq_res)
self.assertTrue((freqs == freqs_np).all() and
(coherency[:, 0] == coherency_np).all() and
(phase_lag[:, 0] == phase_lag_np).all())
# - check the behavior of parameter `axis` using multidimensional data
num_channel = 4
data_length = 5000
x_multidim = np.random.normal(size=(num_channel, data_length))
y_multidim = np.random.normal(size=(num_channel, data_length))
freqs, coherency, phase_lag =\
elephant.spectral.welch_cohere(x_multidim, y_multidim)
freqs_T, coherency_T, phase_lag_T =\
elephant.spectral.welch_cohere(x_multidim.T, y_multidim.T, axis=0)
self.assertTrue(np.all(freqs==freqs_T))
self.assertTrue(np.all(coherency==coherency_T.T))
self.assertTrue(np.all(phase_lag==phase_lag_T.T))
[docs] def test_welch_cohere_input_types(self):
# generate a test data
sampling_period = 0.001
x = n.AnalogSignal(np.array(np.random.normal(size=5000)),
sampling_period=sampling_period*pq.s,
units='mV')
y = n.AnalogSignal(np.array(np.random.normal(size=5000)),
sampling_period=sampling_period*pq.s,
units='mV')
# outputs from AnalogSignal input are of Quantity type
# (standard usage)
freqs_neo, coherency_neo, phase_lag_neo =\
elephant.spectral.welch_cohere(x, y)
self.assertTrue(isinstance(freqs_neo, pq.quantity.Quantity))
self.assertTrue(isinstance(phase_lag_neo, pq.quantity.Quantity))
# outputs from Quantity array input are of Quantity type
freqs_pq, coherency_pq, phase_lag_pq =\
elephant.spectral.welch_cohere(x.magnitude.flatten()*x.units,
y.magnitude.flatten()*y.units, fs=1/sampling_period)
self.assertTrue(isinstance(freqs_pq, pq.quantity.Quantity))
self.assertTrue(isinstance(phase_lag_pq, pq.quantity.Quantity))
# outputs from Numpy ndarray input are NOT of Quantity type
freqs_np, coherency_np, phase_lag_np =\
elephant.spectral.welch_cohere(x.magnitude.flatten(), y.magnitude.flatten(),
fs=1/sampling_period)
self.assertFalse(isinstance(freqs_np, pq.quantity.Quantity))
self.assertFalse(isinstance(phase_lag_np, pq.quantity.Quantity))
# check if the results from different input types are identical
self.assertTrue((freqs_neo==freqs_pq).all() and
(coherency_neo[:, 0]==coherency_pq).all() and
(phase_lag_neo[:, 0]==phase_lag_pq).all())
self.assertTrue((freqs_neo==freqs_np).all() and
(coherency_neo[:, 0]==coherency_np).all() and
(phase_lag_neo[:, 0]==phase_lag_np).all())
[docs] def test_welch_cohere_multidim_input(self):
# generate multidimensional data
num_channel = 4
data_length = 5000
sampling_period = 0.001
x_np = np.array(np.random.normal(size=(num_channel, data_length)))
y_np = np.array(np.random.normal(size=(num_channel, data_length)))
# Since row-column order in AnalogSignal is different from the
# convention in NumPy/SciPy, `data_np` needs to be transposed when its
# used to define an AnalogSignal
x_neo = n.AnalogSignal(x_np.T, units='mV',
sampling_period=sampling_period*pq.s)
y_neo = n.AnalogSignal(y_np.T, units='mV',
sampling_period=sampling_period*pq.s)
x_neo_1dim = n.AnalogSignal(x_np[0], units='mV',
sampling_period=sampling_period*pq.s)
y_neo_1dim = n.AnalogSignal(y_np[0], units='mV',
sampling_period=sampling_period*pq.s)
# check if the results from different input types are identical
freqs_np, coherency_np, phase_lag_np =\
elephant.spectral.welch_cohere(x_np, y_np, fs=1/sampling_period)
freqs_neo, coherency_neo, phase_lag_neo =\
elephant.spectral.welch_cohere(x_neo, y_neo)
freqs_neo_1dim, coherency_neo_1dim, phase_lag_neo_1dim =\
elephant.spectral.welch_cohere(x_neo_1dim, y_neo_1dim)
self.assertTrue(np.all(freqs_np==freqs_neo))
self.assertTrue(np.all(coherency_np.T==coherency_neo))
self.assertTrue(np.all(phase_lag_np.T==phase_lag_neo))
self.assertTrue(np.all(coherency_neo_1dim[:, 0]==coherency_neo[:, 0]))
self.assertTrue(np.all(phase_lag_neo_1dim[:, 0]==phase_lag_neo[:, 0]))
if __name__ == "__main__":
runner = unittest.TextTestRunner(verbosity=2)
runner.run(suite())