示例#1
0
def bispectrumdx(x, y, z, nfft=None, wind=None, nsamp=None, overlap=None):
  """
  Parameters:
    x    - data vector or time-series
    y    - data vector or time-series  (same dimensions as x)
    z    - data vector or time-series  (same dimensions as x)
    nfft - fft length [default = power of two > segsamp]
    wind - window specification for frequency-domain smoothing
           if 'wind' is a scalar, it specifies the length of the side
              of the square for the Rao-Gabr optimal window  [default=5]
           if 'wind' is a vector, a 2D window will be calculated via
              w2(i,j) = wind(i) * wind(j) * wind(i+j)
           if 'wind' is a matrix, it specifies the 2-D filter directly
    segsamp - samples per segment [default: such that we have 8 segments]
            - if x is a matrix, segsamp is set to the number of rows
    overlap - percentage overlap, allowed range [0,99]. [default = 50];
            - if x is a matrix, overlap is set to 0.

  Output:
    Bspec   - estimated bispectrum: an nfft x nfft array, with origin
              at the center, and axes pointing down and to the right.
    waxis   - vector of frequencies associated with the rows and columns
              of Bspec;  sampling frequency is assumed to be 1.
  """

  (lx, lrecs) = x.shape
  (ly, nrecs) = y.shape
  (lz, krecs) = z.shape

  if lx != ly or lrecs != nrecs or ly != lz or nrecs != krecs:
    raise Exception('x, y and z should have identical dimensions')

  if ly == 1:
    x = x.reshape(1,-1)
    y = y.reshape(1,-1)
    z = z.reshape(1,-1)
    ly = nrecs
    nrecs = 1

  if not overlap: overlap = 50
  overlap = max(0,min(overlap,99))
  if nrecs > 1: overlap = 0
  if not nsamp: nsamp = 0
  if nrecs > 1: nsamp = ly
  if nrecs == 1 and nsamp <= 0:
    nsamp = np.fix(ly/ (8 - 7 * overlap/100))
  if nfft < nsamp:
    nfft = 2**nextpow2(nsamp)

  overlap = np.fix(overlap/100 * nsamp)
  nadvance = nsamp - overlap
  nrecs = np.fix((ly*nrecs - overlap) / nadvance)


  # create the 2-D window
  if not wind: wind = 5

  m = n = 0
  try:
    (m, n) = wind.shape
  except ValueError:
    (m,) = wind.shape
    n = 1
  except AttributeError:
    m = n = 1

  window = wind
  # scalar: wind is size of Rao-Gabr window
  if max(m, n) == 1:
    winsize = wind
    if winsize < 0: winsize = 5 # the window size L
    winsize = winsize - (winsize%2) + 1 # make it odd
    if winsize > 1:
      mwind = np.fix(nfft/winsize) # the scale parameter M
      lby2 = (winsize - 1)/2

      theta = np.array([np.arange(-1*lby2, lby2+1)]) # force a 2D array
      opwind = np.ones([winsize, 1]) * (theta**2) # w(m,n) = m**2
      opwind = opwind + opwind.transpose() + (np.transpose(theta) * theta) # m**2 + n**2 + mn
      opwind = 1 - ((2*mwind/nfft)**2) * opwind
      Hex = np.ones([winsize,1]) * theta
      Hex = abs(Hex) + abs(np.transpose(Hex)) + abs(Hex + np.transpose(Hex))
      Hex = (Hex < winsize)
      opwind = opwind * Hex
      opwind = opwind * (4 * mwind**2) / (7 * np.pi**2)
    else:
      opwind = 1

  # 1-D window passed: convert to 2-D
  elif min(m, n) == 1:
    window = window.reshape(1,-1)

    if np.any(np.imag(window)) != 0:
      print ("1-D window has imaginary components: window ignored")
      window = 1

    if np.any(window) < 0:
      print ("1-D window has negative components: window ignored")
      window = 1

    lwind = np.size(window)
    w = window.ravel(order='F')
    # the full symmetric 1-D
    windf = np.array(w[range(lwind-1, 0, -1) + [window]])
    window = np.array([window], np.zeros([lwind-1,1]))
    # w(m)w(n)w(m+n)
    opwind = (windf * np.transpose(windf)) * hankel(np.flipud(window), window)
    winsize = np.size(window)

  # 2-D window passed: use directly
  else:
    winsize = m

    if m != n:
      print ("2-D window is not square: window ignored")
      window = 1
      winsize = m

    if m%2 == 0:
      print ("2-D window does not have odd length: window ignored")
      window = 1
      winsize = m

    opwind = window

  # accumulate triple products
  Bspec = np.zeros([nfft, nfft]) # the hankel mask (faster)
  mask = hankel(np.arange(nfft),np.array([nfft-1]+range(nfft-1)))
  locseg = np.arange(nsamp).transpose()
  x = x.ravel(order='F')
  y = y.ravel(order='F')
  z = z.ravel(order='F')

  for krec in xrange(nrecs):
    xseg = x[locseg].reshape(1,-1)
    yseg = y[locseg].reshape(1,-1)
    zseg = z[locseg].reshape(1,-1)

    Xf = np.fft.fft(xseg - np.mean(xseg), nfft) / nsamp
    Yf = np.fft.fft(yseg - np.mean(yseg), nfft) / nsamp
    CZf = np.fft.fft(zseg - np.mean(zseg), nfft) / nsamp
    CZf = np.conjugate(CZf).ravel(order='F')

    Bspec = Bspec + \
      flat_eq(Bspec, (Xf * np.transpose(Yf)) * CZf[mask].reshape(nfft, nfft))
    locseg = locseg + int(nadvance)

  Bspec = np.fft.fftshift(Bspec) / nrecs


  # frequency-domain smoothing
  if winsize > 1:
    lby2 = int((winsize-1)/2)
    Bspec = convolve2d(Bspec,opwind)
    Bspec = Bspec[range(lby2+1,lby2+nfft+1), :][:, np.arange(lby2+1,lby2+nfft+1)]


  if nfft%2 == 0:
    waxis = np.transpose(np.arange(-1*nfft/2, nfft/2)) / nfft
  else:
    waxis = np.transpose(np.arange(-1*(nfft-1)/2, (nfft-1)/2+1)) / nfft

  # cont1 = plt.contour(abs(Bspec), 4, waxis, waxis)
  cont = plt.contourf(waxis, waxis, abs(Bspec), 100, cmap=plt.cm.Spectral_r)
  plt.colorbar(cont)
  plt.title('Bispectrum estimated via the direct (FFT) method')
  plt.xlabel('f1')
  plt.ylabel('f2')
  plt.show()

  return (Bspec, waxis)
示例#2
0
def bicoherencex(w, x, y, nfft=None, wind=None, nsamp=None, overlap=None):
    """
  Direct (FD) method for estimating cross-bicoherence
  Parameters:
    w,x,y - data vector or time-series
          - should have identical dimensions
    nfft - fft length [default = power of two > nsamp]
           actual size used is power of two greater than 'nsamp'
    wind - specifies the time-domain window to be applied to each
           data segment; should be of length 'segsamp' (see below);
      otherwise, the default Hanning window is used.
    segsamp - samples per segment [default: such that we have 8 segments]
            - if x is a matrix, segsamp is set to the number of rows
    overlap - percentage overlap, 0 to 99  [default = 50]
            - if y is a matrix, overlap is set to 0.

  Output:
    bic     - estimated cross-bicoherence: an nfft x nfft array, with
              origin at center, and axes pointing down and to the right.
    waxis   - vector of frequencies associated with the rows and columns
              of bic;  sampling frequency is assumed to be 1.
  """

    if w.shape != x.shape or x.shape != y.shape:
        raise ValueError('w, x and y should have identical dimentions')

    (ly, nrecs) = y.shape
    if ly == 1:
        ly = nrecs
        nrecs = 1
        w = w.reshape(1, -1)
        x = x.reshape(1, -1)
        y = y.reshape(1, -1)

    if not nfft:
        nfft = 128

    if not overlap: overlap = 50
    overlap = max(0, min(overlap, 99))
    if nrecs > 1: overlap = 0
    if not nsamp: nsamp = 0
    if nrecs > 1: nsamp = ly
    if nrecs == 1 and nsamp <= 0:
        nsamp = np.fix(ly / (8 - 7 * overlap / 100))
    if nfft < nsamp:
        nfft = 2**nextpow2(nsamp)

    overlap = np.fix(overlap / 100 * nsamp)
    nadvance = nsamp - overlap
    nrecs = np.fix((ly * nrecs - overlap) / nadvance)

    if not wind:
        wind = np.hanning(nsamp)

    try:
        (rw, cw) = wind.shape
    except ValueError:
        (rw, ) = wind.shape
        cw = 1

    if min(rw, cw) != 1 or max(rw, cw) != nsamp:
        print("Segment size is " + str(nsamp))
        print("Wind array is " + str(rw) + " by " + str(cw))
        print("Using default Hanning window")
        wind = np.hanning(nsamp)

    wind = wind.reshape(1, -1)

    # Accumulate triple products
    bic = np.zeros([nfft, nfft])
    Pyy = np.zeros([nfft, 1])
    Pww = np.zeros([nfft, 1])
    Pxx = np.zeros([nfft, 1])

    mask = hankel(np.arange(nfft), np.array([nfft - 1] + range(nfft - 1)))
    Yf12 = np.zeros([nfft, nfft])
    ind = np.transpose(np.arange(nsamp))
    w = w.ravel(order='F')
    x = x.ravel(order='F')
    y = y.ravel(order='F')

    for k in xrange(nrecs):
        ws = w[ind]
        ws = (ws - np.mean(ws)) * wind
        Wf = np.fft.fft(ws, nfft) / nsamp
        CWf = np.conjugate(Wf)
        Pww = Pww + flat_eq(Pww, (Wf * CWf))

        xs = x[ind]
        xs = (xs - np.mean(xs)) * wind
        Xf = np.fft.fft(xs, nfft) / nsamp
        CXf = np.conjugate(Xf)
        Pxx = Pxx + flat_eq(Pxx, (Xf * CXf))

        ys = y[ind]
        ys = (ys - np.mean(ys)) * wind
        Yf = np.fft.fft(ys, nfft) / nsamp
        CYf = np.conjugate(Yf)
        Pyy = Pyy + flat_eq(Pyy, (Yf * CYf))

        Yf12 = flat_eq(Yf12, CYf.ravel(order='F')[mask])
        bic = bic + (Wf * np.transpose(Xf)) * Yf12

        ind = ind + int(nadvance)

    bic = bic / nrecs
    Pww = Pww / nrecs
    Pxx = Pxx / nrecs
    Pyy = Pyy / nrecs
    mask = flat_eq(mask, Pyy.ravel(order='F')[mask])

    bic = abs(bic)**2 / ((Pww * np.transpose(Pxx)) * mask)
    bic = np.fft.fftshift(bic)

    # Contour plot of magnitude bispectrum
    if nfft % 2 == 0:
        waxis = np.transpose(np.arange(-1 * nfft / 2, nfft / 2)) / nfft
    else:
        waxis = np.transpose(np.arange(-1 * (nfft - 1) / 2,
                                       (nfft - 1) / 2 + 1)) / nfft

    cont = plt.contourf(waxis, waxis, bic, 100, cmap=plt.cm.Spectral_r)
    plt.colorbar(cont)
    plt.title('Bicoherence estimated via the direct (FFT) method')
    plt.xlabel('f1')
    plt.ylabel('f2')

    colmax, row = bic.max(0), bic.argmax(0)
    maxval, col = colmax.max(0), colmax.argmax(0)
    print('Max: bic(' + str(waxis[col]) + ',' + str(waxis[col]) + ') = ' +
          str(maxval))
    plt.show()

    return (bic, waxis)
示例#3
0
def bicoherence(y, nfft=None, wind=None, nsamp=None, overlap=None):
  """
  Direct (FD) method for estimating bicoherence
  Parameters:
    y     - data vector or time-series
    nfft - fft length [default = power of two > segsamp]
           actual size used is power of two greater than 'nsamp'
    wind - specifies the time-domain window to be applied to each
           data segment; should be of length 'segsamp' (see below);
      otherwise, the default Hanning window is used.
    segsamp - samples per segment [default: such that we have 8 segments]
            - if x is a matrix, segsamp is set to the number of rows
    overlap - percentage overlap, allowed range [0,99]. [default = 50];
            - if x is a matrix, overlap is set to 0.

  Output:
    bic     - estimated bicoherence: an nfft x nfft array, with origin
              at the center, and axes pointing down and to the right.
    waxis   - vector of frequencies associated with the rows and columns
              of bic;  sampling frequency is assumed to be 1.
  """

  # Parameter checks

  (ly, nrecs) = y.shape
  if ly == 1:
    y = y.reshape(1, -1)
    ly = nrecs
    nrecs = 1

  if not nfft: nfft = 128
  if not overlap: overlap = 50
  if nrecs > 1: overlap = 0
  if not nsamp: nsamp = 0
  if nrecs > 1: nsamp = ly

  if nrecs > 1 and nsamp <= 0:
    nsamp = np.fix(ly / (8 - 7 * overlap/100))
  if nfft  < nsamp:
    nfft = 2**nextpow2(nsamp)

  overlap  = np.fix(nsamp * overlap/100)
  nadvance = nsamp - overlap
  nrecs    = np.fix ((ly*nrecs - overlap) / nadvance)


  if not wind:
    wind = np.hanning(nsamp)

  try:
    (rw, cw) = wind.shape
  except ValueError:
    (rw,) = wind.shape
    cw = 1

  if min(rw, cw) == 1 or max(rw, cw) == nsamp:
    print ("Segment size is " + str(nsamp))
    print ("Wind array is " + str(rw) + " by " + str(cw))
    print ("Using default Hanning window")
    wind = np.hanning(nsamp)

  wind = wind.reshape(1,-1)


  # Accumulate triple products

  bic = np.zeros([nfft, nfft])
  Pyy  = np.zeros([nfft,1])

  mask = hankel(np.arange(nfft),np.array([nfft-1]+range(nfft-1)))
  Yf12 = np.zeros([nfft,nfft])
  ind  = np.arange(nsamp)
  y = y.ravel(order='F')

  for k in xrange(nrecs):
    ys = y[ind]
    ys = (ys.reshape(1,-1) - np.mean(ys)) * wind

    Yf = np.fft.fft(ys, nfft)/nsamp
    CYf = np.conjugate(Yf)
    Pyy = Pyy + flat_eq(Pyy, (Yf*CYf))

    Yf12 = flat_eq(Yf12, CYf.ravel(order='F')[mask])

    bic = bic + ((Yf * np.transpose(Yf)) * Yf12)
    ind = ind + int(nadvance)


  bic = bic / nrecs
  Pyy = Pyy / nrecs
  mask = flat_eq(mask, Pyy.ravel(order='F')[mask])
  bic = abs(bic)**2 / ((Pyy * np.transpose(Pyy)) *  mask)
  bic = np.fft.fftshift(bic)

  if nfft%2 == 0:
    waxis = np.transpose(np.arange(-1*nfft/2, nfft/2)) / nfft
  else:
    waxis = np.transpose(np.arange(-1*(nfft-1)/2, (nfft-1)/2+1)) / nfft

  cont = plt.contourf(waxis,waxis,bic,100, cmap=plt.cm.Spectral_r)
  plt.colorbar(cont)
  plt.title('Bicoherence estimated via the direct (FFT) method')
  plt.xlabel('f1')
  plt.ylabel('f2')

  colmax, row = bic.max(0), bic.argmax(0)
  maxval, col = colmax.max(0), colmax.argmax(0)
  print ( 'Max: bic('+str(waxis[col])+','+str(waxis[col])+') = '+str(maxval))
  plt.show()

  return (bic, waxis)
示例#4
0
def bispectrumi(y, nlag=None, nsamp=None, overlap=None,
  flag='biased', nfft=None, wind=None):
  """
  Parameters:
    y       - data vector or time-series
    nlag    - number of lags to compute [must be specified]
    segsamp - samples per segment    [default: row dimension of y]
    overlap - percentage overlap     [default = 0]
    flag    - 'biased' or 'unbiased' [default is 'unbiased']
    nfft    - FFT length to use      [default = 128]
    wind    - window function to apply:
              if wind=0, the Parzen window is applied (default)
              otherwise the hexagonal window with unity values is applied.

  Output:
    Bspec   - estimated bispectrum  it is an nfft x nfft array
              with origin at the center, and axes pointing down and to the right
    waxis   - frequency-domain axis associated with the bispectrum.
            - the i-th row (or column) of Bspec corresponds to f1 (or f2)
              value of waxis(i).
  """

  (ly, nrecs) = y.shape
  if ly == 1:
    y = y.reshape(1,-1)
    ly = nrecs
    nrecs = 1

  if not overlap: overlap = 0
  overlap = min(99, max(overlap,0))
  if nrecs > 1: overlap = 0
  if not nsamp: nsamp = ly
  if nsamp > ly or nsamp <= 0: nsamp = ly
  if not 'flag': flag = 'biased'
  if not nfft: nfft = 128
  if not wind: wind = 0

  nlag = min(nlag, nsamp-1)
  if nfft < 2*nlag+1:
    nfft = 2^nextpow2(nsamp)


  # create the lag window
  Bspec = np.zeros([nfft, nfft])
  if wind == 0:
    indx = np.array([range(1,nlag+1)]).T
    window = make_arr((1, np.sin(np.pi*indx/nlag) / (np.pi*indx/nlag)), axis=0)
  else:
    window = np.ones([nlag+1,1])
  window = make_arr((window, np.zeros([nlag,1])), axis=0)

  # cumulants in non-redundant region
  overlap  = np.fix(nsamp * overlap / 100)
  nadvance = nsamp - overlap
  nrecord  = np.fix((ly*nrecs - overlap) / nadvance)

  c3 = np.zeros([nlag+1,nlag+1])
  ind = np.arange(nsamp)
  y = y.ravel(order='F')

  s = 0
  for k in xrange(nrecord):
    x = y[ind].ravel(order='F')
    x = x - np.mean(x)
    ind = ind + int(nadvance)

    for j in xrange(nlag+1):
      z = x[range(nsamp-j)] * x[range(j, nsamp)]
      for i in xrange(j,nlag+1):
        Sum = np.dot(z[range(nsamp-i)].T, x[range(i,nsamp)])
        if flag == 'biased': Sum = Sum/nsamp
        else: Sum = Sum / (nsamp-i)
        c3[i,j] = c3[i,j] + Sum

  c3 = c3 / nrecord

  # cumulants elsewhere by symmetry
  c3 = c3 + np.tril(c3,-1).T   # complete I quadrant
  c31 = c3[1:nlag+1, 1:nlag+1]
  c32 = np.zeros([nlag, nlag])
  c33 = np.zeros([nlag, nlag])
  c34 = np.zeros([nlag, nlag])
  for i in xrange(nlag):
    x = c31[i:nlag, i]
    c32[nlag-1-i,0:nlag-i] = x.T
    c34[0:nlag-i, nlag-1-i] = x
    if i+1 < nlag:
      x = np.flipud(x[1:len(x)])
      c33 = c33 + np.diag(x,i+1) + np.diag(x,-(i+1))

  c33  = c33 + np.diag(c3[0, nlag:0:-1])

  cmat = make_arr(
    (make_arr((c33, c32, np.zeros([nlag,1])), axis=1),
    make_arr((make_arr((c34, np.zeros([1,nlag])), axis=0), c3), axis=1)),
    axis=0
  )

  # apply lag-domain window
  wcmat = cmat
  if wind != -1:
    indx = np.arange(-1*nlag, nlag+1).T
    window = window.reshape(-1,1)
    for k in xrange(-nlag, nlag+1):
      wcmat[:, k+nlag] = (cmat[:, k+nlag].reshape(-1,1) * \
              window[abs(indx-k)] * \
              window[abs(indx)] * \
              window[abs(k)]).reshape(-1,)


  # compute 2d-fft, and shift and rotate for proper orientation
  Bspec = np.fft.fft2(wcmat, (nfft, nfft))
  Bspec = np.fft.fftshift(Bspec) # axes d and r; orig at ctr


  if nfft%2 == 0:
    waxis = np.transpose(np.arange(-1*nfft/2, nfft/2)) / nfft
  else:
    waxis = np.transpose(np.arange(-1*(nfft-1)/2, (nfft-1)/2+1)) / nfft

  cont = plt.contourf(waxis, waxis, abs(Bspec), 100, cmap=plt.cm.Spectral_r)
  plt.colorbar(cont)
  plt.title('Bispectrum estimated via the indirect method')
  plt.xlabel('f1')
  plt.ylabel('f2')
  plt.show()

  return (Bspec, waxis)