SpectrumEstimate.coherence#

SpectrumEstimate.coherence(first, second)[source]#

Squared coherence between two series at each frequency.

\[\hat C_{xy}(\omega) = \frac{|\hat f_{xy}(\omega)|^2}{\hat f_{xx}(\omega)\,\hat f_{yy}(\omega)} \in [0, 1],\]

the frequency-domain \(R^2\) of one series on the other. With \(\nu\) degrees of freedom the estimate is biased upward by about \(2 / \nu\) under zero coherence, which the smoothing makes small; a raw periodogram (\(\nu = 2\)) would report coherence one everywhere, which is why none is offered.

Parameters:
  • first (str) – One series label.

  • second (str) – The other; order is immaterial.

Return type:

NDArray[double]

Returns:

(n,) squared coherence.

Raises:

SpecificationError – If either label is unknown.

Example

Two independent noises show the \(2 / \nu\) floor, and a series against its own lag plus noise is coherent where the signal dominates:

>>> rng = np.random.default_rng(0)
>>> est = MultitaperSpectrum(rng.standard_normal((2048, 2))).compute()
>>> floor = 2 / est.degrees_of_freedom
>>> round(float(est.coherence("y1", "y2").mean()), 2), round(floor, 2)
(0.14, 0.14)
>>> y = np.zeros(1024)
>>> for t in range(1, 1024):
...     y[t] = 0.7 * y[t - 1] + rng.standard_normal()
>>> z = np.column_stack([y, np.roll(y, 1) + rng.standard_normal(1024)])
>>> coh = MultitaperSpectrum(z).compute().coherence("y1", "y2")
>>> bool(coh[1:40].mean() > 0.8), bool(coh[-40:].mean() < 0.5)
(True, True)