phasorpy 0.5__cp311-cp311-win_amd64.whl → 0.7__cp311-cp311-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.cp311-win_amd64.pyd +0 -0
- phasorpy/_phasorpy.pyx +466 -11
- phasorpy/_utils.py +222 -37
- phasorpy/cli.py +74 -3
- phasorpy/cluster.py +51 -21
- phasorpy/color.py +11 -7
- phasorpy/component.py +707 -0
- phasorpy/{cursors.py → cursor.py} +31 -33
- phasorpy/datasets.py +117 -7
- phasorpy/experimental.py +310 -0
- phasorpy/io/__init__.py +138 -0
- phasorpy/io/_flimlabs.py +360 -0
- phasorpy/io/_leica.py +331 -0
- phasorpy/io/_ometiff.py +444 -0
- phasorpy/io/_other.py +890 -0
- phasorpy/io/_simfcs.py +652 -0
- phasorpy/lifetime.py +2058 -0
- phasorpy/phasor.py +184 -1754
- phasorpy/plot/__init__.py +27 -0
- phasorpy/plot/_functions.py +723 -0
- phasorpy/plot/_lifetime_plots.py +563 -0
- phasorpy/plot/_phasorplot.py +1507 -0
- phasorpy/plot/_phasorplot_fret.py +561 -0
- phasorpy/utils.py +89 -290
- {phasorpy-0.5.dist-info → phasorpy-0.7.dist-info}/METADATA +3 -3
- phasorpy-0.7.dist-info/RECORD +35 -0
- {phasorpy-0.5.dist-info → phasorpy-0.7.dist-info}/WHEEL +1 -1
- phasorpy/_io.py +0 -2655
- phasorpy/components.py +0 -313
- 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.7.dist-info}/entry_points.txt +0 -0
- {phasorpy-0.5.dist-info → phasorpy-0.7.dist-info}/licenses/LICENSE.txt +0 -0
- {phasorpy-0.5.dist-info → phasorpy-0.7.dist-info}/top_level.txt +0 -0
phasorpy/components.py
DELETED
@@ -1,313 +0,0 @@
|
|
1
|
-
"""Component analysis of phasor coordinates.
|
2
|
-
|
3
|
-
The ``phasorpy.components`` module provides functions to:
|
4
|
-
|
5
|
-
- calculate fractions of two known components by projecting onto the
|
6
|
-
line between the components:
|
7
|
-
|
8
|
-
- :py:func:`two_fractions_from_phasor`
|
9
|
-
|
10
|
-
- calculate phasor coordinates of second component if only one is
|
11
|
-
known (not implemented)
|
12
|
-
|
13
|
-
- calculate fractions of three or four known components by using higher
|
14
|
-
harmonic information (not implemented)
|
15
|
-
|
16
|
-
- calculate fractions of two or three known components by resolving
|
17
|
-
graphically with histogram:
|
18
|
-
|
19
|
-
- :py:func:`graphical_component_analysis`
|
20
|
-
|
21
|
-
- blindly resolve fractions of `n` components by using harmonic
|
22
|
-
information (not implemented)
|
23
|
-
|
24
|
-
"""
|
25
|
-
|
26
|
-
from __future__ import annotations
|
27
|
-
|
28
|
-
__all__ = [
|
29
|
-
'two_fractions_from_phasor',
|
30
|
-
'graphical_component_analysis',
|
31
|
-
]
|
32
|
-
|
33
|
-
import numbers
|
34
|
-
from typing import TYPE_CHECKING
|
35
|
-
|
36
|
-
if TYPE_CHECKING:
|
37
|
-
from ._typing import Any, ArrayLike, NDArray
|
38
|
-
|
39
|
-
import numpy
|
40
|
-
|
41
|
-
from ._phasorpy import (
|
42
|
-
_fraction_on_segment,
|
43
|
-
_is_inside_circle,
|
44
|
-
_is_inside_stadium,
|
45
|
-
_segment_direction_and_length,
|
46
|
-
)
|
47
|
-
|
48
|
-
|
49
|
-
def two_fractions_from_phasor(
|
50
|
-
real: ArrayLike,
|
51
|
-
imag: ArrayLike,
|
52
|
-
components_real: ArrayLike,
|
53
|
-
components_imag: ArrayLike,
|
54
|
-
/,
|
55
|
-
) -> NDArray[Any]:
|
56
|
-
"""Return fraction of first of two components from phasor coordinates.
|
57
|
-
|
58
|
-
Return the relative distance (normalized by the distance between the two
|
59
|
-
components) to the second component for each phasor coordinate projected
|
60
|
-
onto the line between two components.
|
61
|
-
|
62
|
-
Parameters
|
63
|
-
----------
|
64
|
-
real : array_like
|
65
|
-
Real component of phasor coordinates.
|
66
|
-
imag : array_like
|
67
|
-
Imaginary component of phasor coordinates.
|
68
|
-
components_real : array_like, shape (2,)
|
69
|
-
Real coordinates of first and second components.
|
70
|
-
components_imag : array_like, shape (2,)
|
71
|
-
Imaginary coordinates of first and second components.
|
72
|
-
|
73
|
-
Returns
|
74
|
-
-------
|
75
|
-
fraction : ndarray
|
76
|
-
Fractions of first component.
|
77
|
-
|
78
|
-
Raises
|
79
|
-
------
|
80
|
-
ValueError
|
81
|
-
If the real or imaginary coordinates of the known components are
|
82
|
-
not of size 2.
|
83
|
-
|
84
|
-
See Also
|
85
|
-
--------
|
86
|
-
:ref:`sphx_glr_tutorials_api_phasorpy_components.py`
|
87
|
-
|
88
|
-
Notes
|
89
|
-
-----
|
90
|
-
The fraction of the second component is ``1.0 - fraction``.
|
91
|
-
|
92
|
-
For now, calculation of fraction of components from different
|
93
|
-
channels or frequencies is not supported. Only one pair of components can
|
94
|
-
be analyzed and will be broadcast to all channels/frequencies.
|
95
|
-
|
96
|
-
Examples
|
97
|
-
--------
|
98
|
-
>>> two_fractions_from_phasor(
|
99
|
-
... [0.6, 0.5, 0.4], [0.4, 0.3, 0.2], [0.2, 0.9], [0.4, 0.3]
|
100
|
-
... ) # doctest: +NUMBER
|
101
|
-
array([0.44, 0.56, 0.68])
|
102
|
-
|
103
|
-
"""
|
104
|
-
components_real = numpy.asarray(components_real)
|
105
|
-
components_imag = numpy.asarray(components_imag)
|
106
|
-
if components_real.shape != (2,):
|
107
|
-
raise ValueError(f'{components_real.shape=} != (2,)')
|
108
|
-
if components_imag.shape != (2,):
|
109
|
-
raise ValueError(f'{components_imag.shape=} != (2,)')
|
110
|
-
if (
|
111
|
-
components_real[0] == components_real[1]
|
112
|
-
and components_imag[0] == components_imag[1]
|
113
|
-
):
|
114
|
-
raise ValueError('components must have different coordinates')
|
115
|
-
|
116
|
-
return _fraction_on_segment( # type: ignore[no-any-return]
|
117
|
-
real,
|
118
|
-
imag,
|
119
|
-
components_real[0],
|
120
|
-
components_imag[0],
|
121
|
-
components_real[1],
|
122
|
-
components_imag[1],
|
123
|
-
)
|
124
|
-
|
125
|
-
|
126
|
-
def graphical_component_analysis(
|
127
|
-
real: ArrayLike,
|
128
|
-
imag: ArrayLike,
|
129
|
-
components_real: ArrayLike,
|
130
|
-
components_imag: ArrayLike,
|
131
|
-
/,
|
132
|
-
*,
|
133
|
-
radius: float = 0.05,
|
134
|
-
fractions: ArrayLike | None = None,
|
135
|
-
) -> tuple[NDArray[Any], ...]:
|
136
|
-
r"""Return fractions of two or three components from phasor coordinates.
|
137
|
-
|
138
|
-
The graphical method is based on moving circular cursors along the line
|
139
|
-
between pairs of components and quantifying the phasors for each
|
140
|
-
fraction.
|
141
|
-
|
142
|
-
Parameters
|
143
|
-
----------
|
144
|
-
real : array_like
|
145
|
-
Real component of phasor coordinates.
|
146
|
-
imag : array_like
|
147
|
-
Imaginary component of phasor coordinates.
|
148
|
-
components_real : array_like, shape (2,) or (3,)
|
149
|
-
Real coordinates for two or three components.
|
150
|
-
components_imag : array_like, shape (2,) or (3,)
|
151
|
-
Imaginary coordinates for two or three components.
|
152
|
-
radius : float, optional, default: 0.05
|
153
|
-
Radius of cursor.
|
154
|
-
fractions : array_like or int, optional
|
155
|
-
Number of equidistant fractions, or 1D array of fraction values.
|
156
|
-
Fraction values must be in range [0.0, 1.0].
|
157
|
-
If an integer, ``numpy.linspace(0.0, 1.0, fractions)`` fraction values
|
158
|
-
are used.
|
159
|
-
If None (default), the number of fractions is determined from the
|
160
|
-
longest distance between any pair of components and the radius of
|
161
|
-
the cursor (see Notes below).
|
162
|
-
|
163
|
-
Returns
|
164
|
-
-------
|
165
|
-
counts : tuple of ndarray
|
166
|
-
Counts along each line segment connecting components.
|
167
|
-
Ordered 0-1 (2 components) or 0-1, 0-2, 1-2 (3 components).
|
168
|
-
|
169
|
-
Raises
|
170
|
-
------
|
171
|
-
ValueError
|
172
|
-
The array shapes of `real` and `imag`, or `components_real` and
|
173
|
-
`components_imag` do not match.
|
174
|
-
The number of components is not 2 or 3.
|
175
|
-
Fraction values are out of range [0.0, 1.0].
|
176
|
-
|
177
|
-
See Also
|
178
|
-
--------
|
179
|
-
:ref:`sphx_glr_tutorials_api_phasorpy_components.py`
|
180
|
-
|
181
|
-
Notes
|
182
|
-
-----
|
183
|
-
For now, calculation of fraction of components from different
|
184
|
-
channels or frequencies is not supported. Only one set of components can
|
185
|
-
be analyzed and will be broadcast to all channels/frequencies.
|
186
|
-
|
187
|
-
The graphical method was first introduced in [1]_.
|
188
|
-
|
189
|
-
If no `fractions` are provided, the number of fractions (:math:`N`) used
|
190
|
-
is determined from the longest distance between any pair of components
|
191
|
-
(:math:`D`) and the radius of the cursor (:math:`R`):
|
192
|
-
|
193
|
-
.. math::
|
194
|
-
|
195
|
-
N = \frac{2 \cdot D}{R} + 1
|
196
|
-
|
197
|
-
The fractions can be retrieved by:
|
198
|
-
|
199
|
-
.. code-block:: python
|
200
|
-
|
201
|
-
fractions = numpy.linspace(0.0, 1.0, len(counts[0]))
|
202
|
-
|
203
|
-
References
|
204
|
-
----------
|
205
|
-
|
206
|
-
.. [1] Ranjit S, Datta R, Dvornikov A, and Gratton E.
|
207
|
-
`Multicomponent analysis of phasor plot in a single pixel to
|
208
|
-
calculate changes of metabolic trajectory in biological systems
|
209
|
-
<https://doi.org/10.1021/acs.jpca.9b07880>`_.
|
210
|
-
*J Phys Chem A*, 123(45): 9865-9873 (2019)
|
211
|
-
|
212
|
-
Examples
|
213
|
-
--------
|
214
|
-
Count the number of phasors between two components:
|
215
|
-
|
216
|
-
>>> graphical_component_analysis(
|
217
|
-
... [0.6, 0.3], [0.35, 0.38], [0.2, 0.9], [0.4, 0.3], fractions=6
|
218
|
-
... ) # doctest: +NUMBER
|
219
|
-
(array([0, 0, 1, 0, 1, 0], dtype=uint64),)
|
220
|
-
|
221
|
-
Count the number of phasors between the combinations of three components:
|
222
|
-
|
223
|
-
>>> graphical_component_analysis(
|
224
|
-
... [0.4, 0.5],
|
225
|
-
... [0.2, 0.3],
|
226
|
-
... [0.0, 0.2, 0.9],
|
227
|
-
... [0.0, 0.4, 0.3],
|
228
|
-
... fractions=6,
|
229
|
-
... ) # doctest: +NUMBER +NORMALIZE_WHITESPACE
|
230
|
-
(array([0, 1, 1, 1, 1, 0], dtype=uint64),
|
231
|
-
array([0, 1, 0, 0, 0, 0], dtype=uint64),
|
232
|
-
array([0, 1, 2, 0, 0, 0], dtype=uint64))
|
233
|
-
|
234
|
-
"""
|
235
|
-
real = numpy.asarray(real)
|
236
|
-
imag = numpy.asarray(imag)
|
237
|
-
components_real = numpy.asarray(components_real)
|
238
|
-
components_imag = numpy.asarray(components_imag)
|
239
|
-
if (
|
240
|
-
real.shape != imag.shape
|
241
|
-
or components_real.shape != components_imag.shape
|
242
|
-
):
|
243
|
-
raise ValueError('input array shapes must match')
|
244
|
-
if components_real.ndim != 1:
|
245
|
-
raise ValueError(
|
246
|
-
'component arrays are not one-dimensional: '
|
247
|
-
f'{components_real.ndim} dimensions found'
|
248
|
-
)
|
249
|
-
num_components = len(components_real)
|
250
|
-
if num_components not in {2, 3}:
|
251
|
-
raise ValueError('number of components must be 2 or 3')
|
252
|
-
|
253
|
-
if fractions is None:
|
254
|
-
longest_distance = 0
|
255
|
-
for i in range(num_components):
|
256
|
-
a_real = components_real[i]
|
257
|
-
a_imag = components_imag[i]
|
258
|
-
for j in range(i + 1, num_components):
|
259
|
-
b_real = components_real[j]
|
260
|
-
b_imag = components_imag[j]
|
261
|
-
_, _, length = _segment_direction_and_length(
|
262
|
-
a_real, a_imag, b_real, b_imag
|
263
|
-
)
|
264
|
-
longest_distance = max(longest_distance, length)
|
265
|
-
fractions = numpy.linspace(
|
266
|
-
0.0, 1.0, int(round(longest_distance / (radius / 2) + 1))
|
267
|
-
)
|
268
|
-
elif isinstance(fractions, (int, numbers.Integral)):
|
269
|
-
fractions = numpy.linspace(0.0, 1.0, fractions)
|
270
|
-
else:
|
271
|
-
fractions = numpy.asarray(fractions)
|
272
|
-
if fractions.ndim != 1:
|
273
|
-
raise ValueError('fractions is not a one-dimensional array')
|
274
|
-
|
275
|
-
counts = []
|
276
|
-
for i in range(num_components):
|
277
|
-
a_real = components_real[i]
|
278
|
-
a_imag = components_imag[i]
|
279
|
-
for j in range(i + 1, num_components):
|
280
|
-
b_real = components_real[j]
|
281
|
-
b_imag = components_imag[j]
|
282
|
-
ab_real = a_real - b_real
|
283
|
-
ab_imag = a_imag - b_imag
|
284
|
-
|
285
|
-
component_counts = []
|
286
|
-
for f in fractions:
|
287
|
-
if f < 0.0 or f > 1.0:
|
288
|
-
raise ValueError(f'fraction {f} out of bounds [0.0, 1.0]')
|
289
|
-
if num_components == 2:
|
290
|
-
mask = _is_inside_circle(
|
291
|
-
real,
|
292
|
-
imag,
|
293
|
-
b_real + f * ab_real, # cursor_real
|
294
|
-
b_imag + f * ab_imag, # cursor_imag
|
295
|
-
radius,
|
296
|
-
)
|
297
|
-
else:
|
298
|
-
# num_components == 3
|
299
|
-
mask = _is_inside_stadium(
|
300
|
-
real,
|
301
|
-
imag,
|
302
|
-
b_real + f * ab_real, # cursor_real
|
303
|
-
b_imag + f * ab_imag, # cursor_imag
|
304
|
-
components_real[3 - i - j], # c_real
|
305
|
-
components_imag[3 - i - j], # c_imag
|
306
|
-
radius,
|
307
|
-
)
|
308
|
-
fraction_counts = numpy.sum(mask, dtype=numpy.uint64)
|
309
|
-
component_counts.append(fraction_counts)
|
310
|
-
|
311
|
-
counts.append(numpy.asarray(component_counts))
|
312
|
-
|
313
|
-
return tuple(counts)
|
phasorpy/io.py
DELETED