phasorpy.phasor#

Calculate, convert, calibrate, and reduce phasor coordinates.

The phasorpy.phasor module provides functions to:

phasorpy.phasor.lifetime_fraction_from_amplitude(lifetime, amplitude, *, axis=-1)[source]#

Return fractional intensity from pre-exponential amplitude.

Parameters:
  • lifetime (array_like) – Lifetime of components.

  • amplitude (array_like) – Pre-exponential amplitudes of lifetime components.

  • axis (int, optional) – Axis over which to compute fractional intensities. The default is the last axis (-1).

Returns:

fraction – Fractional intensities, normalized to sum to 1 along axis.

Return type:

ndarray

Notes

The fractional intensity \(\alpha\) of component \(j\) with lifetime \(\tau\) and pre-exponential amplitude \(a\) is:

\[\alpha_{j} = \frac{a_{j} \tau_{j}}{\sum_{j} a_{j} \tau_{j}}\]

Examples

>>> lifetime_fraction_from_amplitude(
...     [4.0, 1.0], [1.0, 1.0]
... )  
array([0.8, 0.2])
phasorpy.phasor.lifetime_fraction_to_amplitude(lifetime, fraction, *, axis=-1)[source]#

Return pre-exponential amplitude from fractional intensity.

Parameters:
  • lifetime (array_like) – Lifetime components.

  • fraction (array_like) – Fractional intensities of lifetime components. Fractions are normalized to sum to 1.

  • axis (int, optional) – Axis over which to compute pre-exponential amplitudes. The default is the last axis (-1).

Returns:

amplitude – Pre-exponential amplitudes. The product of amplitude and lifetime sums to 1 along axis.

Return type:

ndarray

Notes

The pre-exponential amplitude \(a\) of component \(j\) with lifetime \(\tau\) and fractional intensity \(\alpha\) is:

\[a_{j} = \frac{\alpha_{j}}{\tau_{j} \cdot \sum_{j} \alpha_{j}}\]

Examples

>>> lifetime_fraction_to_amplitude(
...     [4.0, 1.0], [1.6, 0.4]
... )  
array([0.2, 0.2])
phasorpy.phasor.lifetime_from_frequency(frequency, *, unit_conversion=0.001)[source]#

Return single component lifetime best resolved at frequency.

Parameters:
  • frequency (array_like) – Laser pulse or modulation frequency.

  • unit_conversion (float, optional, default: 1e-3) – Product of frequency and lifetime units’ prefix factors. The default is 1e-3 for MHz and ns, or Hz and ms. Use 1.0 for Hz and s.

Returns:

lifetime – Single component lifetime best resolved at frequency.

Return type:

ndarray

Notes

The lifetime \(\tau\) that is best resolved at frequency \(f\) is (Redford & Clegg 2005. Eq. B.6):

\[ \begin{align}\begin{aligned}\omega &= 2 \pi f\\\tau^2 &= \frac{1 + \sqrt{3}}{2 \omega^2}\end{aligned}\end{align} \]

Examples

Measurements at frequencies of 47 and 186 MHz are best for measuring lifetimes near 4 and 1 ns respectively:

>>> lifetime_from_frequency([46.5, 186])  
array([4, 1])
phasorpy.phasor.lifetime_to_frequency(lifetime, *, unit_conversion=0.001)[source]#

Return optimal frequency for resolving single component lifetime.

Parameters:
  • lifetime (array_like) – Single component lifetime.

  • unit_conversion (float, optional, default: 1e-3) – Product of frequency and lifetime units’ prefix factors. The default is 1e-3 for MHz and ns, or Hz and ms. Use 1.0 for Hz and s.

Returns:

frequency – Optimal laser pulse or modulation frequency for resolving lifetime.

Return type:

ndarray

Notes

The optimal frequency \(f\) to resolve a single component lifetime \(\tau\) is (Redford & Clegg 2005. Eq. B.6):

\[ \begin{align}\begin{aligned}\omega &= 2 \pi f\\\omega^2 &= \frac{1 + \sqrt{3}}{2 \tau^2}\end{aligned}\end{align} \]

Examples

Measurements of a lifetime near 4 ns should be made at 47 MHz, near 1 ns at 186 MHz:

>>> lifetime_to_frequency([4.0, 1.0])  
array([46.5, 186])
phasorpy.phasor.lifetime_to_signal(frequency, lifetime, fraction=None, *, mean=None, background=None, samples=64, harmonic=None, zero_phase=None, zero_stdev=None, preexponential=False, unit_conversion=0.001)[source]#

Return synthetic signal from lifetime components.

Return synthetic signal, instrument response function (IRF), and time axis, sampled over one period of the fundamental frequency. The signal is convoluted with the IRF, which is approximated by a normal distribution.

Parameters:
  • frequency (float) – Fundamental laser pulse or modulation frequency in MHz.

  • lifetime (array_like) – Lifetime components in ns.

  • fraction (array_like, optional) – Fractional intensities or pre-exponential amplitudes of the lifetime components. Fractions are normalized to sum to 1. Must be specified if lifetime is not a scalar.

  • mean (array_like, optional, default: 1.0) – Average signal intensity (DC). Must be scalar for now.

  • background (array_like, optional, default: 0.0) – Background signal intensity. Must be smaller than mean.

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

  • harmonic (int, sequence of int, or 'all', optional, default: 'all') – Harmonics used to synthesize signal. If ‘all’, all harmonics are used. Else, harmonics must be at least one and no larger than half of samples. Use ‘all’ to synthesize an exponential time-domain decay signal, or 1 to synthesize a homodyne signal.

  • zero_phase (float, optional) – Position of instrument response function in radians. Must be in range 0.0 to \(\pi\). The default is the 8th sample.

  • zero_stdev (float, optional) – Standard deviation of instrument response function in radians. Must be at least 1.5 samples and no more than one tenth of samples to allow for sufficient sampling of the function. The default is 1.5 samples. Increase samples to narrow the IRF.

  • preexponential (bool, optional, default: False) – If true, fraction values are pre-exponential amplitudes, else fractional intensities.

  • unit_conversion (float, optional, default: 1e-3) – Product of frequency and lifetime units’ prefix factors. The default is 1e-3 for MHz and ns, or Hz and ms. Use 1.0 for Hz and s.

Returns:

  • signal (ndarray) – Signal generated from lifetimes at frequency, convoluted with instrument response function.

  • zero (ndarray) – Instrument response function.

  • time (ndarray) – Time for each sample in signal in units of lifetime.

Notes

This implementation is based on an inverse digital Fourier transform (DFT). Because DFT cannot be used on signals with discontinuities (for example, an exponential decay starting at zero) without producing strong artifacts (ripples), the signal is convoluted with a continuous instrument response function (IRF). The minimum width of the IRF is limited due to sampling requirements.

Examples

Synthesize a multi-exponential time-domain decay signal for two lifetime components of 4.2 and 0.9 ns at 40 MHz:

>>> signal, zero, times = lifetime_to_signal(
...     40, [4.2, 0.9], fraction=[0.8, 0.2], samples=16
... )
>>> signal  
array([0.2846, 0.1961, 0.1354, ..., 0.8874, 0.6029, 0.4135])

Synthesize a homodyne frequency-domain waveform signal for a single lifetime:

>>> signal, zero, times = lifetime_to_signal(
...     40.0, 4.2, samples=16, harmonic=1
... )
>>> signal  
array([0.2047, -0.05602, -0.156, ..., 1.471, 1.031, 0.5865])
phasorpy.phasor.phasor_at_harmonic(real, harmonic, other_harmonic, /, **kwargs)[source]#

Return phasor coordinates on universal semicircle at other harmonics.

Return phasor coordinates at any harmonic, given the real component of phasor coordinates of a single exponential lifetime at a certain harmonic. The input and output phasor coordinates lie on the universal semicircle.

Parameters:
  • real (array_like) – Real component of phasor coordinates of single exponential lifetime at harmonic.

  • harmonic (array_like) – Harmonic of real coordinate. Must be integer >= 1.

  • other_harmonic (array_like) – Harmonic for which to return phasor coordinates. Must be integer >= 1.

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

Returns:

  • real_other (ndarray) – Real component of phasor coordinates at other_harmonic.

  • imag_other (ndarray) – Imaginary component of phasor coordinates at other_harmonic.

Notes

The phasor coordinates \(g_{n}\) (real_other) and \(s_{n}\) (imag_other) of a single exponential lifetime at harmonic \(n\) (other_harmonic) is calculated from the real part of the phasor coordinates \(g_{m}\) (real) at harmonic \(m\) (harmonic) according to (Torrado, Malacrida, & Ranjit. 2022. Eq. 25):

\[ \begin{align}\begin{aligned}g_{n} &= \frac{m^2 \cdot g_{m}}{n^2 + (m^2-n^2) \cdot g_{m}}\\s_{n} &= \sqrt{G_{n} - g_{n}^2}\end{aligned}\end{align} \]

This function is equivalent to the following operations:

phasor_from_lifetime(
    frequency=other_harmonic,
    lifetime=phasor_to_apparent_lifetime(
        real, sqrt(real - real * real), frequency=harmonic
    )[0],
)

Examples

The phasor coordinates at higher harmonics are approaching the origin:

>>> phasor_at_harmonic(0.5, 1, [1, 2, 4, 8])  
(array([0.5, 0.2, 0.05882, 0.01538]), array([0.5, 0.4, 0.2353, 0.1231]))
phasorpy.phasor.phasor_calibrate(real, imag, reference_mean, reference_real, reference_imag, /, frequency, lifetime, *, harmonic=None, skip_axis=None, fraction=None, preexponential=False, unit_conversion=0.001, method='mean', nan_safe=True, reverse=False)[source]#

Return calibrated/referenced phasor coordinates.

Calibration of phasor coordinates from time-resolved measurements is necessary to account for the instrument response function (IRF) and delays in the electronics.

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

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

  • reference_mean (array_like or None) – Intensity of phasor coordinates from reference of known lifetime. Used to re-normalize averaged phasor coordinates.

  • reference_real (array_like) – Real component of phasor coordinates from reference of known lifetime. Must be measured with the same instrument setting as the phasor coordinates to be calibrated. Dimensions must be the same as real.

  • reference_imag (array_like) – Imaginary component of phasor coordinates from reference of known lifetime. Must be measured with the same instrument setting as the phasor coordinates to be calibrated.

  • frequency (array_like) – Fundamental laser pulse or modulation frequency in MHz.

  • lifetime (array_like) – Lifetime components in ns. Must be scalar or one-dimensional.

  • harmonic (int, sequence of int, or 'all', default: 1) – Harmonics included in real and imag. If an integer, the harmonics at which real and imag were acquired or calculated. If a sequence, the harmonics included in the first axis of real and imag. If ‘all’, the first axis of real and imag contains lower harmonics. The default is the first harmonic (fundamental frequency).

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

  • fraction (array_like, optional) – Fractional intensities or pre-exponential amplitudes of the lifetime components. Fractions are normalized to sum to 1. Must be same size as lifetime.

  • preexponential (bool, optional) – If true, fraction values are pre-exponential amplitudes, else fractional intensities (default).

  • unit_conversion (float, optional) – Product of frequency and lifetime units’ prefix factors. The default is 1e-3 for MHz and ns, or Hz and ms. Use 1.0 for Hz and s.

  • method (str, optional) –

    Method used for calculating center of reference phasor coordinates:

    • 'mean': Arithmetic mean.

    • 'median': Spatial median.

  • nan_safe (bool, optional) – Ensure method is applied to same elements of reference arrays. By default, distribute NaNs among reference arrays before applying method.

  • reverse (bool, optional) – Reverse calibration.

Returns:

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

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

Raises:

ValueError – The array shapes of real and imag, or reference_real and reference_imag do not match. Number of harmonics does not match the first axis of real and imag.

Notes

This function is a convenience wrapper for the following operations:

phasor_transform(
    real,
    imag,
    *polar_from_reference_phasor(
        *phasor_center(
            reference_mean,
            reference_real,
            reference_imag,
            skip_axis,
            method,
            nan_safe,
        )[1:],
        *phasor_from_lifetime(
            frequency,
            lifetime,
            fraction,
            preexponential,
            unit_conversion,
        ),
    ),
)

Calibration can be reversed such that

real, imag == phasor_calibrate(
    *phasor_calibrate(real, imag, *args, **kwargs),
    *args,
    reverse=True,
    **kwargs
)

Examples

>>> phasor_calibrate(
...     [0.1, 0.2, 0.3],
...     [0.4, 0.5, 0.6],
...     [1.0, 1.0, 1.0],
...     [0.2, 0.3, 0.4],
...     [0.5, 0.6, 0.7],
...     frequency=80,
...     lifetime=4,
... )  
(array([0.0658, 0.132, 0.198]), array([0.2657, 0.332, 0.399]))

Undo the previous calibration:

>>> phasor_calibrate(
...     [0.0658, 0.132, 0.198],
...     [0.2657, 0.332, 0.399],
...     [1.0, 1.0, 1.0],
...     [0.2, 0.3, 0.4],
...     [0.5, 0.6, 0.7],
...     frequency=80,
...     lifetime=4,
...     reverse=True,
... )  
(array([0.1, 0.2, 0.3]), array([0.4, 0.5, 0.6]))
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_from_apparent_lifetime(phase_lifetime, modulation_lifetime, /, frequency, *, unit_conversion=0.001, **kwargs)[source]#

Return phasor coordinates from apparent single lifetimes.

Parameters:
  • phase_lifetime (ndarray) – Apparent single lifetime from phase.

  • modulation_lifetime (ndarray, optional) – Apparent single lifetime from modulation. If None, modulation_lifetime is same as phase_lifetime.

  • frequency (array_like) – Laser pulse or modulation frequency in MHz.

  • unit_conversion (float, optional) – Product of frequency and lifetime units’ prefix factors. The default is 1e-3 for MHz and ns, or Hz and ms. Use 1.0 for Hz and s.

  • **kwargs

    Optional arguments passed to numpy universal functions.

Returns:

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

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

Notes

The apparent single lifetimes phase_lifetime (\(\tau_{\phi}\)) and modulation_lifetime (\(\tau_{M}\)) are converted to phasor coordinates real (\(G\)) and imag (\(S\)) at frequency \(f\) according to:

\[ \begin{align}\begin{aligned}\omega &= 2 \pi f\\\phi & = \arctan(\omega \tau_{\phi})\\M &= 1 / \sqrt{1 + (\omega \tau_{M})^2}\\G &= M \cdot \cos{\phi}\\S &= M \cdot \sin{\phi}\end{aligned}\end{align} \]

Examples

If the apparent single lifetimes from phase and modulation are equal, the phasor coordinates lie on the universal semicircle, else inside:

>>> phasor_from_apparent_lifetime(
...     1.9894, [1.9894, 2.4113], frequency=80.0
... )  
(array([0.5, 0.45]), array([0.5, 0.45]))

Zero and infinite apparent single lifetimes define the endpoints of the universal semicircle:

>>> phasor_from_apparent_lifetime(
...     [0.0, 1e9], [0.0, 1e9], frequency=80
... )  
(array([1, 0.0]), array([0, 0.0]))
phasorpy.phasor.phasor_from_fret_acceptor(frequency, donor_lifetime, acceptor_lifetime, *, fret_efficiency=0.0, donor_freting=1.0, donor_bleedthrough=0.0, acceptor_bleedthrough=0.0, acceptor_background=0.0, background_real=0.0, background_imag=0.0, unit_conversion=0.001, **kwargs)[source]#

Return phasor coordinates of FRET acceptor channel.

Calculate phasor coordinates of a FRET (Förster Resonance Energy Transfer) acceptor channel as a function of frequency, donor and acceptor lifetimes, FRET efficiency, fraction of donors undergoing FRET, fraction of directly excited acceptors, fraction of donor fluorescence in acceptor channel, and background fluorescence.

The phasor coordinates of the acceptor channel contain fractions of:

  • acceptor sensitized by energy transfer

  • directly excited acceptor

  • donor bleedthrough

  • background fluorescence

Parameters:
  • frequency (array_like) – Laser pulse or modulation frequency in MHz.

  • donor_lifetime (array_like) – Lifetime of donor without FRET in ns.

  • acceptor_lifetime (array_like) – Lifetime of acceptor in ns.

  • fret_efficiency (array_like, optional, default 0) – FRET efficiency in range [0..1].

  • donor_freting (array_like, optional, default 1) – Fraction of donors participating in FRET. Range [0..1].

  • donor_bleedthrough (array_like, optional, default 0) – Weight of donor fluorescence in acceptor channel relative to fluorescence of fully sensitized acceptor. A weight of 1 means the fluorescence from donor and fully sensitized acceptor are equal. The background in the donor channel does not bleed through.

  • acceptor_bleedthrough (array_like, optional, default 0) – Weight of fluorescence from directly excited acceptor relative to fluorescence of fully sensitized acceptor. A weight of 1 means the fluorescence from directly excited acceptor and fully sensitized acceptor are equal.

  • acceptor_background (array_like, optional, default 0) – Weight of background fluorescence in acceptor channel relative to fluorescence of fully sensitized acceptor. A weight of 1 means the fluorescence of background and fully sensitized acceptor are equal.

  • background_real (array_like, optional, default 0) – Real component of background fluorescence phasor coordinate at frequency.

  • background_imag (array_like, optional, default 0) – Imaginary component of background fluorescence phasor coordinate at frequency.

  • unit_conversion (float, optional) – Product of frequency and lifetime units’ prefix factors. The default is 1e-3 for MHz and ns, or Hz and ms. Use 1.0 for Hz and s.

  • **kwargs

    Optional arguments passed to numpy universal functions.

Returns:

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

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

Examples

Compute the phasor coordinates of a FRET acceptor channel at three FRET efficiencies:

>>> phasor_from_fret_acceptor(
...     frequency=80,
...     donor_lifetime=4.2,
...     acceptor_lifetime=3.0,
...     fret_efficiency=[0.0, 0.3, 1.0],
...     donor_freting=0.9,
...     donor_bleedthrough=0.1,
...     acceptor_bleedthrough=0.1,
...     acceptor_background=0.1,
...     background_real=0.11,
...     background_imag=0.12,
... )  
(array([0.1996, 0.05772, 0.2867]), array([0.3225, 0.3103, 0.4292]))
phasorpy.phasor.phasor_from_fret_donor(frequency, donor_lifetime, *, fret_efficiency=0.0, donor_freting=1.0, donor_background=0.0, background_real=0.0, background_imag=0.0, unit_conversion=0.001, **kwargs)[source]#

Return phasor coordinates of FRET donor channel.

Calculate phasor coordinates of a FRET (Förster Resonance Energy Transfer) donor channel as a function of frequency, donor lifetime, FRET efficiency, fraction of donors undergoing FRET, and background fluorescence.

The phasor coordinates of the donor channel contain fractions of:

  • donor not undergoing energy transfer

  • donor quenched by energy transfer

  • background fluorescence

Parameters:
  • frequency (array_like) – Laser pulse or modulation frequency in MHz.

  • donor_lifetime (array_like) – Lifetime of donor without FRET in ns.

  • fret_efficiency (array_like, optional, default 0) – FRET efficiency in range [0..1].

  • donor_freting (array_like, optional, default 1) – Fraction of donors participating in FRET. Range [0..1].

  • donor_background (array_like, optional, default 0) – Weight of background fluorescence in donor channel relative to fluorescence of donor without FRET. A weight of 1 means the fluorescence of background and donor without FRET are equal.

  • background_real (array_like, optional, default 0) – Real component of background fluorescence phasor coordinate at frequency.

  • background_imag (array_like, optional, default 0) – Imaginary component of background fluorescence phasor coordinate at frequency.

  • unit_conversion (float, optional) – Product of frequency and lifetime units’ prefix factors. The default is 1e-3 for MHz and ns, or Hz and ms. Use 1.0 for Hz and s.

  • **kwargs

    Optional arguments passed to numpy universal functions.

Returns:

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

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

Examples

Compute the phasor coordinates of a FRET donor channel at three FRET efficiencies:

>>> phasor_from_fret_donor(
...     frequency=80,
...     donor_lifetime=4.2,
...     fret_efficiency=[0.0, 0.3, 1.0],
...     donor_freting=0.9,
...     donor_background=0.1,
...     background_real=0.11,
...     background_imag=0.12,
... )  
(array([0.1766, 0.2737, 0.1466]), array([0.3626, 0.4134, 0.2534]))
phasorpy.phasor.phasor_from_lifetime(frequency, lifetime, fraction=None, *, preexponential=False, unit_conversion=0.001, keepdims=False)[source]#

Return phasor coordinates from lifetime components.

Calculate phasor coordinates as a function of frequency, single or multiple lifetime components, and the pre-exponential amplitudes or fractional intensities of the components.

Parameters:
  • frequency (array_like) – Laser pulse or modulation frequency in MHz. A scalar or one-dimensional sequence.

  • lifetime (array_like) – Lifetime components in ns. See notes below for allowed dimensions.

  • fraction (array_like, optional) – Fractional intensities or pre-exponential amplitudes of the lifetime components. Fractions are normalized to sum to 1. See notes below for allowed dimensions.

  • preexponential (bool, optional, default: False) – If true, fraction values are pre-exponential amplitudes, else fractional intensities.

  • unit_conversion (float, optional, default: 1e-3) – Product of frequency and lifetime units’ prefix factors. The default is 1e-3 for MHz and ns, or Hz and ms. Use 1.0 for Hz and s.

  • keepdims (bool, optional, default: False) – If true, length-one dimensions are left in phasor coordinates.

Returns:

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

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

    See notes below for dimensions of the returned arrays.

Raises:

ValueError – Input arrays exceed their allowed dimensionality or do not match.

Notes

The phasor coordinates \(G\) (real) and \(S\) (imag) for many lifetime components \(j\) with lifetimes \(\tau\) and pre-exponential amplitudes \(\alpha\) at frequency \(f\) are:

\[ \begin{align}\begin{aligned}\omega &= 2 \pi f\\g_{j} &= \alpha_{j} / (1 + (\omega \tau_{j})^2)\\G &= \sum_{j} g_{j}\\S &= \sum_{j} \omega \tau_{j} g_{j}\end{aligned}\end{align} \]

The relation between pre-exponential amplitudes \(a\) and fractional intensities \(\alpha\) is:

\[ \begin{align}\begin{aligned}F_{DC} &= \sum_{j} a_{j} \tau_{j}\\\alpha_{j} &= a_{j} \tau_{j} / F_{DC}\end{aligned}\end{align} \]

The following combinations of lifetime and fraction parameters are supported:

  • lifetime is scalar or one-dimensional, holding single component lifetimes. fraction is None. Return arrays of shape (frequency.size, lifetime.size).

  • lifetime is two-dimensional, fraction is one-dimensional. The last dimensions match in size, holding lifetime components and their fractions. Return arrays of shape (frequency.size, lifetime.shape[1]).

  • lifetime is one-dimensional, fraction is two-dimensional. The last dimensions must match in size, holding lifetime components and their fractions. Return arrays of shape (frequency.size, fraction.shape[1]).

  • lifetime and fraction are up to two-dimensional of same shape. The last dimensions hold lifetime components and their fractions. Return arrays of shape (frequency.size, lifetime.shape[0]).

Length-one dimensions are removed from returned arrays if keepdims is false (default).

Examples

Phasor coordinates of a single lifetime component (in ns) at a frequency of 80 MHz:

>>> phasor_from_lifetime(80.0, 1.9894368)  
(0.5, 0.5)

Phasor coordinates of two lifetime components with equal fractional intensities:

>>> phasor_from_lifetime(
...     80.0, [3.9788735, 0.9947183], [0.5, 0.5]
... )  
(0.5, 0.4)

Phasor coordinates of two lifetime components with equal pre-exponential amplitudes:

>>> phasor_from_lifetime(
...     80.0, [3.9788735, 0.9947183], [0.5, 0.5], preexponential=True
... )  
(0.32, 0.4)

Phasor coordinates of many single-component lifetimes (fractions omitted):

>>> phasor_from_lifetime(
...     80.0, [3.9788735, 1.9894368, 0.9947183]
... )  
(array([0.2, 0.5, 0.8]), array([0.4, 0.5, 0.4]))

Phasor coordinates of two lifetime components with varying fractions:

>>> phasor_from_lifetime(
...     80.0, [3.9788735, 0.9947183], [[1, 0], [0.5, 0.5], [0, 1]]
... )  
(array([0.2, 0.5, 0.8]), array([0.4, 0.4, 0.4]))

Phasor coordinates of multiple two-component lifetimes with constant fractions, keeping dimensions:

>>> phasor_from_lifetime(
...     80.0, [[3.9788735, 0.9947183], [1.9894368, 1.9894368]], [0.5, 0.5]
... )  
(array([0.5, 0.5]), array([0.4, 0.5]))

Phasor coordinates of multiple two-component lifetimes with specific fractions at multiple frequencies. Frequencies are in Hz, lifetimes in ns:

>>> phasor_from_lifetime(
...     [40e6, 80e6],
...     [[1e-9, 0.9947183e-9], [3.9788735e-9, 0.9947183e-9]],
...     [[0, 1], [0.5, 0.5]],
...     unit_conversion=1.0,
... )  
(array([[0.941, 0.721], [0.8, 0.5]]), array([[0.235, 0.368], [0.4, 0.4]]))
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=-1, 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, optional) – Axis over which to compute phasor coordinates. The default is 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\ge3\) samples of the signal \(F\) af 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 (\(NaN\) or \(\infty\)). 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_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 \(\infty\)).

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_semicircle(samples=101, /)[source]#

Return equally spaced phasor coordinates on universal semicircle.

Parameters:

samples (int, optional, default: 101) – Number of coordinates to return.

Returns:

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

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

Raises:

ValueError – The number of samples is smaller than 1.

Notes

If more than one sample, the first and last phasor coordinates returned are (0, 0) and (1, 0). The center coordinate, if any, is (0.5, 0.5).

The universal semicircle is composed of the phasor coordinates of single lifetime components, where the relation of polar coordinates (phase \(\phi\) and modulation \(M\)) is:

\[M = \cos{\phi}\]

Examples

Calculate three phasor coordinates on universal semicircle:

>>> phasor_semicircle(3)  
(array([0, 0.5, 1]), array([0.0, 0.5, 0]))
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 out of 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 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_apparent_lifetime(real, imag, /, frequency, *, unit_conversion=0.001, **kwargs)[source]#

Return apparent single lifetimes from phasor coordinates.

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

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

  • frequency (array_like) – Laser pulse or modulation frequency in MHz.

  • unit_conversion (float, optional) – Product of frequency and returned lifetime units’ prefix factors. The default is 1e-3 for MHz and ns, or Hz and ms. Use 1.0 for Hz and s.

  • **kwargs

    Optional arguments passed to numpy universal functions.

Returns:

  • phase_lifetime (ndarray) – Apparent single lifetime from angular component of phasor coordinates.

  • modulation_lifetime (ndarray) – Apparent single lifetime from radial component of phasor coordinates.

Notes

The phasor coordinates real (\(G\)) and imag (\(S\)) are converted to apparent single lifetimes phase_lifetime (\(\tau_{\phi}\)) and modulation_lifetime (\(\tau_{M}\)) at frequency \(f\) according to:

\[ \begin{align}\begin{aligned}\omega &= 2 \pi f\\\tau_{\phi} &= \omega^{-1} \cdot S / G\\\tau_{M} &= \omega^{-1} \cdot \sqrt{1 / (S^2 + G^2) - 1}\end{aligned}\end{align} \]

Examples

The apparent single lifetimes from phase and modulation are equal only if the phasor coordinates lie on the universal semicircle:

>>> phasor_to_apparent_lifetime(
...     0.5, [0.5, 0.45], frequency=80
... )  
(array([1.989, 1.79]), array([1.989, 2.188]))

Apparent single lifetimes of phasor coordinates outside the universal semicircle are undefined:

>>> phasor_to_apparent_lifetime(-0.1, 1.1, 80)  
(-21.8, 0.0)

Apparent single lifetimes at the universal semicircle endpoints are infinite and zero:

>>> phasor_to_apparent_lifetime([0, 1], [0, 0], 80)  
(array([inf, 0]), array([inf, 0]))
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]))
phasorpy.phasor.polar_from_apparent_lifetime(phase_lifetime, modulation_lifetime, /, frequency, *, unit_conversion=0.001, **kwargs)[source]#

Return polar coordinates from apparent single lifetimes.

Parameters:
  • phase_lifetime (ndarray) – Apparent single lifetime from phase.

  • modulation_lifetime (ndarray, optional) – Apparent single lifetime from modulation. If None, modulation_lifetime is same as phase_lifetime.

  • frequency (array_like) – Laser pulse or modulation frequency in MHz.

  • unit_conversion (float, optional) – Product of frequency and lifetime units’ prefix factors. The default is 1e-3 for MHz and ns, or Hz and ms. Use 1.0 for Hz and s.

  • **kwargs

    Optional arguments passed to numpy universal functions.

Returns:

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

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

Notes

The apparent single lifetimes phase_lifetime (\(\tau_{\phi}\)) and modulation_lifetime (\(\tau_{M}\)) are converted to polar coordinates phase (\(\phi\)) and modulation (\(M\)) at frequency \(f\) according to:

\[ \begin{align}\begin{aligned}\omega &= 2 \pi f\\\phi & = \arctan(\omega \tau_{\phi})\\M &= 1 / \sqrt{1 + (\omega \tau_{M})^2}\end{aligned}\end{align} \]

Examples

If the apparent single lifetimes from phase and modulation are equal, the polar coordinates lie on the universal semicircle, else inside:

>>> polar_from_apparent_lifetime(
...     1.9894, [1.9894, 2.4113], frequency=80.0
... )  
(array([0.7854, 0.7854]), array([0.7071, 0.6364]))
phasorpy.phasor.polar_from_reference(measured_phase, measured_modulation, known_phase, known_modulation, /, **kwargs)[source]#

Return polar coordinates for calibration from reference coordinates.

Return rotation angle and scale factor for calibrating phasor coordinates from measured and known polar coordinates of a reference, for example, a sample of known lifetime.

Parameters:
  • measured_phase (array_like) – Angular component of measured polar coordinates in radians.

  • measured_modulation (array_like) – Radial component of measured polar coordinates.

  • known_phase (array_like) – Angular component of reference polar coordinates in radians.

  • known_modulation (array_like) – Radial component of reference polar coordinates.

  • **kwargs

    Optional arguments passed to numpy universal functions.

Returns:

  • phase_zero (ndarray) – Angular component of polar coordinates for calibration in radians.

  • modulation_zero (ndarray) – Radial component of polar coordinates for calibration.

Examples

>>> polar_from_reference(0.2, 0.4, 0.4, 1.3)
(0.2, 3.25)
phasorpy.phasor.polar_from_reference_phasor(measured_real, measured_imag, known_real, known_imag, /, **kwargs)[source]#

Return polar coordinates for calibration from reference phasor.

Return rotation angle and scale factor for calibrating phasor coordinates from measured and known phasor coordinates of a reference, for example, a sample of known lifetime.

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

  • measured_imag (array_like) – Imaginary component of measured phasor coordinates.

  • known_real (array_like) – Real component of reference phasor coordinates.

  • known_imag (array_like) – Imaginary component of reference phasor coordinates.

  • **kwargs

    Optional arguments passed to numpy universal functions.

Returns:

  • phase_zero (ndarray) – Angular component of polar coordinates for calibration in radians.

  • modulation_zero (ndarray) – Radial component of polar coordinates for calibration.

Notes

This function performs the following operations:

polar_from_reference(
    *phasor_to_polar(measured_real, measured_imag),
    *phasor_to_polar(known_real, known_imag),
)

Examples

>>> polar_from_reference_phasor(0.5, 0.0, 1.0, 0.0)
(0.0, 2.0)
phasorpy.phasor.polar_to_apparent_lifetime(phase, modulation, /, frequency, *, unit_conversion=0.001, **kwargs)[source]#

Return apparent single lifetimes from polar coordinates.

Parameters:
  • phase (array_like) – Angular component of polar coordinates.

  • imag (array_like) – Radial component of polar coordinates.

  • frequency (array_like) – Laser pulse or modulation frequency in MHz.

  • unit_conversion (float, optional) – Product of frequency and returned lifetime units’ prefix factors. The default is 1e-3 for MHz and ns, or Hz and ms. Use 1.0 for Hz and s.

  • **kwargs

    Optional arguments passed to numpy universal functions.

Returns:

  • phase_lifetime (ndarray) – Apparent single lifetime from phase.

  • modulation_lifetime (ndarray) – Apparent single lifetime from modulation.

Notes

The polar coordinates phase (\(\phi\)) and modulation (\(M\)) are converted to apparent single lifetimes phase_lifetime (\(\tau_{\phi}\)) and modulation_lifetime (\(\tau_{M}\)) at frequency \(f\) according to:

\[ \begin{align}\begin{aligned}\omega &= 2 \pi f\\\tau_{\phi} &= \omega^{-1} \cdot \tan{\phi}\\\tau_{M} &= \omega^{-1} \cdot \sqrt{1 / M^2 - 1}\end{aligned}\end{align} \]

Examples

The apparent single lifetimes from phase and modulation are equal only if the polar coordinates lie on the universal semicircle:

>>> polar_to_apparent_lifetime(
...     math.pi / 4, numpy.hypot([0.5, 0.45], [0.5, 0.45]), frequency=80
... )  
(array([1.989, 1.989]), array([1.989, 2.411]))