不确定如何标题这个问题。我遇到过一些情况,我有一个数据列表,可能带有一些属性注释,我想将它们收集到组中。
例如,我可能有这样的文件:
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
我希望对每组读数进行分组,将它们分成条件(在这种情况下,发生事件),这样我最终得到的结构就像
[['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']]
在它的通用形式中,它是: 查找条件,收集数据直到该条件再次为真,重复
现在,我会做类似
的事情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
产生我想要的东西,但它很丑陋且难以理解。我相当肯定必须有一个更好的方法
我的猜测是它涉及一些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
然后像
一样使用它event_grouper = Grouper(is_event)
result_as_iterators = (x[1] for x in itertools.groupby(lines, event_grouper))
然后把它变成我做的字典
event_dictionary = [{event: readings} for event, *readings in result_as_iterators]
给出了
[
{'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']}
]
答案 0 :(得分: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
答案 1 :(得分:5)
使用itertools.groupby
,您可以根据密钥轻松对内容进行分组,例如'event' in line
。所以,作为第一步:
>>> for k, g in itertools.groupby(lines, lambda line: 'event' in line):
... print(k, list(g))
当然,这并不是将事件与其价值观结合在一起。我怀疑你真的不想要事件及其值,但实际上更喜欢有event: [values]
的字典或(event, [values])
的列表。在这种情况下,你差不多完成了。例如,要获取该字典,只需使用石斑鱼配方(或zip(*[iter(groups)]*2)
)成对分组,然后使用字典理解将这些对中的k, v
映射到next(k): list(v)
。 / p>
另一方面,如果你真的做想要他们在一起,那就是相同的步骤,但最后有一个[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
是的,它是7行(可以通过一些技巧缩小到4行)而不是3(通过嵌套理解以丑陋的方式缩小到1),但是你理解并且可以调试的7行比3行魔法更有用。 / p>
当您迭代由此函数创建的生成器时,它会为您提供行列表,如下所示:
>>> for event in groupify(lines):
... print(event)
这将打印:
['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']
如果你想要一个列表而不是一个生成器(所以你可以索引它,或者迭代它两次),你可以做同样的事情来将任何其他的迭代变成一个列表:
events = list(groupify(lines))
答案 2 :(得分:4)
我希望itertools
有一个能够做你想要的功能。对于娱乐价值,在现代Python中你可以做类似
from itertools import groupby, accumulate, tee
def splitter(source, fn):
s0, s1 = tee(source)
tick = accumulate(fn(line) for line in s1)
grouped = groupby(s0, lambda x: next(tick))
return (list(g) for k,g in grouped)
给出了
>>> with open("event.dat") as fp:
... s = list(splitter(fp, lambda x: x.strip().endswith("event")))
...
>>> s
[['some event\n', 'reading: 25.4\n', 'reading: 23.4\n', 'reading: 25.1\n'],
['different event\n', 'reading: 22.3\n', 'reading: 21.1\n', 'reading: 26.0\n', 'reading: 25.2\n'],
['another event\n', 'reading: 25.5\n', 'reading: 25.1']]
但说实话,我可能会做@abarnert所做的事。
答案 3 :(得分:2)
您可以使用列表推导使代码更简洁:
# Load the file
lines = [l.rstrip() for l in open("test.txt") ]
# Record the line indices where events start/stop
events = [ i for i in range(len(lines)) if "event" in lines[i] ]
events.append( len(lines) ) # required to get the last event
# Group the lines into their respective events
groups = [ lines[events[i]:events[i+1]] for i in range(len(events)-1) ]
print groups
输出:
[['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']]
我不确定你在原始可读性方面获得了多少,但是通过这些评论来理解它是非常简单的。
答案 4 :(得分: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))
然后如果你需要它:
result_as_lists = [list(x) for x in result_as_iterators]
为了允许并发使用,每次使用时都需要一个新的石斑鱼功能对象(因此它有自己的计数)。您可能会发现将其设为类更简单:
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())