phasorpy 0.3__cp312-cp312-win_arm64.whl → 0.5__cp312-cp312-win_arm64.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/_io.py +2655 -0
- phasorpy/_phasorpy.cp312-win_arm64.pyd +0 -0
- phasorpy/_phasorpy.pyx +119 -47
- phasorpy/_utils.py +165 -18
- phasorpy/cli.py +2 -0
- phasorpy/cluster.py +170 -0
- phasorpy/color.py +17 -13
- phasorpy/components.py +18 -18
- phasorpy/conftest.py +2 -0
- phasorpy/cursors.py +9 -9
- phasorpy/datasets.py +169 -10
- phasorpy/io.py +7 -1809
- phasorpy/phasor.py +281 -100
- phasorpy/plot.py +276 -36
- phasorpy/utils.py +12 -7
- phasorpy/version.py +14 -5
- {phasorpy-0.3.dist-info → phasorpy-0.5.dist-info}/METADATA +11 -15
- phasorpy-0.5.dist-info/RECORD +26 -0
- {phasorpy-0.3.dist-info → phasorpy-0.5.dist-info}/WHEEL +1 -1
- {phasorpy-0.3.dist-info → phasorpy-0.5.dist-info/licenses}/LICENSE.txt +1 -1
- phasorpy-0.3.dist-info/RECORD +0 -24
- {phasorpy-0.3.dist-info → phasorpy-0.5.dist-info}/entry_points.txt +0 -0
- {phasorpy-0.3.dist-info → phasorpy-0.5.dist-info}/top_level.txt +0 -0
Binary file
|
phasorpy/_phasorpy.pyx
CHANGED
@@ -4,15 +4,10 @@
|
|
4
4
|
# cython: wraparound = False
|
5
5
|
# cython: cdivision = True
|
6
6
|
# cython: nonecheck = False
|
7
|
+
# cython: freethreading_compatible = True
|
7
8
|
|
8
9
|
"""Cython implementation of low-level functions for the PhasorPy library."""
|
9
10
|
|
10
|
-
# TODO: replace short with unsigned char when Cython supports it
|
11
|
-
# https://github.com/cython/cython/pull/6196#issuecomment-2209509572
|
12
|
-
|
13
|
-
# TODO: use fused return types for functions returning more than two items
|
14
|
-
# https://github.com/cython/cython/issues/6328
|
15
|
-
|
16
11
|
cimport cython
|
17
12
|
|
18
13
|
from cython.parallel import parallel, prange
|
@@ -49,6 +44,12 @@ ctypedef fused float_t:
|
|
49
44
|
float
|
50
45
|
double
|
51
46
|
|
47
|
+
ctypedef fused uint_t:
|
48
|
+
uint8_t
|
49
|
+
uint16_t
|
50
|
+
uint32_t
|
51
|
+
uint64_t
|
52
|
+
|
52
53
|
ctypedef fused signal_t:
|
53
54
|
uint8_t
|
54
55
|
uint16_t
|
@@ -445,7 +446,7 @@ cdef (double, double) _phasor_from_fret_donor(
|
|
445
446
|
double omega,
|
446
447
|
double donor_lifetime,
|
447
448
|
double fret_efficiency,
|
448
|
-
double
|
449
|
+
double donor_fretting,
|
449
450
|
double donor_background,
|
450
451
|
double background_real,
|
451
452
|
double background_imag,
|
@@ -465,16 +466,16 @@ cdef (double, double) _phasor_from_fret_donor(
|
|
465
466
|
elif fret_efficiency > 1.0:
|
466
467
|
fret_efficiency = 1.0
|
467
468
|
|
468
|
-
if
|
469
|
-
|
470
|
-
elif
|
471
|
-
|
469
|
+
if donor_fretting < 0.0:
|
470
|
+
donor_fretting = 0.0
|
471
|
+
elif donor_fretting > 1.0:
|
472
|
+
donor_fretting = 1.0
|
472
473
|
|
473
474
|
if donor_background < 0.0:
|
474
475
|
donor_background = 0.0
|
475
476
|
|
476
|
-
f_pure = 1.0 -
|
477
|
-
f_quenched = (1.0 - fret_efficiency) *
|
477
|
+
f_pure = 1.0 - donor_fretting
|
478
|
+
f_quenched = (1.0 - fret_efficiency) * donor_fretting
|
478
479
|
sum = f_pure + f_quenched + donor_background
|
479
480
|
if sum < 1e-9:
|
480
481
|
# no signal in donor channel
|
@@ -510,7 +511,7 @@ cdef (double, double) _phasor_from_fret_acceptor(
|
|
510
511
|
double donor_lifetime,
|
511
512
|
double acceptor_lifetime,
|
512
513
|
double fret_efficiency,
|
513
|
-
double
|
514
|
+
double donor_fretting,
|
514
515
|
double donor_bleedthrough,
|
515
516
|
double acceptor_bleedthrough,
|
516
517
|
double acceptor_background,
|
@@ -535,10 +536,10 @@ cdef (double, double) _phasor_from_fret_acceptor(
|
|
535
536
|
elif fret_efficiency > 1.0:
|
536
537
|
fret_efficiency = 1.0
|
537
538
|
|
538
|
-
if
|
539
|
-
|
540
|
-
elif
|
541
|
-
|
539
|
+
if donor_fretting < 0.0:
|
540
|
+
donor_fretting = 0.0
|
541
|
+
elif donor_fretting > 1.0:
|
542
|
+
donor_fretting = 1.0
|
542
543
|
|
543
544
|
if donor_bleedthrough < 0.0:
|
544
545
|
donor_bleedthrough = 0.0
|
@@ -569,7 +570,7 @@ cdef (double, double) _phasor_from_fret_acceptor(
|
|
569
570
|
quenched_imag,
|
570
571
|
1.0,
|
571
572
|
1.0 - fret_efficiency,
|
572
|
-
1.0 -
|
573
|
+
1.0 - donor_fretting
|
573
574
|
)
|
574
575
|
|
575
576
|
# phasor of acceptor at frequency
|
@@ -591,8 +592,8 @@ cdef (double, double) _phasor_from_fret_acceptor(
|
|
591
592
|
sensitized_imag = mod * sin(phi)
|
592
593
|
|
593
594
|
# weighted average
|
594
|
-
f_donor = donor_bleedthrough * (1.0 -
|
595
|
-
f_acceptor =
|
595
|
+
f_donor = donor_bleedthrough * (1.0 - donor_fretting * fret_efficiency)
|
596
|
+
f_acceptor = donor_fretting * fret_efficiency
|
596
597
|
sum = f_donor + f_acceptor + acceptor_bleedthrough + acceptor_background
|
597
598
|
if sum < 1e-9:
|
598
599
|
# no signal in acceptor channel
|
@@ -913,7 +914,7 @@ cdef (float_t, float_t) _phasor_at_harmonic(
|
|
913
914
|
int harmonic,
|
914
915
|
int other_harmonic,
|
915
916
|
) noexcept nogil:
|
916
|
-
"""Return phasor coordinates on semicircle at other harmonic."""
|
917
|
+
"""Return phasor coordinates on universal semicircle at other harmonic."""
|
917
918
|
if isnan(real):
|
918
919
|
return <float_t> NAN, <float_t> NAN
|
919
920
|
|
@@ -976,7 +977,7 @@ cdef (float_t, float_t) _phasor_divide(
|
|
976
977
|
|
977
978
|
|
978
979
|
@cython.ufunc
|
979
|
-
cdef
|
980
|
+
cdef unsigned char _is_inside_range(
|
980
981
|
float_t x, # point
|
981
982
|
float_t y,
|
982
983
|
float_t xmin, # x range
|
@@ -996,7 +997,7 @@ cdef short _is_inside_range(
|
|
996
997
|
|
997
998
|
|
998
999
|
@cython.ufunc
|
999
|
-
cdef
|
1000
|
+
cdef unsigned char _is_inside_rectangle(
|
1000
1001
|
float_t x, # point
|
1001
1002
|
float_t y,
|
1002
1003
|
float_t x0, # segment start
|
@@ -1038,7 +1039,7 @@ cdef short _is_inside_rectangle(
|
|
1038
1039
|
|
1039
1040
|
|
1040
1041
|
@cython.ufunc
|
1041
|
-
cdef
|
1042
|
+
cdef unsigned char _is_inside_polar_rectangle(
|
1042
1043
|
float_t x, # point
|
1043
1044
|
float_t y,
|
1044
1045
|
float_t angle_min, # phase, -pi to pi
|
@@ -1048,7 +1049,7 @@ cdef short _is_inside_polar_rectangle(
|
|
1048
1049
|
) noexcept nogil:
|
1049
1050
|
"""Return whether point is inside polar rectangle.
|
1050
1051
|
|
1051
|
-
Angles should be in range -pi
|
1052
|
+
Angles should be in range [-pi, pi], else performance is degraded.
|
1052
1053
|
|
1053
1054
|
"""
|
1054
1055
|
cdef:
|
@@ -1077,7 +1078,7 @@ cdef short _is_inside_polar_rectangle(
|
|
1077
1078
|
|
1078
1079
|
|
1079
1080
|
@cython.ufunc
|
1080
|
-
cdef
|
1081
|
+
cdef unsigned char _is_inside_circle(
|
1081
1082
|
float_t x, # point
|
1082
1083
|
float_t y,
|
1083
1084
|
float_t x0, # circle center
|
@@ -1094,7 +1095,7 @@ cdef short _is_inside_circle(
|
|
1094
1095
|
|
1095
1096
|
|
1096
1097
|
@cython.ufunc
|
1097
|
-
cdef
|
1098
|
+
cdef unsigned char _is_inside_ellipse(
|
1098
1099
|
float_t x, # point
|
1099
1100
|
float_t y,
|
1100
1101
|
float_t x0, # ellipse center
|
@@ -1129,7 +1130,7 @@ cdef short _is_inside_ellipse(
|
|
1129
1130
|
|
1130
1131
|
|
1131
1132
|
@cython.ufunc
|
1132
|
-
cdef
|
1133
|
+
cdef unsigned char _is_inside_ellipse_(
|
1133
1134
|
float_t x, # point
|
1134
1135
|
float_t y,
|
1135
1136
|
float_t x0, # ellipse center
|
@@ -1158,7 +1159,7 @@ cdef short _is_inside_ellipse_(
|
|
1158
1159
|
|
1159
1160
|
|
1160
1161
|
@cython.ufunc
|
1161
|
-
cdef
|
1162
|
+
cdef unsigned char _is_inside_stadium(
|
1162
1163
|
float_t x, # point
|
1163
1164
|
float_t y,
|
1164
1165
|
float_t x0, # line start
|
@@ -1204,7 +1205,7 @@ _is_near_segment = _is_inside_stadium
|
|
1204
1205
|
|
1205
1206
|
|
1206
1207
|
@cython.ufunc
|
1207
|
-
cdef
|
1208
|
+
cdef unsigned char _is_near_line(
|
1208
1209
|
float_t x, # point
|
1209
1210
|
float_t y,
|
1210
1211
|
float_t x0, # line start
|
@@ -1470,7 +1471,7 @@ cdef float_t _distance_from_line(
|
|
1470
1471
|
|
1471
1472
|
|
1472
1473
|
@cython.ufunc
|
1473
|
-
cdef (
|
1474
|
+
cdef (float_t, float_t, float_t) _segment_direction_and_length(
|
1474
1475
|
float_t x0, # segment start
|
1475
1476
|
float_t y0,
|
1476
1477
|
float_t x1, # segment end
|
@@ -1494,7 +1495,7 @@ cdef (double, double, double) _segment_direction_and_length(
|
|
1494
1495
|
|
1495
1496
|
|
1496
1497
|
@cython.ufunc
|
1497
|
-
cdef (
|
1498
|
+
cdef (float_t, float_t, float_t, float_t) _intersection_circle_circle(
|
1498
1499
|
float_t x0, # circle 0
|
1499
1500
|
float_t y0,
|
1500
1501
|
float_t r0,
|
@@ -1532,15 +1533,15 @@ cdef (double, double, double, double) _intersection_circle_circle(
|
|
1532
1533
|
hd = sqrt(dd) / dr
|
1533
1534
|
ld = ll / dr
|
1534
1535
|
return (
|
1535
|
-
ld * dx + hd * dy + x0,
|
1536
|
-
ld * dy - hd * dx + y0,
|
1537
|
-
ld * dx - hd * dy + x0,
|
1538
|
-
ld * dy + hd * dx + y0,
|
1536
|
+
<float_t> (ld * dx + hd * dy + x0),
|
1537
|
+
<float_t> (ld * dy - hd * dx + y0),
|
1538
|
+
<float_t> (ld * dx - hd * dy + x0),
|
1539
|
+
<float_t> (ld * dy + hd * dx + y0),
|
1539
1540
|
)
|
1540
1541
|
|
1541
1542
|
|
1542
1543
|
@cython.ufunc
|
1543
|
-
cdef (
|
1544
|
+
cdef (float_t, float_t, float_t, float_t) _intersection_circle_line(
|
1544
1545
|
float_t x, # circle
|
1545
1546
|
float_t y,
|
1546
1547
|
float_t r,
|
@@ -1575,10 +1576,10 @@ cdef (double, double, double, double) _intersection_circle_line(
|
|
1575
1576
|
return NAN, NAN, NAN, NAN
|
1576
1577
|
rdd = sqrt(rdd)
|
1577
1578
|
return (
|
1578
|
-
x + (dd * dy + copysign(1.0, dy) * dx * rdd) / dr,
|
1579
|
-
y + (-dd * dx + fabs(dy) * rdd) / dr,
|
1580
|
-
x + (dd * dy - copysign(1.0, dy) * dx * rdd) / dr,
|
1581
|
-
y + (-dd * dx - fabs(dy) * rdd) / dr,
|
1579
|
+
x + <float_t> ((dd * dy + copysign(1.0, dy) * dx * rdd) / dr),
|
1580
|
+
y + <float_t> ((-dd * dx + fabs(dy) * rdd) / dr),
|
1581
|
+
x + <float_t> ((dd * dy - copysign(1.0, dy) * dx * rdd) / dr),
|
1582
|
+
y + <float_t> ((-dd * dx - fabs(dy) * rdd) / dr),
|
1582
1583
|
)
|
1583
1584
|
|
1584
1585
|
|
@@ -1659,7 +1660,7 @@ cdef float_t _blend_lighten(
|
|
1659
1660
|
|
1660
1661
|
|
1661
1662
|
@cython.ufunc
|
1662
|
-
cdef (
|
1663
|
+
cdef (float_t, float_t, float_t) _phasor_threshold_open(
|
1663
1664
|
float_t mean,
|
1664
1665
|
float_t real,
|
1665
1666
|
float_t imag,
|
@@ -1721,7 +1722,7 @@ cdef (double, double, double) _phasor_threshold_open(
|
|
1721
1722
|
|
1722
1723
|
|
1723
1724
|
@cython.ufunc
|
1724
|
-
cdef (
|
1725
|
+
cdef (float_t, float_t, float_t) _phasor_threshold_closed(
|
1725
1726
|
float_t mean,
|
1726
1727
|
float_t real,
|
1727
1728
|
float_t imag,
|
@@ -1783,7 +1784,7 @@ cdef (double, double, double) _phasor_threshold_closed(
|
|
1783
1784
|
|
1784
1785
|
|
1785
1786
|
@cython.ufunc
|
1786
|
-
cdef (
|
1787
|
+
cdef (float_t, float_t, float_t) _phasor_threshold_mean_open(
|
1787
1788
|
float_t mean,
|
1788
1789
|
float_t real,
|
1789
1790
|
float_t imag,
|
@@ -1803,7 +1804,7 @@ cdef (double, double, double) _phasor_threshold_mean_open(
|
|
1803
1804
|
|
1804
1805
|
|
1805
1806
|
@cython.ufunc
|
1806
|
-
cdef (
|
1807
|
+
cdef (float_t, float_t, float_t) _phasor_threshold_mean_closed(
|
1807
1808
|
float_t mean,
|
1808
1809
|
float_t real,
|
1809
1810
|
float_t imag,
|
@@ -1823,7 +1824,7 @@ cdef (double, double, double) _phasor_threshold_mean_closed(
|
|
1823
1824
|
|
1824
1825
|
|
1825
1826
|
@cython.ufunc
|
1826
|
-
cdef (
|
1827
|
+
cdef (float_t, float_t, float_t) _phasor_threshold_nan(
|
1827
1828
|
float_t mean,
|
1828
1829
|
float_t real,
|
1829
1830
|
float_t imag,
|
@@ -2159,3 +2160,74 @@ def _median_filter_2d(
|
|
2159
2160
|
image[i, j] = filtered_image[i, j]
|
2160
2161
|
|
2161
2162
|
free(kernel)
|
2163
|
+
|
2164
|
+
|
2165
|
+
###############################################################################
|
2166
|
+
# Decoder functions
|
2167
|
+
|
2168
|
+
|
2169
|
+
@cython.boundscheck(True)
|
2170
|
+
def _flimlabs_signal(
|
2171
|
+
uint_t[:, :, ::] signal, # channel, pixel, bin
|
2172
|
+
list data, # list[list[list[[int, int]]]]
|
2173
|
+
ssize_t channel = -1 # -1 == None
|
2174
|
+
):
|
2175
|
+
"""Return TCSPC histogram image from FLIM LABS JSON intensity data."""
|
2176
|
+
cdef:
|
2177
|
+
uint_t[::] signal_
|
2178
|
+
list channels, pixels
|
2179
|
+
ssize_t c, i, h, count
|
2180
|
+
|
2181
|
+
if channel < 0:
|
2182
|
+
c = 0
|
2183
|
+
for channels in data:
|
2184
|
+
i = 0
|
2185
|
+
for pixels in channels:
|
2186
|
+
signal_ = signal[c, i]
|
2187
|
+
for h, count in pixels:
|
2188
|
+
signal_[h] = <uint_t> count
|
2189
|
+
i += 1
|
2190
|
+
c += 1
|
2191
|
+
else:
|
2192
|
+
i = 0
|
2193
|
+
for pixels in data[channel]:
|
2194
|
+
signal_ = signal[0, i]
|
2195
|
+
for h, count in pixels:
|
2196
|
+
signal_[h] = <uint_t> count
|
2197
|
+
i += 1
|
2198
|
+
|
2199
|
+
|
2200
|
+
@cython.boundscheck(True)
|
2201
|
+
def _flimlabs_mean(
|
2202
|
+
float_t[:, ::] mean, # channel, pixel
|
2203
|
+
list data, # list[list[list[[int, int]]]]
|
2204
|
+
ssize_t channel = -1 # -1 == None
|
2205
|
+
):
|
2206
|
+
"""Return mean intensity image from FLIM LABS JSON intensity data."""
|
2207
|
+
cdef:
|
2208
|
+
float_t[::] mean_
|
2209
|
+
list channels, pixels
|
2210
|
+
ssize_t c, i, h, count
|
2211
|
+
double sum
|
2212
|
+
|
2213
|
+
if channel < 0:
|
2214
|
+
c = 0
|
2215
|
+
for channels in data:
|
2216
|
+
mean_ = mean[c]
|
2217
|
+
i = 0
|
2218
|
+
for pixels in channels:
|
2219
|
+
sum = 0.0
|
2220
|
+
for h, count in pixels:
|
2221
|
+
sum += <double> count
|
2222
|
+
mean_[i] = <float_t> (sum / 256.0)
|
2223
|
+
i += 1
|
2224
|
+
c += 1
|
2225
|
+
else:
|
2226
|
+
i = 0
|
2227
|
+
mean_ = mean[0]
|
2228
|
+
for pixels in data[channel]:
|
2229
|
+
sum = 0.0
|
2230
|
+
for h, count in pixels:
|
2231
|
+
sum += <double> count
|
2232
|
+
mean_[i] = <float_t> (sum / 256.0)
|
2233
|
+
i += 1
|
phasorpy/_utils.py
CHANGED
@@ -1,28 +1,30 @@
|
|
1
|
-
"""Private auxiliary and convenience functions.
|
2
|
-
|
3
|
-
"""
|
1
|
+
"""Private auxiliary and convenience functions."""
|
4
2
|
|
5
3
|
from __future__ import annotations
|
6
4
|
|
7
|
-
__all__
|
5
|
+
__all__ = [
|
8
6
|
'chunk_iter',
|
9
7
|
'dilate_coordinates',
|
10
8
|
'kwargs_notnone',
|
11
9
|
'parse_harmonic',
|
12
10
|
'parse_kwargs',
|
11
|
+
'parse_signal_axis',
|
12
|
+
'parse_skip_axis',
|
13
13
|
'phasor_from_polar_scalar',
|
14
14
|
'phasor_to_polar_scalar',
|
15
15
|
'scale_matrix',
|
16
|
+
'set_module',
|
16
17
|
'sort_coordinates',
|
17
18
|
'update_kwargs',
|
18
19
|
]
|
19
20
|
|
20
21
|
import math
|
21
22
|
import numbers
|
23
|
+
from collections.abc import Sequence
|
22
24
|
from typing import TYPE_CHECKING
|
23
25
|
|
24
26
|
if TYPE_CHECKING:
|
25
|
-
from ._typing import Any,
|
27
|
+
from ._typing import Any, ArrayLike, Literal, NDArray, Iterator
|
26
28
|
|
27
29
|
import numpy
|
28
30
|
|
@@ -247,6 +249,131 @@ def phasor_from_polar_scalar(
|
|
247
249
|
return real, imag
|
248
250
|
|
249
251
|
|
252
|
+
def parse_signal_axis(
|
253
|
+
signal: ArrayLike,
|
254
|
+
/,
|
255
|
+
axis: int | str | None = None,
|
256
|
+
) -> tuple[int, str]:
|
257
|
+
"""Return axis over which phasor coordinates are computed.
|
258
|
+
|
259
|
+
The axis parameter is not validated against the signal shape.
|
260
|
+
|
261
|
+
Parameters
|
262
|
+
----------
|
263
|
+
signal : array_like
|
264
|
+
Image stack.
|
265
|
+
axis : int or str, optional
|
266
|
+
Axis over which phasor coordinates are computed.
|
267
|
+
By default, the 'H' or 'C' axes if `signal` contains such
|
268
|
+
dimension names, else the last axis (-1).
|
269
|
+
|
270
|
+
Returns
|
271
|
+
-------
|
272
|
+
axis : int
|
273
|
+
Axis over which phasor coordinates are computed.
|
274
|
+
axis_label : str
|
275
|
+
Axis label from `signal.dims` if any.
|
276
|
+
|
277
|
+
Raises
|
278
|
+
------
|
279
|
+
ValueError
|
280
|
+
Axis not found in signal.dims or invalid for signal type.
|
281
|
+
|
282
|
+
Examples
|
283
|
+
--------
|
284
|
+
>>> parse_signal_axis([])
|
285
|
+
(-1, '')
|
286
|
+
>>> parse_signal_axis([], 1)
|
287
|
+
(1, '')
|
288
|
+
>>> class DataArray:
|
289
|
+
... dims = ('C', 'H', 'Y', 'X')
|
290
|
+
...
|
291
|
+
>>> parse_signal_axis(DataArray())
|
292
|
+
(1, 'H')
|
293
|
+
>>> parse_signal_axis(DataArray(), 'C')
|
294
|
+
(0, 'C')
|
295
|
+
>>> parse_signal_axis(DataArray(), 1)
|
296
|
+
(1, 'H')
|
297
|
+
|
298
|
+
"""
|
299
|
+
if hasattr(signal, 'dims'):
|
300
|
+
assert isinstance(signal.dims, tuple)
|
301
|
+
if axis is None:
|
302
|
+
for ax in 'HC':
|
303
|
+
if ax in signal.dims:
|
304
|
+
return signal.dims.index(ax), ax
|
305
|
+
return -1, signal.dims[-1]
|
306
|
+
if isinstance(axis, int):
|
307
|
+
return axis, signal.dims[axis]
|
308
|
+
if axis in signal.dims:
|
309
|
+
return signal.dims.index(axis), axis
|
310
|
+
raise ValueError(f'{axis=} not found in {signal.dims}')
|
311
|
+
if axis is None:
|
312
|
+
return -1, ''
|
313
|
+
if isinstance(axis, int):
|
314
|
+
return axis, ''
|
315
|
+
raise ValueError(f'{axis=} not valid for {type(signal)=}')
|
316
|
+
|
317
|
+
|
318
|
+
def parse_skip_axis(
|
319
|
+
skip_axis: int | Sequence[int] | None,
|
320
|
+
/,
|
321
|
+
ndim: int,
|
322
|
+
prepend_axis: bool = False,
|
323
|
+
) -> tuple[tuple[int, ...], tuple[int, ...]]:
|
324
|
+
"""Return axes to skip and not to skip.
|
325
|
+
|
326
|
+
This helper function is used to validate and parse `skip_axis`
|
327
|
+
parameters.
|
328
|
+
|
329
|
+
Parameters
|
330
|
+
----------
|
331
|
+
skip_axis : int or sequence of int, optional
|
332
|
+
Axes to skip. If None, no axes are skipped.
|
333
|
+
ndim : int
|
334
|
+
Dimensionality of array in which to skip axes.
|
335
|
+
prepend_axis : bool, optional
|
336
|
+
Prepend one dimension and include in `skip_axis`.
|
337
|
+
|
338
|
+
Returns
|
339
|
+
-------
|
340
|
+
skip_axis : tuple of int
|
341
|
+
Ordered, positive values of `skip_axis`.
|
342
|
+
other_axis : tuple of int
|
343
|
+
Axes indices not included in `skip_axis`.
|
344
|
+
|
345
|
+
Raises
|
346
|
+
------
|
347
|
+
IndexError
|
348
|
+
If any `skip_axis` value is out of bounds of `ndim`.
|
349
|
+
|
350
|
+
Examples
|
351
|
+
--------
|
352
|
+
>>> parse_skip_axis((1, -2), 5)
|
353
|
+
((1, 3), (0, 2, 4))
|
354
|
+
|
355
|
+
>>> parse_skip_axis((1, -2), 5, True)
|
356
|
+
((0, 2, 4), (1, 3, 5))
|
357
|
+
|
358
|
+
"""
|
359
|
+
if ndim < 0:
|
360
|
+
raise ValueError(f'invalid {ndim=}')
|
361
|
+
if skip_axis is None:
|
362
|
+
if prepend_axis:
|
363
|
+
return (0,), tuple(range(1, ndim + 1))
|
364
|
+
return (), tuple(range(ndim))
|
365
|
+
if not isinstance(skip_axis, Sequence):
|
366
|
+
skip_axis = (skip_axis,)
|
367
|
+
if any(i >= ndim or i < -ndim for i in skip_axis):
|
368
|
+
raise IndexError(f'skip_axis={skip_axis} out of range for {ndim=}')
|
369
|
+
skip_axis = sorted(int(i % ndim) for i in skip_axis)
|
370
|
+
if prepend_axis:
|
371
|
+
skip_axis = [0] + [i + 1 for i in skip_axis]
|
372
|
+
ndim += 1
|
373
|
+
other_axis = tuple(i for i in range(ndim) if i not in skip_axis)
|
374
|
+
return tuple(skip_axis), other_axis
|
375
|
+
|
376
|
+
|
250
377
|
def parse_harmonic(
|
251
378
|
harmonic: int | Sequence[int] | Literal['all'] | str | None,
|
252
379
|
harmonic_max: int | None = None,
|
@@ -259,7 +386,7 @@ def parse_harmonic(
|
|
259
386
|
|
260
387
|
Parameters
|
261
388
|
----------
|
262
|
-
harmonic : int,
|
389
|
+
harmonic : int, sequence of int, 'all', or None
|
263
390
|
Harmonic parameter to parse.
|
264
391
|
harmonic_max : int, optional
|
265
392
|
Maximum value allowed in `hamonic`. Must be one or greater.
|
@@ -278,7 +405,7 @@ def parse_harmonic(
|
|
278
405
|
Raises
|
279
406
|
------
|
280
407
|
IndexError
|
281
|
-
Any element is out of range `[1
|
408
|
+
Any element is out of range `[1, harmonic_max]`.
|
282
409
|
ValueError
|
283
410
|
Elements are not unique.
|
284
411
|
Harmonic is empty.
|
@@ -299,7 +426,7 @@ def parse_harmonic(
|
|
299
426
|
if harmonic < 1 or (
|
300
427
|
harmonic_max is not None and harmonic > harmonic_max
|
301
428
|
):
|
302
|
-
raise IndexError(f'{harmonic=} out of range [1
|
429
|
+
raise IndexError(f'{harmonic=} out of range [1, {harmonic_max}]')
|
303
430
|
return [int(harmonic)], False
|
304
431
|
|
305
432
|
if isinstance(harmonic, str):
|
@@ -311,7 +438,7 @@ def parse_harmonic(
|
|
311
438
|
return list(range(1, harmonic_max + 1)), True
|
312
439
|
raise ValueError(f'{harmonic=!r} is not a valid harmonic')
|
313
440
|
|
314
|
-
h = numpy.atleast_1d(
|
441
|
+
h = numpy.atleast_1d(harmonic)
|
315
442
|
if h.size == 0:
|
316
443
|
raise ValueError(f'{harmonic=} is empty')
|
317
444
|
if h.dtype.kind not in 'iu' or h.ndim != 1:
|
@@ -322,14 +449,14 @@ def parse_harmonic(
|
|
322
449
|
raise IndexError(f'{harmonic=} element > {harmonic_max}]')
|
323
450
|
if numpy.unique(h).size != h.size:
|
324
451
|
raise ValueError(f'{harmonic=} elements must be unique')
|
325
|
-
return
|
452
|
+
return [int(i) for i in harmonic], True
|
326
453
|
|
327
454
|
|
328
455
|
def chunk_iter(
|
329
456
|
shape: tuple[int, ...],
|
330
457
|
chunk_shape: tuple[int, ...],
|
331
458
|
/,
|
332
|
-
|
459
|
+
dims: Sequence[str] | None = None,
|
333
460
|
*,
|
334
461
|
pattern: str | None = None,
|
335
462
|
squeeze: bool = False,
|
@@ -343,11 +470,11 @@ def chunk_iter(
|
|
343
470
|
Shape of C-order ndarray to chunk.
|
344
471
|
chunk_shape : tuple of int
|
345
472
|
Shape of chunks in the most significant dimensions.
|
346
|
-
|
473
|
+
dims : sequence of str, optional
|
347
474
|
Labels for each axis in shape if `pattern` is None.
|
348
475
|
pattern : str, optional
|
349
476
|
String to format chunk indices.
|
350
|
-
If None, use ``_[{
|
477
|
+
If None, use ``_[{dims[index]}{chunk_index[index]}]`` for each axis.
|
351
478
|
squeeze : bool
|
352
479
|
If true, do not include length-1 chunked dimensions in label
|
353
480
|
unless dimensions are part of `chunk_shape`.
|
@@ -384,11 +511,11 @@ def chunk_iter(
|
|
384
511
|
ndim = len(shape)
|
385
512
|
|
386
513
|
sep = '_'
|
387
|
-
if
|
388
|
-
|
514
|
+
if dims is None:
|
515
|
+
dims = sep * ndim
|
389
516
|
sep = ''
|
390
|
-
elif ndim != len(
|
391
|
-
raise ValueError(f'{len(shape)=} != {len(
|
517
|
+
elif ndim != len(dims):
|
518
|
+
raise ValueError(f'{len(shape)=} != {len(dims)=}')
|
392
519
|
|
393
520
|
if pattern is not None:
|
394
521
|
try:
|
@@ -406,7 +533,7 @@ def chunk_iter(
|
|
406
533
|
|
407
534
|
chunked_shape = []
|
408
535
|
pattern_list = []
|
409
|
-
for i, (size, chunk_size, ax) in enumerate(zip(shape, chunk_shape,
|
536
|
+
for i, (size, chunk_size, ax) in enumerate(zip(shape, chunk_shape, dims)):
|
410
537
|
if size <= 0:
|
411
538
|
raise ValueError('shape must contain positive sizes')
|
412
539
|
if chunk_size <= 0:
|
@@ -452,3 +579,23 @@ def chunk_iter(
|
|
452
579
|
for i in range(ndim)
|
453
580
|
),
|
454
581
|
)
|
582
|
+
|
583
|
+
|
584
|
+
def set_module(globs: dict[str, Any], /) -> None:
|
585
|
+
"""Set ``__module__`` attribute for objects in ``__all__``.
|
586
|
+
|
587
|
+
Parameters
|
588
|
+
----------
|
589
|
+
globs : dict
|
590
|
+
Module namespace to modify.
|
591
|
+
|
592
|
+
Examples
|
593
|
+
--------
|
594
|
+
>>> set_module(globals())
|
595
|
+
|
596
|
+
"""
|
597
|
+
name = globs['__name__']
|
598
|
+
for item in globs['__all__']:
|
599
|
+
obj = globs[item]
|
600
|
+
if hasattr(obj, '__module__'):
|
601
|
+
obj.__module__ = name
|