【问题标题】:compute mean in python for a generator在 python 中计算生成器的平均值
【发布时间】:2011-06-25 05:28:26
【问题描述】:

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

问题在于,如果您将生成器传递给 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)

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

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

【问题讨论】:

  • 你会知道你的生成器会产生多少随机数,不是吗?
  • @Sven Marnach:假设生成器正在读取文件?
  • 如果您真的不想存储数据(并且不想实现自己的较慢的sum 函数),您可以创建一个计数生成器并像这样调用它:co = countingGen(); mean = sum(co(data))/co.getCount()

标签: python generator mean


【解决方案1】:

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

其中最简单的(据我所知)通常是credited to 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标准库没有包含这么简单的代码。

【讨论】:

    【解决方案2】:

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

    def my_mean(values):
        n = 0
        Sum = 0.0
        for v in values:
            Sum += v
            n += 1
        return Sum / n
    

    【讨论】:

    • Sum 等大写字母通常是为类保留的。
    • @xApple,我试图使它类似于问题中的代码;您会看到该变量在那里也被命名为Sum。就我个人而言,我会遵循 PEP 8 中的约定。
    • sum是内置的,所以你应该使用sum_total
    【解决方案3】:
    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() in Python 3.4it calls list() on the input:

    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
    

    其中_sum() 返回一个准确的和(math.fsum() 类似函数,除了float 还支持FractionDecimal)。

    【讨论】:

      【解决方案4】:

      老式的做法:

      def my_mean(values):
         sum, n = 0, 0
         for x in values:
            sum += x
            n += 1
         return float(sum)/n
      

      【讨论】:

        【解决方案5】:

        一种方法是

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

        但这实际上是临时存储数字。

        【讨论】:

          【解决方案6】:

          您的方法很好,但您应该改用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
          

          【讨论】:

          • Sum 等大写字母通常保留给类。
          【解决方案7】:

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

          from itertools import izip, count
          reduce(lambda c,i: (c*(i[1]-1) + float(i[0]))/i[1], izip(values,count(1)),0)
          

          【讨论】:

            【解决方案8】:
            def my_mean(values):
                n = 0
                sum = 0
                for v in values:
                    sum += v
                    n += 1
                return sum/n
            

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

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

            【讨论】:

            • 通过 sum = 0 你屏蔽了内置函数。
            【解决方案9】:

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

            reduce(np.add, generator)/length
            

            【讨论】:

              【解决方案10】:

              试试:

              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”仍将使用中间存储)。

              【讨论】:

              • 这会临时存储整个列表。内存方面,相当于先转换成列表再使用sum(a)/len(a),但使用列表会更快。
              • 好点,真的——我只是在看 tee() 是如何实现的。当这种情况发生时,我讨厌它。 :-)
              • 你会认为tee可以通过只存储克隆迭代器之间的“差异”来实现,即一个已经消费但另一个还没有消费的元素。
              猜你喜欢
              • 1970-01-01
              • 2020-07-06
              • 2021-01-13
              • 2011-12-04
              • 2016-05-27
              • 1970-01-01
              • 1970-01-01
              • 2016-01-02
              • 2016-02-15
              相关资源
              最近更新 更多