Почему использование np.mean () и mean () дало мне другой выходной номер? - PullRequest
2 голосов
/ 07 августа 2020

Интересно отметить, что использование np.mean () или mean () дало мне разные результаты.

from statistics import mean
import numpy as np
import matplotlib.pyplot as plt

xs = np.array([1, 2, 3, 4, 5, 6])
ys = np.array([5, 4, 6, 5, 6, 7])

def best_fit_slope(xs, ys):
    numerator = (mean(xs)*mean(ys)) - mean(xs*ys)
    denominator = mean(xs)**2 - mean(xs**2)
    return numerator/denominator 

m = best_fit_slope(xs, ys)
print(m)

output >>> 0.8333333333333334

Но если я заменю mean () by np.mean () вывод >>> 0.42857142857142866.

Я следил за этим видео: this video . Он просто использовал mean () и выдал 0,42857. Кто-нибудь может объяснить, почему есть разница? Я знаю большинство операций линейной алгебры или операций с массивами, я бы предпочел использовать np.mean ().

Ответы [ 2 ]

2 голосов
/ 07 августа 2020

Это связано с тем, что пакет statistics пытается дать вам согласованные выходные данные в зависимости от типа numeri c, который вы передаете, поэтому он обрабатывает int, float, decimal.Decimal, fractions.Fraction как вы бы надеялись. К сожалению, типы numpy плохо работают с иерархией типов python numeri c. Итак, мы можем взглянуть на исходный код (это версия Python, ваша среда выполнения, вероятно, использует быструю версию C, но они должны работать эквивалентно ...):

def mean(data):
    """Return the sample arithmetic mean of data.
    >>> mean([1, 2, 3, 4, 4])
    2.8
    >>> from fractions import Fraction as F
    >>> mean([F(3, 7), F(1, 21), F(5, 3), F(1, 3)])
    Fraction(13, 21)
    >>> from decimal import Decimal as D
    >>> mean([D("0.5"), D("0.75"), D("0.625"), D("0.375")])
    Decimal('0.5625')
    If ``data`` is empty, StatisticsError will be raised.
    """
    if iter(data) is data:
        data = list(data)
    n = len(data)
    if n < 1:
        raise StatisticsError('mean requires at least one data point')
    T, total, count = _sum(data)
    assert count == n
    return _convert(total/n, T)

Итак, по сути, он использует sum с распознаванием типов, которое возвращает тип, общее количество и количество. По сути, total/count приводится к T. Примечание:

In [28]: T, total, count = statistics._sum(np.array([1,2,3]))

In [29]: T, total, count
Out[29]: (numpy.int64, Fraction(6, 1), 3)

In [30]: total / count
Out[30]: Fraction(2, 1)

In [31]: T(total / count)
Out[31]: 2

Обратите внимание, все целые числа объектов, которые вы видите здесь, на самом деле являются numpy.int64, а не ванильными int объектами. Но почему этого не происходит, когда мы делаем statistics.mean([1,2,3,4])? Ну, поскольку библиотека была построена с использованием обычных типов python numeri c, взглянув на функцию _convert:

def _convert(value, T):
    """Convert value to given numeric type T."""
    if type(value) is T:
        # This covers the cases where T is Fraction, or where value is
        # a NAN or INF (Decimal or float).
        return value
    if issubclass(T, int) and value.denominator != 1:
        T = float
    try:
        # FIXME: what do we do if this overflows?
        return T(value)
    except TypeError:
        if issubclass(T, Decimal):
            return T(value.numerator)/T(value.denominator)
        else:
            raise

Вы заметите, это особые случаи: if issubclass(T, int) and value.denominator != 1, т.е. у вас есть int, а знаменатель не один, поэтому вам нужно число с плавающей запятой:

        T = float

ОДНАКО:

In [36]: issubclass(np.int64, int)
Out[36]: False

Итак, T это просто np.int64 , и:

In [37]: total / count
Out[37]: Fraction(2, 1)

In [38]: np.int64(total / count)
Out[38]: 2
2 голосов
/ 07 августа 2020

Интересно, что этот нюанс явно не задокументирован в официальных документах , но его можно вывести из предоставленных примеров.

statistics.mean изо всех сил пытается обеспечить вывод тот же тип входа. Когда вы даете ему np.array([1, 2, 3, 4, 5, 6]) (массив np.int32), он предполагает, что ожидается результат int:

xs = np.array([1, 2, 3, 4, 5, 6])
print(mean(xs))
# 3
print(type(mean(xs)))
# <class 'numpy.int32'>

Достаточно заставить одно значение в массиве float, чтобы «убедить "если мы хотим вернуть float:

xs = np.array([1.0, 2, 3, 4, 5, 6])
# or np.array([1,2,3,4,5,6],dtype=np.float64) or anyother way that gives `dtype` np.float
print(mean(xs))
# 3.5
print(type(mean(xs)))
# <class 'numpy.float64'>

Если мы достаточно углубимся в его реализацию, мы сможем увидеть, откуда взялось это поведение. Он использует функцию _sum, описанную ниже:

def _sum(data, start=0):
    """_sum(data [, start]) -> (type, sum, count)

    Return a high-precision sum of the given numeric data as a fraction,
    together with the type to be converted to and the count of items.

    If optional argument ``start`` is given, it is added to the total.
    If ``data`` is empty, ``start`` (defaulting to 0) is returned.


    Examples
    --------

    >>> _sum([3, 2.25, 4.5, -0.5, 1.0], 0.75)
    (<class 'float'>, Fraction(11, 1), 5)

    Some sources of round-off error will be avoided:

    # Built-in sum returns zero.
    >>> _sum([1e50, 1, -1e50] * 1000)
    (<class 'float'>, Fraction(1000, 1), 3000)

    Fractions and Decimals are also supported:

    >>> from fractions import Fraction as F
    >>> _sum([F(2, 3), F(7, 5), F(1, 4), F(5, 6)])
    (<class 'fractions.Fraction'>, Fraction(63, 20), 4)

    >>> from decimal import Decimal as D
    >>> data = [D("0.1375"), D("0.2108"), D("0.3061"), D("0.0419")]
    >>> _sum(data)
    (<class 'decimal.Decimal'>, Fraction(6963, 10000), 4)

    Mixed types are currently treated as an error, except that int is
    allowed.
    """
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...