17

我正在做一些统计工作,我有一个(大量)随机数集合来计算平均值,我想使用生成器,因为我只需要计算平均值,所以我不需要存储号码。

问题是 numpy.mean 如果您将其传递给生成器,则会中断。我可以编写一个简单的函数来做我想做的事,但我想知道是否有合适的内置方法来做到这一点?

如果我可以说“sum(values)/len(values)”就好了,但是 len 不适用于生成器,并且对已经消耗的值求和。

这是一个例子:

import numpy 

def my_mean(values):
    n = 0
    Sum = 0.0
    try:
        while True:
            Sum += next(values)
            n += 1
    except StopIteration: pass
    return float(Sum)/n

X = [k for k in range(1,7)]
Y = (k for k in range(1,7))

print numpy.mean(X)
print my_mean(Y)

这些都给出了相同的正确答案,购买 my_mean 不适用于列表,而 numpy.mean 不适用于生成器。

我真的很喜欢使用生成器的想法,但是这样的细节似乎会破坏事情。

4

10 回答 10

25

一般来说,如果您正在对浮点数进行流式平均计算,那么您最好使用数值更稳定的算法,而不是简单地将生成器相加并除以长度。

其中最简单的(我知道)通常归功于 Knuth,并且还计算方差。该链接包含一个 python 实现,但为了完整起见,这里只复制了平均部分。

def mean(data):
    n = 0
    mean = 0.0
 
    for x in data:
        n += 1
        mean += (x - mean)/n

    if n < 1:
        return float('nan')
    else:
        return mean

我知道这个问题已经很老了,但它仍然是谷歌上的第一个热门话题,所以发布似乎很合适。我仍然很难过 python 标准库不包含这段简单的代码。

于 2015-06-26T00:25:33.860 回答
7

只需对代码进行一项简单的更改,您就可以同时使用两者。生成器可以互换地用于 for 循环中的列表。

def my_mean(values):
    n = 0
    Sum = 0.0
    for v in values:
        Sum += v
        n += 1
    return Sum / n
于 2011-02-10T23:20:18.537 回答
7
def my_mean(values):
    total = 0
    for n, v in enumerate(values, 1):
        total += v
    return total / n

print my_mean(X)
print my_mean(Y)

statistics.mean()Python 3.4 中有,但它调用list()输入

def mean(data):
    if iter(data) is data:
        data = list(data)
    n = len(data)
    if n < 1:
        raise StatisticsError('mean requires at least one data point')
    return _sum(data)/n

where_sum()返回一个准确的总和(math.fsum()除了float还支持Fraction,的 -like 函数Decimal)。

于 2011-02-11T20:29:10.517 回答
3

老式的做法:

def my_mean(values):
   sum, n = 0, 0
   for x in values:
      sum += x
      n += 1
   return float(sum)/n
于 2011-02-10T23:21:03.417 回答
1

一种方法是

numpy.fromiter(Y, int).mean()

但这实际上暂时存储了数字。

于 2011-02-10T23:17:25.897 回答
1

您的方法是一个很好的方法,但是您应该改用for x in y成语而不是反复调用next,直到获得StopIteration. 这适用于列表和生成器:

def my_mean(values):
    n = 0
    Sum = 0.0

    for value in values:
        Sum += value
        n += 1
    return float(Sum)/n
于 2011-02-10T23:20:11.420 回答
1

您可以在不知道数组大小的情况下使用 reduce:

from itertools import izip, count
reduce(lambda c,i: (c*(i[1]-1) + float(i[0]))/i[1], izip(values,count(1)),0)
于 2016-04-07T16:07:44.590 回答
0
def my_mean(values):
    n = 0
    sum = 0
    for v in values:
        sum += v
        n += 1
    return sum/n

上面的代码与您的代码非常相似,除了使用for迭代values,无论您获得列表还是迭代器,您都很好。然而,pythonsum方法非常优化,因此除非列表真的非常长,否则您可能会更乐意临时存储数据。

(另请注意,由于您使用的是 python3,所以不需要float(sum)/n

于 2011-02-10T23:18:23.727 回答
0

如果您事先知道生成器的长度,并且希望避免将完整列表存储在内存中,则可以使用:

reduce(np.add, generator)/length
于 2015-06-25T12:38:17.990 回答
-1

尝试:

import itertools

def mean(i):
    (i1, i2) = itertools.tee(i, 2)
    return sum(i1) / sum(1 for _ in i2)

print mean([1,2,3,4,5])

tee将为任何可迭代对象i(例如生成器、列表等)复制您的迭代器,允许您使用一个副本进行求和,另一个用于计数。

(请注意,“tee”仍将使用中间存储)。

于 2011-02-10T23:16:57.020 回答