netsse.tools.misc_func
======================

.. py:module:: netsse.tools.misc_func

.. autoapi-nested-parse::

   **Miscellaneous functions** for NetSSE.

   .. dropdown:: Copyright (C) 2023-2026 Technical University of Denmark, R.E.G. Mounet
       :color: primary
       :icon: law

       *This code is part of the NetSSE software.*

       NetSSE is free software: you can redistribute it and/or modify it under
       the terms of the GNU General Public License as published by the Free
       Software Foundation, either version 3 of the License, or (at your
       option) any later version.

       NetSSE is distributed in the hope that it will be useful, but WITHOUT
       ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
       FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
       for more details.

       You should have received a copy of the GNU General Public License along
       with this program.  If not, see https://www.gnu.org/licenses/.

       To credit the author, users are encouraged to use below reference:

       .. code-block:: text

           Mounet, R. E. G., & Nielsen, U. D. NetSSE: An open-source Python package
           for network-based sea state estimation from ships, buoys, and other
           observation platforms (version 2.2). Technical University of Denmark,
           GitLab. February 2026. https://doi.org/10.11583/DTU.26379811.

   *Last updated on 20-02-2026 by R.E.G. Mounet*



Functions
---------

.. autoapisummary::

   netsse.tools.misc_func.ang_diff
   netsse.tools.misc_func.ang_interp
   netsse.tools.misc_func.ang_mean
   netsse.tools.misc_func.ang_std
   netsse.tools.misc_func.areSame_mat
   netsse.tools.misc_func.areSame_vec
   netsse.tools.misc_func.find_nearest_gridpoint
   netsse.tools.misc_func.id_generator
   netsse.tools.misc_func.knots2ms
   netsse.tools.misc_func.ms2knots
   netsse.tools.misc_func.pad
   netsse.tools.misc_func.re_range
   netsse.tools.misc_func.weighted_quantile
   netsse.tools.misc_func.weighted_std
   netsse.tools.misc_func.wrap


Module Contents
---------------

.. py:function:: ang_diff(alpha, beta, unit='deg')

   Calculates the signed difference between two angles.

   :param alpha: Input angle as a scalar value or as an array of angles.
   :type alpha: float or array_like
   :param beta: Input angle as a scalar value or as an array of angles.
   :type beta: float or array_like
   :param unit: Specifies the unit of the angles as ``'rad'`` or
                ``'deg'``. The default is ``'deg'``.
   :type unit: {'deg','rad'}, optional

   :returns: **delta** -- The output difference between alpha and beta in the range [-180,180]
             degrees.

             .. note::
                 This function subtracts ``alpha`` from ``beta``, i.e.,
                 computes the signed difference ``beta-alpha``, taking into account
                 the circularity at 360 degrees.
   :rtype: float or array_like

   .. seealso::

      :py:obj:`re_range`
          Converts angles to a new range of values.

      :py:obj:`ang_mean`
          Calculates the mean angle from an array of angular data.

      :py:obj:`ang_std`
          Calculates the circular standard deviation for an array of angular data.

   .. rubric:: Example

   >>> delta = ang_diff(alpha, beta, unit='deg')


.. py:function:: ang_interp(x, alpha, xq, axis=0, unit='deg', datetime=False)

   Interpolates angular data.

   This function properly handles the circularity of angles at 360
   degrees.

   :param x: Array of independent variable where the angles ``alpha`` are
             defined.
   :type x: array_like
   :param alpha: Array of angles to be interpolated.
   :type alpha: array_like
   :param xq: Array of independent variable where the angles ``alpha`` must be
              interpolated.
   :type xq: array_like
   :param axis: If ``alpha`` is a 2d-array, the axis of ``alpha`` along which to
                interpolate. The default is ``0``.
   :type axis: {0,1}, optional
   :param unit: Specifies the unit of the angles as ``'rad'`` or
                ``'deg'``. The default is ``'deg'``.
   :type unit: {'deg','rad'}, optional
   :param datetime: If ``True``, then ``x`` and ``xq`` are assumed to be arrays of
                    datetime objects.
   :type datetime: bool, default False

   :returns: **alpha_q** -- Interpolated angles at ``xq``.
   :rtype: array_like

   .. seealso::

      :py:obj:`ang_diff`
          Calculates the signed difference between two angles.

      :py:obj:`ang_mean`
          Calculates the mean angle from an array of angular data.

      :py:obj:`ang_std`
          Calculates the circular standard deviation for an array of angular data.

   .. rubric:: Example

   >>> alpha_q = ang_interp(x,alpha,xq,axis=0,unit='deg')


.. py:function:: ang_mean(angles, unit='deg')

   Calculates the mean angle from an array of angular data.

   This function properly handles the circularity of angles at 360
   degrees.

   :param angles: Array of angles.
   :type angles: array_like
   :param unit: Specifies the unit of the angles as ``'rad'`` or
                ``'deg'``. The default is ``'deg'``.
   :type unit: {'deg','rad'}, optional

   :returns: **mean_a** -- Mean angle in the range [-180,180) degrees.
   :rtype: float

   .. seealso::

      :py:obj:`ang_diff`
          Calculates the signed difference between two angles.

      :py:obj:`ang_std`
          Calculates the circular standard deviation of an array of angular data.

   .. rubric:: Example

   >>> mean_angle = ang_mean(angles, unit='deg')


.. py:function:: ang_std(angles, unit='deg')

   Calculates the circular standard deviation of an array of
   angular data.

   This function properly handles the circularity of angles at 360
   degrees. The standard "two-pass" computation is implemented.

   :param angles: Array of angles.
   :type angles: array_like
   :param unit: Specifies the unit of the angles as ``'rad'`` or
                ``'deg'``. The default is ``'deg'``.
   :type unit: {'deg','rad'}, optional

   :returns: **std_a** -- Circular standard deviation.
   :rtype: float

   .. seealso::

      :py:obj:`ang_diff`
          Calculates the signed difference between two angles.

      :py:obj:`ang_mean`
          Calculates the mean angle from an array of angular data.

   .. rubric:: Example

   >>> std_angle = ang_std(angles, unit='deg')


.. py:function:: areSame_mat(A, B)

   Checks whether two matrices are identical, element-wise.

   :param A: Input matrix.
   :type A: 2d-array
   :param B: Input matrix.
   :type B: 2d-array

   :returns: ``False`` for ``A!=B``, ``True`` for ``A=B``.
   :rtype: bool

   .. note::
       If ``A`` and ``B`` do `not` have the same shape, then
       ``False`` is returned.

   .. seealso::

      :py:obj:`areSame_vec`
          Checks whether two vectors are identical, element-wise.

   .. rubric:: Example

   >>> areSame_mat(A,B)


.. py:function:: areSame_vec(a, b)

   Checks whether two vectors are identical, element-wise.

   :param a: Input vector.
   :type a: 1d-array
   :param b: Input vector.
   :type b: 1d-array

   :returns: ``False`` for ``a!=b``, ``True`` for ``a=b``.
   :rtype: bool

   .. note::
       If ``a`` and ``b`` do `not` have the same shape, then
       ``False`` is returned.

   .. seealso::

      :py:obj:`areSame_mat`
          Checks whether two matrices are identical, element-wise.

   .. rubric:: Example

   >>> areSame_vec(a,b)


.. py:function:: find_nearest_gridpoint(lat_wps, lon_wps, lat_grid, lon_grid, interv=0.01)

   Finds the nearest neighbours to a series of waypoints within a grid
   of points.

   .. note::
       The grid points do `not` need to be uniformly spaced in any direction.
       The grid can also have been rotated of any angle about the
       vertical direction (e.g., to be aligned with some shoreline).

   For a given waypoint at ``(lat_wp,lon_wp)``, the nearest-neighbour
   candidates are first selected within the gridpoints that fall in the
   geographical area delimited by ``lat_wp*[1-interv,1+interv]`` in latitude
   and ``lon_wp*[1-interv,1+interv]`` in longitude. Then, the distance between
   the waypoint and the nearest-neighbour candidates is computed. The output
   neighbour is found as the candidate that minimises this distance.

   :param lat_wps: Vector of waypoint latitudes [deg].
   :type lat_wps: float, or 1d-array of shape (Nwp,)
   :param lon_wps: Vector of waypoint longitudes [deg].
   :type lon_wps: float, or 1d-array of shape (Nwp,)
   :param lat_grid: Matrix defining the latitudes of the grid points [deg].
   :type lat_grid: 2d-array
   :param lon_grid: Matrix defining the longitudes of the grid points [deg].
   :type lon_grid: 2d-array
   :param interv: Half-width [-] of the interval band fraction in latitude and longitude,
                  used for screening the potential nearest-neighbour candidates.
   :type interv: float, default 0.01

   :returns: * **index_nearest** (*numpy.array of shape (Nwp,2)*) -- The coordinates in `(latitude,longitude)` of the nearest gridpoints
               to the waypoints [deg].
             * **dist_nearest** (*numpy.array of shape (Nwp,)*) -- The distance between the nearest gridpoints and the waypoints [km].

   .. rubric:: Example

   >>> index_nearest, dist_nearest =
   ...     find_nearest_gridpoint(lat_wps, lon_wps, lat_grid, lon_grid, interv=0.01)


.. py:function:: id_generator(size=6, chars=string.ascii_uppercase + string.digits)

   Randomly generates a cryptographically secure string of a given size.

   :param size: String size
   :type size: int, default 6
   :param chars: List of allowed characters
   :type chars: str, default 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'

   :returns: Output random string
   :rtype: str

   .. rubric:: Example

   >>> id_generator(size=6, chars=string.ascii_uppercase+string.digits)


.. py:function:: knots2ms(U_kn)

   Converts a speed from knots to meters per second.

   :param U_kn: Speed in knots [kn].
   :type U_kn: float or array_like

   :returns: **U_ms** -- Speed in meters per second [m/s].
   :rtype: float or array_like

   .. seealso::

      :py:obj:`ms2knots`
          Converts a speed from meters per second to knots.

   .. rubric:: Example

   >>> U_ms = knots2ms(U_kn)


.. py:function:: ms2knots(U_ms)

   Converts a speed from meters per second to knots.

   :param U_ms: Speed in meters per second [m/s].
   :type U_ms: float or array_like

   :returns: **U_kn** -- Speed in knots [kn].
   :rtype: float or array_like

   .. seealso::

      :py:obj:`knots2ms`
          Converts a speed from knots to meters per second.

   .. rubric:: Example

   >>> U_kn = ms2knots(U_ms)


.. py:function:: pad(U, axis=1, add=0)

   Adds zeros to an array along one of its dimensions.

   :param U: Input array to be padded with zeros at the beginning.
   :type U: array_like of shape (n,m) or (n,)
   :param axis: If ``U`` is a 2d-array, the axis of ``U`` along which to pad.
                The default is ``1``.
   :type axis: {1,0}, optional
   :param add: Term that is added to the concatenated zeros.
   :type add: float or array_like, default 0

   :returns: **U_pad** -- Extended array, where ``U`` has been padded with a first column
             (respectively, row) of zeros (plus an optional added term).

             .. note::

                 - If ``U`` is a 2d-array, then the output array ``U_pad`` has a
                   new shape of ``(n+1*(axis==0),m+1*(axis==1))``.
                 - If ``U`` is a 1d-array, then the output array ``U_pad`` has a
                   new shape of ``(n+1,)``. A zero element has been added at the
                   beginning.
   :rtype: array_like

   .. seealso::

      :py:obj:`wrap`
          Wraps an array around one of its dimensions.

   .. rubric:: Example

   >>> U_wrap = pad(U,axis=1,add=0)


.. py:function:: re_range(theta, start=0, unit='deg')

   Converts angles to a new range (of extent 360 degrees).

   :param theta: Array of angles to be rearranged.
   :type theta: array_like
   :param start: Lower boundary of the range that should contain the new values of
                 ``theta``. The upper boundary will be set automatically to
                 ``start + 360`` degrees.
   :type start: float, default 0
   :param unit: Specifies the unit of ``theta`` and ``start`` as ``'rad'`` or
                ``'deg'``. The default is ``'deg'``.
   :type unit: {'deg','rad'}, optional

   :returns: **theta_rerange** -- Array of angles where the elements of ``theta`` have been converted
             to the new range ``[start, start+360)`` degrees.
   :rtype: array_like

   .. seealso::

      :py:obj:`ang_diff`
          Calculates the signed difference between two angles.

   .. rubric:: Example

   >>> theta_rerange = re_range(theta,start=0,unit='deg')


.. py:function:: weighted_quantile(x, q, w, a=0.5, b=0.5, axis=-1)

   Computes the `q`-th quantiles of weighted data.

   The quantiles are computed along one of the axes of the input array
   ``x``.

   :param x: Input data array for which the quantiles must be computed.
   :type x: array_like
   :param q: Sequence of quantiles to compute, which must be between 0 and 1.
   :type q: 1d-array of shape (Nq,)
   :param w: Array of weights. The arrays ``x`` and ``w`` must be broadcastable.
   :type w: array_like
   :param a: User-defined constant used in the computation of the quantiles.
             The default is 0.5 to minimize biases (Rogers, 2003).
   :type a: floats, default 0.5
   :param b: User-defined constant used in the computation of the quantiles.
             The default is 0.5 to minimize biases (Rogers, 2003).
   :type b: floats, default 0.5
   :param axis: Axis of ``x`` along which the ``q``-quantiles are computed.
   :type axis: integer, default -1

   :returns: **v** -- Values of the ``q``-quantiles computed along the specified ``axis``
             of the input array ``x`` with weights ``w``.
   :rtype: array_like

   .. seealso::

      :py:obj:`weighted_std`
          Computes the weighted standard deviation of some data.

   .. rubric:: References

   Rogers, J.W., 2003. Estimating the variance of percentiles using replicate
   weights, in: Proc. of the Joint Statistical Meetings, Survey Research
   Methods Section, American Statistical Association. pp. 3525–3532.
   URL: http://www.asasrms.org/Proceedings/y2003/Files/JSM2003-000742.pdf

   .. rubric:: Example

   >>> v = weighted_quantile(x, q, w, a=0.5, b=0.5, axis=-1)


.. py:function:: weighted_std(a, w, axis=None, ddof=1)

   Computes the weighted standard deviation of some data.

   The weighted standard deviation is a measure of the spread of a
   distribution of the array elements from the mean, where some of the
   elements are more significant than others. The weighted standard deviation
   is calculated based on the weighted mean and it attaches more importance to
   data that have more weight than to data with less weight
   (National Institute of Standards and Technology, 1996).

   :param a: Variable for which the weighted std. must be computed.
   :type a: array_like
   :param w: Array of weights. All elements must be positive.
             The array ``w`` must broadcastable with the array ``a``.
   :type w: array_like
   :param axis: Axis or axes along which the standard deviation is computed.
                The default is to compute the weighted standard deviation of the
                flattened array.
   :type axis: None or int, optional
   :param ddof: Delta Degrees of Freedom. The divisor used in the calculations is
                ``(N_nz - ddof)/N_nz``, where ``N_nz`` represents the number of
                non-zero weights.
   :type ddof: int, default 0

   :returns: **std** -- Weighted standard deviation of the data.
   :rtype: array_like

   .. seealso::

      :py:obj:`weighted_quantile`
          Computes the `q`-th quantiles of weighted data.

   .. rubric:: References

   National Institute of Standards and Technology, 1996. Formula for the
   weighted standard deviation. Available at:
   https://www.itl.nist.gov/div898/software/dataplot/refman2/ch2/weightsd.pdf
   (Consulted on 06-08-2023).

   .. rubric:: Example

   >>> std = weighted_std(a,w,axis=None,ddof=1)


.. py:function:: wrap(U, axis=1, add=0)

   Wraps an array around one of its dimensions.

   :param U: Input array to be wrapped around.
   :type U: array_like of shape (n,m) or (n,)
   :param axis: If ``U`` is a 2d-array, the axis of ``U`` along which to wrap.
                The default is ``1``.
   :type axis: {1,0}, optional
   :param add: Term that is added to the repeated part of ``U`` before
               concatenation.
   :type add: float or array_like, default 0

   :returns: **U_wrap** -- Extended array, where the first column (respectively, row)
             of ``U`` has been repeated by concatenation at the end of ``U``.

             .. note::

                 - If ``U`` is a 2d-array, then the output array has a shape of
                   ``(n+1*(axis==0),m+1*(axis==1))``.
                 - If ``U`` is a 1d-array, then the output array has a shape of
                   ``(n+1,)``. The first element of ``U`` has been repeated at the
                   end.
   :rtype: array_like

   .. seealso::

      :py:obj:`pad`
          Adds zeros to an array along one of its dimensions.

   .. rubric:: Example

   >>> U_wrap = wrap(U,axis=1,add=0)


