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

1# SPDX-License-Identifier: BSD-3-Clause 

2# Copyright (c) 2023 Scipp contributors (https://github.com/scipp) 

3# @author Simon Heybrock 

4 

5from __future__ import annotations 

6 

7from typing import Any, Callable, Optional 

8 

9import numpy as np 

10 

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 

17 

18 

19def mean(x: VariableLikeType, dim: Optional[str] = None) -> VariableLikeType: 

20 """Arithmetic mean of elements in the input. 

21 

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. 

28 

29 See :py:func:`scipp.sum` on how rounding errors for float32 inputs are handled. 

30 

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. 

38 

39 Returns 

40 ------- 

41 : Same type as x 

42 The mean of the input values. 

43 

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) 

57 

58 

59def nanmean(x: VariableLikeType, dim: Optional[str] = None) -> VariableLikeType: 

60 """Arithmetic mean of elements in the input ignoring NaN's. 

61 

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. 

68 

69 See :py:func:`scipp.sum` on how rounding errors for float32 inputs are handled. 

70 

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. 

78 

79 Returns 

80 ------- 

81 : Same type as x 

82 The mean of the input values which are not NaN. 

83 

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) 

97 

98 

99def median(x: VariableLikeType, dim: Dims = None) -> VariableLikeType: 

100 """Compute the median of the input values. 

101 

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 

105 

106 - odd ``N``: ``x[(N-1)/2]`` 

107 - even ``N``: ``(x[N/2-1] + x[N/2]) / 2`` 

108 

109 Note 

110 ---- 

111 Masks are broadcast to the shape of ``x``. 

112 This can lead to a large temporary memory usage. 

113 

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. 

121 

122 Returns 

123 ------- 

124 : Same type as x 

125 The median of the input values. 

126 

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. 

133 

134 See Also 

135 -------- 

136 scipp.nanmedian: 

137 Ignore NaN's when calculating the median. 

138 

139 Examples 

140 -------- 

141 ``median`` is available as a method: 

142 

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 

149 

150 The median can be computed along a given dimension: 

151 

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] 

155 

156 Masked elements are ignored: 

157 

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 ) 

177 

178 

179def nanmedian(x: VariableLikeType, dim: Dims = None) -> VariableLikeType: 

180 """Compute the median of the input values ignoring NaN's. 

181 

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 

185 

186 - odd ``N``: ``x[(N-1)/2]`` 

187 - even ``N``: ``(x[N/2-1] + x[N/2]) / 2`` 

188 

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. 

196 

197 Returns 

198 ------- 

199 : Same type as x 

200 The median of the input values. 

201 

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. 

211 

212 See Also 

213 -------- 

214 scipp.median: 

215 Compute the median without special handling of NaN's. 

216 

217 Examples 

218 -------- 

219 ``nanmedian`` is available as a method: 

220 

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 """ 

228 

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 ) 

235 

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 ) 

245 

246 

247def var(x: VariableLikeType, dim: Dims = None, *, ddof: int) -> VariableLikeType: 

248 r"""Compute the variance of the input values. 

249 

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 

252 

253 .. math:: 

254 

255 \mathsf{var}(x) = \frac1{N - \mathsf{ddof}} 

256 \sum_{i=1}^{N}\, {(x_i - \bar{x})}^2 

257 

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. 

261 

262 Note 

263 ---- 

264 Masks are broadcast to the shape of ``x``. 

265 This can lead to a large temporary memory usage. 

266 

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. 

280 

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. 

284 

285 Returns 

286 ------- 

287 : Same type as x 

288 The variance of the input values. 

289 

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. 

296 

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. 

307 

308 Examples 

309 -------- 

310 ``var`` is available as a method: 

311 

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 

317 

318 Select a dimension to reduce: 

319 

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 ) 

335 

336 

337def nanvar(x: VariableLikeType, dim: Dims = None, *, ddof: int) -> VariableLikeType: 

338 r"""Compute the variance of the input values ignoring NaN's. 

339 

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 

342 

343 .. math:: 

344 

345 \mathsf{nanvar}(x) = \frac1{N - \mathsf{ddof}} 

346 \sum_{i=1}^{N}\, {(x_i - \bar{x})}^2 

347 

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. 

351 

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. 

365 

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. 

369 

370 Returns 

371 ------- 

372 : Same type as x 

373 The variance of the non-NaN input values. 

374 

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. 

384 

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. 

393 

394 Examples 

395 -------- 

396 ``nanvar`` is available as a method: 

397 

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 """ 

404 

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 ) 

411 

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 ) 

421 

422 

423def std(x: VariableLikeType, dim: Dims = None, *, ddof: int) -> VariableLikeType: 

424 r"""Compute the standard deviation of the input values. 

425 

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 

428 

429 .. math:: 

430 

431 \mathsf{std}(x)^2 = \frac1{N - \mathsf{ddof}} 

432 \sum_{i=1}^{N}\, {(x_i - \bar{x})}^2 

433 

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. 

437 

438 Note 

439 ---- 

440 Masks are broadcast to the shape of ``x``. 

441 This can lead to a large temporary memory usage. 

442 

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. 

457 

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. 

461 

462 Returns 

463 ------- 

464 : Same type as x 

465 The standard deviation of the input values. 

466 

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. 

474 

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. 

486 

487 Examples 

488 -------- 

489 ``std`` is available as a method: 

490 

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 

496 

497 Select a dimension to reduce: 

498 

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 ) 

514 

515 

516def nanstd(x: VariableLikeType, dim: Dims = None, *, ddof: int) -> VariableLikeType: 

517 r"""Compute the standard deviation of the input values ignoring NaN's. 

518 

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 

521 

522 .. math:: 

523 

524 \mathsf{nanstd}(x)^2 = \frac1{N - \mathsf{ddof}} 

525 \sum_{i=1}^{N}\, {(x_i - \bar{x})}^2 

526 

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. 

530 

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. 

545 

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. 

549 

550 Returns 

551 ------- 

552 : Same type as x 

553 The standard deviation of the input values. 

554 

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. 

565 

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. 

574 

575 Examples 

576 -------- 

577 ``nanstd`` is available as a method: 

578 

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 """ 

585 

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 ) 

592 

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 ) 

602 

603 

604def sum(x: VariableLikeType, dim: Dims = None) -> VariableLikeType: 

605 """Sum of elements in the input. 

606 

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. 

612 

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. 

620 

621 Returns 

622 ------- 

623 : Same type as x 

624 The sum of the input values. 

625 

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 

638 

639 

640def nansum(x: VariableLikeType, dim: Optional[str] = None) -> VariableLikeType: 

641 """Sum of elements in the input ignoring NaN's. 

642 

643 See :py:func:`scipp.sum` on how rounding errors for float32 inputs are handled. 

644 

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. 

652 

653 Returns 

654 ------- 

655 : Same type as x 

656 The sum of the input values which are not NaN. 

657 

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) 

667 

668 

669def min(x: VariableLikeType, dim: Optional[str] = None) -> VariableLikeType: 

670 """Minimum of elements in the input. 

671 

672 Warning 

673 ------- 

674 

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. 

679 

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. 

687 

688 Returns 

689 ------- 

690 : 

691 The minimum of the input values. 

692 

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) 

706 

707 

708def max(x: VariableLikeType, dim: Optional[str] = None) -> VariableLikeType: 

709 """Maximum of elements in the input. 

710 

711 Warning 

712 ------- 

713 

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. 

718 

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. 

726 

727 Returns 

728 ------- 

729 : 

730 The maximum of the input values. 

731 

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) 

745 

746 

747def nanmin(x: VariableLikeType, dim: Optional[str] = None) -> VariableLikeType: 

748 """Minimum of elements in the input ignoring NaN's. 

749 

750 Warning 

751 ------- 

752 

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). 

757 

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. 

765 

766 Returns 

767 ------- 

768 : 

769 The minimum of the input values. 

770 

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) 

784 

785 

786def nanmax(x: VariableLikeType, dim: Optional[str] = None) -> VariableLikeType: 

787 """Maximum of elements in the input ignoring NaN's. 

788 

789 Warning 

790 ------- 

791 

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). 

796 

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. 

804 

805 Returns 

806 ------- 

807 : 

808 The maximum of the input values. 

809 

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) 

823 

824 

825def all(x: VariableLikeType, dim: Optional[str] = None) -> VariableLikeType: 

826 """Logical AND over input values. 

827 

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. 

835 

836 Returns 

837 ------- 

838 : 

839 A variable containing ``True`` if all input values (along the given dimension) 

840 are ``True``. 

841 

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) 

851 

852 

853def any(x: VariableLikeType, dim: Optional[str] = None) -> VariableLikeType: 

854 """Logical OR over input values. 

855 

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. 

863 

864 Returns 

865 ------- 

866 : 

867 A variable containing ``True`` if any input values (along the given dimension) 

868 are ``True``. 

869 

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) 

879 

880 

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) 

895 

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}') 

917 

918 

919def _dims_to_axis(x: VariableLikeType, dim: tuple[str, ...]) -> tuple[int, ...]: 

920 return tuple(_dim_index(x.dims, d) for d in dim) 

921 

922 

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 

930 

931 

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 

939 

940 

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") 

944 

945 

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")