dkist-processing-math 2.1.2__py3-none-any.whl → 2.2.0rc1__py3-none-any.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.
changelog/18.misc.rst ADDED
@@ -0,0 +1 @@
1
+ Update bitbucket pipelines to use common scripts for checking for changelog snippets and verifying doc builds.
changelog/19.misc.rst ADDED
@@ -0,0 +1 @@
1
+ Update Bitbucket pipelines to use execute script for standard steps.
@@ -0,0 +1,2 @@
1
+ Add `stddev_numpy_arrays` function for computing the per-pixel standard deviation of an iterable of arrays.
2
+ If a `generator` is provided this function is very memory-light, even for huge inputs.
@@ -38,3 +38,65 @@ def average_numpy_arrays(arrays: Union[Iterable[np.ndarray], np.ndarray]) -> np.
38
38
  if output is not None:
39
39
  return output / (count + 1)
40
40
  raise ValueError("data_arrays is empty")
41
+
42
+
43
+ def stddev_numpy_arrays(arrays: Iterable[np.ndarray]) -> np.ndarray:
44
+ r"""
45
+ Given an iterable of numpy arrays, calculate the pixel-wise standard deviation and return it in a numpy array.
46
+
47
+ This function uses Welford's single-pass algorithm. The standard deviation for the :math:`n^{th}` array is
48
+
49
+ .. math::
50
+ \sigma_n = \sqrt{\frac{M_{2,n}}{n}}
51
+
52
+ where
53
+
54
+ .. math::
55
+ M_{2,n} = M_{2,n-1} + (x_n - \bar{x}_{n-1})(x_n - \bar{x}_n)
56
+
57
+ and
58
+
59
+ .. math::
60
+ \bar{x}_n = \bar{x}_{n-1} + \frac{x_n - \bar{x}_{n-1}}{n}
61
+
62
+ Note that in computing :math:`\sigma` we divide by :math:`n` instead of :math:`(n-1)`. This is to match the default
63
+ behavior of `numpy.std`.
64
+
65
+ Parameters
66
+ ----------
67
+ arrays
68
+ The arrays to be to compute the standard deviation of
69
+
70
+ Returns
71
+ -------
72
+ The standard deviation of the input arrays
73
+ """
74
+ if isinstance(arrays, np.ndarray):
75
+ raise ValueError(
76
+ "Need an Iterator with at least two arrays, but a single array was provided."
77
+ )
78
+
79
+ running_mean = 0.0
80
+ running_M2 = 0.0
81
+ count = 0
82
+ for array in arrays:
83
+ count += 1
84
+ try:
85
+ delta = array - running_mean
86
+ except ValueError:
87
+ raise ValueError(
88
+ f"All arrays must be the same shape. "
89
+ f"Shape of initial array = {running_mean.shape} "
90
+ f"Shape of current array = {array.shape}"
91
+ )
92
+ running_mean += delta / count
93
+ delta2 = array - running_mean
94
+ running_M2 += delta * delta2
95
+
96
+ match count:
97
+ case 0:
98
+ raise ValueError("arrays is empty")
99
+ case 1:
100
+ raise ValueError(f"Need at least two arrays. Provided Iterator only has {count}")
101
+ case _:
102
+ return np.sqrt(running_M2 / count)
@@ -4,8 +4,13 @@ import pytest
4
4
  rng = np.random.default_rng()
5
5
 
6
6
 
7
+ @pytest.fixture
8
+ def test_array_shape() -> tuple[int, int]:
9
+ return (10, 10)
10
+
11
+
7
12
  @pytest.fixture()
8
- def numpy_arrays():
13
+ def numpy_arrays(test_array_shape):
9
14
  """
10
15
  Create an iterable of random 10 x 10 arrays
11
16
 
@@ -14,7 +19,11 @@ def numpy_arrays():
14
19
  List[np.ndarray]
15
20
 
16
21
  """
17
- arrays = [rng.standard_normal((10, 10)), rng.standard_normal((10, 10))]
22
+ arrays = [
23
+ rng.standard_normal(test_array_shape),
24
+ rng.standard_normal(test_array_shape),
25
+ rng.standard_normal(test_array_shape),
26
+ ]
18
27
  return arrays
19
28
 
20
29
 
@@ -32,7 +41,7 @@ def numpy_array(numpy_arrays):
32
41
 
33
42
 
34
43
  @pytest.fixture()
35
- def numpy_arrays_wrong_shape():
44
+ def numpy_arrays_wrong_shape(test_array_shape):
36
45
  """
37
46
  Create an iterable of random arrays of differing sizes
38
47
 
@@ -41,7 +50,10 @@ def numpy_arrays_wrong_shape():
41
50
  List[np.ndarray]
42
51
 
43
52
  """
44
- arrays = [rng.standard_normal((10, 10)), rng.standard_normal((10, 15))]
53
+ arrays = [
54
+ rng.standard_normal(test_array_shape),
55
+ rng.standard_normal(tuple(i * 2 for i in test_array_shape)),
56
+ ]
45
57
  return arrays
46
58
 
47
59
 
@@ -2,6 +2,7 @@ import numpy as np
2
2
  import pytest
3
3
 
4
4
  from dkist_processing_math.statistics import average_numpy_arrays
5
+ from dkist_processing_math.statistics import stddev_numpy_arrays
5
6
 
6
7
 
7
8
  @pytest.fixture()
@@ -60,3 +61,24 @@ def test_average_with_large_values(int_arrays_with_large_values):
60
61
  """
61
62
  expected = np.ones((10, 10)) * 17500
62
63
  np.testing.assert_equal(expected, average_numpy_arrays(int_arrays_with_large_values))
64
+
65
+
66
+ def test_stddev_numpy_arrays(numpy_arrays):
67
+ expected_result = np.std(numpy_arrays, axis=0)
68
+ result = stddev_numpy_arrays(numpy_arrays)
69
+
70
+ np.testing.assert_allclose(result, expected_result)
71
+
72
+
73
+ def test_stddec_numpy_arrays_failures(numpy_arrays, numpy_arrays_wrong_shape):
74
+ with pytest.raises(ValueError, match="Need an Iterator with at least two arrays"):
75
+ stddev_numpy_arrays(numpy_arrays[0])
76
+
77
+ with pytest.raises(ValueError, match="All arrays must be the same shape"):
78
+ stddev_numpy_arrays(numpy_arrays_wrong_shape)
79
+
80
+ with pytest.raises(ValueError, match="Need at least two arrays"):
81
+ stddev_numpy_arrays([numpy_arrays[0]])
82
+
83
+ with pytest.raises(ValueError, match="arrays is empty"):
84
+ stddev_numpy_arrays([])
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.2
2
2
  Name: dkist-processing-math
3
- Version: 2.1.2
3
+ Version: 2.2.0rc1
4
4
  Summary: Common math functionality used in the DKIST calibrations suite
5
5
  Author-email: NSO / AURA <dkistdc@nso.edu>
6
6
  License: BSD-3-Clause
@@ -1,15 +1,18 @@
1
1
  changelog/.gitempty,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ changelog/18.misc.rst,sha256=nhzzLztW_sNOvZ0EeO0jCOpMf1StXdl6IpsJWS6sc4I,111
3
+ changelog/19.misc.rst,sha256=oCFAWH7tQxLKxn6VfQBzAocWj903IvwXlgotpVuRSXY,69
4
+ changelog/20.feature.rst,sha256=5gRsTu_0ql426Z1Wn-x9IXiALTiIZYpczx3_9Y-HYR4,195
2
5
  dkist_processing_math/__init__.py,sha256=Mf7liCQC9M5YgFrz2NZ0dZ5smvcuw47ww6MC9mijAHw,249
3
6
  dkist_processing_math/arithmetic.py,sha256=h8E4DUBGQLDUiNWmZb2kZRrLMmjEpdl1S_mIi3ntZ6w,3694
4
7
  dkist_processing_math/feature.py,sha256=vwrkXBlK7wM6-KFaWxhBsMpHiHbpsWOiuGOhf3PRLSY,2003
5
8
  dkist_processing_math/linear_algebra.py,sha256=3ycmWklgLI89NjQgwgY81Zv1m_L4jJXHnt70TzQDVWQ,4179
6
- dkist_processing_math/statistics.py,sha256=Dgt1gSDQDN2XGV38xSQ3ZUmyIeGMZRoBl2vGHcADAYs,1233
9
+ dkist_processing_math/statistics.py,sha256=_N6LPRTAFT6lQXv8DJtzK6icIHT3JsM4YWR7JfOSiHA,3058
7
10
  dkist_processing_math/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
- dkist_processing_math/tests/conftest.py,sha256=Pg7lDltF_N7SVIjAi0rRADXBftsSzw0o94-SfKW1Zko,1333
11
+ dkist_processing_math/tests/conftest.py,sha256=TV0o-R2lU1MvZleme0w-akcgO5wOTlFNKhjSlmDwkqg,1593
9
12
  dkist_processing_math/tests/test_arithmetic.py,sha256=18YI4Fr_m0KU0dYR8_ef7CMmnOlb-Zc4I8ERgoYdxdA,5658
10
13
  dkist_processing_math/tests/test_feature.py,sha256=ZB8r_IUrAyTAlKbxEXwr8Q2zS7KPmlJhaU1l0guK0Xc,2311
11
14
  dkist_processing_math/tests/test_linear_algebra.py,sha256=k-XqIIBPsenNMKqo2nQnDkkN-6T1nvVT8BrJnupAIFU,6495
12
- dkist_processing_math/tests/test_statistics.py,sha256=0_bFuKxcu6hfimScB5du38qzABq2syX7SQrA9ZjwbuA,2173
15
+ dkist_processing_math/tests/test_statistics.py,sha256=N0S8C9Bwhd1VgCAkm6RqIZF_Ulwrcet9wqfbndw8KZs,2998
13
16
  dkist_processing_math/tests/test_transform.py,sha256=M761Yceh3FKV00udvb6_AeKuyBpQrUAG-mLfO-PbHmc,20253
14
17
  dkist_processing_math/transform/__init__.py,sha256=gZGyavJgtuquv8vc_VkXraJjz_8MkDTk9UwGQhthHwM,617
15
18
  dkist_processing_math/transform/affine.py,sha256=wJvL-R3DMpOmEnCKL8YI8eRUE7enH2Vg-mSLLpfCEoE,8595
@@ -24,7 +27,7 @@ docs/landing_page.rst,sha256=JCyR9SRhYS6zeEUlonIzko1kQ0tAFDIfi7pCoQanZeA,251
24
27
  docs/make.bat,sha256=mBAhtURwhQ7yc95pqwJzlhqBSvRknr1aqZ5s8NKvdKs,4513
25
28
  docs/requirements.txt,sha256=Kbl_X4c7RQZw035YTeNB63We6I7pvXFU4T0Uflp2yDY,29
26
29
  licenses/LICENSE.rst,sha256=piZaQplkzOMmH1NXg6QIdo9wwo9pPCoHkvm2-DmH76E,1462
27
- dkist_processing_math-2.1.2.dist-info/METADATA,sha256=lK_TFbwqPyS8_FCZ2dKcmmpqy1tDIXJcCo-QESZxpbI,4284
28
- dkist_processing_math-2.1.2.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
29
- dkist_processing_math-2.1.2.dist-info/top_level.txt,sha256=7NeFrOlR4xhZ3LMT4jN99LQugukjr6l9u3w_s6b-uO4,46
30
- dkist_processing_math-2.1.2.dist-info/RECORD,,
30
+ dkist_processing_math-2.2.0rc1.dist-info/METADATA,sha256=20cGjV34kHUjkZI-Nm67oi8Y6VbYUg2_V1ELyN_kl08,4287
31
+ dkist_processing_math-2.2.0rc1.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
32
+ dkist_processing_math-2.2.0rc1.dist-info/top_level.txt,sha256=7NeFrOlR4xhZ3LMT4jN99LQugukjr6l9u3w_s6b-uO4,46
33
+ dkist_processing_math-2.2.0rc1.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (75.6.0)
2
+ Generator: setuptools (75.8.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5