首页 文章

我如何使用Python的itertools.groupby()?

提问于
浏览
385

我没有't been able to find an understandable explanation of how to actually use Python' s itertools.groupby() 功能 . 我想要做的是:

  • 拿一个清单 - 在这种情况下,是一个物化的 lxml 元素的子项

  • 根据某些标准将其分组

  • 然后分别迭代这些组中的每一个 .

我已经审核了the documentationthe examples,但我在尝试将它们应用到一个简单的数字列表之外时遇到了麻烦 .

那么,我如何使用 itertools.groupby() ?我应该使用另一种技术吗?阅读良好的指针也将受到赞赏 .

12 回答

  • 19

    IMPORTANT NOTE: 你必须先 sort your data .


    我没有得到的部分是在示例构造中

    groups = []
    uniquekeys = []
    for k, g in groupby(data, keyfunc):
       groups.append(list(g))    # Store group iterator as a list
       uniquekeys.append(k)
    

    k 是当前分组键, g 是一个迭代器,可用于迭代该分组键定义的组 . 换句话说, groupby 迭代器本身返回迭代器 .

    这是一个例子,使用更清晰的变量名称:

    from itertools import groupby
    
    things = [("animal", "bear"), ("animal", "duck"), ("plant", "cactus"), ("vehicle", "speed boat"), ("vehicle", "school bus")]
    
    for key, group in groupby(things, lambda x: x[0]):
        for thing in group:
            print "A %s is a %s." % (thing[1], key)
        print " "
    

    这会给你输出:

    熊是动物 . 鸭子是动物 . 仙人掌是一种植物 . 快艇是一种车辆 . 校车是一种车辆 .

    在此示例中, things 是元组列表,其中每个元组中的第一个项目是第二个项目所属的组 .

    groupby() 函数有两个参数:(1)要分组的数据和(2)将其分组的函数 .

    这里, lambda x: x[0] 告诉 groupby() 使用每个元组中的第一项作为分组键 .

    在上面的 for 语句中, groupby 返回三个(键,组迭代器)对 - 每个唯一键一次 . 您可以使用返回的迭代器迭代该组中的每个项目 .

    以下是使用列表推导的相同数据的略有不同的示例:

    for key, group in groupby(things, lambda x: x[0]):
        listOfThings = " and ".join([thing[1] for thing in group])
        print key + "s:  " + listOfThings + "."
    

    这会给你输出:

    动物:熊和鸭 . 植物:仙人掌 . 车辆:快艇和校车 .

  • 7

    你能告诉我们你的代码吗?

    Python文档中的示例非常简单:

    groups = []
    uniquekeys = []
    for k, g in groupby(data, keyfunc):
        groups.append(list(g))      # Store group iterator as a list
        uniquekeys.append(k)
    

    因此,在您的情况下,数据是节点列表,keyfunc是条件函数的逻辑所在的位置,然后 groupby() 对数据进行分组 .

    在调用 groupby 之前,您必须小心 sort the data ,否则它将无效 . groupby 方法实际上只是迭代一个列表,每当密钥更改它创建一个新组 .

  • 9

    使用groupby的neato技巧是在一行中运行长度编码:

    [(c,len(list(cgen))) for c,cgen in groupby(some_string)]
    

    将给出一个2元组的列表,其中第一个元素是char,第二个元素是重复的数量 .

    编辑:请注意,这是将 itertools.groupby 与SQL GROUP BY 语义分开的内容:itertools不会预先对迭代器进行排序,因此不会合并具有相同"key"的组 .

  • 1

    itertools.groupby 是一个用于分组项目的工具 .

    the docs开始,我们会进一步收集它可能做的事情:

    #[k for k,g in groupby('AAAABBBCCDAABBB')] - > A B C D A B#[list(g)for k,g in groupby('AAAABBBCCD')] - > AAAA BBB CC D

    groupby 对象产生组组为生成器的键组对 .

    特征

    • A.将连续项目组合在一起

    • B.给定一个已排序的可迭代项,对所有出现的项进行分组

    • C.指定如何使用键功能对项目进行分组

    比较

    # Define a printer for comparing outputs
    >>> def print_groupby(iterable, key=None):
    ...    for k, g in it.groupby(iterable, key):
    ...        print("key: '{}'--> group: {}".format(k, list(g)))
    
    # Feature A: group consecutive occurrences
    >>> print_groupby("BCAACACAADBBB")
    key: 'B'--> group: ['B']
    key: 'C'--> group: ['C']
    key: 'A'--> group: ['A', 'A']
    key: 'C'--> group: ['C']
    key: 'A'--> group: ['A']
    key: 'C'--> group: ['C']
    key: 'A'--> group: ['A', 'A']
    key: 'D'--> group: ['D']
    key: 'B'--> group: ['B', 'B', 'B']
    
    # Feature B: group all occurrences
    >>> print_groupby(sorted("BCAACACAADBBB"))
    key: 'A'--> group: ['A', 'A', 'A', 'A', 'A']
    key: 'B'--> group: ['B', 'B', 'B', 'B']
    key: 'C'--> group: ['C', 'C', 'C']
    key: 'D'--> group: ['D']
    
    # Feature C: group by a key function
    >>> key = lambda x: x.islower()
    >>> print_groupby(sorted("bCAaCacAADBbB"), key)
    key: 'False'--> group: ['A', 'A', 'A', 'B', 'B', 'C', 'C', 'D']
    key: 'True'--> group: ['a', 'a', 'b', 'b', 'c']
    

    用途

    注意:后面的几个例子来自VíctorTerrón的PyCon (talk) (Spanish),"Kung Fu at Dawn with Itertools" . 另见用C编写的 groupby source code .


    响应

    # OP: Yes, you can use `groupby`, e.g. 
    [do_something(list(g)) for _, g in groupby(lxml_elements, key=criteria_func)]
    
  • 553

    另一个例子:

    for key, igroup in itertools.groupby(xrange(12), lambda x: x // 5):
        print key, list(igroup)
    

    结果是

    0 [0, 1, 2, 3, 4]
    1 [5, 6, 7, 8, 9]
    2 [10, 11]
    

    请注意,igroup是一个迭代器(文档调用它的子迭代器) .

    这对于分块生成器很有用:

    def chunker(items, chunk_size):
        '''Group items in chunks of chunk_size'''
        for _key, group in itertools.groupby(enumerate(items), lambda x: x[0] // chunk_size):
            yield (g[1] for g in group)
    
    with open('file.txt') as fobj:
        for chunk in chunker(fobj):
            process(chunk)
    

    groupby的另一个例子 - 当键没有排序时 . 在以下示例中,xx中的项目按yy中的值进行分组 . 在这种情况下,首先输出一组零,然后输出一组1,然后再输出一组零 .

    xx = range(10)
    yy = [0, 0, 0, 1, 1, 1, 0, 0, 0, 0]
    for group in itertools.groupby(iter(xx), lambda x: yy[x]):
        print group[0], list(group[1])
    

    生产环境 :

    0 [0, 1, 2]
    1 [3, 4, 5]
    0 [6, 7, 8, 9]
    
  • 34

    警告:

    语法列表(groupby(...))将无法按您的意图运行 . 它似乎破坏了内部迭代器对象,所以使用

    for x in list(groupby(range(10))):
        print(list(x[1]))
    

    将产生:

    []
    []
    []
    []
    []
    []
    []
    []
    []
    [9]
    

    相反,列表(groupby(...)),尝试[(k,list(g))for k,g in groupby(...)],或者如果经常使用该语法,

    def groupbylist(*args, **kwargs):
        return [(k, list(g)) for k, g in groupby(*args, **kwargs)]
    

    并且可以访问groupby功能,同时避免那些讨厌的(对于小数据)迭代器 .

  • 24

    我想举一个例子,其中没有排序的groupby不起作用 . 改编自James Sulak的例子

    from itertools import groupby
    
    things = [("vehicle", "bear"), ("animal", "duck"), ("animal", "cactus"), ("vehicle", "speed boat"), ("vehicle", "school bus")]
    
    for key, group in groupby(things, lambda x: x[0]):
        for thing in group:
            print "A %s is a %s." % (thing[1], key)
        print " "
    

    输出是

    A bear is a vehicle.
    
    A duck is a animal.
    A cactus is a animal.
    
    A speed boat is a vehicle.
    A school bus is a vehicle.
    

    有两个车辆组,而一个人只能期待一组

  • 66

    @CaptSolo,我试过你的例子,但它没有用 .

    from itertools import groupby 
    [(c,len(list(cs))) for c,cs in groupby('Pedro Manoel')]
    

    输出:

    [('P', 1), ('e', 1), ('d', 1), ('r', 1), ('o', 1), (' ', 1), ('M', 1), ('a', 1), ('n', 1), ('o', 1), ('e', 1), ('l', 1)]
    

    尽你所能看,有两个和两个e,但他们分成了不同的组 . 那时我意识到你需要对传递给groupby函数的列表进行排序 . 所以,正确的用法是:

    name = list('Pedro Manoel')
    name.sort()
    [(c,len(list(cs))) for c,cs in groupby(name)]
    

    输出:

    [(' ', 1), ('M', 1), ('P', 1), ('a', 1), ('d', 1), ('e', 2), ('l', 1), ('n', 1), ('o', 2), ('r', 1)]
    

    只记得,如果列表没有排序,groupby函数 will not work

  • 5

    我如何使用Python的itertools.groupby()?

    您可以使用groupby将事物分组以进行迭代 . 你给groupby一个iterable,并给一个可选的 key 函数/ callable来检查它们从iterable出来时的项目,然后它返回一个迭代器,给出一个两元组的key可调用的结果和实际的item另一个可迭代的 . 从帮助:

    groupby(iterable[, keyfunc]) -> create an iterator which returns
    (key, sub-iterator) grouped by each value of key(value).
    

    下面是groupby使用协程按计数分组的示例,它使用密钥可调用(在本例中为 coroutine.send )来为多次迭代和元素的分组子迭代器吐出计数:

    import itertools
    
    
    def grouper(iterable, n):
        def coroutine(n):
            yield # queue up coroutine
            for i in itertools.count():
                for j in range(n):
                    yield i
        groups = coroutine(n)
        next(groups) # queue up coroutine
    
        for c, objs in itertools.groupby(iterable, groups.send):
            yield c, list(objs)
        # or instead of materializing a list of objs, just:
        # return itertools.groupby(iterable, groups.send)
    
    list(grouper(range(10), 3))
    

    版画

    [(0, [0, 1, 2]), (1, [3, 4, 5]), (2, [6, 7, 8]), (3, [9])]
    
  • 22

    排序和分组

    from itertools import groupby
    
    val = [{'name': 'satyajit', 'address': 'btm', 'pin': 560076}, 
           {'name': 'Mukul', 'address': 'Silk board', 'pin': 560078},
           {'name': 'Preetam', 'address': 'btm', 'pin': 560076}]
    
    
    for pin, list_data in groupby(sorted(val, key=lambda k: k['pin']),lambda x: x['pin']):
    ...     print pin
    ...     for rec in list_data:
    ...             print rec
    ... 
    o/p:
    
    560076
    {'name': 'satyajit', 'pin': 560076, 'address': 'btm'}
    {'name': 'Preetam', 'pin': 560076, 'address': 'btm'}
    560078
    {'name': 'Mukul', 'pin': 560078, 'address': 'Silk board'}
    
  • 1

    我遇到的一个有用的例子可能会有所帮助:

    from itertools import groupby
    
    #user input
    
    myinput = input()
    
    #creating empty list to store output
    
    myoutput = []
    
    for k,g in groupby(myinput):
    
        myoutput.append((len(list(g)),int(k)))
    
    print(*myoutput)
    

    样本输入:14445221

    样本输出:(1,1)(3,4)(1,5)(2,2)(1,1)

  • 5

    你可以编写自己的groupby函数:

    def groupby(data):
                    kv = {}
                    for k,v in data:
                        if k not in kv:
                             kv[k]=[v]
                        else:
                            kv[k].append(v)
               return kv
    
         Run on ipython:
           In [10]: data = [('a', 1), ('b',2),('a',2)]
    
            In [11]: groupby(data)
            Out[11]: {'a': [1, 2], 'b': [2]}
    

相关问题