phasorpy.phasor#
Process phasor coordinates.
The phasorpy.phasor module provides fundamental functions to:
calculate phasor coordinates from time-resolved and spectral signals:
synthesize signals from phasor coordinates:
convert between phasor and polar coordinates (phase and modulation):
convert phasor coordinates to complex number representation:
transform phasor coordinates:
linearly combine two phasor coordinates:
reduce the dimensionality of arrays of phasor coordinates:
find nearest-neighbor phasor coordinates to other phasor coordinates:
- 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 be 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 that method is applied to same elements of input arrays. By default, NaNs are distributed among input arrays before applying method. This may be disabled if the phasor coordinates were previously filtered by
phasor_threshold().**kwargs – Optional arguments passed to
numpy.nanmean()ornumpy.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_combine(int0, real0, imag0, int1, real1, imag1, fraction0, fraction1=None, **kwargs)[source]#
Return linear combination of two phasor coordinates.
Combine two sets of phasor coordinates using intensity-weighted mixing. This simulates the phasor coordinates that would result from a mixture of two components with known individual phasor coordinates.
- Parameters:
int0 (array_like) – Intensity of first phasor coordinates.
real0 (array_like) – Real component of first phasor coordinates.
imag0 (array_like) – Imaginary component of first phasor coordinates.
int1 (array_like) – Intensity of second phasor coordinates.
real1 (array_like) – Real component of second phasor coordinates.
imag1 (array_like) – Imaginary component of second phasor coordinates.
fraction0 (array_like) – Fraction of first phasor coordinates.
fraction1 (array_like, optional) – Fraction of second phasor coordinates. By default, fraction1 is set to 1 - fraction0.
**kwargs – Optional arguments passed to numpy universal functions.
- Returns:
intensity (ndarray) – Intensity of the linearly combined phasor coordinates.
real (ndarray) – Real component of the linearly combined phasor coordinates.
imag (ndarray) – Imaginary component of the linearly combined phasor coordinates.
Notes
The phasor coordinates (\(I\), \(G\), \(S\)) of the linear combination of two phasor coordinates (\(I_{0}\), \(G_{0}\), \(S_{0}\), and \(I_{1}\), \(G_{1}\), \(S_{1}\)) with fractions \(f_{0}\) and \(f_{1}\) of the first and second coordinates are:
\[ \begin{align}\begin{aligned}f'_{0} &= f_{0} / (f_{0} + f_{1})\\f'_{1} &= 1 - f'_{0}\\I &= I_{0} \cdot f'_{0} + I_{1} \cdot f'_{1}\\G &= (G_{0} \cdot I_{0} \cdot f'_{0} + G_{1} \cdot I_{1} \cdot f'_{1}) / I\\S &= (S_{0} \cdot I_{0} \cdot f'_{0} + S_{1} \cdot I_{1} \cdot f'_{1}) / I\end{aligned}\end{align} \]If the intensity \(I\) is zero, the linearly combined phasor coordinates are undefined (NaN).
Examples
Calculate the linear combination of two phasor coordinates with equal intensities at three fractional intensities:
>>> phasor_combine(1.0, 0.6, 0.3, 1.0, 0.4, 0.2, [1.0, 0.2, 0.9]) (array([1, 1, 1]), array([0.6, 0.44, 0.58]), array([0.3, 0.22, 0.29]))
- 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_from_polar(phase, modulation, /, **kwargs)[source]#
Return phasor coordinates from polar coordinates.
- Parameters:
phase (array_like) – Angular component of polar coordinates in radians.
modulation (array_like) – Radial component of polar coordinates.
**kwargs –
Optional arguments passed to numpy universal functions.
- Returns:
real (ndarray) – Real component of phasor coordinates.
imag (ndarray) – Imaginary component of phasor coordinates.
See also
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’ axis if signal contains such dimension names, else the last axis (-1).
harmonic (int, sequence of int, or 'all', optional) – Harmonics for which to return phasor coordinates. 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. By default, samples are assumed to be uniformly spaced along one period. The array length must equal the number of samples along axis. Cannot be used with harmonic!=1 or use_fft=True.
use_fft (bool, optional) – Use a real-valued forward Fast Fourier Transform (FFT). Else, use a Cython implementation that is optimized (faster and memory-efficient) 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.rfftormkl_fft.interfaces.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, default: True) – Return normalized phasor coordinates. 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. Unnormalized phasor coordinates cannot be used with most of PhasorPy’s functions (which expect normalized coordinates) 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, multithreading 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.
IndexError – harmonic is smaller than 1 or greater than half the samples along axis.
TypeError – The signal, dtype, or harmonic types are not supported.
See also
phasorpy.phasor.phasor_to_signal,phasorpy.phasor.phasor_normalize, Benchmark phasor_from_signalNotes
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 the case of only constant background in time-resolved signals, or as the result of a linear combination of non-zero spectral phasor 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 is 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, multithreading 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.
See also
Notes
This function uses a linear search, which is inefficient for a 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 when multiple neighbors have 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 this 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, optional, default: 1) – Number of signal samples over which mean_unnormalized was integrated.
dtype (dtype_like, optional) – Data type of output arrays. Either float32 or float64. The default is float64 unless real_unnormalized 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 mean_unnormalized (\(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_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 an array of complex numbers:
>>> 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:
real (array_like) – Real component of phasor coordinates.
imag (array_like) – Imaginary component of phasor coordinates.
**kwargs –
Optional arguments passed to numpy universal functions.
- Returns:
phase (ndarray) – Angular component of polar coordinates in radians.
modulation (ndarray) – Radial component of polar coordinates.
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 &= \operatorname{atan2}(S, G)\\M &= \sqrt{G^2 + S^2}\end{aligned}\end{align} \]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 whose two coordinate axes (the first two principal components) capture the largest variance.
The transformed coordinates are no longer phasor coordinates. However, they can be used for visualization and cursor analysis because the transformation is affine (it preserves collinearity and distance ratios).
- Parameters:
real (array_like) – Real component of multi-harmonic phasor coordinates. The first axis is the frequency dimension. If less than two-dimensional, length-1 dimensions are prepended.
imag (array_like) – Imaginary component of multi-harmonic phasor coordinates. Must have the 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 the same quadrant and the projection of [1, 0] lies at [1, 0].
- Returns:
x (ndarray) – X-coordinates of projected phasor coordinates. If reorient is False, this is the coordinate on the first principal component axis. The shape is
real.shape[1:].y (ndarray) – Y-coordinates of projected phasor coordinates. If reorient is False, this is the coordinate on the second principal component axis. The shape is
real.shape[1:].transformation_matrix (ndarray) – Affine transformation matrix used to project phasor coordinates. The shape is
(2, 2 * real.shape[0]).
See also
Notes
This implementation does not work with coordinates containing undefined values (NaN).
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 is described 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, optional, 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. By default, 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 set to zero if samples is greater than twice the number of harmonics.
axis (int, optional, default: -1) – 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.irfftormkl_fft.interfaces.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
See also
Notes
The reconstructed signal may be undefined if the input phasor coordinates or the signal mean contain NaN values.
Examples
Reconstruct the 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 the 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) – Rotation angle in radians.
modulation (array_like, optional, default: 1) – 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 (\(M\)) about 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]))