Coverage for install/scipp/core/reduction.py: 65%
106 statements
« prev ^ index » next coverage.py v7.4.0, created at 2024-04-28 01:28 +0000
« prev ^ index » next coverage.py v7.4.0, created at 2024-04-28 01:28 +0000
1# SPDX-License-Identifier: BSD-3-Clause
2# Copyright (c) 2023 Scipp contributors (https://github.com/scipp)
3# @author Simon Heybrock
5from __future__ import annotations
7from typing import Any, Callable, Optional
9import numpy as np
11from .._scipp import core as _cpp
12from ..typing import Dims, VariableLikeType
13from . import concepts
14from ._cpp_wrapper_util import call_func as _call_cpp_func
15from .data_group import DataGroup, data_group_nary
16from .variable import array
19def mean(x: VariableLikeType, dim: Optional[str] = None) -> VariableLikeType:
20 """Arithmetic mean of elements in the input.
22 If the input has variances, the variances stored in the output are based on
23 the "standard deviation of the mean", i.e.,
24 :math:`\\sigma_{mean} = \\sigma / \\sqrt{N}`.
25 :math:`N` is the length of the input dimension.
26 :math:`\\sigma` is estimated as the average of the standard deviations of
27 the input elements along that dimension.
29 See :py:func:`scipp.sum` on how rounding errors for float32 inputs are handled.
31 Parameters
32 ----------
33 x: scipp.typing.VariableLike
34 Input data.
35 dim:
36 Dimension along which to calculate the mean. If not
37 given, the mean over all dimensions is calculated.
39 Returns
40 -------
41 : Same type as x
42 The mean of the input values.
44 See Also
45 --------
46 scipp.var:
47 Compute the variance.
48 scipp.std:
49 Compute the standard deviation.
50 scipp.nanmean:
51 Ignore NaN's when calculating the mean.
52 """
53 if dim is None:
54 return _call_cpp_func(_cpp.mean, x)
55 else:
56 return _call_cpp_func(_cpp.mean, x, dim=dim)
59def nanmean(x: VariableLikeType, dim: Optional[str] = None) -> VariableLikeType:
60 """Arithmetic mean of elements in the input ignoring NaN's.
62 If the input has variances, the variances stored in the output are based on
63 the "standard deviation of the mean", i.e.,
64 :math:`\\sigma_{mean} = \\sigma / \\sqrt{N}`.
65 :math:`N` is the length of the input dimension.
66 :math:`\\sigma` is estimated as the average of the standard deviations of
67 the input elements along that dimension.
69 See :py:func:`scipp.sum` on how rounding errors for float32 inputs are handled.
71 Parameters
72 ----------
73 x: scipp.typing.VariableLike
74 Input data.
75 dim:
76 Dimension along which to calculate the mean. If not
77 given, the nanmean over all dimensions is calculated.
79 Returns
80 -------
81 : Same type as x
82 The mean of the input values which are not NaN.
84 See Also
85 --------
86 scipp.nanvar:
87 Compute the variance, ignoring NaN's.
88 scipp.nanstd:
89 Compute the standard deviation, ignoring NaN's.
90 scipp.mean:
91 Compute the mean without special handling of NaN.
92 """
93 if dim is None:
94 return _call_cpp_func(_cpp.nanmean, x)
95 else:
96 return _call_cpp_func(_cpp.nanmean, x, dim=dim)
99def median(x: VariableLikeType, dim: Dims = None) -> VariableLikeType:
100 """Compute the median of the input values.
102 The median is the middle value of a sorted copy of the input array
103 along each reduced dimension.
104 That is, for an array of ``N`` unmasked values, the median is
106 - odd ``N``: ``x[(N-1)/2]``
107 - even ``N``: ``(x[N/2-1] + x[N/2]) / 2``
109 Note
110 ----
111 Masks are broadcast to the shape of ``x``.
112 This can lead to a large temporary memory usage.
114 Parameters
115 ----------
116 x: scipp.typing.VariableLike
117 Input data.
118 dim:
119 Dimension(s) along which to calculate the median.
120 If not given, the median over a flattened version of the array is calculated.
122 Returns
123 -------
124 : Same type as x
125 The median of the input values.
127 Raises
128 ------
129 scipp.VariancesError
130 If the input has variances.
131 scipp.DTypeError
132 If the input is binned or does otherwise not support computing medians.
134 See Also
135 --------
136 scipp.nanmedian:
137 Ignore NaN's when calculating the median.
139 Examples
140 --------
141 ``median`` is available as a method:
143 >>> x = sc.array(dims=['x'], values=[2, 5, 1, 8, 4])
144 >>> x.median()
145 <scipp.Variable> () float64 [dimensionless] 4
146 >>> x = sc.array(dims=['x'], values=[2, 5, 1, 8])
147 >>> x.median()
148 <scipp.Variable> () float64 [dimensionless] 3.5
150 The median can be computed along a given dimension:
152 >>> x = sc.array(dims=['x', 'y'], values=[[1, 3, 6], [2, 7, 4]])
153 >>> x.median('y')
154 <scipp.Variable> (x: 2) float64 [dimensionless] [3, 4]
156 Masked elements are ignored:
158 >>> x = sc.DataArray(
159 ... sc.array(dims=['x'], values=[5, 3, 4, 3]),
160 ... masks={'m': sc.array(dims=['x'], values=[False, True, False, False])}
161 ... )
162 >>> x.median()
163 <scipp.DataArray>
164 Dimensions: Sizes[]
165 Data:
166 float64 [dimensionless] () 4
167 """
168 return _reduce_with_numpy(
169 x,
170 dim=dim,
171 sc_func=median,
172 np_func=np.median,
173 np_ma_func=np.ma.median,
174 unit_func=lambda u: u,
175 kwargs={},
176 )
179def nanmedian(x: VariableLikeType, dim: Dims = None) -> VariableLikeType:
180 """Compute the median of the input values ignoring NaN's.
182 The median is the middle value of a sorted copy of the input array
183 along each reduced dimension.
184 That is, for an array of ``N`` unmasked, non-NaN values, the median is
186 - odd ``N``: ``x[(N-1)/2]``
187 - even ``N``: ``(x[N/2-1] + x[N/2]) / 2``
189 Parameters
190 ----------
191 x: scipp.typing.VariableLike
192 Input data.
193 dim:
194 Dimension(s) along which to calculate the median.
195 If not given, the median over a flattened version of the array is calculated.
197 Returns
198 -------
199 : Same type as x
200 The median of the input values.
202 Raises
203 ------
204 scipp.VariancesError
205 If the input has variances.
206 scipp.DTypeError
207 If the input is binned or does otherwise not support computing medians.
208 ValueError
209 If the input has masks.
210 Mask out NaN's and then use :func:`scipp.median` instead.
212 See Also
213 --------
214 scipp.median:
215 Compute the median without special handling of NaN's.
217 Examples
218 --------
219 ``nanmedian`` is available as a method:
221 >>> x = sc.array(dims=['x'], values=[2, 5, 1, np.nan, 8, 4])
222 >>> x.nanmedian()
223 <scipp.Variable> () float64 [dimensionless] 4
224 >>> x = sc.array(dims=['x'], values=[2, np.nan, 5, 1, 8])
225 >>> x.nanmedian()
226 <scipp.Variable> () float64 [dimensionless] 3.5
227 """
229 def _catch_masked(*args, **kwargs):
230 # Because there is no np.ma.nanmedian
231 raise ValueError(
232 'nanmedian does not support masked data arrays. '
233 'Consider masking NaN values and calling scipp.median'
234 )
236 return _reduce_with_numpy(
237 x,
238 dim=dim,
239 sc_func=nanmedian,
240 np_func=np.nanmedian,
241 np_ma_func=_catch_masked,
242 unit_func=lambda u: u,
243 kwargs={},
244 )
247def var(x: VariableLikeType, dim: Dims = None, *, ddof: int) -> VariableLikeType:
248 r"""Compute the variance of the input values.
250 This function computes the variance of the input values which is *not*
251 the same as the ``x.variances`` property but instead defined as
253 .. math::
255 \mathsf{var}(x) = \frac1{N - \mathsf{ddof}}
256 \sum_{i=1}^{N}\, {(x_i - \bar{x})}^2
258 where :math:`x_i` are the unmasked ``values`` of the input and
259 :math:`\bar{x}` is the mean, see :func:`scipp.mean`.
260 See the ``ddof`` parameter description for what value to choose.
262 Note
263 ----
264 Masks are broadcast to the shape of ``x``.
265 This can lead to a large temporary memory usage.
267 Parameters
268 ----------
269 x: scipp.typing.VariableLike
270 Input data.
271 dim:
272 Dimension(s) along which to calculate the variance.
273 If not given, the variance over a flattened version of the array is calculated.
274 ddof:
275 'Delta degrees of freedom'.
276 For sample variances, set ``ddof=1`` to obtain an unbiased estimator.
277 For normally distributed variables, set ``ddof=0`` to obtain a maximum
278 likelihood estimate.
279 See :func:`numpy.var` for more details.
281 In contrast to NumPy, this is a required parameter in Scipp to
282 avoid potentially hard-to-find mistakes based on implicit assumptions
283 about what the input data represents.
285 Returns
286 -------
287 : Same type as x
288 The variance of the input values.
290 Raises
291 ------
292 scipp.VariancesError
293 If the input has variances.
294 scipp.DTypeError
295 If the input is binned or does otherwise not support computing variances.
297 See Also
298 --------
299 scipp.variances:
300 Extract the stored variances of a :class:`scipp.Variable`.
301 scipp.mean:
302 Compute the arithmetic mean.
303 scipp.std:
304 Compute the standard deviation.
305 scipp.nanvar:
306 Ignore NaN's when calculating the variance.
308 Examples
309 --------
310 ``var`` is available as a method:
312 >>> x = sc.array(dims=['x'], values=[3, 5, 2, 3])
313 >>> x.var(ddof=0)
314 <scipp.Variable> () float64 [dimensionless] 1.1875
315 >>> x.var(ddof=1)
316 <scipp.Variable> () float64 [dimensionless] 1.58333
318 Select a dimension to reduce:
320 >>> x = sc.array(dims=['x', 'y'], values=[[1, 3, 6], [2, 7, 4]])
321 >>> x.var('y', ddof=0)
322 <scipp.Variable> (x: 2) float64 [dimensionless] [4.22222, 4.22222]
323 >>> x.var('x', ddof=0)
324 <scipp.Variable> (y: 3) float64 [dimensionless] [0.25, 4, 1]
325 """
326 return _reduce_with_numpy(
327 x,
328 dim=dim,
329 sc_func=var,
330 np_func=np.var,
331 np_ma_func=np.ma.var,
332 unit_func=lambda u: u**2,
333 kwargs={'ddof': ddof},
334 )
337def nanvar(x: VariableLikeType, dim: Dims = None, *, ddof: int) -> VariableLikeType:
338 r"""Compute the variance of the input values ignoring NaN's.
340 This function computes the variance of the input values which is *not*
341 the same as the ``x.variances`` property but instead defined as
343 .. math::
345 \mathsf{nanvar}(x) = \frac1{N - \mathsf{ddof}}
346 \sum_{i=1}^{N}\, {(x_i - \bar{x})}^2
348 where :math:`x_i` are the non-NaN ``values`` of the input and
349 :math:`\bar{x}` is the mean, see :func:`scipp.nanmean`.
350 See the ``ddof`` parameter description for what value to choose.
352 Parameters
353 ----------
354 x: scipp.typing.VariableLike
355 Input data.
356 dim:
357 Dimension(s) along which to calculate the variance.
358 If not given, the variance over a flattened version of the array is calculated.
359 ddof:
360 'Delta degrees of freedom'.
361 For sample variances, set ``ddof=1`` to obtain an unbiased estimator.
362 For normally distributed variables, set ``ddof=0`` to obtain a maximum
363 likelihood estimate.
364 See :func:`numpy.nanvar` for more details.
366 In contrast to NumPy, this is a required parameter in Scipp to
367 avoid potentially hard-to-find mistakes based on implicit assumptions
368 about what the input data represents.
370 Returns
371 -------
372 : Same type as x
373 The variance of the non-NaN input values.
375 Raises
376 ------
377 scipp.VariancesError
378 If the input has variances.
379 scipp.DTypeError
380 If the input is binned or does otherwise not support computing variances.
381 ValueError
382 If the input has masks.
383 Mask out NaN's and then use :func:`scipp.var` instead.
385 See Also
386 --------
387 scipp.nanmean:
388 Compute the arithmetic mean ignoring NaN's.
389 scipp.nanstd:
390 Compute the standard deviation, ignoring NaN's.
391 scipp.var:
392 Compute the variance without special handling of NaN's.
394 Examples
395 --------
396 ``nanvar`` is available as a method:
398 >>> x = sc.array(dims=['x'], values=[np.nan, 5, 2, 3])
399 >>> x.nanvar(ddof=0)
400 <scipp.Variable> () float64 [dimensionless] 1.55556
401 >>> x.nanvar(ddof=1)
402 <scipp.Variable> () float64 [dimensionless] 2.33333
403 """
405 def _catch_masked(*args, **kwargs):
406 # Because there is no np.ma.nanvar
407 raise ValueError(
408 'nanvar does not support masked data arrays. '
409 'Consider masking NaN values and calling scipp.var'
410 )
412 return _reduce_with_numpy(
413 x,
414 dim=dim,
415 sc_func=nanvar,
416 np_func=np.nanvar,
417 np_ma_func=_catch_masked,
418 unit_func=lambda u: u**2,
419 kwargs={'ddof': ddof},
420 )
423def std(x: VariableLikeType, dim: Dims = None, *, ddof: int) -> VariableLikeType:
424 r"""Compute the standard deviation of the input values.
426 This function computes the standard deviation of the input values which is *not*
427 related to the ``x.variances`` property but instead defined as
429 .. math::
431 \mathsf{std}(x)^2 = \frac1{N - \mathsf{ddof}}
432 \sum_{i=1}^{N}\, {(x_i - \bar{x})}^2
434 where :math:`x_i` are the unmasked ``values`` of the input and
435 :math:`\bar{x}` is the mean, see :func:`scipp.mean`.
436 See the ``ddof`` parameter description for what value to choose.
438 Note
439 ----
440 Masks are broadcast to the shape of ``x``.
441 This can lead to a large temporary memory usage.
443 Parameters
444 ----------
445 x: scipp.typing.VariableLike
446 Input data.
447 dim:
448 Dimension(s) along which to calculate the standard deviation.
449 If not given, the standard deviation over a flattened version
450 of the array is calculated.
451 ddof:
452 'Delta degrees of freedom'.
453 For sample standard deviations, set ``ddof=1`` to obtain an unbiased estimator.
454 For normally distributed variables, set ``ddof=0`` to obtain a maximum
455 likelihood estimate.
456 See :func:`numpy.std` for more details.
458 In contrast to NumPy, this is a required parameter in Scipp to
459 avoid potentially hard-to-find mistakes based on implicit assumptions
460 about what the input data represents.
462 Returns
463 -------
464 : Same type as x
465 The standard deviation of the input values.
467 Raises
468 ------
469 scipp.VariancesError
470 If the input has variances.
471 scipp.DTypeError
472 If the input is binned or does
473 otherwise not support computing standard deviations.
475 See Also
476 --------
477 scipp.stddevs:
478 Compute the standard deviations from the stored
479 variances of a :class:`scipp.Variable`.
480 scipp.mean:
481 Compute the arithmetic mean.
482 scipp.var:
483 Compute the variance.
484 scipp.nanstd:
485 Ignore NaN's when calculating the standard deviation.
487 Examples
488 --------
489 ``std`` is available as a method:
491 >>> x = sc.array(dims=['x'], values=[3, 5, 2, 3])
492 >>> x.std(ddof=0)
493 <scipp.Variable> () float64 [dimensionless] 1.08972
494 >>> x.std(ddof=1)
495 <scipp.Variable> () float64 [dimensionless] 1.25831
497 Select a dimension to reduce:
499 >>> x = sc.array(dims=['x', 'y'], values=[[1, 3, 6], [2, 7, 4]])
500 >>> x.std('y', ddof=0)
501 <scipp.Variable> (x: 2) float64 [dimensionless] [2.0548, 2.0548]
502 >>> x.std('x', ddof=0)
503 <scipp.Variable> (y: 3) float64 [dimensionless] [0.5, 2, 1]
504 """
505 return _reduce_with_numpy(
506 x,
507 dim=dim,
508 sc_func=std,
509 np_func=np.std,
510 np_ma_func=np.ma.std,
511 unit_func=lambda u: u,
512 kwargs={'ddof': ddof},
513 )
516def nanstd(x: VariableLikeType, dim: Dims = None, *, ddof: int) -> VariableLikeType:
517 r"""Compute the standard deviation of the input values ignoring NaN's.
519 This function computes the standard deviation of the input values which is *not*
520 related to the ``x.variances`` property but instead defined as
522 .. math::
524 \mathsf{nanstd}(x)^2 = \frac1{N - \mathsf{ddof}}
525 \sum_{i=1}^{N}\, {(x_i - \bar{x})}^2
527 where :math:`x_i` are the non-NaN ``values`` of the input and
528 :math:`\bar{x}` is the mean, see :func:`scipp.nanmean`.
529 See the ``ddof`` parameter description for what value to choose.
531 Parameters
532 ----------
533 x: scipp.typing.VariableLike
534 Input data.
535 dim:
536 Dimension(s) along which to calculate the standard deviation.
537 If not given, the standard deviation over a flattened version
538 of the array is calculated.
539 ddof:
540 'Delta degrees of freedom'.
541 For sample standard deviations, set ``ddof=1`` to obtain an unbiased estimator.
542 For normally distributed variables, set ``ddof=0`` to obtain a maximum
543 likelihood estimate.
544 See :func:`numpy.nanstd` for more details.
546 In contrast to NumPy, this is a required parameter in Scipp to
547 avoid potentially hard-to-find mistakes based on implicit assumptions
548 about what the input data represents.
550 Returns
551 -------
552 : Same type as x
553 The standard deviation of the input values.
555 Raises
556 ------
557 scipp.VariancesError
558 If the input has variances.
559 scipp.DTypeError
560 If the input is binned or does
561 otherwise not support computing standard deviations.
562 ValueError
563 If the input has masks.
564 Mask out NaN's and then use :func:`scipp.std` instead.
566 See Also
567 --------
568 scipp.nanmean:
569 Compute the arithmetic mean ignoring NaN's.
570 scipp.nanvar:
571 Compute the variance, ignoring NaN's.
572 scipp.std:
573 Compute the standard deviation without special handling of NaN's.
575 Examples
576 --------
577 ``nanstd`` is available as a method:
579 >>> x = sc.array(dims=['x'], values=[np.nan, 5, 2, 3])
580 >>> x.nanstd(ddof=0)
581 <scipp.Variable> () float64 [dimensionless] 1.24722
582 >>> x.nanstd(ddof=1)
583 <scipp.Variable> () float64 [dimensionless] 1.52753
584 """
586 def _catch_masked(*args, **kwargs):
587 # Because there is no np.ma.nanstd
588 raise ValueError(
589 'nanstd does not support masked data arrays. '
590 'Consider masking NaN values and calling scipp.std'
591 )
593 return _reduce_with_numpy(
594 x,
595 dim=dim,
596 sc_func=nanstd,
597 np_func=np.nanstd,
598 np_ma_func=_catch_masked,
599 unit_func=lambda u: u,
600 kwargs={'ddof': ddof},
601 )
604def sum(x: VariableLikeType, dim: Dims = None) -> VariableLikeType:
605 """Sum of elements in the input.
607 If the input data is in single precision (dtype='float32') this internally uses
608 double precision (dtype='float64') to reduce the effect of accumulated rounding
609 errors. If multiple dimensions are reduced, the current implementation casts back
610 to float32 after handling each dimension, i.e., the result is equivalent to what
611 would be obtained from manually summing individual dimensions.
613 Parameters
614 ----------
615 x: scipp.typing.VariableLike
616 Input data.
617 dim:
618 Optional dimension along which to calculate the sum. If not
619 given, the sum over all dimensions is calculated.
621 Returns
622 -------
623 : Same type as x
624 The sum of the input values.
626 See Also
627 --------
628 scipp.nansum:
629 Ignore NaN's when calculating the sum.
630 """
631 if dim is None:
632 return _call_cpp_func(_cpp.sum, x)
633 elif isinstance(dim, str):
634 return _call_cpp_func(_cpp.sum, x, dim=dim)
635 for d in dim:
636 x = _call_cpp_func(_cpp.sum, x, d)
637 return x
640def nansum(x: VariableLikeType, dim: Optional[str] = None) -> VariableLikeType:
641 """Sum of elements in the input ignoring NaN's.
643 See :py:func:`scipp.sum` on how rounding errors for float32 inputs are handled.
645 Parameters
646 ----------
647 x: scipp.typing.VariableLike
648 Input data.
649 dim:
650 Optional dimension along which to calculate the sum. If not
651 given, the sum over all dimensions is calculated.
653 Returns
654 -------
655 : Same type as x
656 The sum of the input values which are not NaN.
658 See Also
659 --------
660 scipp.sum:
661 Compute the sum without special handling of NaN.
662 """
663 if dim is None:
664 return _call_cpp_func(_cpp.nansum, x)
665 else:
666 return _call_cpp_func(_cpp.nansum, x, dim=dim)
669def min(x: VariableLikeType, dim: Optional[str] = None) -> VariableLikeType:
670 """Minimum of elements in the input.
672 Warning
673 -------
675 Scipp returns DBL_MAX or INT_MAX for empty inputs of float or int dtype,
676 respectively, while NumPy raises. Note that in the case of :py:class:`DataArray`,
677 inputs can also be "empty" if all elements contributing to an output element are
678 masked.
680 Parameters
681 ----------
682 x: scipp.typing.VariableLike
683 Input data.
684 dim:
685 Optional dimension along which to calculate the min. If not
686 given, the min over all dimensions is calculated.
688 Returns
689 -------
690 :
691 The minimum of the input values.
693 See Also
694 --------
695 scipp.max:
696 Element-wise maximum.
697 scipp.nanmin:
698 Same as min but ignoring NaN's.
699 scipp.nanmax:
700 Same as max but ignoring NaN's.
701 """
702 if dim is None:
703 return _call_cpp_func(_cpp.min, x)
704 else:
705 return _call_cpp_func(_cpp.min, x, dim=dim)
708def max(x: VariableLikeType, dim: Optional[str] = None) -> VariableLikeType:
709 """Maximum of elements in the input.
711 Warning
712 -------
714 Scipp returns DBL_MIN or INT_MIN for empty inputs of float or int dtype,
715 respectively, while NumPy raises. Note that in the case of :py:class:`DataArray`,
716 inputs can also be "empty" if all elements contributing to an output element are
717 masked.
719 Parameters
720 ----------
721 x: scipp.typing.VariableLike
722 Input data.
723 dim:
724 Optional dimension along which to calculate the max. If not
725 given, the max over all dimensions is calculated.
727 Returns
728 -------
729 :
730 The maximum of the input values.
732 See Also
733 --------
734 scipp.min:
735 Element-wise minimum.
736 scipp.nanmin:
737 Same as min but ignoring NaN's.
738 scipp.nanmax:
739 Same as max but ignoring NaN's.
740 """
741 if dim is None:
742 return _call_cpp_func(_cpp.max, x)
743 else:
744 return _call_cpp_func(_cpp.max, x, dim=dim)
747def nanmin(x: VariableLikeType, dim: Optional[str] = None) -> VariableLikeType:
748 """Minimum of elements in the input ignoring NaN's.
750 Warning
751 -------
753 Scipp returns DBL_MAX or INT_MAX for empty inputs of float or int dtype,
754 respectively, while NumPy raises. Note that in the case of :py:class:`DataArray`,
755 inputs can also be "empty" if all elements contributing to an output element are
756 masked. The same applies if all elements are NaN (or masked).
758 Parameters
759 ----------
760 x: scipp.typing.VariableLike
761 Input data.
762 dim:
763 Optional dimension along which to calculate the min. If not
764 given, the min over all dimensions is calculated.
766 Returns
767 -------
768 :
769 The minimum of the input values.
771 See Also
772 --------
773 scipp.min:
774 Element-wise minimum without special handling for NaN.
775 scipp.max:
776 Element-wise maximum without special handling for NaN.
777 scipp.nanmax:
778 Same as max but ignoring NaN's.
779 """
780 if dim is None:
781 return _call_cpp_func(_cpp.nanmin, x)
782 else:
783 return _call_cpp_func(_cpp.nanmin, x, dim=dim)
786def nanmax(x: VariableLikeType, dim: Optional[str] = None) -> VariableLikeType:
787 """Maximum of elements in the input ignoring NaN's.
789 Warning
790 -------
792 Scipp returns DBL_MIN or INT_MIN for empty inputs of float or int dtype,
793 respectively, while NumPy raises. Note that in the case of :py:class:`DataArray`,
794 inputs can also be "empty" if all elements contributing to an output element are
795 masked. The same applies if all elements are NaN (or masked).
797 Parameters
798 ----------
799 x: scipp.typing.VariableLike
800 Input data.
801 dim:
802 Optional dimension along which to calculate the max. If not
803 given, the max over all dimensions is calculated.
805 Returns
806 -------
807 :
808 The maximum of the input values.
810 See Also
811 --------
812 scipp.max:
813 Element-wise maximum without special handling for NaN.
814 scipp.min:
815 Element-wise minimum without special handling for NaN.
816 scipp.nanmin:
817 Same as min but ignoring NaN's.
818 """
819 if dim is None:
820 return _call_cpp_func(_cpp.nanmax, x)
821 else:
822 return _call_cpp_func(_cpp.nanmax, x, dim=dim)
825def all(x: VariableLikeType, dim: Optional[str] = None) -> VariableLikeType:
826 """Logical AND over input values.
828 Parameters
829 ----------
830 x: scipp.typing.VariableLike
831 Input data.
832 dim:
833 Optional dimension along which to calculate the AND. If not
834 given, the AND over all dimensions is calculated.
836 Returns
837 -------
838 :
839 A variable containing ``True`` if all input values (along the given dimension)
840 are ``True``.
842 See Also
843 --------
844 scipp.any:
845 Logical OR.
846 """
847 if dim is None:
848 return _call_cpp_func(_cpp.all, x)
849 else:
850 return _call_cpp_func(_cpp.all, x, dim=dim)
853def any(x: VariableLikeType, dim: Optional[str] = None) -> VariableLikeType:
854 """Logical OR over input values.
856 Parameters
857 ----------
858 x: scipp.typing.VariableLike
859 Input data.
860 dim:
861 Optional dimension along which to calculate the OR. If not
862 given, the OR over all dimensions is calculated.
864 Returns
865 -------
866 :
867 A variable containing ``True`` if any input values (along the given dimension)
868 are ``True``.
870 See Also
871 --------
872 scipp.all:
873 Logical AND.
874 """
875 if dim is None:
876 return _call_cpp_func(_cpp.any, x)
877 else:
878 return _call_cpp_func(_cpp.any, x, dim=dim)
881def _reduce_with_numpy(
882 x: VariableLikeType,
883 *,
884 dim: Dims = None,
885 sc_func: Callable[..., VariableLikeType],
886 np_func: Callable[..., np.ndarray],
887 np_ma_func: Callable[..., np.ndarray],
888 unit_func: Callable[[_cpp.Unit], _cpp.Unit],
889 kwargs: dict[str, Any],
890) -> VariableLikeType:
891 if isinstance(x, _cpp.Dataset):
892 return _cpp.Dataset({k: sc_func(v, dim=dim, **kwargs) for k, v in x.items()})
893 if isinstance(x, DataGroup):
894 return data_group_nary(sc_func, x, dim=dim, **kwargs)
896 _expect_no_variance(x, sc_func.__name__)
897 _expect_not_binned(x, sc_func.__name__)
898 reduced_dims, out_dims, axis = _split_dims(x, dim)
899 if isinstance(x, _cpp.Variable):
900 return array(
901 dims=out_dims,
902 values=np_func(x.values, axis=axis, **kwargs),
903 unit=unit_func(x.unit),
904 )
905 if isinstance(x, _cpp.DataArray):
906 if (mask := concepts.irreducible_mask(x, dim)) is not None:
907 masked = np.ma.masked_array(
908 x.values, mask=mask.broadcast(x.dims, x.shape).values
909 )
910 res = np_ma_func(masked, axis=axis, **kwargs)
911 else:
912 res = np_func(x.values, axis=axis, **kwargs)
913 return concepts.rewrap_reduced_data(
914 x, array(dims=out_dims, values=res, unit=x.unit), dim
915 )
916 raise TypeError(f'invalid argument of type {type(x)} to {sc_func}')
919def _dims_to_axis(x: VariableLikeType, dim: tuple[str, ...]) -> tuple[int, ...]:
920 return tuple(_dim_index(x.dims, d) for d in dim)
923def _dim_index(dims: tuple[str, ...], dim: str) -> int:
924 try:
925 return dims.index(dim)
926 except ValueError:
927 raise _cpp.DimensionError(
928 f'Expected dimension to be in {dims}, got {dim}'
929 ) from None
932def _split_dims(
933 x: VariableLikeType, dim: Dims
934) -> tuple[tuple[str, ...], tuple[str, ...], tuple[int, ...]]:
935 reduced_dims = concepts.concrete_dims(x, dim)
936 out_dims = tuple(d for d in x.dims if d not in reduced_dims)
937 axis = _dims_to_axis(x, reduced_dims)
938 return reduced_dims, out_dims, axis
941def _expect_no_variance(x: VariableLikeType, op: str) -> None:
942 if x.variances is not None:
943 raise _cpp.VariancesError(f"'{op}' does not support variances")
946def _expect_not_binned(x: VariableLikeType, op: str) -> None:
947 if x.bins is not None:
948 raise _cpp.DTypeError(f"'{op}' does not support binned data")