Skip to content

statistics.stdev() raises AttributeError when data contains infinity #140938

Closed

Description

@T90REAL

Bug report

Bug description:

statistics.stdev() crashes when processing data containing inf, while statistics.variance() handles the same input correctly. This violates the invariant stdev = sqrt(variance).

import statistics

data = [1.0, 2.0, float('inf'), 4.0, 5.0]

# Works fine
print(statistics.variance(data))  # Returns: inf

# Crashes
statistics.stdev(data) # AttributeError: 'float' object has no attribute 'numerator'
inf
Traceback (most recent call last):
  File "/data/src/test.py", line 9, in <module>
    statistics.stdev(data) # AttributeError: 'float' object has no attribute 'numerator'
    ^^^^^^^^^^^^^^^^^^^^^^
  File "/home/py312/lib/python3.12/statistics.py", line 974, in stdev
    return _float_sqrt_of_frac(mss.numerator, mss.denominator)
                               ^^^^^^^^^^^^^
AttributeError: 'float' object has no attribute 'numerator'

Expected: stdev(data) should return inf (since sqrt(variance) = sqrt(inf) = inf)

CPython versions tested on:

3.12

Operating systems tested on:

Linux

Linked PRs

Activity

  1. added
    type-bugAn unexpected behavior, bug, or error
    on Nov 3, 2025
  2. added
    stdlibStandard Library Python modules in the Lib/ directory
    on Nov 3, 2025
  3. picnixz commented on Nov 3, 2025

    @picnixz
    Member

    I'm not sure statistics.variance is even remotely correct here. For instance, if we consider the mean of the sample as simply being the averaged sum, it would be $\infty$. So, statistics.variance(data, float('inf')) == statistics.variance(data), which is not the case. The first one gives nan while the second returns $\infty$ but the second argument is meant to be the known mean, so we already have inconsistencies.

    @rhettinger Was statistics ever meant to handle infinite values in data points? I see that geometric_mean handles NaN inputs but that's the only function that does so.

  4. skirpichev commented on Nov 3, 2025

    @skirpichev
    Member

    Perhaps, just this:

    diff --git a/Lib/statistics.py b/Lib/statistics.py
    index 3d805cb0739..c01c2a945a2 100644
    --- a/Lib/statistics.py
    +++ b/Lib/statistics.py
    @@ -621,7 +621,9 @@ def stdev(data, xbar=None):
         mss = ss / (n - 1)
         if issubclass(T, Decimal):
             return _decimal_sqrt_of_frac(mss.numerator, mss.denominator)
    -    return _float_sqrt_of_frac(mss.numerator, mss.denominator)
    +    elif issubclass(T, Fraction):
    +        return _float_sqrt_of_frac(mss.numerator, mss.denominator)
    +    return math.sqrt(mss)
     
     
     def pstdev(data, mu=None):
  5. T90REAL commented on Nov 3, 2025

    @T90REAL
    Author

    statistics.pstdev() also has this issue, and it could be solved in the same way.

  6. self-assigned this
    on Nov 5, 2025
  7. rhettinger commented on Nov 5, 2025

    @rhettinger
    Contributor

    A case could be made that the variance and standard deviation functions should return a Decimal('NaN') or math.nan if there are one or more positive of negative infinities in the input. The concepts of variance and standard deviation are all about differences from the mean. When you difference infinities, you get a NaN.

    @@ -1530,7 +1530,8 @@ def _ss(data, c=None):
         elif None in sx_partials:
             # The sum will be a NAN or INF. We can ignore all the finite
             # partials, and just look at this special one.
    -        ssd = c = sx_partials[None]
    +        c = sx_partials[None]
    +        ssd = type(c)('nan')
             assert not _isfinite(ssd)

    Also, the downstream functions would need a try/except to handle the non-Fraction return.

    @picnixz

    Was statistics ever meant to handle infinite values in data points?

    Some effort was made to handle infinities but the effort was not thorough.

    I would like to see this one fixed but don't want to go fuzzing every function. A decade of deployment revealed that this isn't something users cared about.

  8. rhettinger commented on Nov 6, 2025

    @rhettinger
    Contributor

    After more thought, it is even less clear what the correct result should be.

    Since pstdev([x]) returns 0.0 for any value of x, an inf input should return 0.0 as well.

    However pstdev([data]) is defined as sqrt(sum((x - mean(data))**2 for x in data) / len(data)) and that returns nan when data=math.inf.

    Then again, pstdev([0, x]) returns x / 2 for any value of x, so an inf input should return math.inf.

    A case can be made for any of: 0.0, nan, and inf.

    It may be reasonable to replace the AttributeError with: ValueError('stdev() inputs must be finite').

  9. gpshead commented on Nov 11, 2025

    @gpshead
    Member

    A ValueError was my first thought as well.

  10. added a commit that references this issue on Nov 13, 2025
  11. gpshead commented on Nov 13, 2025

    @gpshead
    Member

    i LOL the github UI, I think i just left comments directly on rhettinger@0d6d50c#r170488297 - regardless, add some text to the ValueError indicating that an inf or nan was encountered. and cover nan in the unittest. otherwise, looks good overall.

  12. added a commit that references this issue on Nov 14, 2025
  13. added a commit that references this issue on Nov 14, 2025
  14. added 2 commits that reference this issue on Nov 15, 2025
  15. gpshead commented on Nov 15, 2025

    @gpshead
    Member

    thanks for the report and fix!

  16. added a commit that references this issue on Nov 15, 2025
  17. added a commit that references this issue on Dec 6, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

stdlibStandard Library Python modules in the Lib/ directorytype-bugAn unexpected behavior, bug, or error

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions