vectorose.util ============== .. py:module:: vectorose.util .. autoapi-nested-parse:: Utility functions This module provides utility functions for manipulating vectors in Cartesian and spherical coordinates. .. rubric:: References .. [#fisher-lewis-embleton] Fisher, N. I., Lewis, T., & Embleton, B. J. J. (1993). Statistical analysis of spherical data ([New ed.], 1. paperback ed). Cambridge Univ. Press. Classes ------- .. autoapisummary:: vectorose.util.AngularIndex vectorose.util.AngleName vectorose.util.MagnitudeType Functions --------- .. autoapisummary:: vectorose.util.convert_vectors_to_data_frame vectorose.util.compute_vector_magnitudes vectorose.util.flatten_vector_field vectorose.util.remove_zero_vectors vectorose.util.normalise_array vectorose.util.normalise_vectors vectorose.util.convert_vectors_to_axes vectorose.util.create_symmetric_vectors_from_axes vectorose.util.convert_spherical_to_cartesian_coordinates vectorose.util.compute_vector_orientation_angles vectorose.util.compute_spherical_coordinates vectorose.util.convert_to_math_spherical_coordinates vectorose.util.convert_math_spherical_coordinates_to_vr_coordinates vectorose.util.rotate_vectors vectorose.util.compute_arc_lengths Module Contents --------------- .. py:class:: AngularIndex Bases: :py:obj:`enum.IntEnum` Angular index definition. Stores the index of the different angles to avoid ambiguity in code. .. py:attribute:: PHI :value: 0 Angle phi, in-plane with respect to positive ``y``; index 0. .. py:attribute:: THETA :value: 1 Angle theta, incline with respect to positive ``z``; index 1. .. py:class:: AngleName Bases: :py:obj:`str`, :py:obj:`enum.Enum` Angular index definition. Stores the name of the different angles to avoid ambiguity in code. .. py:attribute:: PHI :value: 'phi' Angle phi, in-plane with respect to positive ``y``; index 0. .. py:attribute:: THETA :value: 'theta' Angle theta, incline with respect to positive ``z``; index 1. .. py:class:: MagnitudeType Bases: :py:obj:`enum.IntEnum` Type of vector magnitude. .. py:attribute:: THREE_DIMENSIONAL :value: 0 Euclidean magnitude in 3D space. .. py:attribute:: IN_PLANE :value: 1 Magnitude of the ``(x,y)``-projection of the vector. .. py:function:: convert_vectors_to_data_frame(vectors: numpy.ndarray) -> pandas.DataFrame Convert vector array into a DataFrame. Convert an array of vectors into a pandas :class:`pandas.DataFrame`. :param vectors: Array of shape ``(n, 3)`` or ``(n, 6)`` containing the vectors. If three columns are present, they are considered as the ``x, y, z`` vector components, respectively. If six columns are present, the final three columns are considered as the vector components, while the first three columns are considered the ``x, y, z`` spatial locations of the vectors. :returns: :class:`pandas.DataFrame` -- Data frame of the same shape as `vectors`. Spatial columns, if present, are labelled ``x, y, z`` while the vector component columns are labelled ``vx, vy, vz``. .. py:function:: compute_vector_magnitudes(vectors: numpy.ndarray) -> numpy.ndarray Compute vector magnitudes. Compute vector magnitudes in 3D, as well as the component of the magnitude in the ``(x,y)``-plane. :param vectors: Array of shape ``(n, 3)`` containing the x, y and z *components* of the ``n`` 3-dimensional vectors. :returns: :class:`numpy.ndarray` -- Array of shape ``(n, 2)`` containing the vector magnitudes. The first column contains the true 3D vector magnitude. While the second column contains the magnitude of the projection onto the ``xy``-plane. .. rubric:: Notes The vector magnitudes are computed using the following equations: .. math:: \| v \| &= \sqrt{{v_x}^2 + {v_y}^2 + {v_z} ^ 2} \| v \|_{xy} &= \sqrt{{v_x}^2 + {v_y}^2} The both magnitudes are implemented in :func:`numpy.linalg.norm`. .. py:function:: flatten_vector_field(vector_field: numpy.ndarray) -> numpy.ndarray Flatten a vector field into a 2D vector list. Convert an n-dimensional vector image volume into a 2D list of vectors, with rows reflecting vectors and the columns reflecting each component. :param vector_field: Array containing the vector field. If this array is 2D, then the rows are considered to correspond to the vectors, while the columns correspond to the components. If the vector has higher dimension, the last axis is assumed to distinguish between the components. :returns: :class:`numpy.ndarray` -- 2D array containing the vectors as rows and the components as columns. If the original array was 2D, this original array is returned without copying. .. py:function:: remove_zero_vectors(vectors: numpy.ndarray) -> numpy.ndarray Prune zero-vectors. Remove vectors of zero magnitude from the list of vectors. :param vectors: ``n`` by 6 or ``n`` by 3 array of vectors. If the array has 6 columns, *the last 3 are assumed to be the vector components*. :returns: :class:`numpy.ndarray` -- List of vectors with the same number of columns as the original without any vectors of zero magnitude. .. py:function:: normalise_array(arr: numpy.ndarray, axis: Optional[int] = None) -> numpy.ndarray Normalise an array. Normalise the provided array so that all entries sum to one along the specified axis. :param arr: The array to normalise. This array can have any shape. :param axis: The axis along which to normalise. If `None`, then overall normalisation is performed. :returns: :class:`numpy.ndarray` -- The normalised array, such that the sum of all entries is 1 along the specified axis. .. py:function:: normalise_vectors(vectors: numpy.ndarray) -> Tuple[numpy.ndarray, numpy.ndarray] Normalise an array of vectors. Rescale a series of vectors to ensure that all non-zero vectors have unit length. :param vectors: ``n`` by 6 or ``n`` by 3 array of vectors. If the array has 6 columns, *the last 3 are assumed to be the vector components*. This array must contain **no zero-vectors**. :returns: * **normalised_vectors** (:class:`numpy.ndarray`) -- Array of the same shape as `vectors`, but with all vector components rescaled to ensure that the vectors have unit length. * **magnitudes** (:class:`numpy.ndarray`) -- Array of shape ``(n,)`` containing the magnitud of each vector. .. rubric:: Notes This function does not modify the original array. A new array is created and returned. The 3D magnitude is used to perform the normalisation. This magnitude is computed as .. math:: \|\vec{v}\| = \sqrt{v_x^2 + v_y^2 + v_z^2} where :math:`v_i` refers to the component of :math:`\vec{v}` along the *i*-th axis. .. py:function:: convert_vectors_to_axes(vectors: numpy.ndarray) -> numpy.ndarray Convert vectors to axes. Reflect all vectors so that they are oriented in the four octants that have positive z-values. These correspond to the axes conventionally used in directional statistics (see the book by Fisher, Lewis and Embleton [#fisher-lewis-embleton]_). :param vectors: Array of shape ``(n, 3)`` or ``(n, 6)`` containing the vectors. The last three columns are assumed to be the vector components. :returns: :class:`numpy.ndarray` -- Array of the same shape as the original, but with all vectors oriented towards a non-negative Z value. .. py:function:: create_symmetric_vectors_from_axes(axes: numpy.ndarray) -> numpy.ndarray Create a set of symmetric vectors from axes. Duplicate a collection of axes to produce vectors pointing in both directions corresponding to each orientation. :param axes: Array of shape ``(n, 3)`` or ``(n, 6)`` containing the axes. All entries in this array should have a positive Z-components. The vector coordinates are assumed to be in the last three columns if spatial coordinates are also present. :returns: :class:`numpy.ndarray` -- Array of shape ``(2n, 3)`` containing the vectors along each direction. The inverted vectors appear in the same order as the axes **after the non-inverted vectors**. .. warning:: The inverted vectors, having negative z-values, are appended after the non-inverted vectors. Corresponding vectors are **not** interleaved. .. py:function:: convert_spherical_to_cartesian_coordinates(angular_coordinates: numpy.ndarray, radius: Union[float, numpy.ndarray] = 1, use_degrees: bool = False) -> numpy.ndarray Convert spherical coordinates to cartesian coordinates. Convert spherical coordinates provided in terms of phi and theta into cartesian coordinates. For the conversion to be possible, a sphere radius must also be specified. If none is provided, the sphere is assumed to be the unit sphere. :param angular_coordinates: Array with >=2 columns representing :math:`\phi` and :math:`\theta`, respectively (see :class:`AngularIndex`), and ``n`` rows representing the data points. This function can also be used on the output of :func:`np.mgrid`, if the arrays have been stacked such that the final axis is used to distinguish between phi and theta. :param radius: A :class:`float` or :class:`numpy.ndarray` representing the radius of the sphere. If the value passed is an array, it must have ``n`` rows, one for each data point. Default: ``radius=1``. :param use_degrees: Indicate whether the provided angular coordinates are in degrees. If `False` (default), radians are assumed. :returns: :class:`numpy.ndarray` -- Array with 3 columns, corresponding to the cartesian coordinates in X, Y, Z, and ``n`` rows, one for each data point. If mgrids are provided, then multiple sheets will be returned in this array, with the -1 axis still used to distinguish between x, y, z. .. rubric:: Notes The equations governing the conversion are: .. math:: x &= r \sin(\theta)\sin(\phi) y &= r \cos(\theta)\sin(\phi) z &= r \cos(\phi) The input is provided as a 2D array with 2 columns representing the angles phi and theta, and ``n`` rows, representing the datapoints. The returned array is also a 2D array, with three columns (X, Y, Z) and ``n`` rows. .. py:function:: compute_vector_orientation_angles(vectors: numpy.ndarray, use_degrees: bool = False) -> numpy.ndarray Compute the vector orientation angles phi and theta. For all provided vectors, compute the ``phi`` and ``theta`` angles. The ``phi`` angle corresponds to the co-latitude, representing the tilt with respect to the ``z``-axis, while ``theta`` is the azimuthal angle in the ``xy``-plane with respect to the positive ``y``-axis. :param vectors: Array of shape ``(n, 3)`` containing 3 columns, corresponding to the x, y and z *components* of ``n`` 3-dimensional vectors. :param use_degrees: Indicate whether the returned angles should be in degrees. Otherwise, the angles are in **radians**. :returns: :class:`numpy.ndarray` -- Array of shape ``(n, 2)`` containing the ``phi`` and ``theta`` angles for each vector. .. rubric:: Notes In this package, we define the angles to be: * :math:`\phi` - The angle of tilt with respect to the positive :math:`z`-axis. A vector with :math:`\phi=0` will be oriented parallel to the :math:`z`-axis, while a vector with :math:`\phi=\pi/2` will be oriented parallel to the :math:`(x,y)`-plane. A vector with :math:`\phi=\pi` will be oriented parallel to the negative :math:`z`-axis. * :math:`\theta` - The orientation in the :math:`(x,y)`-plane with respect to the *positive* :math:`y`-axis. A vector with :math:`\theta=0` will be parallel to the *positive* :math:`y`-axis, while a vector with :math:`\theta=\pi/2` will be oriented parallel to the *positive* :math:`x`-axis. These angles are computed in the following manner: .. math:: \phi_i &= \textup{arctan} \left( \frac{\sqrt{{x_i} ^ 2 + {y_i} ^ 2}}{z_i} \right) \theta_i &= \textup{arctan} \left( \frac{x_i}{y_i} \right) To ensure that each direction has a unique description, we restrict the angles to specific ranges. The ``phi`` angle is in the range ``0 <= phi <= 180`` degrees, or ``0 <= phi <= pi`` radians, while the ``theta`` angle is in the range ``0 <= theta < 360`` degrees or ``0 <= theta < 2 * pi`` radians. .. py:function:: compute_spherical_coordinates(vectors: numpy.ndarray, use_degrees: bool = False) -> numpy.ndarray Compute spherical coordinates for a set of vectors. Compute true spherical coordinates for a set of provided vectors. These coordinates express a vector as an orientation, consisting of the angles phi and theta, and a magnitude. :param vectors: 2D NumPy array containing 3 columns, corresponding to the x, y and z **components** of the vectors, and ``n`` rows, one for each vector. **Note:** We only require the vector *components*, not the *coordinates* in space. :param use_degrees: indicate whether the returned angles should be in degrees. If ``False`` (default), the angles will be returned in *radians*. :returns: :class:`numpy.ndarray` -- Array of shape ``(n, 3)`` containing the vectors in spherical coordinates, consisting of ``phi``, ``theta`` and ``magnitude`` columns. .. seealso:: :obj:`compute_compute_vector_orientation_angles` Compute phi and theta angles from Cartesian coordinates. :obj:`numpy.linalg.norm` Compute the magnitude (norm) of vectors in Cartesian coordinates. .. py:function:: convert_to_math_spherical_coordinates(original_angles: numpy.ndarray, use_degrees: bool = False) -> numpy.ndarray Convert to the mathematical definition of spherical coordinates. Directional statistics texts, such as the work by Fisher, Lewis and Embleton, [#fisher-lewis-embleton]_ define the spherical coordinates differently than we do in this code. For compatibility with statistical procedures described in such works, this function converts spherical coordinates in our representation to the standard definition. :param original_angles: Array of shape ``(n, 2)`` containing the phi, theta angles computed using our definition of spherical coordinates, defined in the function :func:`.compute_vector_orientation_angles`. :param use_degrees: Indicate whether the original spherical coordinates are in degrees, and whether the resulting transformed vectors should also be in degrees. If `False`, all angles are assumed to be in radians. :returns: :class:`numpy.ndarray` -- Array of the same shape as the input `original_angles`, but with the angles defined following Fisher, Lewis and Embleton's definitions. [#fisher-lewis-embleton]_ .. rubric:: Notes The polar coordinates in section 2.2 (a) of by Fisher, Lewis and Embleton [#fisher-lewis-embleton]_ define the angle :math:`\theta` as the angle of inclination from the vertical axis, while the in-plane angle :math:`\phi` is the counter-clockwise (anticlockwise) angle in the ``xy``-plane, measured with respect to the ``+x`` axis. .. py:function:: convert_math_spherical_coordinates_to_vr_coordinates(original_angles: numpy.ndarray, use_degrees: bool = False) -> numpy.ndarray Convert mathematical spherical coordinates to vectorose conventions. Directional statistics texts, such as the work by Fisher, Lewis and Embleton, [#fisher-lewis-embleton]_ define the spherical coordinates differently than we do in this code. For compatibility with statistical procedures described in such works, this function converts spherical coordinates in the standard definition to our representation of spherical coordinates. :param original_angles: Array of shape ``(n, 2)`` containing the phi, theta angles computed using the standard mathematical spherical coordinates, described by Fisher, Lewis and Embleton. [#fisher-lewis-embleton]_ :param use_degrees: Indicate whether the original spherical coordinates are in degrees, and whether the resulting transformed vectors should also be in degrees. If `False`, all angles are assumed to be in radians. :returns: :class:`numpy.ndarray` -- Array of the same shape as the input `original_angles`, but with the angles defined as in the function :func:`.compute_vector_orientation_angles`. .. rubric:: Notes The polar coordinates in section 2.2 (a) of by Fisher, Lewis and Embleton [#fisher-lewis-embleton]_ define the angle :math:`\theta` as the angle of inclination from the vertical axis, while the in-plane angle :math:`\phi` is the counter-clockwise (anticlockwise) angle in the ``xy``-plane, measured with respect to the ``+y`` axis. .. py:function:: rotate_vectors(vectors: numpy.ndarray, new_pole: numpy.ndarray) -> numpy.ndarray Rotate a set of vectors. Rotate vectors so that the top pole of the sphere is rotated to a specified location. :param vectors: Array containing the Cartesian vector components to rotate, of shape ``(n, 3)``, where ``n`` represents the number of 3D vectors. :param new_pole: Vector coordinates corresponding to the new pole position after rotating, also in cartesian coordinates. :returns: :class:`numpy.ndarray` -- Array of shape ``(n, 3)`` containing the rotated vector components. .. seealso:: :obj:`scipy.spatial.transform.Rotation` Abstraction used for the rotations. .. rubric:: Notes Although the approach described by Fisher, Lewis and Embleton [#fisher-lewis-embleton]_ was initially used, we replaced it with the :class:`scipy.spatial.transform.Rotation` class present in SciPy. .. py:function:: compute_arc_lengths(vector: numpy.ndarray, vector_collection: numpy.ndarray) -> numpy.ndarray Compute the arc lengths between a vector and many vectors. For each vector in a set of vectors, compute the arc length to a specified vector. The arc length is the angular distance on the surface of the unit sphere. :param vector: Array of shape ``(3,)`` containing the reference vector from which all arc lengths are measured. :param vector_collection: Array of shape ``(n, 3)`` containing ``n`` three-dimensional vectors. The arc lengths of each vector will be measured with respect to `vector`. :returns: :class:`numpy.ndarray` -- Array of shape ``(n,)`` containing the arc length from the reference `vector` to each of the vectors in the provided collection. .. warning:: The angular distance reported can also be interpreted as the angle in **radians** between the reference vector and each respective vector. .. rubric:: Notes The results can only be interpreted on the unit sphere. While vectors of any length may be passed in, the arc length interpretation relies on a unit sphere. Otherwise, the results can still be interpreted as angles between the vector tails, but **should not** be considered any sort of magnitude-linked arc length. The explanation for this method comes in part from Section 5.3.1(ii) in Fisher, Lewis and Embleton [#fisher-lewis-embleton]_. This method relies on the relationship between dot-products and angles, as .. math:: \mathbf{u} \cdot \mathbf{v} = \|\mathbf{u}\| \cdot \|\mathbf{v}\| \cos \theta where :math:`\theta` is the angle between the tails of vectors :math:`\mathbf{u}` and :math:`\mathbf{v}`.