如何根据条件将列表中的数据收集到组中?

Joe*_*ult 6 python grouping parsing

不知道如何标题这个问题.我遇到过一些情况,我有一个数据列表,可能带有一些属性注释,我想将它们收集到组中.

例如,也许我有这样的文件:

some event
reading: 25.4
reading: 23.4
reading: 25.1
different event
reading: 22.3
reading: 21.1
reading: 26.0
reading: 25.2
another event
reading: 25.5
reading: 25.1
Run Code Online (Sandbox Code Playgroud)

并且我想对每组读数进行分组,将它们分成条件(在这种情况下,发生事件),这样我最终得到的结构就像

[['some event',
  'reading: 25.4',
  'reading: 23.4',
  'reading: 25.1'],
 ['different event',
  'reading: 22.3',
  'reading: 21.1',
  'reading: 26.0',
  'reading: 25.2'],
 ['another event',
  'reading: 25.5',
  'reading: 25.1']]
Run Code Online (Sandbox Code Playgroud)

在它的通用形式中,它是:查找条件,收集数据直到该条件再次为真,重复

现在,我会做类似的事情

events = []
current_event = []

for line in lines:
    if is_event(line):
        if current_event:
            events.append(current_event)
        current_event = [line]

    else:
        current_event.append(line)
else:
    if current_event:
        events.append(current_event)


def is_event(line):
    return 'event' in line
Run Code Online (Sandbox Code Playgroud)

它产生了我想要的东西,但它很丑陋且难以理解.我相当肯定必须有更好的方法

我的猜测是它涉及一些itertools魔法,但我是itertools的新手并不能完全包围所有这些.

谢谢!

更新

我实际上已经和史蒂夫杰索普的Grouper课程一起回答了.这是我正在做的事情:

class Grouper(object):
    def __init__(self, condition_function):
        self.count = 0
        self.condition_function = condition_function

    def __call__(self, line):
        if self.condition_function(line):
            self.count += 1
        return self.count
Run Code Online (Sandbox Code Playgroud)

然后像它一样使用它

event_grouper = Grouper(is_event)
result_as_iterators = (x[1] for x in itertools.groupby(lines, event_grouper))
Run Code Online (Sandbox Code Playgroud)

然后把它变成我做的字典

event_dictionary = [{event: readings} for event, *readings in result_as_iterators]
Run Code Online (Sandbox Code Playgroud)

这使

[
 {'some event': ['reading: 25.4', 'reading: 23.4', 'reading: 25.1']},
 {'different event': ['reading: 22.3','reading: 21.1','reading: 26.0','reading: 25.2']},
 {'another event': ['reading: 25.5', 'reading: 25.1']}
]
Run Code Online (Sandbox Code Playgroud)

Izk*_*ata 5

我怀疑itertools(或集合)可以使它更清晰,除非确切的模式在那里实现.

我注意到两件事:

  • 总是有一个当前的事件(因为第一行是一个事件)
  • 总是将该行附加到当前事件(因此事件本身始终是current_event[0]

因此,如果您有当前事件,则可以跳过检查,并且您也不必创建特殊情况.另外,由于"当前"事件总是最后一个,我们可以使用负索引直接跳转到它:

events = []

for line in lines:
    if is_event(line):
        events.append([])
    events[-1].append(line)

def is_event(line):
    return 'event' in line
Run Code Online (Sandbox Code Playgroud)


aba*_*ert 5

有了itertools.groupby,您可以根据键轻松地对事物进行分组'event' in line.所以,作为第一步:

>>> for k, g in itertools.groupby(lines, lambda line: 'event' in line):
...     print(k, list(g))
Run Code Online (Sandbox Code Playgroud)

当然,这并不是将事件与其价值观结合在一起.我怀疑你真的不希望事件与它们的值一起,但实际上更喜欢有一个event: [values]或一个列表(event, [values]).在这种情况下,你差不多完成了.例如,要获得该dict,只需使用石斑鱼配方(或zip(*[iter(groups)]*2))分组成对,然后使用dict理解来映射k, v这些对next(k): list(v).

在另一方面,如果你真的希望他们在一起,这是相同的步骤,但用列表[next(k)] + list(v)]的末尾.

但是,如果你不真正了解groupby不够好,把这一描述成代码,你应该写的东西你明白了.这并不太难:

def groupify(lines):
    event = []
    for line in lines:
        if 'event' in line:
            if event: yield event
            event = [line]
        else:
            event.append(line)
    if event: yield event
Run Code Online (Sandbox Code Playgroud)

是的,它是7行(可以通过一些技巧缩小到4行)而不是3行(通过嵌套理解以丑陋的方式缩小为1),但是你理解并且可以调试的7行比3行魔法更有用.

迭代由此函数创建的生成器时,它会为您提供行列表,如下所示:

>>> for event in groupify(lines):
...     print(event)
Run Code Online (Sandbox Code Playgroud)

这将打印:

['some event', 'reading: 25.4', 'reading: 23.4', 'reading: 25.1']
['different event', 'reading: 22.3', 'reading: 21.1', 'reading: 26.0', 'reading: 25.2']
['another event', 'reading: 25.5', 'reading: 25.1']
Run Code Online (Sandbox Code Playgroud)

如果你想要一个列表而不是一个生成器(所以你可以索引它,或者迭代它两次),你可以做同样的事情来将任何其他的iterable转换成一个列表:

events = list(groupify(lines))
Run Code Online (Sandbox Code Playgroud)


Ste*_*sop 2

您可以利用 Python 中的函数具有状态这一事实。此石斑鱼功能与 DSM 的功能相同accumulate(fn(line) for line in s1)

def grouper(line):
    if is_event(line):
        grouper.count += 1
    return grouper.count
grouper.count = 0

result_as_iterators = (x[1] for x in itertools.groupby(lines, grouper))
Run Code Online (Sandbox Code Playgroud)

然后如果你需要它:

result_as_lists = [list(x) for x in result_as_iterators]
Run Code Online (Sandbox Code Playgroud)

为了允许并发使用,每次使用时都需要一个新的石斑鱼函数对象(以便它有自己的计数)。您可能会发现将其设为一个类更简单:

class Grouper(object):
    def __init__(self):
        self.count = 0
    def __call__(self, line):
        if is_event(line):
            self.count += 1
        return self.count

results_as_iterators = itertools.groupby(lines, Grouper())
Run Code Online (Sandbox Code Playgroud)