【问题标题】:Accumulate items in a list of tuples在元组列表中累积项目
【发布时间】:2013-05-07 17:33:41
【问题描述】:

我有一个如下所示的元组列表:

lst = [(0, 0), (2, 3), (4, 3), (5, 1)]

累积第一个和第二个元组元素之和的最佳方法是什么?使用上面的示例,我正在寻找生成此列表的最佳方法:

new_lst = [(0, 0), (2, 3), (6, 6), (11, 7)]

我正在寻找 Python 2.6 中的解决方案

【问题讨论】:

  • 如果你只想要最后一个元组使用reduce(lambda x, y: (x[0] + y[0], x[1] + y[1]), lst, (0, 0))
  • 不,我想要整个列表,而不仅仅是最后一个元素。请参阅我提供的示例。
  • 仅供参考:这将是 haskell 实现:acc = scanl1 (\acc x -> (fst x + fst acc, snd x + snd acc))

标签: python python-2.6


【解决方案1】:

我认为最好的解决方案是itertools.accumulate() 来累积值,并使用zip() 来拆分列并将它们合并回来。这意味着生成器只处理单个列,并使该方法完全可扩展。

>>> from itertools import accumulate
>>> lst = [(0, 0), (2, 3), (4, 3), (5, 1)]
>>> list(zip(*map(accumulate, zip(*lst))))
[(0, 0), (2, 3), (6, 6), (11, 7)]

我们使用zip() 获取列,然后将itertools.accumulate() 应用于每一列,然后使用zip() 将它们合并回原始格式。

此方法适用于任何可迭代的,而不仅仅是序列,并且应该相对有效。

3.2之前,accumulate可以定义为:

def accumulate(iterator):
    total = 0
    for item in iterator:
        total += item
        yield total

(文档页面给出了一个更通用的实现,但是对于这个用例,我们可以使用这个简单的实现)。

【讨论】:

  • 如果您仍然使用itertools,为什么不同时使用izipimap
  • @ThijsvanDien 因为itertools.accumulate() 只存在于 3.2+ 中,而在 3.x 中,izip()imap() 不存在(zip()map() 是 3 中的生成器。 x,所以它们是多余的)。
  • 这真的很方便。我没有意识到itertools.accumulate 的存在。我可能已经编写了自己的累加器函数六次了。
  • @Blckknght 我只是在写这个答案时才意识到。这是一个相当新的添加,是的,它是一个有用的小东西。
【解决方案2】:

这个生成器怎么样:

def accumulate_tuples(iterable):
    accum_a = accum_b = 0
    for a, b in iterable:
        accum_a += a
        accum_b += b
        yield accum_a, accum_b

如果您需要一份清单,请致电list(accumulate_tuples(your_list))

这是一个适用于任意长度元组的版本:

def accumulate_tuples(iterable):
    it = iter(iterable):
    accum = next(it) # initialize with the first value
    yield accum
    for val in it: # iterate over the rest of the values
        accum = tuple(a+b for a, b in zip(accum, val))
        yield accum

【讨论】:

  • python 没有针对此类事情的可读 1 行解决方案总是让我感到惊讶。
  • @Elazar 这不是一个超级常见的用例。用人们很少使用的东西来混淆标准库是没有意义的。也就是说,我的答案是 3.2+ 中的单行。
【解决方案3】:
>> reduce(lambda x,y: (x[0] + y[0], x[1] + y[1]), lst)
 (11, 7)

编辑。我可以看到您更新的问题。要获取运行列表,您可以执行以下操作:

>> [reduce(lambda x,y: (x[0]+y[0], x[1]+y[1]), lst[:i]) for i in range(1,len(lst)+1)]
[(0, 0), (2, 3), (6, 6), (11, 7)]

效率不是很高,但至少它可以工作并且可以满足您的需求:)

【讨论】:

  • 不,我想要整个列表,而不仅仅是最后一个元素。请参阅我提供的示例。
  • 一开始没看到那个例子,你后来加的吗?更新了我的答案。
【解决方案4】:

这适用于tuples 或其他iterables 的任何长度。

from collections import defaultdict

def accumulate(lst):
    sums = defaultdict(int)
    for item in lst:
        for index, subitem in enumerate(item):
            sums[index] += subitem
        yield [sums[index] for index in xrange(len(sums))]

print [tuple(x) for x in accumulate([(0, 0), (2, 3), (4, 3), (5, 1)])]

在 Python 2.7+ 中,您将使用 Counter 而不是 defaultdict(int)

【讨论】:

  • 我不确定我是否理解它是如何工作的。它是否依赖于整数键按键顺序迭代的字典?这似乎有风险(我认为这是一个实现细节)。
【解决方案5】:

这是一种非常糟糕的方法(就性能而言),因为list.append 很昂贵,但它确实有效。

last = lst[0]
new_list = [last]
for t in lst[1:]:
    last += t
    new_list.append(last)

【讨论】:

    【解决方案6】:

    简单方法:

    >> x = [(0, 0), (2, 3), (4, 3), (5, 1)]
    >>> [(sum(a for a,b in x[:t] ),sum(b for a,b in x[:t])) for t in range(1,len(x)+1)]
    [(0, 0), (2, 3), (6, 6), (11, 7)]
    

    【讨论】:

      【解决方案7】:
      lst = [(0, 0), (2, 3), (4, 3), (5, 1)]
      
      lst2 = [lst[0]]
      for idx in range(1, len(lst)):
          newItem = [0,0]
          for idx2 in range(0, idx + 1):
              newItem[0] = newItem[0] + lst[idx2][0]
              newItem[1] = newItem[1] + lst[idx2][1]
          lst2.append(newItem)
      
      print(lst2)
      

      【讨论】:

        【解决方案8】:

        您可以使用以下功能

        >>> def my_accumulate(lst):
              new_lst = [lst[0]]
              for x, y in lst[1:]:
                new_lst.append((new_lst[-1][0]+x, new_lst[-1][1]+y))
              return new_lst
        
        >>> lst = [(0, 0), (2, 3), (4, 3), (5, 1)]
        >>> my_accumulate(lst)
        [(0, 0), (2, 3), (6, 6), (11, 7)]
        

        【讨论】:

          【解决方案9】:

          将我的代码更改为更简洁的版本:

          lst = [(0, 0), (2, 3), (4, 3), (5, 1)]
          
          def accumulate(the_list):
              the_item = iter(the_list)
              accumulator = next(the_item)
              while True:
                  yield accumulator
                  accumulator = tuple(x+y for (x,y) in zip (accumulator, next(the_item)))
          
          new_lst = list(accumulate(lst))
          

          【讨论】:

            猜你喜欢
            • 2019-05-31
            • 2016-07-23
            • 1970-01-01
            • 2020-09-14
            • 1970-01-01
            • 2018-01-29
            • 2022-11-28
            • 2022-11-02
            • 2017-06-20
            相关资源
            最近更新 更多