phasorpy 0.5__cp313-cp313-win_amd64.whl → 0.6__cp313-cp313-win_amd64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- phasorpy/__init__.py +2 -3
- phasorpy/_phasorpy.cp313-win_amd64.pyd +0 -0
- phasorpy/_phasorpy.pyx +185 -2
- phasorpy/_utils.py +121 -9
- phasorpy/cli.py +56 -3
- phasorpy/cluster.py +42 -6
- phasorpy/components.py +226 -55
- phasorpy/experimental.py +312 -0
- phasorpy/io/__init__.py +137 -0
- phasorpy/io/_flimlabs.py +350 -0
- phasorpy/io/_leica.py +329 -0
- phasorpy/io/_ometiff.py +445 -0
- phasorpy/io/_other.py +782 -0
- phasorpy/io/_simfcs.py +627 -0
- phasorpy/phasor.py +307 -1
- phasorpy/plot/__init__.py +27 -0
- phasorpy/plot/_functions.py +717 -0
- phasorpy/plot/_lifetime_plots.py +553 -0
- phasorpy/plot/_phasorplot.py +1119 -0
- phasorpy/plot/_phasorplot_fret.py +559 -0
- phasorpy/utils.py +84 -296
- {phasorpy-0.5.dist-info → phasorpy-0.6.dist-info}/METADATA +2 -2
- phasorpy-0.6.dist-info/RECORD +34 -0
- {phasorpy-0.5.dist-info → phasorpy-0.6.dist-info}/WHEEL +1 -1
- phasorpy/_io.py +0 -2655
- phasorpy/io.py +0 -9
- phasorpy/plot.py +0 -2318
- phasorpy/version.py +0 -80
- phasorpy-0.5.dist-info/RECORD +0 -26
- {phasorpy-0.5.dist-info → phasorpy-0.6.dist-info}/entry_points.txt +0 -0
- {phasorpy-0.5.dist-info → phasorpy-0.6.dist-info}/licenses/LICENSE.txt +0 -0
- {phasorpy-0.5.dist-info → phasorpy-0.6.dist-info}/top_level.txt +0 -0
phasorpy/phasor.py
CHANGED
@@ -17,6 +17,7 @@ The ``phasorpy.phasor`` module provides functions to:
|
|
17
17
|
- :py:func:`phasor_from_lifetime`
|
18
18
|
- :py:func:`phasor_from_apparent_lifetime`
|
19
19
|
- :py:func:`phasor_to_apparent_lifetime`
|
20
|
+
- :py:func:`phasor_to_normal_lifetime`
|
20
21
|
|
21
22
|
- convert to and from polar coordinates (phase and modulation):
|
22
23
|
|
@@ -69,6 +70,10 @@ The ``phasorpy.phasor`` module provides functions to:
|
|
69
70
|
- :py:func:`phasor_filter_pawflim`
|
70
71
|
- :py:func:`phasor_threshold`
|
71
72
|
|
73
|
+
- find nearest neighbor phasor coordinates from another set of phasor coordinates:
|
74
|
+
|
75
|
+
- :py:func:`phasor_nearest_neighbor`
|
76
|
+
|
72
77
|
"""
|
73
78
|
|
74
79
|
from __future__ import annotations
|
@@ -92,11 +97,14 @@ __all__ = [
|
|
92
97
|
'phasor_from_polar',
|
93
98
|
'phasor_from_signal',
|
94
99
|
'phasor_multiply',
|
100
|
+
'phasor_nearest_neighbor',
|
95
101
|
'phasor_normalize',
|
96
102
|
'phasor_semicircle',
|
103
|
+
'phasor_semicircle_intersect',
|
97
104
|
'phasor_threshold',
|
98
105
|
'phasor_to_apparent_lifetime',
|
99
106
|
'phasor_to_complex',
|
107
|
+
'phasor_to_normal_lifetime',
|
100
108
|
'phasor_to_polar',
|
101
109
|
'phasor_to_principal_plane',
|
102
110
|
'phasor_to_signal',
|
@@ -125,7 +133,9 @@ import numpy
|
|
125
133
|
|
126
134
|
from ._phasorpy import (
|
127
135
|
_gaussian_signal,
|
136
|
+
_intersect_semicircle_line,
|
128
137
|
_median_filter_2d,
|
138
|
+
_nearest_neighbor_2d,
|
129
139
|
_phasor_at_harmonic,
|
130
140
|
_phasor_divide,
|
131
141
|
_phasor_from_apparent_lifetime,
|
@@ -142,6 +152,7 @@ from ._phasorpy import (
|
|
142
152
|
_phasor_threshold_nan,
|
143
153
|
_phasor_threshold_open,
|
144
154
|
_phasor_to_apparent_lifetime,
|
155
|
+
_phasor_to_normal_lifetime,
|
145
156
|
_phasor_to_polar,
|
146
157
|
_phasor_transform,
|
147
158
|
_phasor_transform_const,
|
@@ -674,7 +685,7 @@ def lifetime_to_signal(
|
|
674
685
|
mean = 1.0
|
675
686
|
mean = numpy.asarray(mean)
|
676
687
|
mean -= background
|
677
|
-
if numpy.any(mean
|
688
|
+
if numpy.any(mean < 0.0):
|
678
689
|
raise ValueError('mean - background must not be less than zero')
|
679
690
|
|
680
691
|
scale = samples / (2.0 * math.pi)
|
@@ -794,6 +805,65 @@ def phasor_semicircle(
|
|
794
805
|
return real, imag
|
795
806
|
|
796
807
|
|
808
|
+
def phasor_semicircle_intersect(
|
809
|
+
real0: ArrayLike,
|
810
|
+
imag0: ArrayLike,
|
811
|
+
real1: ArrayLike,
|
812
|
+
imag1: ArrayLike,
|
813
|
+
/,
|
814
|
+
**kwargs: Any,
|
815
|
+
) -> tuple[NDArray[Any], NDArray[Any], NDArray[Any], NDArray[Any]]:
|
816
|
+
"""Return intersection of line through phasors with universal semicircle.
|
817
|
+
|
818
|
+
Return the phasor coordinates of two intersections of the universal
|
819
|
+
semicircle with the line between two phasor coordinates.
|
820
|
+
Return NaN if the line does not intersect the semicircle.
|
821
|
+
|
822
|
+
Parameters
|
823
|
+
----------
|
824
|
+
real0 : array_like
|
825
|
+
Real component of first set of phasor coordinates.
|
826
|
+
imag0 : array_like
|
827
|
+
Imaginary component of first set of phasor coordinates.
|
828
|
+
real1 : array_like
|
829
|
+
Real component of second set of phasor coordinates.
|
830
|
+
imag1 : array_like
|
831
|
+
Imaginary component of second set of phasor coordinates.
|
832
|
+
**kwargs
|
833
|
+
Optional `arguments passed to numpy universal functions
|
834
|
+
<https://numpy.org/doc/stable/reference/ufuncs.html#ufuncs-kwargs>`_.
|
835
|
+
|
836
|
+
Returns
|
837
|
+
-------
|
838
|
+
real0 : ndarray
|
839
|
+
Real component of first intersect of phasors with semicircle.
|
840
|
+
imag0 : ndarray
|
841
|
+
Imaginary component of first intersect of phasors with semicircle.
|
842
|
+
real1 : ndarray
|
843
|
+
Real component of second intersect of phasors with semicircle.
|
844
|
+
imag1 : ndarray
|
845
|
+
Imaginary component of second intersect of phasors with semicircle.
|
846
|
+
|
847
|
+
Examples
|
848
|
+
--------
|
849
|
+
Calculate two intersects of a line through two phasor coordinates
|
850
|
+
with the universal semicircle:
|
851
|
+
|
852
|
+
>>> phasor_semicircle_intersect(0.2, 0.25, 0.6, 0.25) # doctest: +NUMBER
|
853
|
+
(0.066, 0.25, 0.933, 0.25)
|
854
|
+
|
855
|
+
The line between two phasor coordinates may not intersect the semicircle
|
856
|
+
at two points:
|
857
|
+
|
858
|
+
>>> phasor_semicircle_intersect(0.2, 0.0, 0.6, 0.25) # doctest: +NUMBER
|
859
|
+
(nan, nan, 0.817, 0.386)
|
860
|
+
|
861
|
+
"""
|
862
|
+
return _intersect_semicircle_line( # type: ignore[no-any-return]
|
863
|
+
real0, imag0, real1, imag1, **kwargs
|
864
|
+
)
|
865
|
+
|
866
|
+
|
797
867
|
def phasor_to_complex(
|
798
868
|
real: ArrayLike,
|
799
869
|
imag: ArrayLike,
|
@@ -1577,6 +1647,7 @@ def phasor_to_polar(
|
|
1577
1647
|
See Also
|
1578
1648
|
--------
|
1579
1649
|
phasorpy.phasor.phasor_from_polar
|
1650
|
+
:ref:`sphx_glr_tutorials_phasorpy_lifetime_geometry.py`
|
1580
1651
|
|
1581
1652
|
Examples
|
1582
1653
|
--------
|
@@ -1684,6 +1755,7 @@ def phasor_to_apparent_lifetime(
|
|
1684
1755
|
See Also
|
1685
1756
|
--------
|
1686
1757
|
phasorpy.phasor.phasor_from_apparent_lifetime
|
1758
|
+
:ref:`sphx_glr_tutorials_phasorpy_lifetime_geometry.py`
|
1687
1759
|
|
1688
1760
|
Notes
|
1689
1761
|
-----
|
@@ -1819,6 +1891,86 @@ def phasor_from_apparent_lifetime(
|
|
1819
1891
|
)
|
1820
1892
|
|
1821
1893
|
|
1894
|
+
def phasor_to_normal_lifetime(
|
1895
|
+
real: ArrayLike,
|
1896
|
+
imag: ArrayLike,
|
1897
|
+
/,
|
1898
|
+
frequency: ArrayLike,
|
1899
|
+
*,
|
1900
|
+
unit_conversion: float = 1e-3,
|
1901
|
+
**kwargs: Any,
|
1902
|
+
) -> NDArray[Any]:
|
1903
|
+
r"""Return normal lifetimes from phasor coordinates.
|
1904
|
+
|
1905
|
+
The normal lifetime of phasor coordinates represents the single lifetime
|
1906
|
+
equivalent corresponding to the perpendicular projection of the coordinates
|
1907
|
+
onto the universal semicircle, as defined in [3]_.
|
1908
|
+
|
1909
|
+
Parameters
|
1910
|
+
----------
|
1911
|
+
real : array_like
|
1912
|
+
Real component of phasor coordinates.
|
1913
|
+
imag : array_like
|
1914
|
+
Imaginary component of phasor coordinates.
|
1915
|
+
frequency : array_like
|
1916
|
+
Laser pulse or modulation frequency in MHz.
|
1917
|
+
unit_conversion : float, optional
|
1918
|
+
Product of `frequency` and returned `lifetime` units' prefix factors.
|
1919
|
+
The default is 1e-3 for MHz and ns, or Hz and ms.
|
1920
|
+
Use 1.0 for Hz and s.
|
1921
|
+
**kwargs
|
1922
|
+
Optional `arguments passed to numpy universal functions
|
1923
|
+
<https://numpy.org/doc/stable/reference/ufuncs.html#ufuncs-kwargs>`_.
|
1924
|
+
|
1925
|
+
Returns
|
1926
|
+
-------
|
1927
|
+
normal_lifetime : ndarray
|
1928
|
+
Normal lifetime from of phasor coordinates.
|
1929
|
+
|
1930
|
+
See Also
|
1931
|
+
--------
|
1932
|
+
:ref:`sphx_glr_tutorials_phasorpy_lifetime_geometry.py`
|
1933
|
+
|
1934
|
+
Notes
|
1935
|
+
-----
|
1936
|
+
The phasor coordinates `real` (:math:`G`) and `imag` (:math:`S`)
|
1937
|
+
are converted to normal lifetimes `normal_lifetime` (:math:`\tau_{N}`)
|
1938
|
+
at frequency :math:`f` according to:
|
1939
|
+
|
1940
|
+
.. math::
|
1941
|
+
|
1942
|
+
\omega &= 2 \pi f
|
1943
|
+
|
1944
|
+
G_{N} &= 0.5 \cdot (1 + \cos{\arctan{\frac{S}{G - 0.5}}})
|
1945
|
+
|
1946
|
+
\tau_{N} &= \sqrt{\frac{1 - G_{N}}{\omega^{2} \cdot G_{N}}}
|
1947
|
+
|
1948
|
+
References
|
1949
|
+
----------
|
1950
|
+
|
1951
|
+
.. [3] Silberberg M, and Grecco H. `pawFLIM: reducing bias and
|
1952
|
+
uncertainty to enable lower photon count in FLIM experiments
|
1953
|
+
<https://doi.org/10.1088/2050-6120/aa72ab>`_.
|
1954
|
+
*Methods Appl Fluoresc*, 5(2): 024016 (2017)
|
1955
|
+
|
1956
|
+
Examples
|
1957
|
+
--------
|
1958
|
+
The normal lifetimes of phasor coordinates with a real component of 0.5
|
1959
|
+
are independent of the imaginary component:
|
1960
|
+
|
1961
|
+
>>> phasor_to_normal_lifetime(
|
1962
|
+
... 0.5, [0.5, 0.45], frequency=80
|
1963
|
+
... ) # doctest: +NUMBER
|
1964
|
+
array([1.989, 1.989])
|
1965
|
+
|
1966
|
+
"""
|
1967
|
+
omega = numpy.array(frequency, dtype=numpy.float64) # makes copy
|
1968
|
+
omega *= math.pi * 2.0 * unit_conversion
|
1969
|
+
return _phasor_to_normal_lifetime( # type: ignore[no-any-return]
|
1970
|
+
real, imag, omega, **kwargs
|
1971
|
+
)
|
1972
|
+
|
1973
|
+
|
1822
1974
|
def lifetime_to_frequency(
|
1823
1975
|
lifetime: ArrayLike,
|
1824
1976
|
*,
|
@@ -2138,6 +2290,11 @@ def phasor_from_lifetime(
|
|
2138
2290
|
ValueError
|
2139
2291
|
Input arrays exceed their allowed dimensionality or do not match.
|
2140
2292
|
|
2293
|
+
See Also
|
2294
|
+
--------
|
2295
|
+
:ref:`sphx_glr_tutorials_api_phasorpy_phasor_from_lifetime.py`
|
2296
|
+
:ref:`sphx_glr_tutorials_phasorpy_lifetime_geometry.py`
|
2297
|
+
|
2141
2298
|
Notes
|
2142
2299
|
-----
|
2143
2300
|
The phasor coordinates :math:`G` (`real`) and :math:`S` (`imag`) for
|
@@ -2517,6 +2674,7 @@ def phasor_from_fret_donor(
|
|
2517
2674
|
--------
|
2518
2675
|
phasorpy.phasor.phasor_from_fret_acceptor
|
2519
2676
|
:ref:`sphx_glr_tutorials_api_phasorpy_fret.py`
|
2677
|
+
:ref:`sphx_glr_tutorials_applications_phasorpy_fret_efficiency.py`
|
2520
2678
|
|
2521
2679
|
Examples
|
2522
2680
|
--------
|
@@ -3452,6 +3610,154 @@ def phasor_threshold(
|
|
3452
3610
|
return mean, real, imag
|
3453
3611
|
|
3454
3612
|
|
3613
|
+
def phasor_nearest_neighbor(
|
3614
|
+
real: ArrayLike,
|
3615
|
+
imag: ArrayLike,
|
3616
|
+
neighbor_real: ArrayLike,
|
3617
|
+
neighbor_imag: ArrayLike,
|
3618
|
+
/,
|
3619
|
+
*,
|
3620
|
+
values: ArrayLike | None = None,
|
3621
|
+
dtype: DTypeLike | None = None,
|
3622
|
+
distance_max: float | None = None,
|
3623
|
+
num_threads: int | None = None,
|
3624
|
+
) -> NDArray[Any]:
|
3625
|
+
"""Return indices or values of nearest neighbor from other coordinates.
|
3626
|
+
|
3627
|
+
For each phasor coordinate, find the nearest neighbor in another set of
|
3628
|
+
phasor coordinates and return its flat index. If more than one neighbor
|
3629
|
+
has the same distance, return the smallest index.
|
3630
|
+
|
3631
|
+
For phasor coordinates that are NaN, or have a distance to the nearest
|
3632
|
+
neighbor that is larger than `distance_max`, return an index of -1.
|
3633
|
+
|
3634
|
+
If `values` are provided, return the values corresponding to the nearest
|
3635
|
+
neighbor coordinates instead of indices. Return NaN values for indices
|
3636
|
+
that are -1.
|
3637
|
+
|
3638
|
+
This function does not support multi-harmonic, multi-channel, or
|
3639
|
+
multi-frequency phasor coordinates.
|
3640
|
+
|
3641
|
+
Parameters
|
3642
|
+
----------
|
3643
|
+
real : array_like
|
3644
|
+
Real component of phasor coordinates.
|
3645
|
+
imag : array_like
|
3646
|
+
Imaginary component of phasor coordinates.
|
3647
|
+
neighbor_real : array_like
|
3648
|
+
Real component of neighbor phasor coordinates.
|
3649
|
+
neighbor_imag : array_like
|
3650
|
+
Imaginary component of neighbor phasor coordinates.
|
3651
|
+
values : array_like, optional
|
3652
|
+
Array of values corresponding to neighbor coordinates.
|
3653
|
+
If provided, return the values corresponding to the nearest
|
3654
|
+
neighbor coordinates.
|
3655
|
+
distance_max : float, optional
|
3656
|
+
Maximum Euclidean distance to consider a neighbor valid.
|
3657
|
+
By default, all neighbors are considered.
|
3658
|
+
dtype : dtype_like, optional
|
3659
|
+
Floating point data type used for calculation and output values.
|
3660
|
+
Either `float32` or `float64`. The default is `float64`.
|
3661
|
+
num_threads : int, optional
|
3662
|
+
Number of OpenMP threads to use for parallelization.
|
3663
|
+
By default, multi-threading is disabled.
|
3664
|
+
If zero, up to half of logical CPUs are used.
|
3665
|
+
OpenMP may not be available on all platforms.
|
3666
|
+
|
3667
|
+
Returns
|
3668
|
+
-------
|
3669
|
+
nearest : ndarray
|
3670
|
+
Flat indices (or the corresponding values if provided) of the nearest
|
3671
|
+
neighbor coordinates.
|
3672
|
+
|
3673
|
+
Raises
|
3674
|
+
------
|
3675
|
+
ValueError
|
3676
|
+
If the shapes of `real`, and `imag` do not match.
|
3677
|
+
If the shapes of `neighbor_real` and `neighbor_imag` do not match.
|
3678
|
+
If the shapes of `values` and `neighbor_real` do not match.
|
3679
|
+
If `distance_max` is less than or equal to zero.
|
3680
|
+
|
3681
|
+
See Also
|
3682
|
+
--------
|
3683
|
+
:ref:`sphx_glr_tutorials_applications_phasorpy_fret_efficiency.py`
|
3684
|
+
|
3685
|
+
Notes
|
3686
|
+
-----
|
3687
|
+
This function uses linear search, which is inefficient for large
|
3688
|
+
number of coordinates or neighbors.
|
3689
|
+
``scipy.spatial.KDTree.query()`` would be more efficient in those cases.
|
3690
|
+
However, KDTree is known to return non-deterministic results in case of
|
3691
|
+
multiple neighbors with the same distance.
|
3692
|
+
|
3693
|
+
Examples
|
3694
|
+
--------
|
3695
|
+
>>> phasor_nearest_neighbor(
|
3696
|
+
... [0.1, 0.5, numpy.nan],
|
3697
|
+
... [0.1, 0.5, numpy.nan],
|
3698
|
+
... [0, 0.4],
|
3699
|
+
... [0, 0.4],
|
3700
|
+
... values=[10, 20],
|
3701
|
+
... )
|
3702
|
+
array([10, 20, nan])
|
3703
|
+
|
3704
|
+
"""
|
3705
|
+
dtype = numpy.dtype(dtype)
|
3706
|
+
if dtype.char not in {'f', 'd'}:
|
3707
|
+
raise ValueError(f'{dtype=} is not a floating point type')
|
3708
|
+
|
3709
|
+
real = numpy.ascontiguousarray(real, dtype=dtype)
|
3710
|
+
imag = numpy.ascontiguousarray(imag, dtype=dtype)
|
3711
|
+
neighbor_real = numpy.ascontiguousarray(neighbor_real, dtype=dtype)
|
3712
|
+
neighbor_imag = numpy.ascontiguousarray(neighbor_imag, dtype=dtype)
|
3713
|
+
|
3714
|
+
if real.shape != imag.shape:
|
3715
|
+
raise ValueError(f'{real.shape=} != {imag.shape=}')
|
3716
|
+
if neighbor_real.shape != neighbor_imag.shape:
|
3717
|
+
raise ValueError(f'{neighbor_real.shape=} != {neighbor_imag.shape=}')
|
3718
|
+
|
3719
|
+
shape = real.shape
|
3720
|
+
real = real.ravel()
|
3721
|
+
imag = imag.ravel()
|
3722
|
+
neighbor_real = neighbor_real.ravel()
|
3723
|
+
neighbor_imag = neighbor_imag.ravel()
|
3724
|
+
|
3725
|
+
indices = numpy.empty(
|
3726
|
+
real.shape, numpy.min_scalar_type(-neighbor_real.size)
|
3727
|
+
)
|
3728
|
+
|
3729
|
+
if distance_max is None:
|
3730
|
+
distance_max = numpy.inf
|
3731
|
+
else:
|
3732
|
+
distance_max = float(distance_max)
|
3733
|
+
if distance_max <= 0:
|
3734
|
+
raise ValueError(f'{distance_max=} <= 0')
|
3735
|
+
|
3736
|
+
num_threads = number_threads(num_threads)
|
3737
|
+
|
3738
|
+
_nearest_neighbor_2d(
|
3739
|
+
indices,
|
3740
|
+
real,
|
3741
|
+
imag,
|
3742
|
+
neighbor_real,
|
3743
|
+
neighbor_imag,
|
3744
|
+
distance_max,
|
3745
|
+
num_threads,
|
3746
|
+
)
|
3747
|
+
|
3748
|
+
if values is None:
|
3749
|
+
return numpy.asarray(indices.reshape(shape))
|
3750
|
+
|
3751
|
+
values = numpy.ascontiguousarray(values, dtype=dtype).ravel()
|
3752
|
+
if values.shape != neighbor_real.shape:
|
3753
|
+
raise ValueError(f'{values.shape=} != {neighbor_real.shape=}')
|
3754
|
+
|
3755
|
+
nearest_values = values[indices]
|
3756
|
+
nearest_values[indices == -1] = numpy.nan
|
3757
|
+
|
3758
|
+
return numpy.asarray(nearest_values.reshape(shape))
|
3759
|
+
|
3760
|
+
|
3455
3761
|
def phasor_center(
|
3456
3762
|
mean: ArrayLike,
|
3457
3763
|
real: ArrayLike,
|
@@ -0,0 +1,27 @@
|
|
1
|
+
"""Plot phasor coordinates and related data.
|
2
|
+
|
3
|
+
The ``phasorpy.plot`` module provides functions and classes to visualize
|
4
|
+
phasor coordinates and related data using the
|
5
|
+
`matplotlib <https://matplotlib.org/>`_ library.
|
6
|
+
|
7
|
+
"""
|
8
|
+
|
9
|
+
from __future__ import annotations
|
10
|
+
|
11
|
+
__all__: list[str] = []
|
12
|
+
|
13
|
+
from .._utils import init_module
|
14
|
+
from ._functions import *
|
15
|
+
from ._lifetime_plots import *
|
16
|
+
from ._phasorplot import *
|
17
|
+
from ._phasorplot_fret import *
|
18
|
+
|
19
|
+
# The `init_module()` function dynamically populates the `__all__` list with
|
20
|
+
# all public symbols imported from submodules or defined in this module.
|
21
|
+
# Any name not starting with an underscore will be automatically exported
|
22
|
+
# when using "from phasorpy.plot import *"
|
23
|
+
|
24
|
+
init_module(globals())
|
25
|
+
del init_module
|
26
|
+
|
27
|
+
# flake8: noqa: F401, F403
|