Coverage for install/scipp/core/variable.py: 62%
95 statements
« prev ^ index » next coverage.py v7.6.1, created at 2024-11-17 01:51 +0000
« prev ^ index » next coverage.py v7.6.1, created at 2024-11-17 01:51 +0000
1# SPDX-License-Identifier: BSD-3-Clause
2# Copyright (c) 2023 Scipp contributors (https://github.com/scipp)
3# @author Matthew Andrew
4# ruff: noqa: E501
6from __future__ import annotations
8import warnings
9from collections.abc import Iterable, Sequence
10from contextlib import contextmanager
11from typing import Any, Generator, SupportsIndex, TypeVar
13import numpy as _np
14from numpy.typing import ArrayLike
16from .._scipp import core as _cpp
17from ..typing import DTypeLike
18from ..units import DefaultUnit, default_unit
19from ._sizes import _parse_dims_shape_sizes
20from .cpp_classes import DType, Unit, Variable
22NumberOrVar = TypeVar('NumberOrVar', int, float, Variable)
25def scalar(
26 value: Any,
27 *,
28 variance: Any = None,
29 unit: Unit | str | DefaultUnit | None = default_unit,
30 dtype: DTypeLike | None = None,
31) -> Variable:
32 """Constructs a zero dimensional :class:`Variable` with a unit and optional
33 variance.
35 Parameters
36 ----------
37 value:
38 Initial value.
39 variance:
40 Optional, initial variance.
41 unit:
42 Optional, unit.
43 dtype: scipp.typing.DTypeLike
44 Optional, type of underlying data. By default,
45 the dtype is inferred from the `value` argument.
46 Cannot be specified for value types of
47 Dataset or DataArray.
49 Returns
50 -------
51 :
52 A scalar (zero-dimensional) Variable.
54 See Also
55 --------
56 scipp.array, scipp.empty, scipp.index, scipp.ones, scipp.zeros
58 Examples
59 --------
60 With deduced dtype and default unit:
62 >>> sc.scalar(3.14)
63 <scipp.Variable> () float64 [dimensionless] 3.14
65 >>> sc.scalar('a string')
66 <scipp.Variable> () string <no unit> "a string"
68 Or specifying a unit and dtype:
70 >>> sc.scalar(3.14, unit='m', dtype=int)
71 <scipp.Variable> () int64 [m] 3
73 Calling ``scalar`` with a list (or similar array-like object) will store that
74 object in a scalar variable and *not* create an array variable:
76 >>> sc.scalar([1, 2, 3])
77 <scipp.Variable> () PyObject <no unit> [1, 2, 3]
78 """
79 return _cpp.Variable( # type: ignore[no-any-return]
80 dims=(), values=value, variances=variance, unit=unit, dtype=dtype
81 )
84def index(value: Any, *, dtype: DTypeLike | None = None) -> Variable:
85 """Constructs a zero dimensional :class:`Variable` representing an index.
87 This is equivalent to calling :py:func:`scipp.scalar` with unit=None.
89 Parameters
90 ----------
91 value:
92 Initial value.
93 dtype: scipp.typing.DTypeLike
94 Optional, type of underlying data. By default,
95 the dtype is inferred from the `value` argument.
97 Returns
98 -------
99 :
100 A scalar (zero-dimensional) variable without unit.
102 See Also
103 --------
104 scipp.scalar, scipp.array
106 Examples
107 --------
109 >>> sc.index(123)
110 <scipp.Variable> () int64 <no unit> 123
111 """
112 return scalar(value=value, dtype=dtype, unit=None)
115def zeros(
116 *,
117 dims: Sequence[str] | None = None,
118 shape: Sequence[int] | None = None,
119 sizes: dict[str, int] | None = None,
120 unit: Unit | str | DefaultUnit | None = default_unit,
121 dtype: DTypeLike = DType.float64,
122 with_variances: bool = False,
123) -> Variable:
124 """Constructs a :class:`Variable` with default initialized values with
125 given dimension labels and shape.
127 The dims and shape can also be specified using a `sizes` dict.
128 Optionally can add default initialized variances.
129 Only keyword arguments accepted.
131 Parameters
132 ----------
133 dims:
134 Optional (if sizes is specified), dimension labels.
135 shape:
136 Optional (if sizes is specified), dimension sizes.
137 sizes:
138 Optional, dimension label to size map.
139 unit:
140 Unit of contents.
141 dtype: scipp.typing.DTypeLike
142 Type of underlying data.
143 with_variances:
144 If True includes variances initialized to 0.
146 Returns
147 -------
148 :
149 A variable filled with 0's.
151 See Also
152 --------
153 scipp.array, scipp.empty, scipp.ones, scipp.scalar, scipp.zeros_like
155 Examples
156 --------
158 >>> sc.zeros(dims=['x'], shape=[4])
159 <scipp.Variable> (x: 4) float64 [dimensionless] [0, 0, 0, 0]
161 >>> sc.zeros(sizes={'x': 4})
162 <scipp.Variable> (x: 4) float64 [dimensionless] [0, 0, 0, 0]
164 >>> sc.zeros(sizes={'y': 3}, with_variances=True)
165 <scipp.Variable> (y: 3) float64 [dimensionless] [0, 0, 0] [0, 0, 0]
167 >>> sc.zeros(sizes={'z': 3}, unit='kg', dtype=int)
168 <scipp.Variable> (z: 3) int64 [kg] [0, 0, 0]
169 """
171 return _cpp.zeros( # type: ignore[no-any-return]
172 **_parse_dims_shape_sizes(dims, shape, sizes),
173 unit=unit,
174 dtype=dtype,
175 with_variances=with_variances,
176 )
179def ones(
180 *,
181 dims: Sequence[str] | None = None,
182 shape: Sequence[int] | None = None,
183 sizes: dict[str, int] | None = None,
184 unit: Unit | str | DefaultUnit | None = default_unit,
185 dtype: DTypeLike = DType.float64,
186 with_variances: bool = False,
187) -> Variable:
188 """Constructs a :class:`Variable` with values initialized to 1 with
189 given dimension labels and shape.
191 The dims and shape can also be specified using a `sizes` dict.
193 Parameters
194 ----------
195 dims:
196 Optional (if sizes is specified), dimension labels.
197 shape:
198 Optional (if sizes is specified), dimension sizes.
199 sizes:
200 Optional, dimension label to size map.
201 unit:
202 Unit of contents.
203 dtype: scipp.typing.DTypeLike
204 Type of underlying data.
205 with_variances:
206 If True includes variances initialized to 1.
208 Returns
209 -------
210 :
211 A variable filled with 1's.
213 See Also
214 --------
215 scipp.array, scipp.empty, scipp.ones_like, scipp.scalar, scipp.zeros
217 Examples
218 --------
220 >>> sc.ones(dims=['x'], shape=[4])
221 <scipp.Variable> (x: 4) float64 [dimensionless] [1, 1, 1, 1]
223 >>> sc.ones(sizes={'x': 4})
224 <scipp.Variable> (x: 4) float64 [dimensionless] [1, 1, 1, 1]
226 >>> sc.ones(sizes={'y': 3}, with_variances=True)
227 <scipp.Variable> (y: 3) float64 [dimensionless] [1, 1, 1] [1, 1, 1]
229 >>> sc.ones(sizes={'z': 3}, unit='kg', dtype=int)
230 <scipp.Variable> (z: 3) int64 [kg] [1, 1, 1]
231 """
232 return _cpp.ones( # type: ignore[no-any-return]
233 **_parse_dims_shape_sizes(dims, shape, sizes),
234 unit=unit,
235 dtype=dtype,
236 with_variances=with_variances,
237 )
240def empty(
241 *,
242 dims: Sequence[str] | None = None,
243 shape: Sequence[int] | None = None,
244 sizes: dict[str, int] | None = None,
245 unit: Unit | str | DefaultUnit | None = default_unit,
246 dtype: DTypeLike = DType.float64,
247 with_variances: bool = False,
248 aligned: bool = True,
249) -> Variable:
250 """Constructs a :class:`Variable` with uninitialized values with given
251 dimension labels and shape.
253 The dims and shape can also be specified using a `sizes` dict.
255 Warning
256 -------
257 'Uninitialized' means that values have undetermined values.
258 Reading elements before writing to them is undefined behavior.
259 Consider using :py:func:`scipp.zeros` unless you
260 know what you are doing and require maximum performance.
262 Parameters
263 ----------
264 dims:
265 Optional (if sizes is specified), dimension labels.
266 shape:
267 Optional (if sizes is specified), dimension sizes.
268 sizes:
269 Optional, dimension label to size map.
270 unit:
271 Unit of contents.
272 dtype: scipp.typing.DTypeLike
273 Type of underlying data.
274 with_variances:
275 If True includes uninitialized variances.
276 aligned:
277 Initial value for the alignment flag.
279 Returns
280 -------
281 :
282 A variable with uninitialized elements.
284 See Also
285 --------
286 scipp.array, scipp.empty_like, scipp.ones, scipp.scalar, scipp.zeros
288 Examples
289 --------
291 >>> var = sc.empty(dims=['x'], shape=[4])
292 >>> var[:] = sc.scalar(2.0) # initialize values before printing
293 >>> var
294 <scipp.Variable> (x: 4) float64 [dimensionless] [2, 2, 2, 2]
295 """
296 return _cpp.empty( # type: ignore[no-any-return]
297 **_parse_dims_shape_sizes(dims, shape, sizes),
298 unit=unit,
299 dtype=dtype,
300 with_variances=with_variances,
301 aligned=aligned,
302 )
305def full(
306 *,
307 value: Any,
308 variance: Any = None,
309 dims: Sequence[str] | None = None,
310 shape: Sequence[int] | None = None,
311 sizes: dict[str, int] | None = None,
312 unit: Unit | str | DefaultUnit | None = default_unit,
313 dtype: DTypeLike | None = None,
314) -> Variable:
315 """Constructs a :class:`Variable` with values initialized to the specified
316 value with given dimension labels and shape.
318 The dims and shape can also be specified using a `sizes` dict.
320 Parameters
321 ----------
322 value:
323 The value to fill the Variable with.
324 variance:
325 The variance to fill the Variable with.
326 dims:
327 Optional (if sizes is specified), dimension labels.
328 shape:
329 Optional (if sizes is specified), dimension sizes.
330 sizes:
331 Optional, dimension label to size map.
332 unit:
333 Unit of contents.
334 dtype: scipp.typing.DTypeLike
335 Type of underlying data.
337 Returns
338 -------
339 :
340 A variable filled with given value.
342 See Also
343 --------
344 scipp.empty, scipp.full_like, scipp.ones, scipp.zeros
346 Examples
347 --------
349 >>> sc.full(value=2, dims=['x'], shape=[2])
350 <scipp.Variable> (x: 2) int64 [dimensionless] [2, 2]
352 >>> sc.full(value=2, sizes={'x': 2})
353 <scipp.Variable> (x: 2) int64 [dimensionless] [2, 2]
355 >>> sc.full(value=5, sizes={'y': 3, 'x': 2})
356 <scipp.Variable> (y: 3, x: 2) int64 [dimensionless] [5, 5, ..., 5, 5]
358 >>> sc.full(value=2.0, variance=0.1, sizes={'x': 3}, unit='s')
359 <scipp.Variable> (x: 3) float64 [s] [2, 2, 2] [0.1, 0.1, 0.1]
360 """
361 return (
362 scalar(value=value, variance=variance, unit=unit, dtype=dtype)
363 .broadcast(**_parse_dims_shape_sizes(dims, shape, sizes))
364 .copy()
365 )
368def vector(
369 value: ArrayLike, *, unit: Unit | str | DefaultUnit | None = default_unit
370) -> Variable:
371 """Constructs a zero dimensional :class:`Variable` holding a single length-3
372 vector.
374 Parameters
375 ----------
376 value:
377 Initial value, a list or 1-D numpy array.
378 unit:
379 Unit of contents.
381 Returns
382 -------
383 :
384 A scalar (zero-dimensional) variable of a vector.
386 See Also
387 --------
388 scipp.vectors:
389 Construct an array of vectors.
390 scipp.spatial.as_vectors:
391 Construct vectors from Scipp Variables
393 Examples
394 --------
396 >>> sc.vector(value=[1, 2, 3])
397 <scipp.Variable> () vector3 [dimensionless] (1, 2, 3)
399 >>> sc.vector(value=[4, 5, 6], unit='m')
400 <scipp.Variable> () vector3 [m] (4, 5, 6)
401 """
402 return vectors(dims=(), values=value, unit=unit)
405def vectors(
406 *,
407 dims: Sequence[str],
408 values: ArrayLike,
409 unit: Unit | str | DefaultUnit | None = default_unit,
410) -> Variable:
411 """Constructs a :class:`Variable` with given dimensions holding an array
412 of length-3 vectors.
414 Parameters
415 ----------
416 dims:
417 Dimension labels.
418 values:
419 Initial values.
420 unit:
421 Unit of contents.
423 Returns
424 -------
425 :
426 A variable of vectors with given values.
428 See Also
429 --------
430 scipp.vector:
431 Construct a single vector.
432 scipp.spatial.as_vectors:
433 Construct vectors from Scipp Variables
435 Examples
436 --------
438 >>> sc.vectors(dims=['x'], values=[[1, 2, 3]])
439 <scipp.Variable> (x: 1) vector3 [dimensionless] [(1, 2, 3)]
441 >>> var = sc.vectors(dims=['x'], values=[[1, 2, 3], [4, 5, 6]])
442 >>> var
443 <scipp.Variable> (x: 2) vector3 [dimensionless] [(1, 2, 3), (4, 5, 6)]
444 >>> var.values
445 array([[1., 2., 3.],
446 [4., 5., 6.]])
448 >>> sc.vectors(dims=['x'], values=[[1, 2, 3], [4, 5, 6]], unit='mm')
449 <scipp.Variable> (x: 2) vector3 [mm] [(1, 2, 3), (4, 5, 6)]
450 """
451 return _cpp.Variable( # type: ignore[no-any-return]
452 dims=dims, values=values, unit=unit, dtype=DType.vector3
453 )
456def array(
457 *,
458 dims: Iterable[str],
459 values: ArrayLike,
460 variances: ArrayLike | None = None,
461 unit: Unit | str | DefaultUnit | None = default_unit,
462 dtype: DTypeLike | None = None,
463) -> Variable:
464 """Constructs a :class:`Variable` with given dimensions, containing given
465 values and optional variances.
467 Dimension and value shape must match.
468 Only keyword arguments accepted.
470 Parameters
471 ----------
472 dims:
473 Dimension labels
474 values: numpy.typing.ArrayLike
475 Initial values.
476 variances: numpy.typing.ArrayLike
477 Initial variances, must be same shape and size as values.
478 unit:
479 Unit of contents.
480 dtype: scipp.typing.DTypeLike
481 Type of underlying data. By default, inferred from `values` argument.
483 Returns
484 -------
485 :
486 A variable with the given values.
488 See Also
489 --------
490 scipp.empty, scipp.ones, scipp.scalar, scipp.zeros
492 Examples
493 --------
495 >>> sc.array(dims=['x'], values=[1, 2, 3])
496 <scipp.Variable> (x: 3) int64 [dimensionless] [1, 2, 3]
498 Multiple dimensions:
500 >>> sc.array(dims=['x', 'y'], values=[[1, 2, 3], [4, 5, 6]])
501 <scipp.Variable> (x: 2, y: 3) int64 [dimensionless] [1, 2, ..., 5, 6]
503 DType upcasting:
505 >>> sc.array(dims=['x'], values=[1, 2, 3.0])
506 <scipp.Variable> (x: 3) float64 [dimensionless] [1, 2, 3]
508 Manually specified dtype:
510 >>> sc.array(dims=['x'], values=[1, 2, 3], dtype=float)
511 <scipp.Variable> (x: 3) float64 [dimensionless] [1, 2, 3]
513 Set unit:
515 >>> sc.array(dims=['x'], values=[1, 2, 3], unit='m')
516 <scipp.Variable> (x: 3) int64 [m] [1, 2, 3]
518 Setting variances:
520 >>> sc.array(dims=['x'], values=[1.0, 2.0, 3.0], variances=[0.1, 0.2, 0.3])
521 <scipp.Variable> (x: 3) float64 [dimensionless] [1, 2, 3] [0.1, 0.2, 0.3]
522 """
523 return _cpp.Variable( # type: ignore[no-any-return]
524 dims=dims, values=values, variances=variances, unit=unit, dtype=dtype
525 )
528def _expect_no_variances(args: dict[str, Variable | None]) -> None:
529 has_variances = [
530 key
531 for key, val in args.items()
532 if val is not None and val.variances is not None
533 ]
534 if has_variances:
535 raise _cpp.VariancesError(
536 'Arguments cannot have variances. ' f'Passed variances in {has_variances}'
537 )
540# Assumes that all arguments are Variable or None.
541def _ensure_same_unit(
542 *, unit: Unit | str | DefaultUnit | None, args: dict[str, Variable | None]
543) -> tuple[dict[str, Variable | None], Unit | str | DefaultUnit | None]:
544 if unit == default_unit:
545 units = {key: val.unit for key, val in args.items() if val is not None}
546 if len(set(units.values())) != 1:
547 raise _cpp.UnitError(
548 f'All units of the following arguments must be equal: {units}. '
549 'You can specify a unit explicitly with the `unit` argument.'
550 )
551 unit = next(iter(units.values()))
552 return {
553 key: _cpp.to_unit(val, unit, copy=False).value if val is not None else None
554 for key, val in args.items()
555 }, unit
558# Process arguments of arange, linspace, etc and return them as plain numbers or None.
559def _normalize_range_args(
560 *, unit: Unit | str | DefaultUnit | None, **kwargs: Any
561) -> tuple[dict[str, Any], Unit | str | DefaultUnit | None]:
562 is_var = {
563 key: isinstance(val, _cpp.Variable)
564 for key, val in kwargs.items()
565 if val is not None
566 }
567 if any(is_var.values()):
568 if not all(is_var.values()):
569 arg_types = {key: type(val) for key, val in kwargs.items()}
570 raise TypeError(
571 'Either all of the following arguments or none have to '
572 f'be variables: {arg_types}'
573 )
574 _expect_no_variances(kwargs)
575 return _ensure_same_unit(unit=unit, args=kwargs)
576 return kwargs, unit
579def linspace(
580 dim: str,
581 start: NumberOrVar,
582 stop: NumberOrVar,
583 num: SupportsIndex,
584 *,
585 endpoint: bool = True,
586 unit: Unit | str | DefaultUnit | None = default_unit,
587 dtype: DTypeLike | None = None,
588) -> Variable:
589 """Constructs a :class:`Variable` with `num` evenly spaced samples,
590 calculated over the interval `[start, stop]`.
592 Parameters
593 ----------
594 dim:
595 Dimension label.
596 start:
597 The starting value of the sequence.
598 stop:
599 The end value of the sequence.
600 num:
601 Number of samples to generate.
602 endpoint:
603 If True, `step` is the last returned value.
604 Otherwise, it is not included.
605 unit:
606 Unit of contents.
607 dtype: scipp.typing.DTypeLike
608 Type of underlying data. By default, inferred from `value` argument.
610 Returns
611 -------
612 :
613 A variable of evenly spaced values.
615 See Also
616 --------
617 scipp.arange, scipp.geomspace, scipp.logspace
619 Examples
620 --------
622 >>> sc.linspace('x', 2.0, 4.0, num=4)
623 <scipp.Variable> (x: 4) float64 [dimensionless] [2, 2.66667, 3.33333, 4]
624 >>> sc.linspace('x', 2.0, 4.0, num=4, endpoint=False)
625 <scipp.Variable> (x: 4) float64 [dimensionless] [2, 2.5, 3, 3.5]
627 >>> sc.linspace('x', 1.5, 3.0, num=4, unit='m')
628 <scipp.Variable> (x: 4) float64 [m] [1.5, 2, 2.5, 3]
629 """
630 range_args, unit = _normalize_range_args(unit=unit, start=start, stop=stop)
631 return array(
632 dims=[dim],
633 values=_np.linspace(**range_args, num=num, endpoint=endpoint),
634 unit=unit,
635 dtype=dtype,
636 )
639def geomspace(
640 dim: str,
641 start: NumberOrVar,
642 stop: NumberOrVar,
643 num: int,
644 *,
645 endpoint: bool = True,
646 unit: Unit | str | DefaultUnit | None = default_unit,
647 dtype: DTypeLike | None = None,
648) -> Variable:
649 """Constructs a :class:`Variable` with values spaced evenly on a log scale
650 (a geometric progression).
652 This is similar to :py:func:`scipp.logspace`, but with endpoints specified
653 directly instead of as exponents.
654 Each output sample is a constant multiple of the previous.
656 Parameters
657 ----------
658 dim:
659 Dimension label.
660 start:
661 The starting value of the sequence.
662 stop:
663 The end value of the sequence.
664 num:
665 Number of samples to generate.
666 endpoint:
667 If True, `step` is the last returned value.
668 Otherwise, it is not included.
669 unit:
670 Unit of contents.
671 dtype: scipp.typing.DTypeLike
672 Type of underlying data. By default, inferred from `value` argument.
674 Returns
675 -------
676 :
677 A variable of evenly spaced values on a logscale.
679 See Also
680 --------
681 scipp.arange, scipp.linspace, scipp.logspace
683 Examples
684 --------
686 >>> sc.geomspace('x', 1.0, 1000.0, num=4)
687 <scipp.Variable> (x: 4) float64 [dimensionless] [1, 10, 100, 1000]
688 >>> sc.geomspace('x', 1.0, 1000.0, num=4, endpoint=False)
689 <scipp.Variable> (x: 4) float64 [dimensionless] [1, 5.62341, 31.6228, 177.828]
691 >>> sc.geomspace('x', 1.0, 100.0, num=3, unit='s')
692 <scipp.Variable> (x: 3) float64 [s] [1, 10, 100]
693 """
694 range_args, unit = _normalize_range_args(unit=unit, start=start, stop=stop)
695 return array(
696 dims=[dim],
697 values=_np.geomspace(**range_args, num=num, endpoint=endpoint),
698 unit=unit,
699 dtype=dtype,
700 )
703def logspace(
704 dim: str,
705 start: NumberOrVar,
706 stop: NumberOrVar,
707 num: int,
708 *,
709 endpoint: bool = True,
710 base: float = 10.0,
711 unit: Unit | str | DefaultUnit | None = default_unit,
712 dtype: DTypeLike | None = None,
713) -> Variable:
714 """Constructs a :class:`Variable` with values spaced evenly on a log scale.
716 This is similar to :py:func:`scipp.geomspace`, but with endpoints specified
717 as exponents.
719 Parameters
720 ----------
721 dim:
722 Dimension label.
723 start:
724 The starting value of the sequence.
725 stop:
726 The end value of the sequence.
727 num:
728 Number of samples to generate.
729 base:
730 The base of the log space.
731 endpoint:
732 If True, `step` is the last returned value.
733 Otherwise, it is not included.
734 unit:
735 Unit of contents.
736 dtype: scipp.typing.DTypeLike
737 Type of underlying data. By default, inferred from `value` argument.
739 Returns
740 -------
741 :
742 A variable of evenly spaced values on a logscale.
744 See Also
745 --------
746 scipp.arange, scipp.geomspace, scipp.linspace
748 Examples
749 --------
751 >>> sc.logspace('x', 1, 4, num=4)
752 <scipp.Variable> (x: 4) float64 [dimensionless] [10, 100, 1000, 10000]
753 >>> sc.logspace('x', 1, 4, num=4, endpoint=False)
754 <scipp.Variable> (x: 4) float64 [dimensionless] [10, 56.2341, 316.228, 1778.28]
756 Set a different base:
758 >>> sc.logspace('x', 1, 4, num=4, base=2)
759 <scipp.Variable> (x: 4) float64 [dimensionless] [2, 4, 8, 16]
761 Set a unit:
763 >>> sc.logspace('x', 1, 3, num=3, unit='m')
764 <scipp.Variable> (x: 3) float64 [m] [10, 100, 1000]
765 """
766 # Passing unit='one' enforces that start and stop are dimensionless.
767 range_args, _ = _normalize_range_args(unit='one', start=start, stop=stop)
768 return array(
769 dims=[dim],
770 values=_np.logspace(**range_args, num=num, base=base, endpoint=endpoint),
771 unit=unit,
772 dtype=dtype,
773 )
776def arange(
777 dim: str,
778 start: NumberOrVar | _np.datetime64 | str,
779 stop: NumberOrVar | _np.datetime64 | str | None = None,
780 step: NumberOrVar | None = None,
781 *,
782 unit: Unit | str | DefaultUnit | None = default_unit,
783 dtype: DTypeLike | None = None,
784) -> Variable:
785 """Creates a :class:`Variable` with evenly spaced values within a given interval.
787 Values are generated within the half-open interval [start, stop).
788 In other words, the interval including start but excluding stop.
790 ``start``, ``stop``, and ``step`` may be given as plain values or as 0-D variables.
791 In the latter case this then implies the unit (the units of all arguments must be
792 identical), but a different unit-scale can still be requested with the ``unit``
793 argument.
795 When all the types or dtypes of the input arguments are the same, the output will
796 also have this dtype. This is different to :func:`numpy.arange` which will always
797 produce 64-bit (int64 or float64) outputs.
799 Warning
800 -------
801 The length of the output might not be numerically stable.
802 See :func:`numpy.arange`.
804 Parameters
805 ----------
806 dim:
807 Dimension label.
808 start:
809 Optional, the starting value of the sequence. Default=0.
810 stop:
811 End of interval. The interval does not include this value,
812 except in some (rare) cases where step is not an integer and
813 floating-point round-off can come into play.
814 step:
815 Spacing between values.
816 unit:
817 Unit of contents.
818 dtype: scipp.typing.DTypeLike
819 Type of underlying data. By default, inferred from `value` argument.
821 Returns
822 -------
823 :
824 A variable of evenly spaced values.
826 See Also
827 --------
828 scipp.geomspace, scipp.linspace, scipp.logspace
830 Examples
831 --------
833 >>> sc.arange('x', 4)
834 <scipp.Variable> (x: 4) int64 [dimensionless] [0, 1, 2, 3]
835 >>> sc.arange('x', 1, 5)
836 <scipp.Variable> (x: 4) int64 [dimensionless] [1, 2, 3, 4]
837 >>> sc.arange('x', 1, 5, 0.5)
838 <scipp.Variable> (x: 8) float64 [dimensionless] [1, 1.5, ..., 4, 4.5]
840 >>> sc.arange('x', 1, 5, unit='m')
841 <scipp.Variable> (x: 4) int64 [m] [1, 2, 3, 4]
843 Datetimes are also supported:
845 >>> sc.arange('t', '2000-01-01T01:00:00', '2000-01-01T01:01:30', 30 * sc.Unit('s'), dtype='datetime64')
846 <scipp.Variable> (t: 3) datetime64 [s] [2000-01-01T01:00:00, 2000-01-01T01:00:30, 2000-01-01T01:01:00]
848 Note that in this case the datetime ``start`` and ``stop`` strings implicitly
849 define the unit. The ``step`` must have the same unit.
850 """
851 if dtype == 'datetime64' and isinstance(start, str):
852 start = datetime(start) # type: ignore[assignment]
853 if not isinstance(stop, str) and stop is not None:
854 raise TypeError(
855 'Argument `stop` must be a string or `None` if `start` is a string.'
856 )
857 stop = stop if stop is None else datetime(stop) # type: ignore[assignment]
858 range_args, unit = _normalize_range_args(
859 unit=unit, start=start, stop=stop, step=step
860 )
861 types = [
862 x.values.dtype if isinstance(x, _cpp.Variable) else _np.asarray(x).dtype
863 for x in (start, stop, step)
864 if x is not None
865 ]
866 if dtype is None:
867 candidates = set(types)
868 if len(candidates) == 1:
869 dtype = next(iter(candidates))
870 if dtype is not None and dtype != 'datetime64':
871 numpy_dtype = str(dtype) if isinstance(dtype, DType) else dtype
872 else:
873 numpy_dtype = None
874 return array(
875 dims=[dim],
876 values=_np.arange(**range_args, dtype=numpy_dtype),
877 unit=unit,
878 dtype=dtype,
879 )
882@contextmanager
883def _timezone_warning_as_error() -> Generator[None, None, None]:
884 with warnings.catch_warnings():
885 warnings.filterwarnings(
886 'error', category=DeprecationWarning, message='parsing timezone'
887 )
888 try:
889 yield
890 except DeprecationWarning:
891 raise ValueError(
892 'Parsing timezone aware datetimes is not supported'
893 ) from None
896def datetime(
897 value: str | int | _np.datetime64,
898 *,
899 unit: Unit | str | DefaultUnit | None = default_unit,
900) -> Variable:
901 """Constructs a zero dimensional :class:`Variable` with a dtype of datetime64.
903 Parameters
904 ----------
905 value:
907 - `str`: Interpret the string according to the ISO 8601 date time format.
908 - `int`: Number of time units (see argument ``unit``)
909 since Scipp's epoch (see :py:func:`scipp.epoch`).
910 - `np.datetime64`: Construct equivalent datetime of Scipp.
912 unit: Unit of the resulting datetime.
913 Can be deduced if ``value`` is a str or np.datetime64 but
914 is required if it is an int.
916 Returns
917 -------
918 :
919 A scalar variable containing a datetime.
921 See Also
922 --------
923 scipp.datetimes:
924 scipp.epoch:
925 Details in:
926 'Dates and Times' section in `Data Types <../../reference/dtype.rst>`_
928 Examples
929 --------
931 >>> sc.datetime('2021-01-10T14:16:15')
932 <scipp.Variable> () datetime64 [s] 2021-01-10T14:16:15
933 >>> sc.datetime('2021-01-10T14:16:15', unit='ns')
934 <scipp.Variable> () datetime64 [ns] 2021-01-10T14:16:15.000000000
935 >>> sc.datetime(1610288175, unit='s')
936 <scipp.Variable> () datetime64 [s] 2021-01-10T14:16:15
938 Get the current time:
940 >>> now = sc.datetime('now', unit='s')
941 """
942 if isinstance(value, str):
943 with _timezone_warning_as_error():
944 return scalar(_np.datetime64(value), unit=unit)
945 return scalar(value, unit=unit, dtype=_cpp.DType.datetime64)
948def datetimes(
949 *,
950 dims: Sequence[str],
951 values: ArrayLike,
952 unit: Unit | str | DefaultUnit | None = default_unit,
953) -> Variable:
954 """Constructs an array :class:`Variable` with a dtype of datetime64.
956 Parameters
957 ----------
958 dims:
959 Dimension labels
960 values: numpy.typing.ArrayLike
961 Numpy array or something that can be converted to a
962 Numpy array of datetimes.
963 unit:
964 Unit for the resulting Variable.
965 Can be deduced if ``values`` contains strings or np.datetime64's.
967 Returns
968 -------
969 :
970 An array variable containing a datetime.
972 See Also
973 --------
974 scipp.datetime:
975 scipp.epoch:
976 Details in:
977 'Dates and Times' section in `Data Types <../../reference/dtype.rst>`_
979 Examples
980 --------
982 >>> sc.datetimes(dims=['t'], values=['2021-01-10T01:23:45',
983 ... '2021-01-11T01:23:45'])
984 <scipp.Variable> (t: 2) datetime64 [s] [2021-01-10T01:23:45, 2021-01-11T01:23:45]
985 >>> sc.datetimes(dims=['t'], values=['2021-01-10T01:23:45',
986 ... '2021-01-11T01:23:45'], unit='h')
987 <scipp.Variable> (t: 2) datetime64 [h] [2021-01-10T01:00:00, 2021-01-11T01:00:00]
988 >>> sc.datetimes(dims=['t'], values=[0, 1610288175], unit='s')
989 <scipp.Variable> (t: 2) datetime64 [s] [1970-01-01T00:00:00, 2021-01-10T14:16:15]
990 """
991 np_unit_str = f'[{u}]' if (u := _cpp.to_numpy_time_string(unit)) else ''
992 with _timezone_warning_as_error():
993 return array(
994 dims=dims, values=_np.asarray(values, dtype=f'datetime64{np_unit_str}')
995 )
998def epoch(*, unit: Unit | str | DefaultUnit | None) -> Variable:
999 """Constructs a zero dimensional :class:`Variable` with a dtype of
1000 datetime64 that contains Scipp's epoch.
1002 Currently, the epoch of datetimes in Scipp is the Unix epoch 1970-01-01T00:00:00.
1004 Parameters
1005 ----------
1006 unit:
1007 Unit of the resulting Variable.
1009 Returns
1010 -------
1011 :
1012 A scalar variable containing the datetime of the epoch.
1014 See Also
1015 --------
1016 scipp.datetime:
1017 scipp.datetimes:
1018 Details in:
1019 'Dates and Times' section in `Data Types <../../reference/dtype.rst>`_
1021 Examples
1022 --------
1024 >>> sc.epoch(unit='s')
1025 <scipp.Variable> () datetime64 [s] 1970-01-01T00:00:00
1026 """
1027 return scalar(0, unit=unit, dtype=_cpp.DType.datetime64)