----------------------------- Interacting with CSDM objects ----------------------------- Basic math operations """"""""""""""""""""" The csdm object supports basic mathematical operations such as additive and multiplicative operations. .. note:: All operations applied to or involving the csdm objects apply only to the components of the dependent variables within the csdm object. These operations do not apply to the dimensions within the csdm object. Consider the following csdm data object. .. doctest:: >>> arr1 = np.arange(6, dtype=np.float32).reshape(2, 3) >>> csdm_obj1 = cp.as_csdm(arr1) >>> # converting the dimension to proper physical dimensions. >>> csdm_obj1.dimensions[0] *= cp.ScalarQuantity("2.64 m") >>> csdm_obj1.dimensions[0].coordinates_offset = "1 km" >>> # converting the dimension to proper physical dimensions. >>> csdm_obj1.dimensions[1] *= cp.ScalarQuantity("10 µs") >>> csdm_obj1.dimensions[1].coordinates_offset = "-0.5 ms" >>> print(csdm_obj1) CSDM( DependentVariable( [[[0. 1. 2.] [3. 4. 5.]]], quantity_type=scalar, numeric_type=float32), LinearDimension([1000. 1002.64 1005.28] m), LinearDimension([-500. -490.] us) ) Additive operations involving a scalar '''''''''''''''''''''''''''''''''''''' **Example 1** .. doctest:: >>> csdm_obj1 += np.pi >>> print(csdm_obj1) CSDM( DependentVariable( [[[3.1415927 4.141593 5.141593 ] [6.141593 7.141593 8.141593 ]]], quantity_type=scalar, numeric_type=float32), LinearDimension([1000. 1002.64 1005.28] m), LinearDimension([-500. -490.] us) ) **Example 2** .. doctest:: >>> csdm_obj2 = csdm_obj1 + (2 - 4j) >>> print(csdm_obj2) CSDM( DependentVariable( [[[ 5.141593-4.j 6.141593-4.j 7.141593-4.j] [ 8.141593-4.j 9.141593-4.j 10.141593-4.j]]], quantity_type=scalar, numeric_type=complex64), LinearDimension([1000. 1002.64 1005.28] m), LinearDimension([-500. -490.] us) ) Multiplicative operations involving scalar / ScalarQuantity ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' **Example 3** .. doctest:: >>> csdm_obj1 = cp.as_csdm(np.ones(6).reshape(2, 3)) >>> csdm_obj2 = csdm_obj1 * 4.693 >>> print(csdm_obj2) CSDM( DependentVariable( [[[4.693 4.693 4.693] [4.693 4.693 4.693]]], quantity_type=scalar, numeric_type=float64), LinearDimension([0. 1. 2.]), LinearDimension([0. 1.]) ) **Example 4** .. doctest:: >>> csdm_obj2 = csdm_obj1 * 3j / 2.4 >>> print(csdm_obj2) CSDM( DependentVariable( [[[0.+1.25j 0.+1.25j 0.+1.25j] [0.+1.25j 0.+1.25j 0.+1.25j]]], quantity_type=scalar, numeric_type=complex128), LinearDimension([0. 1. 2.]), LinearDimension([0. 1.]) ) You may change the dimensionality of the dependent variables by multiplying the csdm object with the appropriate scalar quantity, for example, **Example 5** .. doctest:: >>> csdm_obj1 *= cp.ScalarQuantity("3.23 m") >>> print(csdm_obj1) CSDM( DependentVariable( [[[3.23 3.23 3.23] [3.23 3.23 3.23]]] m, quantity_type=scalar, numeric_type=float64), LinearDimension([0. 1. 2.]), LinearDimension([0. 1.]) ) **Example 6** .. doctest:: >>> csdm_obj1 /= cp.ScalarQuantity("3.23 m") >>> print(csdm_obj1) CSDM( DependentVariable( [[[1. 1. 1.] [1. 1. 1.]]], quantity_type=scalar, numeric_type=float64), LinearDimension([0. 1. 2.]), LinearDimension([0. 1.]) ) Additive operations involving two csdm objects '''''''''''''''''''''''''''''''''''''''''''''' The additive operations are supported between two csdm objects only when the two objects have identical sets of Dimension objects and DependentVariable objects with the same dimensionality. For examples, **Example 7** .. doctest:: >>> csdm1 = cp.as_csdm(np.ones((2, 3)), unit="m/s") >>> csdm2 = cp.as_csdm(np.ones((2, 3)), unit="cm/s") >>> csdm_obj = csdm1 + csdm2 >>> print(csdm_obj) CSDM( DependentVariable( [[[1.01 1.01 1.01] [1.01 1.01 1.01]]] m / s, quantity_type=scalar, numeric_type=float64), LinearDimension([0. 1. 2.]), LinearDimension([0. 1.]) ) An exception will be raised if the DependentVariable objects of the two csdm objects have different dimensionality. **Example 8** .. doctest:: >>> csdm1 = cp.as_csdm(np.ones((2, 3)), unit="m/s") >>> csdm2 = cp.as_csdm(np.ones((2, 3))) >>> csdm_obj = csdm1 + csdm2 # doctest: +SKIP Exception: Cannot operate on dependent variables with physical types: speed and dimensionless. Similarly, an exception will be raised if the dimension objects of the two csdm objects are different. **Example 9** .. doctest:: >>> csdm1 = cp.as_csdm(np.ones((2, 3)), unit="m/s") >>> csdm1.dimensions[1] = cp.MonotonicDimension(coordinates=["1 ms", "1 s"]) >>> csdm2 = cp.as_csdm(np.ones((2, 3)), unit="cm/s") >>> csdm_obj = csdm1 + csdm2 # doctest: +SKIP Exception: Cannot operate on CSDM objects with different dimensions. Basic Slicing and Indexing """""""""""""""""""""""""" The CSDM objects support NumPy basic slicing and indexing and follow the same rules as the NumPy array. Consider the following 3D{1} csdm object. .. doctest:: >>> csdm1 = cp.as_csdm(np.zeros((5, 10, 20)), unit="s") >>> csdm1.dimensions[0] = cp.as_dimension(np.arange(20) * 0.5 + 4.3, unit="kg") >>> csdm1.dimensions[1] = cp.as_dimension([1, 2, 3, 5, 7, 11, 13, 17, 19, 23], unit="mm") >>> csdm1.dimensions[2] = cp.LabeledDimension(labels=list("abcde")) >>> print(csdm1.shape) (20, 10, 5) >>> print(csdm1.dimensions) [LinearDimension(count=20, increment=0.5 kg, coordinates_offset=4.3 kg, quantity_name=mass), MonotonicDimension(coordinates=[ 1. 2. 3. 5. 7. 11. 13. 17. 19. 23.] mm, quantity_name=length, reciprocal={'quantity_name': 'wavenumber'}), LabeledDimension(labels=['a', 'b', 'c', 'd', 'e'])] The above object ``csdm1`` has three dimensions, each with different dimensionality and dimension type. To retrieve a sub-grid of this 3D{1} dataset, use the NumPy indexing scheme. **Example 10** .. doctest:: >>> sub_csdm = csdm1[0] >>> print(sub_csdm.shape) (10, 5) >>> print(sub_csdm.dimensions) [MonotonicDimension(coordinates=[ 1. 2. 3. 5. 7. 11. 13. 17. 19. 23.] mm, quantity_name=length, reciprocal={'quantity_name': 'wavenumber'}), LabeledDimension(labels=['a', 'b', 'c', 'd', 'e'])] The above example returns a 2D{1} cross-section of the 3D{1} datasets corresponding to the index 0 along the first dimension of the ``csdm1`` object as a ``sub_csdm`` csdm object. The two dimensions in ``sub_csdm`` are the MonotonicDimension and LabeledDimension. **Example 11** .. doctest:: >>> sub_csdm = csdm1[::5, 2::2, :] >>> print(sub_csdm.shape) (4, 4, 5) >>> print(sub_csdm.dimensions) [LinearDimension(count=4, increment=2.5 kg, coordinates_offset=4.3 kg, quantity_name=mass), MonotonicDimension(coordinates=[ 3. 7. 13. 19.] mm, quantity_name=length, reciprocal={'quantity_name': 'wavenumber'}), LabeledDimension(labels=['a', 'b', 'c', 'd', 'e'])] The above example returns a 3D{1} dataset, ``sub_csdm``, which contains a sub-grid of the 3D{1} datasets from ``csdm1``. In ``sub_csdm``, the first dimension is a sub-grid of the first dimension from the ``csdm1`` object, where only every fifth grid point is selected. Similarly, the second dimension of the ``sub_csdm`` object is sampled from the second dimension of the ``csdm1`` object, where every second grid point is selected, starting with the entry at the grid index two. The third dimension of the ``sub_csdm`` object is the same as the third object of the ``csdm1`` object. The values of the corresponding linear, monotonic, and labeled dimensions are adjusted accordingly. For example, notice the value of the `count` and `increment` attributes of the linear dimension in ``sub_csdm`` object. **Example 12** .. doctest:: >>> sub_csdm = csdm1[::5, 2::2, -3::-1] >>> print(sub_csdm.shape) (4, 4, 3) >>> print(sub_csdm.dimensions) [LinearDimension(count=4, increment=2.5 kg, coordinates_offset=4.3 kg, quantity_name=mass), MonotonicDimension(coordinates=[ 3. 7. 13. 19.] mm, quantity_name=length, reciprocal={'quantity_name': 'wavenumber'}), LabeledDimension(labels=['c', 'b', 'a'])] The above example is similar to the previous examples, except the third dimension indexed in reversed starting at the third index from the end. .. seealso:: `Basic Slicing and Indexing `_ Support for Numpy methods """"""""""""""""""""""""" In most cases, the csdm object may be used as if it were a NumPy array. See the list of all supported :ref:`numpy_support`. Method that only operate on dimensionless dependent variables ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' **Example 13** .. doctest:: >>> csdm_obj1 = cp.as_csdm(10 ** (np.arange(10) / 10)) >>> new_csdm1 = np.log10(csdm_obj1) >>> print(new_csdm1) CSDM( DependentVariable( [[0. 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9]], quantity_type=scalar, numeric_type=float64), LinearDimension([0. 1. 2. 3. 4. 5. 6. 7. 8. 9.]) ) **Example 14** .. doctest:: >>> new_csdm2 = np.cos(2 * np.pi * new_csdm1) >>> print(new_csdm2) CSDM( DependentVariable( [[ 1. 0.80901699 0.30901699 -0.30901699 -0.80901699 -1. -0.80901699 -0.30901699 0.30901699 0.80901699]], quantity_type=scalar, numeric_type=float64), LinearDimension([0. 1. 2. 3. 4. 5. 6. 7. 8. 9.]) ) **Example 15** .. doctest:: >>> new_csdm2 = np.exp(new_csdm1 * cp.ScalarQuantity("K")) # doctest: +SKIP ValueError: Cannot apply `exp` to quantity with physical type `temperature`. An exception is raised for csdm object with non-dimensionless dependent variables. Method that are independent of the dependent variable dimensionality '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' **Example 16** .. doctest:: >>> new_csdm2 = np.square(new_csdm1 * cp.ScalarQuantity("K")) >>> print(new_csdm2) CSDM( DependentVariable( [[0. 0.01 0.04 0.09 0.16 0.25 0.36 0.49 0.64 0.81]] K2, quantity_type=scalar, numeric_type=float64), LinearDimension([0. 1. 2. 3. 4. 5. 6. 7. 8. 9.]) ) **Example 17** .. doctest:: >>> new_csdm1 = np.sqrt(new_csdm2) >>> print(new_csdm1) CSDM( DependentVariable( [[0. 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9]] K, quantity_type=scalar, numeric_type=float64), LinearDimension([0. 1. 2. 3. 4. 5. 6. 7. 8. 9.]) ) Dimension reduction methods ''''''''''''''''''''''''''' **Example 18** .. doctest:: >>> csdm1 = cp.as_csdm(np.ones((10, 20, 30)), unit="µG") >>> csdm1.shape (30, 20, 10) >>> new = np.sum(csdm1, axis=1) >>> new.shape (30, 10) >>> print(new.dimensions) [LinearDimension(count=30, increment=1.0), LinearDimension(count=10, increment=1.0)] **Example 19** .. doctest:: >>> csdm1 = cp.as_csdm(np.ones((10, 20, 30)), unit="µG") >>> csdm1.shape (30, 20, 10) >>> new = np.sum(csdm1, axis=(1, 2)) >>> new.shape (30,) >>> print(new.dimensions) [LinearDimension(count=30, increment=1.0)] **Example 20** .. doctest:: >>> minimum = np.min(new_csdm1) >>> print(minimum) 0.0 K >>> np.min(new_csdm1) == new_csdm1.min() True .. note:: See the list of all supported :ref:`numpy_support`.