root/MGET/Branches/Jason/PythonPackage/src/GeoEco/AssimilatedModules/lomb_scargle.py @ 974

Revision 974, 6.5 KB (checked in by jjr8, 13 months ago)

Fixed/implemented:
* #542: Add a tool for calculating the moon phase
* #543: Binary output from Predict GLM, Predict GAM, etc. should be NoData? where inputs are NoData?
* Added temporal periodicity analysis tool.

Line 
1#!/usr/bin/env python 
2""" Fast algorithm for spectral analysis of unevenly sampled data
3 
4The Lomb-Scargle method performs spectral analysis on unevenly sampled
5data and is known to be a powerful way to find, and test the 
6significance of, weak periodic signals. The method has previously been
7thought to be 'slow', requiring of order 10(2)N(2) operations to analyze
8N data points. We show that Fast Fourier Transforms (FFTs) can be used
9in a novel way to make the computation of order 10(2)N log N. Despite
10its use of the FFT, the algorithm is in no way equivalent to 
11conventional FFT periodogram analysis.
12 
13Keywords:
14  DATA SAMPLING, FAST FOURIER TRANSFORMATIONS, 
15  SPECTRUM ANALYSIS, SIGNAL  PROCESSING
16 
17Example:
18  > import numpy
19  > import lomb
20  > x = numpy.arange(10)
21  > y = numpy.sin(x)
22  > fx,fy, nout, jmax, prob = lomb.fasper(x,y, 6., 6.)
23 
24Reference: 
25  Press, W. H. & Rybicki, G. B. 1989
26  ApJ vol. 338, p. 277-280.
27  Fast algorithm for spectral analysis of unevenly sampled data
28  bib code: 1989ApJ...338..277P
29 
30""" 
31from numpy import * 
32from numpy.fft import * 
33 
34def __spread__(y, yy, n, x, m): 
35  """
36  Given an array yy(0:n-1), extirpolate (spread) a value y into
37  m actual array elements that best approximate the "fictional"
38  (i.e., possible noninteger) array element number x. The weights
39  used are coefficients of the Lagrange interpolating polynomial
40  Arguments:
41    y : 
42    yy : 
43    n : 
44    x : 
45    m : 
46  Returns:
47     
48  """ 
49  nfac=[0,1,1,2,6,24,120,720,5040,40320,362880] 
50  if m > 10. : 
51    print 'factorial table too small in spread' 
52    return 
53 
54  ix=long(x) 
55  if x == float(ix):   
56    yy[ix]=yy[ix]+y 
57  else: 
58    ilo = long(x-0.5*float(m)+1.0) 
59    ilo = min( max( ilo , 1 ), n-m+1 )   
60    ihi = ilo+m-1 
61    nden = nfac[m] 
62    fac=x-ilo 
63    for j in range(ilo+1,ihi+1): fac = fac*(x-j) 
64    yy[ihi] = yy[ihi] + y*fac/(nden*(x-ihi)) 
65    for j in range(ihi-1,ilo-1,-1): 
66      nden=(nden/(j+1-ilo))*(j-ihi) 
67      yy[j] = yy[j] + y*fac/(nden*(x-j)) 
68 
69def fasper(x,y,ofac,hifac, MACC=4): 
70  """ function fasper
71    Given abscissas x (which need not be equally spaced) and ordinates
72    y, and given a desired oversampling factor ofac (a typical value
73    being 4 or larger). this routine creates an array wk1 with a
74    sequence of nout increasing frequencies (not angular frequencies)
75    up to hifac times the "average" Nyquist frequency, and creates
76    an array wk2 with the values of the Lomb normalized periodogram at
77    those frequencies. The arrays x and y are not altered. This
78    routine also returns jmax such that wk2(jmax) is the maximum
79    element in wk2, and prob, an estimate of the significance of that
80    maximum against the hypothesis of random noise. A small value of prob
81    indicates that a significant periodic signal is present.
82   
83  Reference: 
84    Press, W. H. & Rybicki, G. B. 1989
85    ApJ vol. 338, p. 277-280.
86    Fast algorithm for spectral analysis of unevenly sampled data
87    (1989ApJ...338..277P)
88 
89  Arguments:
90      X   : Abscissas array, (e.g. an array of times).
91      Y   : Ordinates array, (e.g. corresponding counts).
92      Ofac : Oversampling factor.
93      Hifac : Hifac * "average" Nyquist frequency = highest frequency
94           for which values of the Lomb normalized periodogram will
95           be calculated.
96       
97   Returns:
98      Wk1 : An array of Lomb periodogram frequencies.
99      Wk2 : An array of corresponding values of the Lomb periodogram.
100      Nout : Wk1 & Wk2 dimensions (number of calculated frequencies)
101      Jmax : The array index corresponding to the MAX( Wk2 ).
102      Prob : False Alarm Probability of the largest Periodogram value
103      MACC : Number of interpolation points per 1/4 cycle
104            of highest frequency
105 
106  History:
107    02/23/2009, v1.0, MF
108      Translation of IDL code (orig. Numerical recipies)
109  """ 
110  #Check dimensions of input arrays 
111  n = long(len(x)) 
112  if n != len(y): 
113    print 'Incompatible arrays.' 
114    return 
115 
116  nout  = 0.5*ofac*hifac*n 
117  nfreqt = long(ofac*hifac*n*MACC)   #Size the FFT as next power 
118  nfreq = 64L             # of 2 above nfreqt. 
119 
120  while nfreq < nfreqt:   
121    nfreq = 2*nfreq 
122 
123  ndim = long(2*nfreq) 
124   
125  #Compute the mean, variance 
126  ave = y.mean() 
127  ##sample variance because the divisor is N-1 
128  var = ((y-y.mean())**2).sum()/(len(y)-1)   
129  # and range of the data. 
130  xmin = x.min() 
131  xmax = x.max() 
132  xdif = xmax-xmin 
133 
134  #extirpolate the data into the workspaces 
135  wk1 = zeros(ndim, dtype='complex') 
136  wk2 = zeros(ndim, dtype='complex') 
137 
138  fac  = ndim/(xdif*ofac) 
139  fndim = ndim 
140  ck  = ((x-xmin)*fac) % fndim 
141  ckk  = (2.0*ck) % fndim 
142 
143  for j in range(0L, n): 
144    __spread__(y[j]-ave,wk1,ndim,ck[j],MACC) 
145    __spread__(1.0,wk2,ndim,ckk[j],MACC) 
146 
147  #Take the Fast Fourier Transforms 
148  wk1 = ifft( wk1 )*len(wk1) 
149  wk2 = ifft( wk2 )*len(wk1) 
150 
151  wk1 = wk1[1:nout+1] 
152  wk2 = wk2[1:nout+1] 
153  rwk1 = wk1.real 
154  iwk1 = wk1.imag 
155  rwk2 = wk2.real 
156  iwk2 = wk2.imag 
157   
158  df  = 1.0/(xdif*ofac) 
159   
160  #Compute the Lomb value for each frequency 
161  hypo2 = 2.0 * abs( wk2 ) 
162  hc2wt = rwk2/hypo2 
163  hs2wt = iwk2/hypo2 
164 
165  cwt  = sqrt(0.5+hc2wt) 
166  swt  = sign(hs2wt)*(sqrt(0.5-hc2wt)) 
167  den  = 0.5*n+hc2wt*rwk2+hs2wt*iwk2 
168  cterm = (cwt*rwk1+swt*iwk1)**2./den 
169  sterm = (cwt*iwk1-swt*rwk1)**2./(n-den) 
170 
171  wk1 = df*(arange(nout, dtype='float')+1.) 
172  wk2 = (cterm+sterm)/(2.0*var) 
173  pmax = wk2.max() 
174  jmax = wk2.argmax() 
175 
176 
177  #Significance estimation 
178  #expy = exp(-wk2)           
179  #effm = 2.0*(nout)/ofac         
180  #sig = effm*expy 
181  #ind = (sig > 0.01).nonzero() 
182  #sig[ind] = 1.0-(1.0-expy[ind])**effm 
183 
184  #Estimate significance of largest peak value 
185  expy = exp(-pmax)           
186  effm = 2.0*(nout)/ofac         
187  prob = effm*expy 
188 
189  if prob > 0.01:   
190    prob = 1.0-(1.0-expy)**effm 
191 
192  return wk1,wk2,nout,jmax,prob 
193 
194def getSignificance(wk1, wk2, nout, ofac): 
195  """ returns the peak false alarm probabilities
196  Hence the lower is the probability and the more significant is the peak
197  """ 
198  expy = exp(-wk2)           
199  effm = 2.0*(nout)/ofac         
200  sig = effm*expy 
201  ind = (sig > 0.01).nonzero() 
202  sig[ind] = 1.0-(1.0-expy[ind])**effm 
203  return sig
Note: See TracBrowser for help on using the browser.