有没有更优雅的方式来编写这个功能?
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]
它取偶数和奇数索引的平均值,并且如果列表具有奇数个元素则保留最后一个
答案 0 :(得分:8)
你真正想做的是在你的列表中应用2个样本的移动平均值,在数学上你会卷积[.5,.5]的窗口,然后只取偶数样本。为了避免将奇数数组的最后一个元素除以2,你应该复制它,这不会影响偶数数组。
使用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
答案 1 :(得分: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]。
答案 2 :(得分: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)]
答案 3 :(得分: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
答案 4 :(得分: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 documentation。
更新:已修复除以浮点而不是整数。