phasorpy.phasor#

Calculate, convert, and reduce phasor coordinates.

The phasorpy.phasor module provides functions to:

phasorpy.phasor.phasor_center(mean, real, imag, /, *, skip_axis=None, method='mean', nan_safe=True, **kwargs)[source]#

Return center of phasor coordinates.

Parameters:
  • mean (array_like) – Intensity of phasor coordinates.

  • real (array_like) – Real component of phasor coordinates.

  • imag (array_like) – Imaginary component of phasor coordinates.

  • skip_axis (int or sequence of int, optional) – Axes in mean to excluded from center calculation. By default, all axes except harmonics are included.

  • method (str, optional) –

    Method used for center calculation:

    • 'mean': Arithmetic mean of phasor coordinates.

    • 'median': Spatial median of phasor coordinates.

  • nan_safe (bool, optional) – Ensure method is applied to same elements of input arrays. By default, distribute NaNs among input arrays before applying method. May be disabled if phasor coordinates were filtered by phasor_threshold().

  • **kwargs – Optional arguments passed to numpy.nanmean() or numpy.nanmedian().

Returns:

  • mean_center (ndarray) – Intensity center coordinates.

  • real_center (ndarray) – Real center coordinates.

  • imag_center (ndarray) – Imaginary center coordinates.

Raises:

ValueError – If the specified method is not supported. If the shapes of mean, real, and imag do not match.

Examples

Compute center coordinates with the default ‘mean’ method:

>>> phasor_center(
...     [2, 1, 2], [0.1, 0.2, 0.3], [0.4, 0.5, 0.6]
... )
(1.67, 0.2, 0.5)

Compute center coordinates with the ‘median’ method:

>>> phasor_center(
...     [1, 2, 3], [0.1, 0.2, 0.3], [0.4, 0.5, 0.6], method='median'
... )
(2.0, 0.2, 0.5)
phasorpy.phasor.phasor_divide(real, imag, divisor_real, divisor_imag, /, **kwargs)[source]#

Return complex division of two phasors.

Complex division can be used, for example, to deconvolve two signals such as exponential decay and instrument response functions.

Parameters:
  • real (array_like) – Real component of phasor coordinates to divide.

  • imag (array_like) – Imaginary component of phasor coordinates to divide.

  • divisor_real (array_like) – Real component of phasor coordinates to divide by.

  • divisor_imag (array_like) – Imaginary component of phasor coordinates to divide by.

  • **kwargs – Optional arguments passed to numpy universal functions.

Returns:

  • real (ndarray) – Real component of complex division.

  • imag (ndarray) – Imaginary component of complex division.

Notes

The phasor coordinates real (\(G\)) and imag (\(S\)) are divided by phasor coordinates divisor_real (\(g\)) and divisor_imag (\(s\)) according to:

\[ \begin{align}\begin{aligned}d &= g \cdot g + s \cdot s\\G' &= (G \cdot g + S \cdot s) / d\\S' &= (G \cdot s - S \cdot g) / d\end{aligned}\end{align} \]

Examples

Divide two sets of phasor coordinates:

>>> phasor_divide([-0.16, -0.2], [0.22, 0.4], [0.5, 0.6], [0.7, 0.8])
(array([0.1, 0.2]), array([0.3, 0.4]))
phasorpy.phasor.phasor_filter_median(mean, real, imag, /, *, repeat=1, size=3, skip_axis=None, use_scipy=False, num_threads=None, **kwargs)[source]#

Return median-filtered phasor coordinates.

By default, apply a NaN-aware median filter independently to the real and imaginary components of phasor coordinates once with a kernel size of 3 multiplied by the number of dimensions of the input arrays. Return the intensity unchanged.

Parameters:
  • mean (array_like) – Intensity of phasor coordinates.

  • real (array_like) – Real component of phasor coordinates to be filtered.

  • imag (array_like) – Imaginary component of phasor coordinates to be filtered.

  • repeat (int, optional) – Number of times to apply median filter. The default is 1.

  • size (int, optional) – Size of median filter kernel. The default is 3.

  • skip_axis (int or sequence of int, optional) – Axes in mean to exclude from filter. By default, all axes except harmonics are included.

  • use_scipy (bool, optional) – Use scipy.ndimage.median_filter(). This function has undefined behavior if the input arrays contain NaN values but is faster when filtering more than 2 dimensions. See issue #87.

  • num_threads (int, optional) – Number of OpenMP threads to use for parallelization. Applies to filtering in two dimensions when not using scipy. By default, multi-threading is disabled. If zero, up to half of logical CPUs are used. OpenMP may not be available on all platforms.

  • **kwargs – Optional arguments passed to scipy.ndimage.median_filter().

Returns:

  • mean (ndarray) – Unchanged intensity of phasor coordinates.

  • real (ndarray) – Filtered real component of phasor coordinates.

  • imag (ndarray) – Filtered imaginary component of phasor coordinates.

Raises:

ValueError – If repeat is less than 0. If size is less than 1. The array shapes of mean, real, and imag do not match.

Examples

Apply three times a median filter with a kernel size of three:

>>> mean, real, imag = phasor_filter_median(
...     [[1, 2, 3], [4, 5, 6], [7, 8, 9]],
...     [[0.0, 0.0, 0.0], [0.5, 0.5, 0.5], [0.2, 0.2, 0.2]],
...     [[0.3, 0.3, 0.3], [0.6, math.nan, 0.6], [0.4, 0.4, 0.4]],
...     size=3,
...     repeat=3,
... )
>>> mean
array([[1, 2, 3],
       [4, 5, 6],
       [7, 8, 9]])
>>> real
array([[0, 0, 0],
       [0.2, 0.2, 0.2],
       [0.2, 0.2, 0.2]])
>>> imag
array([[0.3, 0.3, 0.3],
       [0.4, nan, 0.4],
       [0.4, 0.4, 0.4]])
phasorpy.phasor.phasor_filter_pawflim(mean, real, imag, /, *, sigma=2.0, levels=1, harmonic=None, skip_axis=None)[source]#

Return pawFLIM wavelet-filtered phasor coordinates.

This function must only be used with calibrated, unprocessed phasor coordinates obtained from FLIM data. The coordinates must not be filtered, thresholded, or otherwise pre-processed.

The pawFLIM wavelet filter is described in [2].

Parameters:
  • mean (array_like) – Intensity of phasor coordinates.

  • real (array_like) – Real component of phasor coordinates to be filtered. Must have at least two harmonics in the first axis.

  • imag (array_like) – Imaginary component of phasor coordinates to be filtered. Must have at least two harmonics in the first axis.

  • sigma (float, optional) – Significance level to test difference between two phasors. Given in terms of the equivalent 1D standard deviations. sigma=2 corresponds to ~95% (or 5%) significance.

  • levels (int, optional) – Number of levels for wavelet decomposition. Controls the maximum averaging area, which has a length of \(2^level\).

  • harmonic (sequence of int or None, optional) – Harmonics included in first axis of real and imag. If None (default), the first axis of real and imag contains lower harmonics starting at and increasing by one. All harmonics must have a corresponding half or double harmonic.

  • skip_axis (int or sequence of int, optional) – Axes in mean to exclude from filter. By default, all axes except harmonics are included.

Returns:

  • mean (ndarray) – Unchanged intensity of phasor coordinates.

  • real (ndarray) – Filtered real component of phasor coordinates.

  • imag (ndarray) – Filtered imaginary component of phasor coordinates.

Raises:

ValueError – If level is less than 0. The array shapes of mean, real, and imag do not match. If real and imag have no harmonic axis. Number of harmonics in harmonic is less than 2 or does not match the first axis of real and imag. Not all harmonics in harmonic have a corresponding half or double harmonic.

References

Examples

Apply a pawFLIM wavelet filter with four significance levels (sigma) and three decomposition levels:

>>> mean, real, imag = phasor_filter_pawflim(
...     [[1, 1], [1, 1]],
...     [[[0.5, 0.8], [0.5, 0.8]], [[0.2, 0.4], [0.2, 0.4]]],
...     [[[0.5, 0.4], [0.5, 0.4]], [[0.4, 0.5], [0.4, 0.5]]],
...     sigma=4,
...     levels=3,
...     harmonic=[1, 2],
... )
>>> mean
array([[1, 1],
       [1, 1]])
>>> real
array([[[0.65, 0.65],
        [0.65, 0.65]],
       [[0.3, 0.3],
        [0.3, 0.3]]])
>>> imag
array([[[0.45, 0.45],
        [0.45, 0.45]],
       [[0.45, 0.45],
        [0.45, 0.45]]])
phasorpy.phasor.phasor_from_polar(phase, modulation, /, **kwargs)[source]#

Return phasor coordinates from polar coordinates.

Parameters:
Returns:

  • real (ndarray) – Real component of phasor coordinates.

  • imag (ndarray) – Imaginary component of phasor coordinates.

Notes

The polar coordinates phase (\(\phi\)) and modulation (\(M\)) are converted to phasor coordinates real (\(G\)) and imag (\(S\)) according to:

\[ \begin{align}\begin{aligned}G &= M \cdot \cos{\phi}\\S &= M \cdot \sin{\phi}\end{aligned}\end{align} \]

Examples

Calculate phasor coordinates from three polar coordinates:

>>> phasor_from_polar(
...     [0.0, math.pi / 4, math.pi / 2], [1.0, math.sqrt(0.5), 1.0]
... )
(array([1, 0.5, 0.0]), array([0, 0.5, 1]))
phasorpy.phasor.phasor_from_signal(signal, /, *, axis=None, harmonic=None, sample_phase=None, use_fft=None, rfft=None, dtype=None, normalize=True, num_threads=None)[source]#

Return phasor coordinates from signal.

Parameters:
  • signal (array_like) – Frequency-domain, time-domain, or hyperspectral data. A minimum of three samples are required along axis. The samples must be uniformly spaced.

  • axis (int or str, optional) – Axis over which to compute phasor coordinates. By default, the ‘H’ or ‘C’ axes if signal contains such dimension names, else the last axis (-1).

  • harmonic (int, sequence of int, or 'all', optional) – Harmonics to return. If ‘all’, return all harmonics for signal samples along axis. Else, harmonics must be at least one and no larger than half the number of signal samples along axis. The default is the first harmonic (fundamental frequency). A minimum of harmonic * 2 + 1 samples are required along axis to calculate correct phasor coordinates at harmonic.

  • sample_phase (array_like, optional) – Phase values (in radians) of signal samples along axis. If None (default), samples are assumed to be uniformly spaced along one period. The array size must equal the number of samples along axis. Cannot be used with harmonic!=1 or use_fft=True.

  • use_fft (bool, optional) – If true, use a real forward Fast Fourier Transform (FFT). If false, use a Cython implementation that is optimized (faster and resource saving) for calculating few harmonics. By default, FFT is only used when all or at least 8 harmonics are calculated, or rfft is specified.

  • rfft (callable, optional) – Drop-in replacement function for numpy.fft.rfft. For example, scipy.fft.rfft or mkl_fft._numpy_fft.rfft. Used to calculate the real forward FFT.

  • dtype (dtype_like, optional) – Data type of output arrays. Either float32 or float64. The default is float64 unless the signal is float32.

  • normalize (bool, optional) – Return normalized phasor coordinates. If true (default), return average of signal along axis and Fourier coefficients divided by sum of signal along axis. Else, return sum of signal along axis and unscaled Fourier coefficients. Un-normalized phasor coordinates cannot be used with most of PhasorPy’s functions but may be required for intermediate processing.

  • num_threads (int, optional) – Number of OpenMP threads to use for parallelization when not using FFT. By default, multi-threading is disabled. If zero, up to half of logical CPUs are used. OpenMP may not be available on all platforms.

Returns:

  • mean (ndarray) – Average of signal along axis (zero harmonic).

  • real (ndarray) – Real component of phasor coordinates at harmonic along axis.

  • imag (ndarray) – Imaginary component of phasor coordinates at harmonic along axis.

Raises:
  • ValueError – The signal has less than three samples along axis. The sample_phase size does not equal the number of samples along axis.

  • IndexErrorharmonic is smaller than 1 or greater than half the samples along axis.

  • TypeError – The signal, dtype, or harmonic types are not supported.

Notes

The normalized phasor coordinates real (\(G\)), imag (\(S\)), and average intensity mean (\(F_{DC}\)) are calculated from \(K \ge 3\) samples of the signal \(F\) at harmonic \(h\) according to:

\[ \begin{align}\begin{aligned}F_{DC} &= \frac{1}{K} \sum_{k=0}^{K-1} F_{k}\\G &= \frac{1}{K} \sum_{k=0}^{K-1} F_{k} \cos{\left (2 \pi h \frac{k}{K} \right )} \cdot \frac{1}{F_{DC}}\\S &= \frac{1}{K} \sum_{k=0}^{K-1} F_{k} \sin{\left (2 \pi h \frac{k}{K} \right )} \cdot \frac{1}{F_{DC}}\end{aligned}\end{align} \]

If \(F_{DC} = 0\), the phasor coordinates are undefined (resulting in NaN or infinity). Use NaN-aware software to further process the phasor coordinates.

The phasor coordinates may be zero, for example, in case of only constant background in time-resolved signals, or as the result of linear combination of non-zero spectral phasors coordinates.

Examples

Calculate phasor coordinates of a phase-shifted sinusoidal waveform:

>>> sample_phase = numpy.linspace(0, 2 * math.pi, 5, endpoint=False)
>>> signal = 1.1 * (numpy.cos(sample_phase - 0.785398) * 2 * 0.707107 + 1)
>>> phasor_from_signal(signal)
(array(1.1), array(0.5), array(0.5))

The sinusoidal signal does not have a second harmonic component:

>>> phasor_from_signal(signal, harmonic=2)
(array(1.1), array(0.0), array(0.0))
phasorpy.phasor.phasor_multiply(real, imag, factor_real, factor_imag, /, **kwargs)[source]#

Return complex multiplication of two phasors.

Complex multiplication can be used, for example, to convolve two signals such as exponential decay and instrument response functions.

Parameters:
  • real (array_like) – Real component of phasor coordinates to multiply.

  • imag (array_like) – Imaginary component of phasor coordinates to multiply.

  • factor_real (array_like) – Real component of phasor coordinates to multiply by.

  • factor_imag (array_like) – Imaginary component of phasor coordinates to multiply by.

  • **kwargs

    Optional arguments passed to numpy universal functions.

Returns:

  • real (ndarray) – Real component of complex multiplication.

  • imag (ndarray) – Imaginary component of complex multiplication.

Notes

The phasor coordinates real (\(G\)) and imag (\(S\)) are multiplied by phasor coordinates factor_real (\(g\)) and factor_imag (\(s\)) according to:

\[ \begin{align}\begin{aligned}G' &= G \cdot g - S \cdot s\\S' &= G \cdot s + S \cdot g\end{aligned}\end{align} \]

Examples

Multiply two sets of phasor coordinates:

>>> phasor_multiply([0.1, 0.2], [0.3, 0.4], [0.5, 0.6], [0.7, 0.8])
(array([-0.16, -0.2]), array([0.22, 0.4]))
phasorpy.phasor.phasor_nearest_neighbor(real, imag, neighbor_real, neighbor_imag, /, *, values=None, dtype=None, distance_max=None, num_threads=None)[source]#

Return indices or values of nearest neighbors from other coordinates.

For each phasor coordinate, find the nearest neighbor in another set of phasor coordinates and return its flat index. If more than one neighbor has the same distance, return the smallest index.

For phasor coordinates that are NaN, or have a distance to the nearest neighbor that is larger than distance_max, return an index of -1.

If values are provided, return the values corresponding to the nearest neighbor coordinates instead of indices. Return NaN values for indices that are -1.

This function does not support multi-harmonic, multi-channel, or multi-frequency phasor coordinates.

Parameters:
  • real (array_like) – Real component of phasor coordinates.

  • imag (array_like) – Imaginary component of phasor coordinates.

  • neighbor_real (array_like) – Real component of neighbor phasor coordinates.

  • neighbor_imag (array_like) – Imaginary component of neighbor phasor coordinates.

  • values (array_like, optional) – Array of values corresponding to neighbor coordinates. If provided, return the values corresponding to the nearest neighbor coordinates.

  • distance_max (float, optional) – Maximum Euclidean distance to consider a neighbor valid. By default, all neighbors are considered.

  • dtype (dtype_like, optional) – Floating point data type used for calculation and output values. Either float32 or float64. The default is float64.

  • num_threads (int, optional) – Number of OpenMP threads to use for parallelization. By default, multi-threading is disabled. If zero, up to half of logical CPUs are used. OpenMP may not be available on all platforms.

Returns:

nearest – Flat indices (or the corresponding values if provided) of the nearest neighbor coordinates.

Return type:

ndarray

Raises:

ValueError – If the shapes of real, and imag do not match. If the shapes of neighbor_real and neighbor_imag do not match. If the shapes of values and neighbor_real do not match. If distance_max is less than or equal to zero.

Notes

This function uses linear search, which is inefficient for large number of coordinates or neighbors. scipy.spatial.KDTree.query() would be more efficient in those cases. However, KDTree is known to return non-deterministic results in case of multiple neighbors with the same distance.

Examples

>>> phasor_nearest_neighbor(
...     [0.1, 0.5, numpy.nan],
...     [0.1, 0.5, numpy.nan],
...     [0, 0.4],
...     [0, 0.4],
...     values=[10, 20],
... )
array([10, 20, nan])
phasorpy.phasor.phasor_normalize(mean_unnormalized, real_unnormalized, imag_unnormalized, /, samples=1, dtype=None)[source]#

Return normalized phasor coordinates.

Use to normalize the phasor coordinates returned by phasor_from_signal(..., normalize=False).

Parameters:
  • mean_unnormalized (array_like) – Unnormalized intensity of phasor coordinates.

  • real_unnormalized (array_like) – Unnormalized real component of phasor coordinates.

  • imag_unnormalized (array_like) – Unnormalized imaginary component of phasor coordinates.

  • samples (int, default: 1) – Number of signal samples over which mean was integrated.

  • dtype (dtype_like, optional) – Data type of output arrays. Either float32 or float64. The default is float64 unless the real is float32.

Returns:

  • mean (ndarray) – Normalized intensity.

  • real (ndarray) – Normalized real component.

  • imag (ndarray) – Normalized imaginary component.

Notes

The average intensity mean (\(F_{DC}\)) and normalized phasor coordinates real (\(G\)) and imag (\(S\)) are calculated from the signal intensity (\(F\)), the number of samples (\(K\)), real_unnormalized (\(G'\)), and imag_unnormalized (\(S'\)) according to:

\[ \begin{align}\begin{aligned}F_{DC} &= F / K\\G &= G' / F\\S &= S' / F\end{aligned}\end{align} \]

If \(F = 0\), the normalized phasor coordinates (\(G\)) and (\(S\)) are undefined (NaN or infinity).

Examples

Normalize phasor coordinates with intensity integrated over 10 samples:

>>> phasor_normalize([0.0, 0.1], [0.0, 0.3], [0.4, 0.5], samples=10)
(array([0, 0.01]), array([nan, 3]), array([inf, 5]))

Normalize multi-harmonic phasor coordinates:

>>> phasor_normalize(0.1, [0.0, 0.3], [0.4, 0.5], samples=10)
(array(0.01), array([0, 3]), array([4, 5]))
phasorpy.phasor.phasor_threshold(mean, real, imag, /, mean_min=None, mean_max=None, *, real_min=None, real_max=None, imag_min=None, imag_max=None, phase_min=None, phase_max=None, modulation_min=None, modulation_max=None, open_interval=False, detect_harmonics=True, **kwargs)[source]#

Return phasor coordinates with values outside interval replaced by NaN.

Interval thresholds can be set for mean intensity, real and imaginary coordinates, and phase and modulation. Phasor coordinates smaller than minimum thresholds or larger than maximum thresholds are replaced with NaN. No threshold is applied by default. NaNs in mean or any real and imag harmonic are propagated to mean and all harmonics in real and imag.

Parameters:
  • mean (array_like) – Intensity of phasor coordinates.

  • real (array_like) – Real component of phasor coordinates.

  • imag (array_like) – Imaginary component of phasor coordinates.

  • mean_min (array_like, optional) – Lower threshold for mean intensity.

  • mean_max (array_like, optional) – Upper threshold for mean intensity.

  • real_min (array_like, optional) – Lower threshold for real coordinates.

  • real_max (array_like, optional) – Upper threshold for real coordinates.

  • imag_min (array_like, optional) – Lower threshold for imaginary coordinates.

  • imag_max (array_like, optional) – Upper threshold for imaginary coordinates.

  • phase_min (array_like, optional) – Lower threshold for phase angle.

  • phase_max (array_like, optional) – Upper threshold for phase angle.

  • modulation_min (array_like, optional) – Lower threshold for modulation.

  • modulation_max (array_like, optional) – Upper threshold for modulation.

  • open_interval (bool, optional) – If true, the interval is open, and the threshold values are not included in the interval. If false (default), the interval is closed, and the threshold values are included in the interval.

  • detect_harmonics (bool, optional) – By default, detect presence of multiple harmonics from array shapes. If false, no harmonics are assumed to be present, and the function behaves like a numpy universal function.

  • **kwargs

    Optional arguments passed to numpy universal functions.

Returns:

  • mean (ndarray) – Thresholded intensity of phasor coordinates.

  • real (ndarray) – Thresholded real component of phasor coordinates.

  • imag (ndarray) – Thresholded imaginary component of phasor coordinates.

Examples

Set phasor coordinates to NaN if mean intensity is smaller than 1.1:

>>> phasor_threshold([1, 2, 3], [0.1, 0.2, 0.3], [0.4, 0.5, 0.6], 1.1)
(array([nan, 2, 3]), array([nan, 0.2, 0.3]), array([nan, 0.5, 0.6]))

Set phasor coordinates to NaN if real component is smaller than 0.15 or larger than 0.25:

>>> phasor_threshold(
...     [1.0, 2.0, 3.0],
...     [0.1, 0.2, 0.3],
...     [0.4, 0.5, 0.6],
...     real_min=0.15,
...     real_max=0.25,
... )
(array([nan, 2, nan]), array([nan, 0.2, nan]), array([nan, 0.5, nan]))

Apply NaNs to other input arrays:

>>> phasor_threshold(
...     [numpy.nan, 2, 3], [0.1, 0.2, 0.3], [0.4, 0.5, numpy.nan]
... )
(array([nan, 2, nan]), array([nan, 0.2, nan]), array([nan, 0.5, nan]))
phasorpy.phasor.phasor_to_complex(real, imag, /, *, dtype=None)[source]#

Return phasor coordinates as complex numbers.

Parameters:
  • real (array_like) – Real component of phasor coordinates.

  • imag (array_like) – Imaginary component of phasor coordinates.

  • dtype (dtype_like, optional) – Data type of output array. Either complex64 or complex128. By default, complex64 if real and imag are float32, else complex128.

Returns:

complex – Phasor coordinates as complex numbers.

Return type:

ndarray

Examples

Convert phasor coordinates to complex number arrays:

>>> phasor_to_complex([0.4, 0.5], [0.2, 0.3])
array([0.4+0.2j, 0.5+0.3j])
phasorpy.phasor.phasor_to_polar(real, imag, /, **kwargs)[source]#

Return polar coordinates from phasor coordinates.

Parameters:

Notes

The phasor coordinates real (\(G\)) and imag (\(S\)) are converted to polar coordinates phase (\(\phi\)) and modulation (\(M\)) according to:

\[ \begin{align}\begin{aligned}\phi &= \arctan(S / G)\\M &= \sqrt{G^2 + S^2}\end{aligned}\end{align} \]
Returns:

  • phase (ndarray) – Angular component of polar coordinates in radians.

  • modulation (ndarray) – Radial component of polar coordinates.

Examples

Calculate polar coordinates from three phasor coordinates:

>>> phasor_to_polar([1.0, 0.5, 0.0], [0.0, 0.5, 1.0])
(array([0, 0.7854, 1.571]), array([1, 0.7071, 1]))
phasorpy.phasor.phasor_to_principal_plane(real, imag, /, *, reorient=True)[source]#

Return multi-harmonic phasor coordinates projected onto principal plane.

Principal component analysis (PCA) is used to project multi-harmonic phasor coordinates onto a plane, along which coordinate axes the phasor coordinates have the largest variations.

The transformed coordinates are not phasor coordinates. However, the coordinates can be used in visualization and cursor analysis since the transformation is affine (preserving collinearity and ratios of distances).

Parameters:
  • real (array_like) – Real component of multi-harmonic phasor coordinates. The first axis is the frequency dimension. If less than 2-dimensional, size-1 dimensions are prepended.

  • imag (array_like) – Imaginary component of multi-harmonic phasor coordinates. Must be of same shape as real.

  • reorient (bool, optional, default: True) – Reorient coordinates for easier visualization. The projected coordinates are rotated and scaled, such that the center lies in same quadrant and the projection of [1, 0] lies at [1, 0].

Returns:

  • x (ndarray) – X-coordinates of projected phasor coordinates. If not reorient, this is the coordinate on the first principal axis. The shape is real.shape[1:].

  • y (ndarray) – Y-coordinates of projected phasor coordinates. If not reorient, this is the coordinate on the second principal axis.

  • transformation_matrix (ndarray) – Affine transformation matrix used to project phasor coordinates. The shape is (2, 2 * real.shape[0]).

Notes

This implementation does not work with coordinates containing undefined NaN values.

The transformation matrix can be used to project multi-harmonic phasor coordinates, where the first axis is the frequency:

x, y = numpy.dot(
    numpy.vstack(
        real.reshape(real.shape[0], -1),
        imag.reshape(imag.shape[0], -1),
    ),
    transformation_matrix,
).reshape(2, *real.shape[1:])

An application of PCA to full-harmonic phasor coordinates from MRI signals can be found in [1].

References

Examples

The phasor coordinates of multi-exponential decays may be almost indistinguishable at certain frequencies but are separated in the projection on the principal plane:

>>> real = [[0.495, 0.502], [0.354, 0.304]]
>>> imag = [[0.333, 0.334], [0.301, 0.349]]
>>> x, y, transformation_matrix = phasor_to_principal_plane(real, imag)
>>> x, y
(array([0.294, 0.262]), array([0.192, 0.242]))
>>> transformation_matrix
array([[0.67, 0.33, -0.09, -0.41], [0.52, -0.52, -0.04, 0.44]])
phasorpy.phasor.phasor_to_signal(mean, real, imag, /, *, samples=64, harmonic=None, axis=-1, irfft=None)[source]#

Return signal from phasor coordinates using inverse Fourier transform.

Parameters:
  • mean (array_like) – Average signal intensity (DC). If not scalar, shape must match the last dimensions of real.

  • real (array_like) – Real component of phasor coordinates. Multiple harmonics, if any, must be in the first axis.

  • imag (array_like) – Imaginary component of phasor coordinates. Must be same shape as real.

  • samples (int, default: 64) – Number of signal samples to return. Must be at least three.

  • harmonic (int, sequence of int, or 'all', optional) – Harmonics included in first axis of real and imag. If None, lower harmonics are inferred from the shapes of phasor coordinates (most commonly, lower harmonics are present if the number of dimensions of mean is one less than real). If ‘all’, the harmonics in the first axis of phasor coordinates are the lower harmonics necessary to synthesize samples. Else, harmonics must be at least one and no larger than half of samples. The phasor coordinates of missing harmonics are zeroed if samples is greater than twice the number of harmonics.

  • axis (int, optional) – Axis at which to return signal samples. The default is the last axis (-1).

  • irfft (callable, optional) – Drop-in replacement function for numpy.fft.irfft. For example, scipy.fft.irfft or mkl_fft._numpy_fft.irfft. Used to calculate the real inverse FFT.

Returns:

signal – Reconstructed signal with samples of one period along the last axis.

Return type:

ndarray

Notes

The reconstructed signal may be undefined if the input phasor coordinates, or signal mean contain NaN values.

Examples

Reconstruct exact signal from phasor coordinates at all harmonics:

>>> sample_phase = numpy.linspace(0, 2 * math.pi, 5, endpoint=False)
>>> signal = 1.1 * (numpy.cos(sample_phase - 0.785398) * 2 * 0.707107 + 1)
>>> signal
array([2.2, 2.486, 0.8566, -0.4365, 0.3938])
>>> phasor_to_signal(
...     *phasor_from_signal(signal, harmonic='all'),
...     harmonic='all',
...     samples=len(signal)
... )
array([2.2, 2.486, 0.8566, -0.4365, 0.3938])

Reconstruct a single-frequency waveform from phasor coordinates at first harmonic:

>>> phasor_to_signal(1.1, 0.5, 0.5, samples=5)
array([2.2, 2.486, 0.8566, -0.4365, 0.3938])
phasorpy.phasor.phasor_transform(real, imag, phase=0.0, modulation=1.0, /, **kwargs)[source]#

Return rotated and scaled phasor coordinates.

This function rotates and uniformly scales phasor coordinates around the origin. It can be used, for example, to calibrate phasor coordinates.

Parameters:
  • real (array_like) – Real component of phasor coordinates to transform.

  • imag (array_like) – Imaginary component of phasor coordinates to transform.

  • phase (array_like, optional, default: 0.0) – Rotation angle in radians.

  • modulation (array_like, optional, default: 1.0) – Uniform scale factor.

  • **kwargs

    Optional arguments passed to numpy universal functions.

Returns:

  • real (ndarray) – Real component of rotated and scaled phasor coordinates.

  • imag (ndarray) – Imaginary component of rotated and scaled phasor coordinates.

Notes

The phasor coordinates real (\(G\)) and imag (\(S\)) are rotated by phase (\(\phi\)) and scaled by modulation_zero (\(M\)) around the origin according to:

\[ \begin{align}\begin{aligned}g &= M \cdot \cos{\phi}\\s &= M \cdot \sin{\phi}\\G' &= G \cdot g - S \cdot s\\S' &= G \cdot s + S \cdot g\end{aligned}\end{align} \]

Examples

Use scalar reference coordinates to rotate and scale phasor coordinates:

>>> phasor_transform(
...     [0.1, 0.2, 0.3], [0.4, 0.5, 0.6], 0.1, 0.5
... )
(array([0.0298, 0.0745, 0.119]), array([0.204, 0.259, 0.3135]))

Use separate reference coordinates for each phasor coordinate:

>>> phasor_transform(
...     [0.1, 0.2, 0.3], [0.4, 0.5, 0.6], [0.2, 0.2, 0.3], [0.5, 0.2, 0.3]
... )
(array([0.00927, 0.0193, 0.0328]), array([0.206, 0.106, 0.1986]))