Source code for owlpy.polarisation.pca
# -----------------------------------------------------------------------------
# OwlPy - AGPLv3
#
# This file is part of the OwlPy library. For licensing information see the
# accompanying file `LICENSE`.
#
# The OwlPy Developers, 21st century
# -----------------------------------------------------------------------------
import numpy as np
from owlpy.error import OwlPyError
from owlpy.util import get_traces_data_as_array
r2d = 180. / np.pi
[docs]class PCAError(OwlPyError):
'''
Raised when PCA failed.
'''
pass
[docs]def pca(traces):
'''
Perform principal component analysis (PCA) of 2- or multi-component signal.
:param traces:
Waveforms of the signals to be analysed. Components are expected in the
order and polarity ``[east, north]`` or ``[east, north, up]``. The
traces must be of same length, sampling rate and data type.
:type traces:
list of :py:class:`obspy.Trace <obspy.core.trace.Trace>`
or :py:class:`pyrocko.Trace <pyrocko.trace.Trace>` objects
:raises:
:py:exc:`~owlpy.error.OwlPyError` if the input traces are
incompatible, :py:exc:`PCAError` if the traces are too short.
:returns:
``(cov, evals, evecs, azimuth, incidence)`` where ``cov`` is the
covariance matrix of the data, ``evals`` are the eigenvalues, ``evecs``
are the eigenvectors, ``azimuth`` is the horizontal direction of the
principal component, measured clockwise from north and ``incidence`` is
incidence angle of the principal component, measured from vertical. The
azimuth is wrapped to the range ``[0, 180)`` because of its +-180 deg
ambiguity, the incidence angle returned in the range ``[0, 90]``. An
incidence angle of 90 deg is returned, if no vertical component is
available. Both angles are returned in [deg].
:rtype:
5-:py:class:`tuple`: three :py:class:`numpy.ndarray` and two
:py:class:`float`.
PCA is useful to find the polarisation of a signal contained in a 2- or
3-component seismic recording. This function estimates the covariance of
the signal, the eigen-system of the covariance and the angles of the first
principal component. Note that the polarity of the polarisation direction
cannot be determined from the PCA alone.
'''
data = get_traces_data_as_array(traces)
cov = np.cov(data)
evals, evecs = np.linalg.eigh(cov)
# evals are returned in ascending order
# first principal component,
pc = evecs[:, -1]
eh = np.sqrt(pc[1]**2 + pc[0]**2)
if len(traces) > 2:
incidence = r2d * np.arctan2(eh, abs(pc[2]))
else:
incidence = 90.
azimuth = r2d * np.arctan2(pc[1], pc[0])
azimuth = ((90. - azimuth) + 180) % 360. - 180.
azimuth %= 180.
return cov, evals, evecs, float(azimuth), float(incidence)