在Python中是否有类似于F# Seq.scan()方法的等效方法?

9

是否有类似于F#的Seq.scan()函数的Python函数?

我想做一些类似于cumsum()cumproduct()的操作,而不需要使用循环。


2
scan(operator.add, [1,2,3,4]) == [1,3,6,10]。不是 mapreduce - kennytm
6个回答

6

我认为Ignacio的解决方案几乎正确,但需要一个类型为('a -> 'a -> 'a)的运算符,并且不会产生第一个元素。

def scan(f, state, it):
  for x in it:
    state = f(state, x)
    yield state
# test
>>> snoc = lambda xs,x: xs+[x]
>>> list(scan(snoc, [], 'abcd'))
[['a'], ['a', 'b'], ['a', 'b', 'c'], ['a', 'b', 'c', 'd']]
>>> list(scan(operator.add, 0, [1,2,3]))
[1,3,6]

具体来说,Seq.scan 的类型是

('State -> 'T -> 'State) -> 'State -> seq<'T> -> seq<'State>

Python中的默认方法是编写一个带有类型的扫描器scan
('State -> 'State -> 'State) -> seq<'State> -> seq<'State>

这是由于Python默认指定了与reduce相同的类型。


1
+1,但在应用第一个缩减之前,我会先让步。我不知道 F# 的扫描,但 Haskell 的确将初始状态作为第一个元素返回(这非常有意义):scanl (+) 0 [1,2,3] # [0,1,3,6]。 - tokland

5
不行。
def scan(op, seq):
  it = iter(seq)
  result = next(it)
  for val in it:
    result = op(result, val)
    yield result

在循环之前,您应该执行一次 yield result,否则结果将缺少第一个元素。 - sepp2k
是的,微软的文档对 Seq.scan<>() 做了一些模糊的解释。 - Ignacio Vazquez-Abrams
谢谢。实际上,F#的Seq.scan()需要另一个参数作为初始状态(结果)...谢谢。 - tk.

2
如果只是进行累加/累乘操作,那么您应该查看numpy的超高效cumsum和cumprod数组操作。请参考cumsumcumprod文档。

1

你可以使用accumulate函数获得相同的功能,这是Python中scan的等效功能:

from itertools import accumulate
nums=[1,2,3,4,5]
nums_added=list(accumulate(nums,lambda x,y:x+y))
print(nums_added)

#prints
# [1,3,6,10,15]

你也可以使用rx(响应式扩展)来使用scan操作符。 这是一个外部库,所以在使用之前需要安装它。 使用rx,你可以像这样做:

from rx import  from_, operators as op
nums=[1,2,3,4,5]
source=from_(nums).pipe(op.scan(lambda x,y:x+y))
source.subscribe(lambda num:print(num)

#prints
#1
#3
#6
#10
#15

Rx 不仅可以做这些,还允许您进行响应式编程。要了解更多,请访问:http://reactivex.io


你可以使用operator.sum代替lambda x,y: x+y - undefined

0

7
reduce() 函数仅返回最终值,而不返回中间值列表。因此,可以获得 sum(),但无法获得 cumsum()。 - tk.

-4

我不确定,但可以试一下

map(function, iterable, ...)¶

更多相关信息请参见docs.python


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