123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602 |
- import numpy as np
- from numpy.testing import (assert_, assert_approx_equal,
- assert_allclose, assert_array_equal, assert_equal,
- assert_array_almost_equal_nulp, suppress_warnings)
- import pytest
- from pytest import raises as assert_raises
- from scipy import signal
- from scipy.fft import fftfreq
- from scipy.signal import (periodogram, welch, lombscargle, csd, coherence,
- spectrogram, stft, istft, check_COLA, check_NOLA)
- from scipy.signal._spectral_py import _spectral_helper
- class TestPeriodogram:
- def test_real_onesided_even(self):
- x = np.zeros(16)
- x[0] = 1
- f, p = periodogram(x)
- assert_allclose(f, np.linspace(0, 0.5, 9))
- q = np.ones(9)
- q[0] = 0
- q[-1] /= 2.0
- q /= 8
- assert_allclose(p, q)
- def test_real_onesided_odd(self):
- x = np.zeros(15)
- x[0] = 1
- f, p = periodogram(x)
- assert_allclose(f, np.arange(8.0)/15.0)
- q = np.ones(8)
- q[0] = 0
- q *= 2.0/15.0
- assert_allclose(p, q, atol=1e-15)
- def test_real_twosided(self):
- x = np.zeros(16)
- x[0] = 1
- f, p = periodogram(x, return_onesided=False)
- assert_allclose(f, fftfreq(16, 1.0))
- q = np.full(16, 1/16.0)
- q[0] = 0
- assert_allclose(p, q)
- def test_real_spectrum(self):
- x = np.zeros(16)
- x[0] = 1
- f, p = periodogram(x, scaling='spectrum')
- g, q = periodogram(x, scaling='density')
- assert_allclose(f, np.linspace(0, 0.5, 9))
- assert_allclose(p, q/16.0)
- def test_integer_even(self):
- x = np.zeros(16, dtype=int)
- x[0] = 1
- f, p = periodogram(x)
- assert_allclose(f, np.linspace(0, 0.5, 9))
- q = np.ones(9)
- q[0] = 0
- q[-1] /= 2.0
- q /= 8
- assert_allclose(p, q)
- def test_integer_odd(self):
- x = np.zeros(15, dtype=int)
- x[0] = 1
- f, p = periodogram(x)
- assert_allclose(f, np.arange(8.0)/15.0)
- q = np.ones(8)
- q[0] = 0
- q *= 2.0/15.0
- assert_allclose(p, q, atol=1e-15)
- def test_integer_twosided(self):
- x = np.zeros(16, dtype=int)
- x[0] = 1
- f, p = periodogram(x, return_onesided=False)
- assert_allclose(f, fftfreq(16, 1.0))
- q = np.full(16, 1/16.0)
- q[0] = 0
- assert_allclose(p, q)
- def test_complex(self):
- x = np.zeros(16, np.complex128)
- x[0] = 1.0 + 2.0j
- f, p = periodogram(x, return_onesided=False)
- assert_allclose(f, fftfreq(16, 1.0))
- q = np.full(16, 5.0/16.0)
- q[0] = 0
- assert_allclose(p, q)
- def test_unk_scaling(self):
- assert_raises(ValueError, periodogram, np.zeros(4, np.complex128),
- scaling='foo')
- def test_nd_axis_m1(self):
- x = np.zeros(20, dtype=np.float64)
- x = x.reshape((2,1,10))
- x[:,:,0] = 1.0
- f, p = periodogram(x)
- assert_array_equal(p.shape, (2, 1, 6))
- assert_array_almost_equal_nulp(p[0,0,:], p[1,0,:], 60)
- f0, p0 = periodogram(x[0,0,:])
- assert_array_almost_equal_nulp(p0[np.newaxis,:], p[1,:], 60)
- def test_nd_axis_0(self):
- x = np.zeros(20, dtype=np.float64)
- x = x.reshape((10,2,1))
- x[0,:,:] = 1.0
- f, p = periodogram(x, axis=0)
- assert_array_equal(p.shape, (6,2,1))
- assert_array_almost_equal_nulp(p[:,0,0], p[:,1,0], 60)
- f0, p0 = periodogram(x[:,0,0])
- assert_array_almost_equal_nulp(p0, p[:,1,0])
- def test_window_external(self):
- x = np.zeros(16)
- x[0] = 1
- f, p = periodogram(x, 10, 'hann')
- win = signal.get_window('hann', 16)
- fe, pe = periodogram(x, 10, win)
- assert_array_almost_equal_nulp(p, pe)
- assert_array_almost_equal_nulp(f, fe)
- win_err = signal.get_window('hann', 32)
- assert_raises(ValueError, periodogram, x,
- 10, win_err) # win longer than signal
- def test_padded_fft(self):
- x = np.zeros(16)
- x[0] = 1
- f, p = periodogram(x)
- fp, pp = periodogram(x, nfft=32)
- assert_allclose(f, fp[::2])
- assert_allclose(p, pp[::2])
- assert_array_equal(pp.shape, (17,))
- def test_empty_input(self):
- f, p = periodogram([])
- assert_array_equal(f.shape, (0,))
- assert_array_equal(p.shape, (0,))
- for shape in [(0,), (3,0), (0,5,2)]:
- f, p = periodogram(np.empty(shape))
- assert_array_equal(f.shape, shape)
- assert_array_equal(p.shape, shape)
- def test_empty_input_other_axis(self):
- for shape in [(3,0), (0,5,2)]:
- f, p = periodogram(np.empty(shape), axis=1)
- assert_array_equal(f.shape, shape)
- assert_array_equal(p.shape, shape)
- def test_short_nfft(self):
- x = np.zeros(18)
- x[0] = 1
- f, p = periodogram(x, nfft=16)
- assert_allclose(f, np.linspace(0, 0.5, 9))
- q = np.ones(9)
- q[0] = 0
- q[-1] /= 2.0
- q /= 8
- assert_allclose(p, q)
- def test_nfft_is_xshape(self):
- x = np.zeros(16)
- x[0] = 1
- f, p = periodogram(x, nfft=16)
- assert_allclose(f, np.linspace(0, 0.5, 9))
- q = np.ones(9)
- q[0] = 0
- q[-1] /= 2.0
- q /= 8
- assert_allclose(p, q)
- def test_real_onesided_even_32(self):
- x = np.zeros(16, 'f')
- x[0] = 1
- f, p = periodogram(x)
- assert_allclose(f, np.linspace(0, 0.5, 9))
- q = np.ones(9, 'f')
- q[0] = 0
- q[-1] /= 2.0
- q /= 8
- assert_allclose(p, q)
- assert_(p.dtype == q.dtype)
- def test_real_onesided_odd_32(self):
- x = np.zeros(15, 'f')
- x[0] = 1
- f, p = periodogram(x)
- assert_allclose(f, np.arange(8.0)/15.0)
- q = np.ones(8, 'f')
- q[0] = 0
- q *= 2.0/15.0
- assert_allclose(p, q, atol=1e-7)
- assert_(p.dtype == q.dtype)
- def test_real_twosided_32(self):
- x = np.zeros(16, 'f')
- x[0] = 1
- f, p = periodogram(x, return_onesided=False)
- assert_allclose(f, fftfreq(16, 1.0))
- q = np.full(16, 1/16.0, 'f')
- q[0] = 0
- assert_allclose(p, q)
- assert_(p.dtype == q.dtype)
- def test_complex_32(self):
- x = np.zeros(16, 'F')
- x[0] = 1.0 + 2.0j
- f, p = periodogram(x, return_onesided=False)
- assert_allclose(f, fftfreq(16, 1.0))
- q = np.full(16, 5.0/16.0, 'f')
- q[0] = 0
- assert_allclose(p, q)
- assert_(p.dtype == q.dtype)
- def test_shorter_window_error(self):
- x = np.zeros(16)
- x[0] = 1
- win = signal.get_window('hann', 10)
- expected_msg = ('the size of the window must be the same size '
- 'of the input on the specified axis')
- with assert_raises(ValueError, match=expected_msg):
- periodogram(x, window=win)
- class TestWelch:
- def test_real_onesided_even(self):
- x = np.zeros(16)
- x[0] = 1
- x[8] = 1
- f, p = welch(x, nperseg=8)
- assert_allclose(f, np.linspace(0, 0.5, 5))
- q = np.array([0.08333333, 0.15277778, 0.22222222, 0.22222222,
- 0.11111111])
- assert_allclose(p, q, atol=1e-7, rtol=1e-7)
- def test_real_onesided_odd(self):
- x = np.zeros(16)
- x[0] = 1
- x[8] = 1
- f, p = welch(x, nperseg=9)
- assert_allclose(f, np.arange(5.0)/9.0)
- q = np.array([0.12477455, 0.23430933, 0.17072113, 0.17072113,
- 0.17072113])
- assert_allclose(p, q, atol=1e-7, rtol=1e-7)
- def test_real_twosided(self):
- x = np.zeros(16)
- x[0] = 1
- x[8] = 1
- f, p = welch(x, nperseg=8, return_onesided=False)
- assert_allclose(f, fftfreq(8, 1.0))
- q = np.array([0.08333333, 0.07638889, 0.11111111, 0.11111111,
- 0.11111111, 0.11111111, 0.11111111, 0.07638889])
- assert_allclose(p, q, atol=1e-7, rtol=1e-7)
- def test_real_spectrum(self):
- x = np.zeros(16)
- x[0] = 1
- x[8] = 1
- f, p = welch(x, nperseg=8, scaling='spectrum')
- assert_allclose(f, np.linspace(0, 0.5, 5))
- q = np.array([0.015625, 0.02864583, 0.04166667, 0.04166667,
- 0.02083333])
- assert_allclose(p, q, atol=1e-7, rtol=1e-7)
- def test_integer_onesided_even(self):
- x = np.zeros(16, dtype=int)
- x[0] = 1
- x[8] = 1
- f, p = welch(x, nperseg=8)
- assert_allclose(f, np.linspace(0, 0.5, 5))
- q = np.array([0.08333333, 0.15277778, 0.22222222, 0.22222222,
- 0.11111111])
- assert_allclose(p, q, atol=1e-7, rtol=1e-7)
- def test_integer_onesided_odd(self):
- x = np.zeros(16, dtype=int)
- x[0] = 1
- x[8] = 1
- f, p = welch(x, nperseg=9)
- assert_allclose(f, np.arange(5.0)/9.0)
- q = np.array([0.12477455, 0.23430933, 0.17072113, 0.17072113,
- 0.17072113])
- assert_allclose(p, q, atol=1e-7, rtol=1e-7)
- def test_integer_twosided(self):
- x = np.zeros(16, dtype=int)
- x[0] = 1
- x[8] = 1
- f, p = welch(x, nperseg=8, return_onesided=False)
- assert_allclose(f, fftfreq(8, 1.0))
- q = np.array([0.08333333, 0.07638889, 0.11111111, 0.11111111,
- 0.11111111, 0.11111111, 0.11111111, 0.07638889])
- assert_allclose(p, q, atol=1e-7, rtol=1e-7)
- def test_complex(self):
- x = np.zeros(16, np.complex128)
- x[0] = 1.0 + 2.0j
- x[8] = 1.0 + 2.0j
- f, p = welch(x, nperseg=8, return_onesided=False)
- assert_allclose(f, fftfreq(8, 1.0))
- q = np.array([0.41666667, 0.38194444, 0.55555556, 0.55555556,
- 0.55555556, 0.55555556, 0.55555556, 0.38194444])
- assert_allclose(p, q, atol=1e-7, rtol=1e-7)
- def test_unk_scaling(self):
- assert_raises(ValueError, welch, np.zeros(4, np.complex128),
- scaling='foo', nperseg=4)
- def test_detrend_linear(self):
- x = np.arange(10, dtype=np.float64) + 0.04
- f, p = welch(x, nperseg=10, detrend='linear')
- assert_allclose(p, np.zeros_like(p), atol=1e-15)
- def test_no_detrending(self):
- x = np.arange(10, dtype=np.float64) + 0.04
- f1, p1 = welch(x, nperseg=10, detrend=False)
- f2, p2 = welch(x, nperseg=10, detrend=lambda x: x)
- assert_allclose(f1, f2, atol=1e-15)
- assert_allclose(p1, p2, atol=1e-15)
- def test_detrend_external(self):
- x = np.arange(10, dtype=np.float64) + 0.04
- f, p = welch(x, nperseg=10,
- detrend=lambda seg: signal.detrend(seg, type='l'))
- assert_allclose(p, np.zeros_like(p), atol=1e-15)
- def test_detrend_external_nd_m1(self):
- x = np.arange(40, dtype=np.float64) + 0.04
- x = x.reshape((2,2,10))
- f, p = welch(x, nperseg=10,
- detrend=lambda seg: signal.detrend(seg, type='l'))
- assert_allclose(p, np.zeros_like(p), atol=1e-15)
- def test_detrend_external_nd_0(self):
- x = np.arange(20, dtype=np.float64) + 0.04
- x = x.reshape((2,1,10))
- x = np.moveaxis(x, 2, 0)
- f, p = welch(x, nperseg=10, axis=0,
- detrend=lambda seg: signal.detrend(seg, axis=0, type='l'))
- assert_allclose(p, np.zeros_like(p), atol=1e-15)
- def test_nd_axis_m1(self):
- x = np.arange(20, dtype=np.float64) + 0.04
- x = x.reshape((2,1,10))
- f, p = welch(x, nperseg=10)
- assert_array_equal(p.shape, (2, 1, 6))
- assert_allclose(p[0,0,:], p[1,0,:], atol=1e-13, rtol=1e-13)
- f0, p0 = welch(x[0,0,:], nperseg=10)
- assert_allclose(p0[np.newaxis,:], p[1,:], atol=1e-13, rtol=1e-13)
- def test_nd_axis_0(self):
- x = np.arange(20, dtype=np.float64) + 0.04
- x = x.reshape((10,2,1))
- f, p = welch(x, nperseg=10, axis=0)
- assert_array_equal(p.shape, (6,2,1))
- assert_allclose(p[:,0,0], p[:,1,0], atol=1e-13, rtol=1e-13)
- f0, p0 = welch(x[:,0,0], nperseg=10)
- assert_allclose(p0, p[:,1,0], atol=1e-13, rtol=1e-13)
- def test_window_external(self):
- x = np.zeros(16)
- x[0] = 1
- x[8] = 1
- f, p = welch(x, 10, 'hann', nperseg=8)
- win = signal.get_window('hann', 8)
- fe, pe = welch(x, 10, win, nperseg=None)
- assert_array_almost_equal_nulp(p, pe)
- assert_array_almost_equal_nulp(f, fe)
- assert_array_equal(fe.shape, (5,)) # because win length used as nperseg
- assert_array_equal(pe.shape, (5,))
- assert_raises(ValueError, welch, x,
- 10, win, nperseg=4) # because nperseg != win.shape[-1]
- win_err = signal.get_window('hann', 32)
- assert_raises(ValueError, welch, x,
- 10, win_err, nperseg=None) # win longer than signal
- def test_empty_input(self):
- f, p = welch([])
- assert_array_equal(f.shape, (0,))
- assert_array_equal(p.shape, (0,))
- for shape in [(0,), (3,0), (0,5,2)]:
- f, p = welch(np.empty(shape))
- assert_array_equal(f.shape, shape)
- assert_array_equal(p.shape, shape)
- def test_empty_input_other_axis(self):
- for shape in [(3,0), (0,5,2)]:
- f, p = welch(np.empty(shape), axis=1)
- assert_array_equal(f.shape, shape)
- assert_array_equal(p.shape, shape)
- def test_short_data(self):
- x = np.zeros(8)
- x[0] = 1
- #for string-like window, input signal length < nperseg value gives
- #UserWarning, sets nperseg to x.shape[-1]
- with suppress_warnings() as sup:
- sup.filter(UserWarning, "nperseg = 256 is greater than input length = 8, using nperseg = 8")
- f, p = welch(x,window='hann') # default nperseg
- f1, p1 = welch(x,window='hann', nperseg=256) # user-specified nperseg
- f2, p2 = welch(x, nperseg=8) # valid nperseg, doesn't give warning
- assert_allclose(f, f2)
- assert_allclose(p, p2)
- assert_allclose(f1, f2)
- assert_allclose(p1, p2)
- def test_window_long_or_nd(self):
- assert_raises(ValueError, welch, np.zeros(4), 1, np.array([1,1,1,1,1]))
- assert_raises(ValueError, welch, np.zeros(4), 1,
- np.arange(6).reshape((2,3)))
- def test_nondefault_noverlap(self):
- x = np.zeros(64)
- x[::8] = 1
- f, p = welch(x, nperseg=16, noverlap=4)
- q = np.array([0, 1./12., 1./3., 1./5., 1./3., 1./5., 1./3., 1./5.,
- 1./6.])
- assert_allclose(p, q, atol=1e-12)
- def test_bad_noverlap(self):
- assert_raises(ValueError, welch, np.zeros(4), 1, 'hann', 2, 7)
- def test_nfft_too_short(self):
- assert_raises(ValueError, welch, np.ones(12), nfft=3, nperseg=4)
- def test_real_onesided_even_32(self):
- x = np.zeros(16, 'f')
- x[0] = 1
- x[8] = 1
- f, p = welch(x, nperseg=8)
- assert_allclose(f, np.linspace(0, 0.5, 5))
- q = np.array([0.08333333, 0.15277778, 0.22222222, 0.22222222,
- 0.11111111], 'f')
- assert_allclose(p, q, atol=1e-7, rtol=1e-7)
- assert_(p.dtype == q.dtype)
- def test_real_onesided_odd_32(self):
- x = np.zeros(16, 'f')
- x[0] = 1
- x[8] = 1
- f, p = welch(x, nperseg=9)
- assert_allclose(f, np.arange(5.0)/9.0)
- q = np.array([0.12477458, 0.23430935, 0.17072113, 0.17072116,
- 0.17072113], 'f')
- assert_allclose(p, q, atol=1e-7, rtol=1e-7)
- assert_(p.dtype == q.dtype)
- def test_real_twosided_32(self):
- x = np.zeros(16, 'f')
- x[0] = 1
- x[8] = 1
- f, p = welch(x, nperseg=8, return_onesided=False)
- assert_allclose(f, fftfreq(8, 1.0))
- q = np.array([0.08333333, 0.07638889, 0.11111111,
- 0.11111111, 0.11111111, 0.11111111, 0.11111111,
- 0.07638889], 'f')
- assert_allclose(p, q, atol=1e-7, rtol=1e-7)
- assert_(p.dtype == q.dtype)
- def test_complex_32(self):
- x = np.zeros(16, 'F')
- x[0] = 1.0 + 2.0j
- x[8] = 1.0 + 2.0j
- f, p = welch(x, nperseg=8, return_onesided=False)
- assert_allclose(f, fftfreq(8, 1.0))
- q = np.array([0.41666666, 0.38194442, 0.55555552, 0.55555552,
- 0.55555558, 0.55555552, 0.55555552, 0.38194442], 'f')
- assert_allclose(p, q, atol=1e-7, rtol=1e-7)
- assert_(p.dtype == q.dtype,
- 'dtype mismatch, %s, %s' % (p.dtype, q.dtype))
- def test_padded_freqs(self):
- x = np.zeros(12)
- nfft = 24
- f = fftfreq(nfft, 1.0)[:nfft//2+1]
- f[-1] *= -1
- fodd, _ = welch(x, nperseg=5, nfft=nfft)
- feven, _ = welch(x, nperseg=6, nfft=nfft)
- assert_allclose(f, fodd)
- assert_allclose(f, feven)
- nfft = 25
- f = fftfreq(nfft, 1.0)[:(nfft + 1)//2]
- fodd, _ = welch(x, nperseg=5, nfft=nfft)
- feven, _ = welch(x, nperseg=6, nfft=nfft)
- assert_allclose(f, fodd)
- assert_allclose(f, feven)
- def test_window_correction(self):
- A = 20
- fs = 1e4
- nperseg = int(fs//10)
- fsig = 300
- ii = int(fsig*nperseg//fs) # Freq index of fsig
- tt = np.arange(fs)/fs
- x = A*np.sin(2*np.pi*fsig*tt)
- for window in ['hann', 'bartlett', ('tukey', 0.1), 'flattop']:
- _, p_spec = welch(x, fs=fs, nperseg=nperseg, window=window,
- scaling='spectrum')
- freq, p_dens = welch(x, fs=fs, nperseg=nperseg, window=window,
- scaling='density')
- # Check peak height at signal frequency for 'spectrum'
- assert_allclose(p_spec[ii], A**2/2.0)
- # Check integrated spectrum RMS for 'density'
- assert_allclose(np.sqrt(np.trapz(p_dens, freq)), A*np.sqrt(2)/2,
- rtol=1e-3)
- def test_axis_rolling(self):
- np.random.seed(1234)
- x_flat = np.random.randn(1024)
- _, p_flat = welch(x_flat)
- for a in range(3):
- newshape = [1,]*3
- newshape[a] = -1
- x = x_flat.reshape(newshape)
- _, p_plus = welch(x, axis=a) # Positive axis index
- _, p_minus = welch(x, axis=a-x.ndim) # Negative axis index
- assert_equal(p_flat, p_plus.squeeze(), err_msg=a)
- assert_equal(p_flat, p_minus.squeeze(), err_msg=a-x.ndim)
- def test_average(self):
- x = np.zeros(16)
- x[0] = 1
- x[8] = 1
- f, p = welch(x, nperseg=8, average='median')
- assert_allclose(f, np.linspace(0, 0.5, 5))
- q = np.array([.1, .05, 0., 1.54074396e-33, 0.])
- assert_allclose(p, q, atol=1e-7, rtol=1e-7)
- assert_raises(ValueError, welch, x, nperseg=8,
- average='unrecognised-average')
- class TestCSD:
- def test_pad_shorter_x(self):
- x = np.zeros(8)
- y = np.zeros(12)
- f = np.linspace(0, 0.5, 7)
- c = np.zeros(7,dtype=np.complex128)
- f1, c1 = csd(x, y, nperseg=12)
- assert_allclose(f, f1)
- assert_allclose(c, c1)
- def test_pad_shorter_y(self):
- x = np.zeros(12)
- y = np.zeros(8)
- f = np.linspace(0, 0.5, 7)
- c = np.zeros(7,dtype=np.complex128)
- f1, c1 = csd(x, y, nperseg=12)
- assert_allclose(f, f1)
- assert_allclose(c, c1)
- def test_real_onesided_even(self):
- x = np.zeros(16)
- x[0] = 1
- x[8] = 1
- f, p = csd(x, x, nperseg=8)
- assert_allclose(f, np.linspace(0, 0.5, 5))
- q = np.array([0.08333333, 0.15277778, 0.22222222, 0.22222222,
- 0.11111111])
- assert_allclose(p, q, atol=1e-7, rtol=1e-7)
- def test_real_onesided_odd(self):
- x = np.zeros(16)
- x[0] = 1
- x[8] = 1
- f, p = csd(x, x, nperseg=9)
- assert_allclose(f, np.arange(5.0)/9.0)
- q = np.array([0.12477455, 0.23430933, 0.17072113, 0.17072113,
- 0.17072113])
- assert_allclose(p, q, atol=1e-7, rtol=1e-7)
- def test_real_twosided(self):
- x = np.zeros(16)
- x[0] = 1
- x[8] = 1
- f, p = csd(x, x, nperseg=8, return_onesided=False)
- assert_allclose(f, fftfreq(8, 1.0))
- q = np.array([0.08333333, 0.07638889, 0.11111111, 0.11111111,
- 0.11111111, 0.11111111, 0.11111111, 0.07638889])
- assert_allclose(p, q, atol=1e-7, rtol=1e-7)
- def test_real_spectrum(self):
- x = np.zeros(16)
- x[0] = 1
- x[8] = 1
- f, p = csd(x, x, nperseg=8, scaling='spectrum')
- assert_allclose(f, np.linspace(0, 0.5, 5))
- q = np.array([0.015625, 0.02864583, 0.04166667, 0.04166667,
- 0.02083333])
- assert_allclose(p, q, atol=1e-7, rtol=1e-7)
- def test_integer_onesided_even(self):
- x = np.zeros(16, dtype=int)
- x[0] = 1
- x[8] = 1
- f, p = csd(x, x, nperseg=8)
- assert_allclose(f, np.linspace(0, 0.5, 5))
- q = np.array([0.08333333, 0.15277778, 0.22222222, 0.22222222,
- 0.11111111])
- assert_allclose(p, q, atol=1e-7, rtol=1e-7)
- def test_integer_onesided_odd(self):
- x = np.zeros(16, dtype=int)
- x[0] = 1
- x[8] = 1
- f, p = csd(x, x, nperseg=9)
- assert_allclose(f, np.arange(5.0)/9.0)
- q = np.array([0.12477455, 0.23430933, 0.17072113, 0.17072113,
- 0.17072113])
- assert_allclose(p, q, atol=1e-7, rtol=1e-7)
- def test_integer_twosided(self):
- x = np.zeros(16, dtype=int)
- x[0] = 1
- x[8] = 1
- f, p = csd(x, x, nperseg=8, return_onesided=False)
- assert_allclose(f, fftfreq(8, 1.0))
- q = np.array([0.08333333, 0.07638889, 0.11111111, 0.11111111,
- 0.11111111, 0.11111111, 0.11111111, 0.07638889])
- assert_allclose(p, q, atol=1e-7, rtol=1e-7)
- def test_complex(self):
- x = np.zeros(16, np.complex128)
- x[0] = 1.0 + 2.0j
- x[8] = 1.0 + 2.0j
- f, p = csd(x, x, nperseg=8, return_onesided=False)
- assert_allclose(f, fftfreq(8, 1.0))
- q = np.array([0.41666667, 0.38194444, 0.55555556, 0.55555556,
- 0.55555556, 0.55555556, 0.55555556, 0.38194444])
- assert_allclose(p, q, atol=1e-7, rtol=1e-7)
- def test_unk_scaling(self):
- assert_raises(ValueError, csd, np.zeros(4, np.complex128),
- np.ones(4, np.complex128), scaling='foo', nperseg=4)
- def test_detrend_linear(self):
- x = np.arange(10, dtype=np.float64) + 0.04
- f, p = csd(x, x, nperseg=10, detrend='linear')
- assert_allclose(p, np.zeros_like(p), atol=1e-15)
- def test_no_detrending(self):
- x = np.arange(10, dtype=np.float64) + 0.04
- f1, p1 = csd(x, x, nperseg=10, detrend=False)
- f2, p2 = csd(x, x, nperseg=10, detrend=lambda x: x)
- assert_allclose(f1, f2, atol=1e-15)
- assert_allclose(p1, p2, atol=1e-15)
- def test_detrend_external(self):
- x = np.arange(10, dtype=np.float64) + 0.04
- f, p = csd(x, x, nperseg=10,
- detrend=lambda seg: signal.detrend(seg, type='l'))
- assert_allclose(p, np.zeros_like(p), atol=1e-15)
- def test_detrend_external_nd_m1(self):
- x = np.arange(40, dtype=np.float64) + 0.04
- x = x.reshape((2,2,10))
- f, p = csd(x, x, nperseg=10,
- detrend=lambda seg: signal.detrend(seg, type='l'))
- assert_allclose(p, np.zeros_like(p), atol=1e-15)
- def test_detrend_external_nd_0(self):
- x = np.arange(20, dtype=np.float64) + 0.04
- x = x.reshape((2,1,10))
- x = np.moveaxis(x, 2, 0)
- f, p = csd(x, x, nperseg=10, axis=0,
- detrend=lambda seg: signal.detrend(seg, axis=0, type='l'))
- assert_allclose(p, np.zeros_like(p), atol=1e-15)
- def test_nd_axis_m1(self):
- x = np.arange(20, dtype=np.float64) + 0.04
- x = x.reshape((2,1,10))
- f, p = csd(x, x, nperseg=10)
- assert_array_equal(p.shape, (2, 1, 6))
- assert_allclose(p[0,0,:], p[1,0,:], atol=1e-13, rtol=1e-13)
- f0, p0 = csd(x[0,0,:], x[0,0,:], nperseg=10)
- assert_allclose(p0[np.newaxis,:], p[1,:], atol=1e-13, rtol=1e-13)
- def test_nd_axis_0(self):
- x = np.arange(20, dtype=np.float64) + 0.04
- x = x.reshape((10,2,1))
- f, p = csd(x, x, nperseg=10, axis=0)
- assert_array_equal(p.shape, (6,2,1))
- assert_allclose(p[:,0,0], p[:,1,0], atol=1e-13, rtol=1e-13)
- f0, p0 = csd(x[:,0,0], x[:,0,0], nperseg=10)
- assert_allclose(p0, p[:,1,0], atol=1e-13, rtol=1e-13)
- def test_window_external(self):
- x = np.zeros(16)
- x[0] = 1
- x[8] = 1
- f, p = csd(x, x, 10, 'hann', 8)
- win = signal.get_window('hann', 8)
- fe, pe = csd(x, x, 10, win, nperseg=None)
- assert_array_almost_equal_nulp(p, pe)
- assert_array_almost_equal_nulp(f, fe)
- assert_array_equal(fe.shape, (5,)) # because win length used as nperseg
- assert_array_equal(pe.shape, (5,))
- assert_raises(ValueError, csd, x, x,
- 10, win, nperseg=256) # because nperseg != win.shape[-1]
- win_err = signal.get_window('hann', 32)
- assert_raises(ValueError, csd, x, x,
- 10, win_err, nperseg=None) # because win longer than signal
- def test_empty_input(self):
- f, p = csd([],np.zeros(10))
- assert_array_equal(f.shape, (0,))
- assert_array_equal(p.shape, (0,))
- f, p = csd(np.zeros(10),[])
- assert_array_equal(f.shape, (0,))
- assert_array_equal(p.shape, (0,))
- for shape in [(0,), (3,0), (0,5,2)]:
- f, p = csd(np.empty(shape), np.empty(shape))
- assert_array_equal(f.shape, shape)
- assert_array_equal(p.shape, shape)
- f, p = csd(np.ones(10), np.empty((5,0)))
- assert_array_equal(f.shape, (5,0))
- assert_array_equal(p.shape, (5,0))
- f, p = csd(np.empty((5,0)), np.ones(10))
- assert_array_equal(f.shape, (5,0))
- assert_array_equal(p.shape, (5,0))
- def test_empty_input_other_axis(self):
- for shape in [(3,0), (0,5,2)]:
- f, p = csd(np.empty(shape), np.empty(shape), axis=1)
- assert_array_equal(f.shape, shape)
- assert_array_equal(p.shape, shape)
- f, p = csd(np.empty((10,10,3)), np.zeros((10,0,1)), axis=1)
- assert_array_equal(f.shape, (10,0,3))
- assert_array_equal(p.shape, (10,0,3))
- f, p = csd(np.empty((10,0,1)), np.zeros((10,10,3)), axis=1)
- assert_array_equal(f.shape, (10,0,3))
- assert_array_equal(p.shape, (10,0,3))
- def test_short_data(self):
- x = np.zeros(8)
- x[0] = 1
- #for string-like window, input signal length < nperseg value gives
- #UserWarning, sets nperseg to x.shape[-1]
- with suppress_warnings() as sup:
- sup.filter(UserWarning, "nperseg = 256 is greater than input length = 8, using nperseg = 8")
- f, p = csd(x, x, window='hann') # default nperseg
- f1, p1 = csd(x, x, window='hann', nperseg=256) # user-specified nperseg
- f2, p2 = csd(x, x, nperseg=8) # valid nperseg, doesn't give warning
- assert_allclose(f, f2)
- assert_allclose(p, p2)
- assert_allclose(f1, f2)
- assert_allclose(p1, p2)
- def test_window_long_or_nd(self):
- assert_raises(ValueError, csd, np.zeros(4), np.ones(4), 1,
- np.array([1,1,1,1,1]))
- assert_raises(ValueError, csd, np.zeros(4), np.ones(4), 1,
- np.arange(6).reshape((2,3)))
- def test_nondefault_noverlap(self):
- x = np.zeros(64)
- x[::8] = 1
- f, p = csd(x, x, nperseg=16, noverlap=4)
- q = np.array([0, 1./12., 1./3., 1./5., 1./3., 1./5., 1./3., 1./5.,
- 1./6.])
- assert_allclose(p, q, atol=1e-12)
- def test_bad_noverlap(self):
- assert_raises(ValueError, csd, np.zeros(4), np.ones(4), 1, 'hann',
- 2, 7)
- def test_nfft_too_short(self):
- assert_raises(ValueError, csd, np.ones(12), np.zeros(12), nfft=3,
- nperseg=4)
- def test_real_onesided_even_32(self):
- x = np.zeros(16, 'f')
- x[0] = 1
- x[8] = 1
- f, p = csd(x, x, nperseg=8)
- assert_allclose(f, np.linspace(0, 0.5, 5))
- q = np.array([0.08333333, 0.15277778, 0.22222222, 0.22222222,
- 0.11111111], 'f')
- assert_allclose(p, q, atol=1e-7, rtol=1e-7)
- assert_(p.dtype == q.dtype)
- def test_real_onesided_odd_32(self):
- x = np.zeros(16, 'f')
- x[0] = 1
- x[8] = 1
- f, p = csd(x, x, nperseg=9)
- assert_allclose(f, np.arange(5.0)/9.0)
- q = np.array([0.12477458, 0.23430935, 0.17072113, 0.17072116,
- 0.17072113], 'f')
- assert_allclose(p, q, atol=1e-7, rtol=1e-7)
- assert_(p.dtype == q.dtype)
- def test_real_twosided_32(self):
- x = np.zeros(16, 'f')
- x[0] = 1
- x[8] = 1
- f, p = csd(x, x, nperseg=8, return_onesided=False)
- assert_allclose(f, fftfreq(8, 1.0))
- q = np.array([0.08333333, 0.07638889, 0.11111111,
- 0.11111111, 0.11111111, 0.11111111, 0.11111111,
- 0.07638889], 'f')
- assert_allclose(p, q, atol=1e-7, rtol=1e-7)
- assert_(p.dtype == q.dtype)
- def test_complex_32(self):
- x = np.zeros(16, 'F')
- x[0] = 1.0 + 2.0j
- x[8] = 1.0 + 2.0j
- f, p = csd(x, x, nperseg=8, return_onesided=False)
- assert_allclose(f, fftfreq(8, 1.0))
- q = np.array([0.41666666, 0.38194442, 0.55555552, 0.55555552,
- 0.55555558, 0.55555552, 0.55555552, 0.38194442], 'f')
- assert_allclose(p, q, atol=1e-7, rtol=1e-7)
- assert_(p.dtype == q.dtype,
- 'dtype mismatch, %s, %s' % (p.dtype, q.dtype))
- def test_padded_freqs(self):
- x = np.zeros(12)
- y = np.ones(12)
- nfft = 24
- f = fftfreq(nfft, 1.0)[:nfft//2+1]
- f[-1] *= -1
- fodd, _ = csd(x, y, nperseg=5, nfft=nfft)
- feven, _ = csd(x, y, nperseg=6, nfft=nfft)
- assert_allclose(f, fodd)
- assert_allclose(f, feven)
- nfft = 25
- f = fftfreq(nfft, 1.0)[:(nfft + 1)//2]
- fodd, _ = csd(x, y, nperseg=5, nfft=nfft)
- feven, _ = csd(x, y, nperseg=6, nfft=nfft)
- assert_allclose(f, fodd)
- assert_allclose(f, feven)
- def test_copied_data(self):
- x = np.random.randn(64)
- y = x.copy()
- _, p_same = csd(x, x, nperseg=8, average='mean',
- return_onesided=False)
- _, p_copied = csd(x, y, nperseg=8, average='mean',
- return_onesided=False)
- assert_allclose(p_same, p_copied)
- _, p_same = csd(x, x, nperseg=8, average='median',
- return_onesided=False)
- _, p_copied = csd(x, y, nperseg=8, average='median',
- return_onesided=False)
- assert_allclose(p_same, p_copied)
- class TestCoherence:
- def test_identical_input(self):
- x = np.random.randn(20)
- y = np.copy(x) # So `y is x` -> False
- f = np.linspace(0, 0.5, 6)
- C = np.ones(6)
- f1, C1 = coherence(x, y, nperseg=10)
- assert_allclose(f, f1)
- assert_allclose(C, C1)
- def test_phase_shifted_input(self):
- x = np.random.randn(20)
- y = -x
- f = np.linspace(0, 0.5, 6)
- C = np.ones(6)
- f1, C1 = coherence(x, y, nperseg=10)
- assert_allclose(f, f1)
- assert_allclose(C, C1)
- class TestSpectrogram:
- def test_average_all_segments(self):
- x = np.random.randn(1024)
- fs = 1.0
- window = ('tukey', 0.25)
- nperseg = 16
- noverlap = 2
- f, _, P = spectrogram(x, fs, window, nperseg, noverlap)
- fw, Pw = welch(x, fs, window, nperseg, noverlap)
- assert_allclose(f, fw)
- assert_allclose(np.mean(P, axis=-1), Pw)
- def test_window_external(self):
- x = np.random.randn(1024)
- fs = 1.0
- window = ('tukey', 0.25)
- nperseg = 16
- noverlap = 2
- f, _, P = spectrogram(x, fs, window, nperseg, noverlap)
- win = signal.get_window(('tukey', 0.25), 16)
- fe, _, Pe = spectrogram(x, fs, win, nperseg=None, noverlap=2)
- assert_array_equal(fe.shape, (9,)) # because win length used as nperseg
- assert_array_equal(Pe.shape, (9,73))
- assert_raises(ValueError, spectrogram, x,
- fs, win, nperseg=8) # because nperseg != win.shape[-1]
- win_err = signal.get_window(('tukey', 0.25), 2048)
- assert_raises(ValueError, spectrogram, x,
- fs, win_err, nperseg=None) # win longer than signal
- def test_short_data(self):
- x = np.random.randn(1024)
- fs = 1.0
- #for string-like window, input signal length < nperseg value gives
- #UserWarning, sets nperseg to x.shape[-1]
- f, _, p = spectrogram(x, fs, window=('tukey',0.25)) # default nperseg
- with suppress_warnings() as sup:
- sup.filter(UserWarning,
- "nperseg = 1025 is greater than input length = 1024, using nperseg = 1024")
- f1, _, p1 = spectrogram(x, fs, window=('tukey',0.25),
- nperseg=1025) # user-specified nperseg
- f2, _, p2 = spectrogram(x, fs, nperseg=256) # to compare w/default
- f3, _, p3 = spectrogram(x, fs, nperseg=1024) # compare w/user-spec'd
- assert_allclose(f, f2)
- assert_allclose(p, p2)
- assert_allclose(f1, f3)
- assert_allclose(p1, p3)
- class TestLombscargle:
- def test_frequency(self):
- """Test if frequency location of peak corresponds to frequency of
- generated input signal.
- """
- # Input parameters
- ampl = 2.
- w = 1.
- phi = 0.5 * np.pi
- nin = 100
- nout = 1000
- p = 0.7 # Fraction of points to select
- # Randomly select a fraction of an array with timesteps
- np.random.seed(2353425)
- r = np.random.rand(nin)
- t = np.linspace(0.01*np.pi, 10.*np.pi, nin)[r >= p]
- # Plot a sine wave for the selected times
- x = ampl * np.sin(w*t + phi)
- # Define the array of frequencies for which to compute the periodogram
- f = np.linspace(0.01, 10., nout)
- # Calculate Lomb-Scargle periodogram
- P = lombscargle(t, x, f)
- # Check if difference between found frequency maximum and input
- # frequency is less than accuracy
- delta = f[1] - f[0]
- assert_(w - f[np.argmax(P)] < (delta/2.))
- def test_amplitude(self):
- # Test if height of peak in normalized Lomb-Scargle periodogram
- # corresponds to amplitude of the generated input signal.
- # Input parameters
- ampl = 2.
- w = 1.
- phi = 0.5 * np.pi
- nin = 100
- nout = 1000
- p = 0.7 # Fraction of points to select
- # Randomly select a fraction of an array with timesteps
- np.random.seed(2353425)
- r = np.random.rand(nin)
- t = np.linspace(0.01*np.pi, 10.*np.pi, nin)[r >= p]
- # Plot a sine wave for the selected times
- x = ampl * np.sin(w*t + phi)
- # Define the array of frequencies for which to compute the periodogram
- f = np.linspace(0.01, 10., nout)
- # Calculate Lomb-Scargle periodogram
- pgram = lombscargle(t, x, f)
- # Normalize
- pgram = np.sqrt(4 * pgram / t.shape[0])
- # Check if difference between found frequency maximum and input
- # frequency is less than accuracy
- assert_approx_equal(np.max(pgram), ampl, significant=2)
- def test_precenter(self):
- # Test if precenter gives the same result as manually precentering.
- # Input parameters
- ampl = 2.
- w = 1.
- phi = 0.5 * np.pi
- nin = 100
- nout = 1000
- p = 0.7 # Fraction of points to select
- offset = 0.15 # Offset to be subtracted in pre-centering
- # Randomly select a fraction of an array with timesteps
- np.random.seed(2353425)
- r = np.random.rand(nin)
- t = np.linspace(0.01*np.pi, 10.*np.pi, nin)[r >= p]
- # Plot a sine wave for the selected times
- x = ampl * np.sin(w*t + phi) + offset
- # Define the array of frequencies for which to compute the periodogram
- f = np.linspace(0.01, 10., nout)
- # Calculate Lomb-Scargle periodogram
- pgram = lombscargle(t, x, f, precenter=True)
- pgram2 = lombscargle(t, x - x.mean(), f, precenter=False)
- # check if centering worked
- assert_allclose(pgram, pgram2)
- def test_normalize(self):
- # Test normalize option of Lomb-Scarge.
- # Input parameters
- ampl = 2.
- w = 1.
- phi = 0.5 * np.pi
- nin = 100
- nout = 1000
- p = 0.7 # Fraction of points to select
- # Randomly select a fraction of an array with timesteps
- np.random.seed(2353425)
- r = np.random.rand(nin)
- t = np.linspace(0.01*np.pi, 10.*np.pi, nin)[r >= p]
- # Plot a sine wave for the selected times
- x = ampl * np.sin(w*t + phi)
- # Define the array of frequencies for which to compute the periodogram
- f = np.linspace(0.01, 10., nout)
- # Calculate Lomb-Scargle periodogram
- pgram = lombscargle(t, x, f)
- pgram2 = lombscargle(t, x, f, normalize=True)
- # check if normalization works as expected
- assert_allclose(pgram * 2 / np.dot(x, x), pgram2)
- assert_approx_equal(np.max(pgram2), 1.0, significant=2)
- def test_wrong_shape(self):
- t = np.linspace(0, 1, 1)
- x = np.linspace(0, 1, 2)
- f = np.linspace(0, 1, 3)
- assert_raises(ValueError, lombscargle, t, x, f)
- def test_zero_division(self):
- t = np.zeros(1)
- x = np.zeros(1)
- f = np.zeros(1)
- assert_raises(ZeroDivisionError, lombscargle, t, x, f)
- def test_lombscargle_atan_vs_atan2(self):
- # https://github.com/scipy/scipy/issues/3787
- # This raised a ZeroDivisionError.
- t = np.linspace(0, 10, 1000, endpoint=False)
- x = np.sin(4*t)
- f = np.linspace(0, 50, 500, endpoint=False) + 0.1
- lombscargle(t, x, f*2*np.pi)
- class TestSTFT:
- def test_input_validation(self):
- def chk_VE(match):
- """Assert for a ValueError matching regexp `match`.
- This little wrapper allows a more concise code layout.
- """
- return pytest.raises(ValueError, match=match)
- # Checks for check_COLA():
- with chk_VE('nperseg must be a positive integer'):
- check_COLA('hann', -10, 0)
- with chk_VE('noverlap must be less than nperseg.'):
- check_COLA('hann', 10, 20)
- with chk_VE('window must be 1-D'):
- check_COLA(np.ones((2, 2)), 10, 0)
- with chk_VE('window must have length of nperseg'):
- check_COLA(np.ones(20), 10, 0)
- # Checks for check_NOLA():
- with chk_VE('nperseg must be a positive integer'):
- check_NOLA('hann', -10, 0)
- with chk_VE('noverlap must be less than nperseg'):
- check_NOLA('hann', 10, 20)
- with chk_VE('window must be 1-D'):
- check_NOLA(np.ones((2, 2)), 10, 0)
- with chk_VE('window must have length of nperseg'):
- check_NOLA(np.ones(20), 10, 0)
- with chk_VE('noverlap must be a nonnegative integer'):
- check_NOLA('hann', 64, -32)
- x = np.zeros(1024)
- z = stft(x)[2]
- # Checks for stft():
- with chk_VE('window must be 1-D'):
- stft(x, window=np.ones((2, 2)))
- with chk_VE('value specified for nperseg is different ' +
- 'from length of window'):
- stft(x, window=np.ones(10), nperseg=256)
- with chk_VE('nperseg must be a positive integer'):
- stft(x, nperseg=-256)
- with chk_VE('noverlap must be less than nperseg.'):
- stft(x, nperseg=256, noverlap=1024)
- with chk_VE('nfft must be greater than or equal to nperseg.'):
- stft(x, nperseg=256, nfft=8)
- # Checks for istft():
- with chk_VE('Input stft must be at least 2d!'):
- istft(x)
- with chk_VE('window must be 1-D'):
- istft(z, window=np.ones((2, 2)))
- with chk_VE('window must have length of 256'):
- istft(z, window=np.ones(10), nperseg=256)
- with chk_VE('nperseg must be a positive integer'):
- istft(z, nperseg=-256)
- with chk_VE('noverlap must be less than nperseg.'):
- istft(z, nperseg=256, noverlap=1024)
- with chk_VE('nfft must be greater than or equal to nperseg.'):
- istft(z, nperseg=256, nfft=8)
- with pytest.warns(UserWarning, match="NOLA condition failed, " +
- "STFT may not be invertible"):
- istft(z, nperseg=256, noverlap=0, window='hann')
- with chk_VE('Must specify differing time and frequency axes!'):
- istft(z, time_axis=0, freq_axis=0)
- # Checks for _spectral_helper():
- with chk_VE("Unknown value for mode foo, must be one of: " +
- r"\{'psd', 'stft'\}"):
- _spectral_helper(x, x, mode='foo')
- with chk_VE("x and y must be equal if mode is 'stft'"):
- _spectral_helper(x[:512], x[512:], mode='stft')
- with chk_VE("Unknown boundary option 'foo', must be one of: " +
- r"\['even', 'odd', 'constant', 'zeros', None\]"):
- _spectral_helper(x, x, boundary='foo')
- scaling = "not_valid"
- with chk_VE(fr"Parameter {scaling=} not in \['spectrum', 'psd'\]!"):
- stft(x, scaling=scaling)
- with chk_VE(fr"Parameter {scaling=} not in \['spectrum', 'psd'\]!"):
- istft(z, scaling=scaling)
- def test_check_COLA(self):
- settings = [
- ('boxcar', 10, 0),
- ('boxcar', 10, 9),
- ('bartlett', 51, 26),
- ('hann', 256, 128),
- ('hann', 256, 192),
- ('blackman', 300, 200),
- (('tukey', 0.5), 256, 64),
- ('hann', 256, 255),
- ]
- for setting in settings:
- msg = '{0}, {1}, {2}'.format(*setting)
- assert_equal(True, check_COLA(*setting), err_msg=msg)
- def test_check_NOLA(self):
- settings_pass = [
- ('boxcar', 10, 0),
- ('boxcar', 10, 9),
- ('boxcar', 10, 7),
- ('bartlett', 51, 26),
- ('bartlett', 51, 10),
- ('hann', 256, 128),
- ('hann', 256, 192),
- ('hann', 256, 37),
- ('blackman', 300, 200),
- ('blackman', 300, 123),
- (('tukey', 0.5), 256, 64),
- (('tukey', 0.5), 256, 38),
- ('hann', 256, 255),
- ('hann', 256, 39),
- ]
- for setting in settings_pass:
- msg = '{0}, {1}, {2}'.format(*setting)
- assert_equal(True, check_NOLA(*setting), err_msg=msg)
- w_fail = np.ones(16)
- w_fail[::2] = 0
- settings_fail = [
- (w_fail, len(w_fail), len(w_fail) // 2),
- ('hann', 64, 0),
- ]
- for setting in settings_fail:
- msg = '{0}, {1}, {2}'.format(*setting)
- assert_equal(False, check_NOLA(*setting), err_msg=msg)
- def test_average_all_segments(self):
- np.random.seed(1234)
- x = np.random.randn(1024)
- fs = 1.0
- window = 'hann'
- nperseg = 16
- noverlap = 8
- # Compare twosided, because onesided welch doubles non-DC terms to
- # account for power at negative frequencies. stft doesn't do this,
- # because it breaks invertibility.
- f, _, Z = stft(x, fs, window, nperseg, noverlap, padded=False,
- return_onesided=False, boundary=None)
- fw, Pw = welch(x, fs, window, nperseg, noverlap, return_onesided=False,
- scaling='spectrum', detrend=False)
- assert_allclose(f, fw)
- assert_allclose(np.mean(np.abs(Z)**2, axis=-1), Pw)
- def test_permute_axes(self):
- np.random.seed(1234)
- x = np.random.randn(1024)
- fs = 1.0
- window = 'hann'
- nperseg = 16
- noverlap = 8
- f1, t1, Z1 = stft(x, fs, window, nperseg, noverlap)
- f2, t2, Z2 = stft(x.reshape((-1, 1, 1)), fs, window, nperseg, noverlap,
- axis=0)
- t3, x1 = istft(Z1, fs, window, nperseg, noverlap)
- t4, x2 = istft(Z2.T, fs, window, nperseg, noverlap, time_axis=0,
- freq_axis=-1)
- assert_allclose(f1, f2)
- assert_allclose(t1, t2)
- assert_allclose(t3, t4)
- assert_allclose(Z1, Z2[:, 0, 0, :])
- assert_allclose(x1, x2[:, 0, 0])
- @pytest.mark.parametrize('scaling', ['spectrum', 'psd'])
- def test_roundtrip_real(self, scaling):
- np.random.seed(1234)
- settings = [
- ('boxcar', 100, 10, 0), # Test no overlap
- ('boxcar', 100, 10, 9), # Test high overlap
- ('bartlett', 101, 51, 26), # Test odd nperseg
- ('hann', 1024, 256, 128), # Test defaults
- (('tukey', 0.5), 1152, 256, 64), # Test Tukey
- ('hann', 1024, 256, 255), # Test overlapped hann
- ]
- for window, N, nperseg, noverlap in settings:
- t = np.arange(N)
- x = 10*np.random.randn(t.size)
- _, _, zz = stft(x, nperseg=nperseg, noverlap=noverlap,
- window=window, detrend=None, padded=False,
- scaling=scaling)
- tr, xr = istft(zz, nperseg=nperseg, noverlap=noverlap,
- window=window, scaling=scaling)
- msg = '{0}, {1}'.format(window, noverlap)
- assert_allclose(t, tr, err_msg=msg)
- assert_allclose(x, xr, err_msg=msg)
- def test_roundtrip_not_nola(self):
- np.random.seed(1234)
- w_fail = np.ones(16)
- w_fail[::2] = 0
- settings = [
- (w_fail, 256, len(w_fail), len(w_fail) // 2),
- ('hann', 256, 64, 0),
- ]
- for window, N, nperseg, noverlap in settings:
- msg = '{0}, {1}, {2}, {3}'.format(window, N, nperseg, noverlap)
- assert not check_NOLA(window, nperseg, noverlap), msg
- t = np.arange(N)
- x = 10 * np.random.randn(t.size)
- _, _, zz = stft(x, nperseg=nperseg, noverlap=noverlap,
- window=window, detrend=None, padded=True,
- boundary='zeros')
- with pytest.warns(UserWarning, match='NOLA'):
- tr, xr = istft(zz, nperseg=nperseg, noverlap=noverlap,
- window=window, boundary=True)
- assert np.allclose(t, tr[:len(t)]), msg
- assert not np.allclose(x, xr[:len(x)]), msg
- def test_roundtrip_nola_not_cola(self):
- np.random.seed(1234)
- settings = [
- ('boxcar', 100, 10, 3), # NOLA True, COLA False
- ('bartlett', 101, 51, 37), # NOLA True, COLA False
- ('hann', 1024, 256, 127), # NOLA True, COLA False
- (('tukey', 0.5), 1152, 256, 14), # NOLA True, COLA False
- ('hann', 1024, 256, 5), # NOLA True, COLA False
- ]
- for window, N, nperseg, noverlap in settings:
- msg = '{0}, {1}, {2}'.format(window, nperseg, noverlap)
- assert check_NOLA(window, nperseg, noverlap), msg
- assert not check_COLA(window, nperseg, noverlap), msg
- t = np.arange(N)
- x = 10 * np.random.randn(t.size)
- _, _, zz = stft(x, nperseg=nperseg, noverlap=noverlap,
- window=window, detrend=None, padded=True,
- boundary='zeros')
- tr, xr = istft(zz, nperseg=nperseg, noverlap=noverlap,
- window=window, boundary=True)
- msg = '{0}, {1}'.format(window, noverlap)
- assert_allclose(t, tr[:len(t)], err_msg=msg)
- assert_allclose(x, xr[:len(x)], err_msg=msg)
- def test_roundtrip_float32(self):
- np.random.seed(1234)
- settings = [('hann', 1024, 256, 128)]
- for window, N, nperseg, noverlap in settings:
- t = np.arange(N)
- x = 10*np.random.randn(t.size)
- x = x.astype(np.float32)
- _, _, zz = stft(x, nperseg=nperseg, noverlap=noverlap,
- window=window, detrend=None, padded=False)
- tr, xr = istft(zz, nperseg=nperseg, noverlap=noverlap,
- window=window)
- msg = '{0}, {1}'.format(window, noverlap)
- assert_allclose(t, t, err_msg=msg)
- assert_allclose(x, xr, err_msg=msg, rtol=1e-4, atol=1e-5)
- assert_(x.dtype == xr.dtype)
- @pytest.mark.parametrize('scaling', ['spectrum', 'psd'])
- def test_roundtrip_complex(self, scaling):
- np.random.seed(1234)
- settings = [
- ('boxcar', 100, 10, 0), # Test no overlap
- ('boxcar', 100, 10, 9), # Test high overlap
- ('bartlett', 101, 51, 26), # Test odd nperseg
- ('hann', 1024, 256, 128), # Test defaults
- (('tukey', 0.5), 1152, 256, 64), # Test Tukey
- ('hann', 1024, 256, 255), # Test overlapped hann
- ]
- for window, N, nperseg, noverlap in settings:
- t = np.arange(N)
- x = 10*np.random.randn(t.size) + 10j*np.random.randn(t.size)
- _, _, zz = stft(x, nperseg=nperseg, noverlap=noverlap,
- window=window, detrend=None, padded=False,
- return_onesided=False, scaling=scaling)
- tr, xr = istft(zz, nperseg=nperseg, noverlap=noverlap,
- window=window, input_onesided=False,
- scaling=scaling)
- msg = '{0}, {1}, {2}'.format(window, nperseg, noverlap)
- assert_allclose(t, tr, err_msg=msg)
- assert_allclose(x, xr, err_msg=msg)
- # Check that asking for onesided switches to twosided
- with suppress_warnings() as sup:
- sup.filter(UserWarning,
- "Input data is complex, switching to return_onesided=False")
- _, _, zz = stft(x, nperseg=nperseg, noverlap=noverlap,
- window=window, detrend=None, padded=False,
- return_onesided=True, scaling=scaling)
- tr, xr = istft(zz, nperseg=nperseg, noverlap=noverlap,
- window=window, input_onesided=False, scaling=scaling)
- msg = '{0}, {1}, {2}'.format(window, nperseg, noverlap)
- assert_allclose(t, tr, err_msg=msg)
- assert_allclose(x, xr, err_msg=msg)
- def test_roundtrip_boundary_extension(self):
- np.random.seed(1234)
- # Test against boxcar, since window is all ones, and thus can be fully
- # recovered with no boundary extension
- settings = [
- ('boxcar', 100, 10, 0), # Test no overlap
- ('boxcar', 100, 10, 9), # Test high overlap
- ]
- for window, N, nperseg, noverlap in settings:
- t = np.arange(N)
- x = 10*np.random.randn(t.size)
- _, _, zz = stft(x, nperseg=nperseg, noverlap=noverlap,
- window=window, detrend=None, padded=True,
- boundary=None)
- _, xr = istft(zz, noverlap=noverlap, window=window, boundary=False)
- for boundary in ['even', 'odd', 'constant', 'zeros']:
- _, _, zz_ext = stft(x, nperseg=nperseg, noverlap=noverlap,
- window=window, detrend=None, padded=True,
- boundary=boundary)
- _, xr_ext = istft(zz_ext, noverlap=noverlap, window=window,
- boundary=True)
- msg = '{0}, {1}, {2}'.format(window, noverlap, boundary)
- assert_allclose(x, xr, err_msg=msg)
- assert_allclose(x, xr_ext, err_msg=msg)
- def test_roundtrip_padded_signal(self):
- np.random.seed(1234)
- settings = [
- ('boxcar', 101, 10, 0),
- ('hann', 1000, 256, 128),
- ]
- for window, N, nperseg, noverlap in settings:
- t = np.arange(N)
- x = 10*np.random.randn(t.size)
- _, _, zz = stft(x, nperseg=nperseg, noverlap=noverlap,
- window=window, detrend=None, padded=True)
- tr, xr = istft(zz, noverlap=noverlap, window=window)
- msg = '{0}, {1}'.format(window, noverlap)
- # Account for possible zero-padding at the end
- assert_allclose(t, tr[:t.size], err_msg=msg)
- assert_allclose(x, xr[:x.size], err_msg=msg)
- def test_roundtrip_padded_FFT(self):
- np.random.seed(1234)
- settings = [
- ('hann', 1024, 256, 128, 512),
- ('hann', 1024, 256, 128, 501),
- ('boxcar', 100, 10, 0, 33),
- (('tukey', 0.5), 1152, 256, 64, 1024),
- ]
- for window, N, nperseg, noverlap, nfft in settings:
- t = np.arange(N)
- x = 10*np.random.randn(t.size)
- xc = x*np.exp(1j*np.pi/4)
- # real signal
- _, _, z = stft(x, nperseg=nperseg, noverlap=noverlap, nfft=nfft,
- window=window, detrend=None, padded=True)
- # complex signal
- _, _, zc = stft(xc, nperseg=nperseg, noverlap=noverlap, nfft=nfft,
- window=window, detrend=None, padded=True,
- return_onesided=False)
- tr, xr = istft(z, nperseg=nperseg, noverlap=noverlap, nfft=nfft,
- window=window)
- tr, xcr = istft(zc, nperseg=nperseg, noverlap=noverlap, nfft=nfft,
- window=window, input_onesided=False)
- msg = '{0}, {1}'.format(window, noverlap)
- assert_allclose(t, tr, err_msg=msg)
- assert_allclose(x, xr, err_msg=msg)
- assert_allclose(xc, xcr, err_msg=msg)
- def test_axis_rolling(self):
- np.random.seed(1234)
- x_flat = np.random.randn(1024)
- _, _, z_flat = stft(x_flat)
- for a in range(3):
- newshape = [1,]*3
- newshape[a] = -1
- x = x_flat.reshape(newshape)
- _, _, z_plus = stft(x, axis=a) # Positive axis index
- _, _, z_minus = stft(x, axis=a-x.ndim) # Negative axis index
- assert_equal(z_flat, z_plus.squeeze(), err_msg=a)
- assert_equal(z_flat, z_minus.squeeze(), err_msg=a-x.ndim)
- # z_flat has shape [n_freq, n_time]
- # Test vs. transpose
- _, x_transpose_m = istft(z_flat.T, time_axis=-2, freq_axis=-1)
- _, x_transpose_p = istft(z_flat.T, time_axis=0, freq_axis=1)
- assert_allclose(x_flat, x_transpose_m, err_msg='istft transpose minus')
- assert_allclose(x_flat, x_transpose_p, err_msg='istft transpose plus')
- def test_roundtrip_scaling(self):
- """Verify behavior of scaling parameter. """
- # Create 1024 sample cosine signal with amplitude 2:
- X = np.zeros(513, dtype=complex)
- X[256] = 1024
- x = np.fft.irfft(X)
- power_x = sum(x**2) / len(x) # power of signal x is 2
- # Calculate magnitude-scaled STFT:
- Zs = stft(x, boundary='even', scaling='spectrum')[2]
- # Test round trip:
- x1 = istft(Zs, boundary=True, scaling='spectrum')[1]
- assert_allclose(x1, x)
- # For a Hann-windowed 256 sample length FFT, we expect a peak at
- # frequency 64 (since it is 1/4 the length of X) with a height of 1
- # (half the amplitude). A Hann window of a perfectly centered sine has
- # the magnitude [..., 0, 0, 0.5, 1, 0.5, 0, 0, ...].
- # Note that in this case the 'even' padding works for the beginning
- # but not for the end of the STFT.
- assert_allclose(abs(Zs[63, :-1]), 0.5)
- assert_allclose(abs(Zs[64, :-1]), 1)
- assert_allclose(abs(Zs[65, :-1]), 0.5)
- # All other values should be zero:
- Zs[63:66, :-1] = 0
- # Note since 'rtol' does not have influence here, atol needs to be set:
- assert_allclose(Zs[:, :-1], 0, atol=np.finfo(Zs.dtype).resolution)
- # Calculate two-sided psd-scaled STFT:
- # - using 'even' padding since signal is axis symmetric - this ensures
- # stationary behavior on the boundaries
- # - using the two-sided transform allows determining the spectral
- # power by `sum(abs(Zp[:, k])**2) / len(f)` for the k-th time slot.
- Zp = stft(x, return_onesided=False, boundary='even', scaling='psd')[2]
- # Calculate spectral power of Zd by summing over the frequency axis:
- psd_Zp = np.sum(Zp.real**2 + Zp.imag**2, axis=0) / Zp.shape[0]
- # Spectral power of Zp should be equal to the signal's power:
- assert_allclose(psd_Zp, power_x)
- # Test round trip:
- x1 = istft(Zp, input_onesided=False, boundary=True, scaling='psd')[1]
- assert_allclose(x1, x)
- # The power of the one-sided psd-scaled STFT can be determined
- # analogously (note that the two sides are not of equal shape):
- Zp0 = stft(x, return_onesided=True, boundary='even', scaling='psd')[2]
- # Since x is real, its Fourier transform is conjugate symmetric, i.e.,
- # the missing 'second side' can be expressed through the 'first side':
- Zp1 = np.conj(Zp0[-2:0:-1, :]) # 'second side' is conjugate reversed
- assert_allclose(Zp[:129, :], Zp0)
- assert_allclose(Zp[129:, :], Zp1)
- # Calculate the spectral power:
- s2 = (np.sum(Zp0.real ** 2 + Zp0.imag ** 2, axis=0) +
- np.sum(Zp1.real ** 2 + Zp1.imag ** 2, axis=0))
- psd_Zp01 = s2 / (Zp0.shape[0] + Zp1.shape[0])
- assert_allclose(psd_Zp01, power_x)
- # Test round trip:
- x1 = istft(Zp0, input_onesided=True, boundary=True, scaling='psd')[1]
- assert_allclose(x1, x)
|