Coverage for install/scipp/core/operations.py: 64%
77 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 Matthew Andrew
4from __future__ import annotations
6from typing import Any, Dict, Literal, Optional, Union, overload
8from .._scipp import core as _cpp
9from ..typing import ScippIndex, VariableLikeType
10from ._cpp_wrapper_util import call_func as _call_cpp_func
11from .comparison import identical
12from .cpp_classes import Dataset, DatasetError, Variable
13from .data_group import DataGroup
14from .unary import to_unit
17def islinspace(x: Variable, dim: Optional[str] = None) -> Variable:
18 """Check if the values of a variable are evenly spaced.
20 Parameters
21 ----------
22 x:
23 Variable to check.
24 dim:
25 Optional variable for the dim to check from the Variable.
27 Returns
28 -------
29 :
30 Variable of value True if the variable contains regularly
31 spaced values, variable of value False otherwise.
32 """
33 if dim is None:
34 return _call_cpp_func(_cpp.islinspace, x)
35 else:
36 return _call_cpp_func(_cpp.islinspace, x, dim)
39def issorted(
40 x: Variable, dim: str, order: Literal['ascending', 'descending'] = 'ascending'
41) -> Variable:
42 """Check if the values of a variable are sorted.
44 - If ``order`` is 'ascending',
45 check if values are non-decreasing along ``dim``.
46 - If ``order`` is 'descending',
47 check if values are non-increasing along ``dim``.
49 Parameters
50 ----------
51 x:
52 Variable to check.
53 dim:
54 Dimension along which order is checked.
55 order:
56 Sorting order.
58 Returns
59 -------
60 :
61 Variable containing one less dim than the original
62 variable with the corresponding boolean value for whether
63 it was sorted along the given dim for the other dimensions.
65 See Also
66 --------
67 scipp.allsorted
68 """
69 return _call_cpp_func(_cpp.issorted, x, dim, order)
72def allsorted(
73 x: Variable, dim: str, order: Literal['ascending', 'descending'] = 'ascending'
74) -> bool:
75 """Check if all values of a variable are sorted.
77 - If ``order`` is 'ascending',
78 check if values are non-decreasing along ``dim``.
79 - If ``order`` is 'descending',
80 check if values are non-increasing along ``dim``.
82 Parameters
83 ----------
84 x:
85 Variable to check.
86 dim:
87 Dimension along which order is checked.
88 order:
89 Sorting order.
91 Returns
92 -------
93 :
94 True if the variable values are monotonously ascending or
95 descending (depending on the requested order), False otherwise.
97 See Also
98 --------
99 scipp.issorted
100 """
101 return _call_cpp_func(_cpp.allsorted, x, dim, order)
104def sort(
105 x: VariableLikeType,
106 key: Union[str, Variable],
107 order: Literal['ascending', 'descending'] = 'ascending',
108) -> VariableLikeType:
109 """Sort variable along a dimension by a sort key or dimension label.
111 - If ``order`` is 'ascending',
112 sort such that values are non-decreasing according to ``key``.
113 - If ``order`` is 'descending',
114 sort such that values are non-increasing according to ``key``.
116 Parameters
117 ----------
118 x: scipp.typing.VariableLike
119 Data to be sorted.
120 key:
121 Either a 1D variable sort key or a dimension label.
122 order:
123 Sorting order.
125 Returns
126 -------
127 : scipp.typing.VariableLike
128 The sorted equivalent of the input with the same type.
130 Raises
131 ------
132 scipp.DimensionError
133 If the key is a Variable that does not have exactly 1 dimension.
134 """
135 return _call_cpp_func(_cpp.sort, x, key, order)
138def values(x: VariableLikeType) -> VariableLikeType:
139 """Return the input without variances.
141 Parameters
142 ----------
143 x: scipp.typing.VariableLike
144 Input data.
146 Returns
147 -------
148 : scipp.typing.VariableLike
149 The same as the input but without variances.
151 See Also
152 --------
153 scipp.variances, scipp.stddevs
154 """
155 return _call_cpp_func(_cpp.values, x)
158def variances(x: VariableLikeType) -> VariableLikeType:
159 """Return the input's variances as values.
161 Parameters
162 ----------
163 x: scipp.typing.VariableLike
164 Input data with variances.
166 Returns
167 -------
168 : scipp.typing.VariableLike
169 The same as the input but with values set to the input's variances
170 and without variances itself.
172 See Also
173 --------
174 scipp.values, scipp.stddevs
175 """
176 return _call_cpp_func(_cpp.variances, x)
179def stddevs(x: VariableLikeType) -> VariableLikeType:
180 """Return the input's standard deviations as values.
182 Parameters
183 ----------
184 x: scipp.typing.VariableLike
185 Input data with variances.
187 Returns
188 -------
189 : scipp.typing.VariableLike
190 The same as the input but with values set to standard deviations computed
191 from the input's variances and without variances itself.
193 See Also
194 --------
195 scipp.values, scipp.variances
196 """
197 return _call_cpp_func(_cpp.stddevs, x)
200def where(condition: Variable, x: Variable, y: Variable) -> Variable:
201 """Return elements chosen from x or y depending on condition.
203 Parameters
204 ----------
205 condition:
206 Variable with dtype=bool.
207 x:
208 Variable with values from which to choose.
209 y:
210 Variable with values from which to choose.
212 Returns
213 -------
214 :
215 Variable with elements from x where condition is True
216 and elements from y elsewhere.
217 """
218 return _call_cpp_func(_cpp.where, condition, x, y)
221def to(
222 var: VariableLikeType,
223 *,
224 unit: Optional[Union[_cpp.Unit, str]] = None,
225 dtype: Optional[Any] = None,
226 copy: bool = True,
227) -> VariableLikeType:
228 """Converts a Variable or DataArray to a different dtype and/or a different unit.
230 If the dtype and unit are both unchanged and ``copy`` is `False`,
231 the object is returned without making a deep copy.
233 This method will choose whether to do the dtype or units translation first, by
234 using the following rules in order:
236 - If either the input or output dtype is float64, the unit translation will be done
237 on the float64 type
238 - If either the input or output dtype is float32, the unit translation will be done
239 on the float32 type
240 - If both the input and output dtypes are integer types, the unit translation will
241 be done on the larger type
242 - In other cases, the dtype is converted first and then the unit translation is done
244 Parameters
245 ----------
246 unit:
247 Target unit. If ``None``, the unit is unchanged.
248 dtype:
249 Target dtype. If ``None``, the dtype is unchanged.
250 copy:
251 If ``False``, return the input object if possible.
252 If ``True``, the function always returns a new object.
254 Returns
255 -------
256 : Same as input
257 New object with specified dtype and unit.
259 Raises
260 ------
261 scipp.DTypeError
262 If the input cannot be converted to the given dtype.
263 scipp.UnitError
264 If the input cannot be converted to the given unit.
266 See Also
267 --------
268 scipp.to_unit, scipp.DataArray.astype, scipp.Variable.astype
269 """
270 if unit is None and dtype is None:
271 raise ValueError("Must provide dtype or unit or both")
273 if dtype is None:
274 return to_unit(var, unit, copy=copy)
276 if unit is None:
277 return var.astype(dtype, copy=copy)
279 if dtype == _cpp.DType.float64:
280 convert_dtype_first = True
281 elif var.dtype == _cpp.DType.float64:
282 convert_dtype_first = False
283 elif dtype == _cpp.DType.float32:
284 convert_dtype_first = True
285 elif var.dtype == _cpp.DType.float32:
286 convert_dtype_first = False
287 elif var.dtype == _cpp.DType.int64 and dtype == _cpp.DType.int32:
288 convert_dtype_first = False
289 elif var.dtype == _cpp.DType.int32 and dtype == _cpp.DType.int64:
290 convert_dtype_first = True
291 else:
292 convert_dtype_first = True
294 if convert_dtype_first:
295 return to_unit(var.astype(dtype, copy=copy), unit=unit, copy=False)
296 else:
297 return to_unit(var, unit=unit, copy=copy).astype(dtype, copy=False)
300@overload
301def merge(lhs: Dataset, rhs: Dataset) -> Dataset: ...
304@overload
305def merge(lhs: DataGroup, rhs: DataGroup) -> DataGroup: ...
308def merge(lhs, rhs):
309 """Merge two datasets or data groups into one.
311 If an item appears in both inputs, it must have an identical value in both.
313 Parameters
314 ----------
315 lhs:
316 First dataset or data group.
317 rhs:
318 Second dataset or data group.
320 Returns
321 -------
322 :
323 A new object that contains the union of all data items,
324 coords, masks and attributes.
326 Raises
327 ------
328 scipp.DatasetError
329 If there are conflicting items with different content.
330 """
331 if isinstance(lhs, Dataset) or isinstance(rhs, Dataset):
332 return _call_cpp_func(_cpp.merge, lhs, rhs)
333 return _merge_data_group(lhs, rhs)
336def _generic_identical(a: Any, b: Any) -> bool:
337 try:
338 return identical(a, b)
339 except TypeError:
340 from numpy import array_equal
342 try:
343 return array_equal(a, b)
344 except TypeError:
345 return a == b
348def _merge_data_group(lhs: DataGroup, rhs: DataGroup) -> DataGroup:
349 res = DataGroup(dict(lhs))
350 for k, v in rhs.items():
351 if k in res and not _generic_identical(res[k], v):
352 raise DatasetError(f"Cannot merge data groups. Mismatch in item {k}")
353 res[k] = v
354 return res
357def label_based_index_to_positional_index(
358 sizes: Dict[str, int],
359 coord: Variable,
360 index: Union[slice[Optional[Variable]], Variable],
361) -> ScippIndex:
362 """Returns the positional index equivalent to label based indexing
363 the coord with values."""
364 dim, *inds = _call_cpp_func(
365 _cpp.label_based_index_to_positional_index,
366 list(sizes.keys()),
367 list(sizes.values()),
368 coord,
369 index,
370 )
371 return (dim, inds[0] if len(inds) == 1 else slice(*inds))