【问题标题】:python groupby itertools list methodspython groupby itertools 列表方法
【发布时间】:2016-05-04 19:11:52
【问题描述】:

我有一个这样的列表: #[YEAR, DAY, VALUE1, VALUE2, VALUE3]

[[2014, 1, 10, 20, 30],
[2014, 1, 3, 7, 4],
[2014, 2, 14, 43,5],
[2014, 2, 33, 1, 6]
...
[2013, 1, 34, 54, 3],
[2013, 2, 23, 33, 2],
...]

我需要按年和天分组,以获得类似的东西:

[[2014, 1, sum[all values1 with day=1), sum(all values2 with day =1), avg(all values3 with day=1)],
[2014, 2, sum[all values1 with day=2), sum(all values2 with day =2), avg(all values3 with day=2)],
....
[2013, 1, sum[all values1 with day=1), sum(all values2 with day =1), avg(all values3 with day=1)],
[2013, 2, sum[all values1 with day=2), sum(all values2 with day =2), avg(all values3 with day=2)],,
....]

如何使用 itertool 做到这一点?我不能使用 pandas 或 numpy,因为我的系统不支持它。非常感谢您的帮助。

【问题讨论】:

  • 我不清楚你想按什么分组。您想按年份对记录进行分组吗?天?年月日?还有什么?
  • 尝试提供一小段可用的数据。上面的代码不是有效的 Python 代码,当有人尝试对您的问题进行试验时,它没有多大帮助。
  • 你的数据已经按(year, day)排序了吗?
  • 是的,按年和日分组

标签: python group-by itertools


【解决方案1】:
import itertools
import operator

key = operator.itemgetter(0,1)
my_list.sort(key=key)
for (year, day), records in itertools.groupby(my_list, key):
    print("Records on", year, day, ":")
    for record in records: print(record)

itertools.groupby 不像 SQL 的 GROUPBY 那样工作。它按顺序分组。这意味着如果您有一个未排序的元素列表,您可能会在同一个键上获得多个组。因此,假设您想根据奇偶校验(偶数与奇数)对整数列表进行分组,那么您可以这样做:

L = [1,2,3,4,5,7,8]  # notice that there's no 6 in the list
itertools.groupby(L, lambda i:i%2)

现在,如果您来自 SQL 世界,您可能会认为这为您提供了两组 - 一组用于偶数,一组用于奇数。虽然这是有道理的,但这不是 Python 做事的方式。它依次考虑每个元素并检查它是否与前一个元素属于同一组。如果是,则将两个元素都添加到组中;否则,每个元素都有自己的组。

所以有了上面的列表,我们得到:

key: 1
elements: [1]

key: 0
elements[2]

key: 1
elements: [3]

key: 0
elements[4]

key: 1
elements: [5,7]  # see what happened here?

因此,如果您希望像在 SQL 中那样进行分组,那么您需要事先按要分组的键(标准)对列表进行排序:

L = [1,2,3,4,5,7,8]  # notice that there's no 6 in the list
L.sort(key=lambda i:i%2)  # now L looks like this: [2,4,1,3,5,7] - the odds and the evens stick together
itertools.groupby(L, lambda i:%2)  # this gives two groups containing all the elements that belong to each group

【讨论】:

  • 能否添加一些上下文?
  • @ppperry: 看看
【解决方案2】:

我试图做一个简短的回答,但没有成功,但我设法让很多 python 内置模块参与其中:

import itertools
import operator
import functools

我将使用functools.reduce 进行求和,但它需要一个自定义函数:

def sum_sum_sum_counter(res, array):
    # Unpack the values of the array
    year, day, val1, val2, val3 = array
    res[0] += val1
    res[1] += val2
    res[2] += val3
    res[3] += 1 # counter
    return res

这个函数有一个计数器,因为你想计算平均值,它比运行平均值实现更直观。

现在有趣的部分:我将按前两个元素分组(假设这些元素已排序,否则之前需要lst = sorted(lst, key=operator.itemgetter(0,1)) 之类的东西:

result = []
for i, values in itertools.groupby(lst, operator.itemgetter(0,1)):
    # Now let's use the reduce function with a start list containing zeros
    calc = functools.reduce(sum_sum_sum_counter, values, [0, 0, 0, 0])
    # Append year, day and the results.
    result.append([i[0], i[1], calc[0], calc[1], calc[2]/calc[3]])

calc[2]/calc[3] 是 value3 的平均值。请记住reduce 函数中的最后一个元素是计数器!总和除以计数就是平均值。

给我一​​个结果:

[[2014, 1, 13, 27, 17.0],
 [2014, 2, 47, 44, 5.5],
 [2013, 1, 34, 54, 3.0],
 [2013, 2, 23, 33, 2.0]]

只需使用您给出的那些值。

【讨论】:

    【解决方案3】:

    在真实数据上,分组前排序可能会变得低效:

    • 首先将消耗完整的迭代器,丢失 函数式编程的一个重要目标,懒惰
    • 与分组 O(n) 相比,排序是 O(n log n)

    SQL+pythonic的方式按一些谓词分组,简单一些 使用 collection.defaultdict 来减少/累积:

    from functools import reduce
    from collections import defaultdict as DD
    
    def groupby( pred, it ):
      return reduce( lambda d,x: d[ pred(x) ].append(x) or d, it, DD(list) )
    

    然后将它与一些谓词函数或 lambda 一起使用:

    >>> words = 'your code might become less readable using reduce'.split()
    >>> groupby( len, words )[4]
    ['your', 'code', 'less']
    

    关于惰性,reduce 在消耗所有输入之前不会返回, 当然也不是。您可以改用 itertools.accumulate, 总是返回相同的默认字典,以懒惰地消耗(和处理变化的组)并且内存占用少。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-06-08
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多