通过平均值减少列表的优雅方法?

5
有没有更优雅的方式来编写这个函数?
def reduce(li):
    result=[0 for i in xrange((len(li)/2)+(len(li)%2))]
    for i,e in enumerate(li):
        result[int(i/2)] += e
    for i in range(len(result)):
        result[i] /= 2
    if (len(li)%2 == 1):
        result[len(result)-1] *= 2
    return result

这里,它的作用是:

a = [0,2,10,12]
b = [0,2,10,12,20]
reduce(a)
>>> [1,11]
reduce(b)
>>> [1,11,20]

它取偶数和奇数索引的平均值,如果列表元素数量为奇数,则保留最后一个元素。

如果没有人回答这个问题或者没有好的答案怎么办? - gok
5个回答

8

您实际想要做的是通过您的列表应用2个样本的移动平均值。从数学上讲,您需要卷积一个窗口[.5,.5],然后仅取偶数样本。为了避免在奇数数组的最后一个元素除以二,您应该将其复制,这不会影响偶数数组。

使用numpy可以变得非常优雅:

import numpy as np

np.convolve(a + [a[-1]], [.5,.5], mode='valid')[::2]
array([  1.,  11.])

np.convolve(b + [b[-1]], [.5,.5], mode='valid')[::2]
array([  1.,  11.,  20.])

您可以使用list(outputarray)将其转换回列表。

如果性能很重要,使用numpy非常有用,优化的C数学代码正在处理工作:

In [10]: %time a=reduce(list(np.arange(1000000))) #chosen answer
CPU times: user 6.38 s, sys: 0.08 s, total: 6.46 s
Wall time: 6.39 s

In [11]: %time c=np.convolve(list(np.arange(1000000)), [.5,.5], mode='valid')[::2]
CPU times: user 0.59 s, sys: 0.01 s, total: 0.60 s
Wall time: 0.61 s

一旦你使用强大的numpy,你就可以做很多惊人的事情。+1 - JoshAdel

2
def reduce(li):
    result = [(x+y)/2.0 for x, y in zip(li[::2], li[1::2])]
    if len(li) % 2:
        result.append(li[-1])
    return result

请注意,您原始的代码存在两个错误:[0,1]将返回0而不是0.5,[5]将返回[4]而不是[5]。

+1 与我的解决方案在精神上相似,巧妙地利用了列表推导。 - JoshAdel

1
这是一个一行代码:
[(0.5*(x+y) if y != None else x)  for x,y in map(None, *(iter(b),) * 2)]

其中b是您想要缩小的原始列表。

编辑:这里有一个变体,可能更清晰,并依赖于itertools

from itertools import izip_longest
[(0.5*(x+y) if y != None else x)  for x,y in izip_longest(*[iter(b)]* 2)]

0
这是另一种尝试,我觉得更加直接,因为它只需要一次遍历:
def reduce(li):

    result = []
    it = iter(li)

    try:
        for i in it:
            result.append((i + next(it)) / 2)
    except StopIteration:
        result.append(li[-1])

    return result

0

这是我的尝试,使用itertools:

import itertools

def reduce(somelist):
    odds = itertools.islice(somelist, 0, None, 2)
    eves = itertools.islice(somelist, 1, None, 2)
    for (x,y) in itertools.izip(odds,evens):
        yield( (x + y) / 2.0)
    if len(somelist) % 2 != 0 : yield(somelist[-1])

>>> [x for x in reduce([0, 2, 10, 12, 20]) ]
[1, 11, 20]

另请参阅:itertools文档

更新:已修复为除以浮点数而非整数。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接