我有一个清单
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'
值是第四个元素,我想获取第三个元素的最小值。迭代应该持续到列表末尾。
最终输出应为
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])
答案 0 :(得分: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)
答案 1 :(得分:1)
这是使用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]]
答案 2 :(得分:1)
在itertools.groupby
的帮助下,我们可以使用itertools.itemgetter
对具有相同第二个元素'online'
或'offline'
的连续列表进行分组,然后仅收集必要的输出列表:
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所说:"Tuples are for heterogeneous data, list are for homogeneous data.",但现在您的列表保留了异构数据。我建议使用namedtuple
,这样可以更容易地区分字段。我将使用typed version模块中的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 function中,并替换其中的某些部分以反映输入数据中的更改:
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)
。
此外,如果您有兴趣,请注意Record
和FullRecord
中的字段重复。您可以通过从具有两个字段process
和status
的父类继承来避免这种情况,但是从namedtuple
继承则是not really pretty。因此,如果这样做,请改用dataclass
。