繁体   English   中英

根据每个组的第一个元素从列表列表中收集元素

[英]Collect elements from a list of lists based on the first elements of each group

我有一个清单

mainlist = [['a','online',20],
            ['a','online',22],
            ['a','offline',26],
            ['a','online',28],
            ['a','offline',31],
            ['a','online',32],
            ['a','online',33],
            ['a','offline',34]]

如果第二个元素是'online' ,而下一个'offline'值是第四个元素,我想获得第三个元素'offline'最小值。 迭代应该进行到列表的末尾。

最终输出应为

outputlist = [['a', 'online', 20, 26], ['a', 'online', 28, 31], ['a', 'online', 32, 34]]

我尝试了下面的代码,但没有帮助我:

from itertools import product

for a, b in product(mainlist,mainlist):
    if a[1] == 'online':
        minvalue=min(a, key=lambda x:x[2])
    if b[1] == 'offline' and b[2] >=minvalue[2]:
        maxvalue=min(b, key=lambda x:x[2])

好像您正在寻找连续的“在线”连胜纪录

只需从头到尾遍历该列表,并记住“联机”何时开始,然后在下一个“脱机”中将此条纹添加到结果中:

mainlist = [['a', 'online', 20], ['a', 'online', 22], ['a', 'offline', 26], ['a', 'online', 28], ['a', 'offline', 31], ['a', 'online', 32], ['a', 'online', 33], ['a', 'offline', 34]]

output = []
first_online = -1
for item, status, num in mainlist:
    if status == 'online':
        if first_online == -1:
            first_online = num
    elif status == 'offline':
        output.append([item, 'online', first_online, num])
        first_online = -1

print(output)

这是使用iter一种方法

例如:

mainlist=iter([['a','online',20],['a','online',22],['a','offline',26],['a','online',28],['a','offline',31],['a','online',32],['a','online',33],['a','offline',34]])

result = []
for i in mainlist:
    if i[1] == 'online':
        result.append(i)
        while True:
            i = next(mainlist)
            if i[1] == "offline":
                result[-1].append(i[-1])
                break

输出:

[['a', 'online', 20, 26], ['a', 'online', 28, 31], ['a', 'online', 32, 34]]

我们可以用itertools.groupby到具有相同的第二元素组连续的名单, 'online''offline' ,的帮助下itertools.itemgetter ,然后只收集必要的输出列表:

from itertools import groupby
from operator import itemgetter

mainlist = [['a', 'online', 20],
            ['a', 'online', 22],
            ['a', 'offline', 26],
            ['a', 'online', 28],
            ['a', 'offline', 31],
            ['a', 'online', 32],
            ['a', 'online', 33],
            ['a', 'offline', 34]]
result = []
for key, group in groupby(mainlist, key=itemgetter(1)):
    if key == 'online':
        output = min(group, key=itemgetter(2)).copy()
        # or `output = next(group).copy()` if data is always sorted
    else:
        next_offline = next(group)
        output.append(next_offline[2])
        result.append(output)
print(result)
# [['a', 'online', 20, 26], ['a', 'online', 28, 31], ['a', 'online', 32, 34]]

我发现此版本比此处介绍的其他版本更好,因为代码未深层嵌套且不使用“标志”变量。


进一步改进:

正如Guido van Rossum所说: 元组用于异构数据,列表用于异构数据。但是现在,您的列表保留了异构数据。 我建议使用namedtuple ,它可以更轻松地区分字段。 我将使用来自typing模块的打字版本 ,但您可以自由使用来自collections 例如,它可能看起来像这样:

from typing import NamedTuple


class Record(NamedTuple):
    process: str
    status: str
    time: int


class FullRecord(NamedTuple):
    process: str
    status: str
    start: int
    end: int

我们可以使用itertools.starmap从您的列表列表中轻松获取Record的列表:

from itertools import starmap

records = list(starmap(Record, mainlist))
# [Record(process='a', status='online', time=20),
#  Record(process='a', status='online', time=22),
#  Record(process='a', status='offline', time=26),
#  Record(process='a', status='online', time=28),
#  Record(process='a', status='offline', time=31),
#  Record(process='a', status='online', time=32),
#  Record(process='a', status='online', time=33),
#  Record(process='a', status='offline', time=34)]

然后,将第一个代码示例包装在generator函数中 ,并替换其中的某些部分以反映输入数据中的更改:

def collect_times(values):
    for key, group in groupby(values, key=Record.status.fget):
        if key == 'online':
            min_online_record = next(group)
        else:
            next_offline_record = next(group)
            yield FullRecord(process=min_online_record.process,
                             status='online',
                             start=min_online_record.time,
                             end=next_offline_record.time)


result = list(collect_times(records))
# [FullRecord(process='a', status='online', start=20, end=26),
#  FullRecord(process='a', status='online', start=28, end=31),
#  FullRecord(process='a', status='online', start=32, end=34)]

就是这样,现在的代码看起来比以前更容易解释。 我们可以看到哪个字段在哪里,它们是通过名称而不是索引来引用的。

请注意,在对数据进行排序时,我会写min_online_record = next(group) ,但如果并非总是如此,则应改写min_online_record = min(group, key=Record.time.fget)

另外,如果您有兴趣,请注意RecordFullRecord的字段重复。 您可以通过从具有两个字段processstatus的父类继承来避免这种status ,但是从namedtuple继承并不是一件很漂亮的事情 因此,如果这样做,请改用dataclass

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM