Ho difficoltà a generare una serie di serie temporali colorate stazionarie, data la loro matrice di covarianza (densità di potenza spettrale (PSD) e densità spettrale di potenza incrociata (CSD)).
So che, date due serie temporali e , posso stimare la loro densità spettrale di potenza (PSD) e densità spettrale incrociata (CSD) utilizzando molte routine ampiamente disponibili, come ad esempio e funzioni in Matlab, ecc. I PSD e CSD compongono la matrice di covarianza:
psd()
csd()
Cosa succede se voglio fare il contrario? Data la matrice di covarianza, come posso generare una realizzazione di e ?
Ti preghiamo di includere qualsiasi teoria di base o di evidenziare eventuali strumenti esistenti che lo fanno (qualsiasi cosa in Python sarebbe eccezionale).
Il mio tentativo
Di seguito è una descrizione di ciò che ho provato e dei problemi che ho notato. È un po 'lungo e mi dispiace se contiene termini che sono stati usati in modo improprio. Se si può sottolineare ciò che è errato, ciò sarebbe molto utile. Ma la mia domanda è quella in grassetto sopra.
- I PSD e i CSD possono essere scritti come valore di aspettativa (o media dell'insieme) dei prodotti delle trasformate di Fourier delle serie temporali. Quindi, la matrice di covarianza può essere scritta come:
dove
- Una matrice di covarianza è una matrice eremitica, con autovalori reali che sono zero o positivi. Quindi, può essere scomposto in
dove è una matrice diagonale i cui elementi diversi da zero sono le radici quadrate degli autovalori di ; è la matrice le cui colonne sono gli autovettori ortogonali di ; è la matrice identità.
- La matrice identità è scritta come
dove
e sono serie di frequenze non correlate e complesse con media zero e varianza unitaria.
- Usando 3. in 2., quindi confrontandolo con 1. Le trasformazioni di Fourier delle serie temporali sono:
- Le serie temporali possono quindi essere ottenute usando routine come la trasformata di Fourier veloce inversa.
Ho scritto una routine in Python per fare questo:
def get_noise_freq_domain_CovarMatrix( comatrix , df , inittime , parityN , seed='none' , N_previous_draws=0 ) :
"""
returns the noise time-series given their covariance matrix
INPUT:
comatrix --- covariance matrix, Nts x Nts x Nf numpy array
( Nts = number of time-series. Nf number of positive and non-Nyquist frequencies )
df --- frequency resolution
inittime --- initial time of the noise time-series
parityN --- is the length of the time-series 'Odd' or 'Even'
seed --- seed for the random number generator
N_previous_draws --- number of random number draws to discard first
OUPUT:
t --- time [s]
n --- noise time-series, Nts x N numpy array
"""
if len( comatrix.shape ) != 3 :
raise InputError , 'Input Covariance matrices must be a 3-D numpy array!'
if comatrix.shape[0] != comatrix.shape[1] :
raise InputError , 'Covariance matrix must be square at each frequency!'
Nts , Nf = comatrix.shape[0] , comatrix.shape[2]
if parityN == 'Odd' :
N = 2 * Nf + 1
elif parityN == 'Even' :
N = 2 * ( Nf + 1 )
else :
raise InputError , "parityN must be either 'Odd' or 'Even'!"
stime = 1 / ( N*df )
t = inittime + stime * np.arange( N )
if seed == 'none' :
print 'Not setting the seed for np.random.standard_normal()'
pass
elif seed == 'random' :
np.random.seed( None )
else :
np.random.seed( int( seed ) )
print N_previous_draws
np.random.standard_normal( N_previous_draws ) ;
zs = np.array( [ ( np.random.standard_normal((Nf,)) + 1j * np.random.standard_normal((Nf,)) ) / np.sqrt(2)
for i in range( Nts ) ] )
ntilde_p = np.zeros( ( Nts , Nf ) , dtype=complex )
for k in range( Nf ) :
C = comatrix[ :,:,k ]
if not np.allclose( C , np.conj( np.transpose( C ) ) ) :
print "Covariance matrix NOT Hermitian! Unphysical."
w , V = sp_linalg.eigh( C )
for m in range( w.shape[0] ) :
w[m] = np.real( w[m] )
if np.abs(w[m]) / np.max(w) < 1e-10 :
w[m] = 0
if w[m] < 0 :
print 'Negative eigenvalue! Simulating unpysical signal...'
ntilde_p[ :,k ] = np.conj( np.sqrt( N / (2*stime) ) * np.dot( V , np.dot( np.sqrt( np.diag( w ) ) , zs[ :,k ] ) ) )
zerofill = np.zeros( ( Nts , 1 ) )
if N % 2 == 0 :
ntilde = np.concatenate( ( zerofill , ntilde_p , zerofill , np.conj(np.fliplr(ntilde_p)) ) , axis = 1 )
else :
ntilde = np.concatenate( ( zerofill , ntilde_p , np.conj(np.fliplr(ntilde_p)) ) , axis = 1 )
n = np.real( sp.ifft( ntilde , axis = 1 ) )
return t , n
Ho applicato questa routine a PSD e CSD, le cui espressioni analitiche sono state ottenute dalla modellazione di alcuni rivelatori con cui sto lavorando. L'importante è che a tutte le frequenze, formino una matrice di covarianza (beh almeno passano tutte quelle if
affermazioni nella routine). La matrice di covarianza è 3x3. Le 3 serie temporali sono state generate circa 9000 volte e i PSD e CSD stimati, calcolati in media su tutte queste realizzazioni, sono riportati di seguito con quelli analitici. Mentre le forme complessive concordano, ci sono notevoli caratteristiche rumorose a determinate frequenze nei CSD (Fig.2). Dopo un primo piano attorno ai picchi dei PSD (Fig.3), ho notato che i PSD sono in realtà sottovalutatie che le caratteristiche rumorose nei CSD si verificano quasi alle stesse frequenze dei picchi nei PSD. Non penso che questa sia una coincidenza e che in qualche modo il potere stia perdendo dai PSD ai CSD. Mi sarei aspettato che le curve si trovassero l'una sopra l'altra, con così tante realizzazioni dei dati.