Skip to content

Commit 9b83ba0

Browse files
authored
docs(base): fix the angle_std formula and the angle_mean and angle_wrap parameter docs (#253)
## What is wrong Documentation only, in `spatialmath/base/vectors.py`: | function | problem | fix | |---|---|---| | `angle_std` | the formula has `sin` in both components of the mean vector; the code (and the correct formula) is `cos` and `sin` | corrected, and says what `R` is | | `angle_mean` | the parameter is documented as `v`, it is `theta`; the range is written `[-\pi, \pi)]` with a stray bracket and the result can be exactly `\pi` | `theta`, range `[-\pi, \pi]` | | `angle_wrap` | the parameter is documented as `v`, it is `theta` | `theta` | ## Added - `angle_std` is the same quantity as `scipy.stats.circstd` with its default arguments. I checked this on 300 random samples, the largest difference is 2e-16. - a note that `angle_mean` has no defined value when the unit vectors cancel (for example `0` and `pi`; it returns `pi/2` because of rounding error) No code changes. ## Found while checking, not changed here `angle_std` can return `nan` for a set of identical angles. The mean resultant length `R` can come out as `1 + 2e-16`, so `-2 * log(R)` is slightly negative and the square root fails with "invalid value encountered in sqrt". For example `np.full(100, -2.9)`. I counted 33 failures in 378 combinations of angle and sample size. I will send that as a separate PR.
1 parent 4f81af6 commit 9b83ba0

1 file changed

Lines changed: 31 additions & 10 deletions

File tree

‎spatialmath/base/vectors.py‎

Lines changed: 31 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -683,14 +683,28 @@ def angle_std(theta: ArrayLike) -> float:
683683
r"""
684684
Standard deviation of angular values
685685
686-
:param theta: angular values
686+
:param theta: angular values, in radians
687687
:type theta: array_like
688-
:return: circular standard deviation
688+
:return: circular standard deviation, in radians
689689
:rtype: float
690690
691+
The circular standard deviation is
692+
691693
.. math::
692694
693-
\sigma_{\theta} = \sqrt{-2 \log \| \left[ \frac{\sum \sin \theta_i}{N}, \frac{\sum \sin \theta_i}{N} \right] \|} \in [0, \infty)
695+
\sigma_{\theta} = \sqrt{-2 \log R} \in [0, \infty), \quad
696+
R = \left\| \left[ \frac{\sum \cos \theta_i}{N}, \frac{\sum \sin \theta_i}{N} \right] \right\|
697+
698+
where :math:`R` is the length of the mean of the unit vectors at angles
699+
:math:`\theta_i`. :math:`R = 1` if all the angles are equal, giving a
700+
standard deviation of zero, and :math:`R \to 0` as the angles spread evenly
701+
around the circle, when the standard deviation grows without bound.
702+
703+
This is the same quantity as ``scipy.stats.circstd`` with its default
704+
arguments.
705+
706+
.. note:: If :math:`R` is exactly zero the result is ``inf``, with a NumPy
707+
divide-by-zero warning.
694708
695709
:seealso: :func:`angle_mean`
696710
"""
@@ -708,16 +722,23 @@ def angle_mean(theta: ArrayLike) -> float:
708722
r"""
709723
Mean of angular values
710724
711-
:param theta: angular values
712-
:type v: array_like
713-
:return: circular mean
725+
:param theta: angular values, in radians
726+
:type theta: array_like
727+
:return: circular mean, in radians
714728
:rtype: float
715729
716-
The circular mean is given by
730+
The circular mean is the angle of the mean of the unit vectors at angles
731+
:math:`\theta_i`
717732
718733
.. math::
719734
720-
\bar{\theta} = \tan^{-1} \frac{\sum \sin \theta_i}{\sum \cos \theta_i} \in [-\pi, \pi)]
735+
\bar{\theta} = \tan^{-1} \frac{\sum \sin \theta_i}{\sum \cos \theta_i} \in [-\pi, \pi]
736+
737+
computed with the two-argument arctangent so that the quadrant is correct.
738+
739+
.. note:: If the unit vectors cancel, for example the angles :math:`0` and
740+
:math:`\pi`, the mean direction is undefined and the result is decided by
741+
rounding error.
721742
722743
:seealso: :func:`angle_std`
723744
"""
@@ -730,8 +751,8 @@ def angle_wrap(theta: ArrayLike, mode: str = "-pi:pi") -> Union[float, NDArray]:
730751
"""
731752
Generalized angle-wrapping
732753
733-
:param v: angles to wrap
734-
:type v: array_like
754+
:param theta: angles to wrap
755+
:type theta: array_like
735756
:param mode: wrapping mode, one of: ``"0:2pi"``, ``"0:pi"``, ``"-pi/2:pi/2"`` or ``"-pi:pi"`` [default]
736757
:type mode: str, optional
737758
:return: wrapped angles

0 commit comments

Comments
 (0)