phasorpy 0.5__cp312-cp312-win_amd64.whl → 0.7__cp312-cp312-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/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
@@ -1,9 +0,0 @@
1
- # phasorpy.io proxy module
2
- # The VSCode debugger cannot to step into or break inside modules named io.py
3
-
4
- from ._io import *
5
- from ._io import __all__, __doc__
6
- from ._utils import set_module
7
-
8
- set_module(globals())
9
- del set_module