递归映射对角线元素并检查条件,Python

递归映射对角线元素并检查条件,Python,python,Python,给定演讲会话列表,由列表表示,其中每个元素都是一个列表,包含开始时间、结束时间和演讲者姓名,例如: a = [ [ 265, 604, "S1" ], [ 604, 2373, "S1" ], [ 2373, 3719, "S1" ], [ 3719, 4910, "S2" ], [ 4910, 6790, "S2" ] ] 我希望将其减少到一个新列表中,其中应合并连续会话 合并是将会话的第一个开始时间与连续会话的结束时间相结合,即: [a

给定演讲会话列表,由列表表示,其中每个元素都是一个列表,包含开始时间、结束时间和演讲者姓名,例如:

a = [ [  265,  604, "S1" ],
      [  604, 2373, "S1" ],
      [ 2373, 3719, "S1" ],
      [ 3719, 4910, "S2" ],
      [ 4910, 6790, "S2" ] ]
我希望将其减少到一个新列表中,其中应合并连续会话

合并是将会话的第一个开始时间与连续会话的结束时间相结合,即:

[a[i][0], a[i+1][1], a[i][2]]
如果连续会话的说话人相同,且会话之间的间隔不太长,即

 a[i+1][0] - a[i][1] < 1000  and  a[i][2] == a[i+1][2]


我正在使用上述条件迭代列表,但由于某些原因,我仅限于前2组元素。

您想要的是找到分组的开始和结束:

def grps(a):
    it = iter(a)
    i = next(it)
    start, spk = i[0], i[2]
    for ele in it:
        if spk != ele[2]:
            yield [start, ele[0], spk]
            spk = ele[2]
            start = ele[0]
    yield start, ele[1], spk


print(list(grps(a)))
这将给你:

[[265, 3719, 'S1'], [3719, 6790, 'S2']]
当你遇到一个新的演讲者时,他们的开始时间是最后一个演讲者的结束时间,你只会在每次遇到一个新的演讲者时更新开始变量,所以你总是输出每个演讲者的开始和结束时间,最后一个我们在循环外让出的演讲者使用他们自己的第二个元素来获得他们的结束时间

如果下一个演讲者没有包含结束时间,即存在间隙,另一种方法是使用前一个元素:

def grps(a):
    it = iter(a)
    prev = next(it)
    start, spk = prev[0], prev[2]
    for ele in it:
        if spk != ele[2]:
            yield [start, prev[1], spk]
            start = ele[0]
            spk = ele[2]
        prev = ele
    yield start, ele[1], spk
但在您的情况下,一旦格式与发布的格式相同,就不需要这样做

或者使用
itertools.groupby

from itertools import groupby
from operator import itemgetter


def gps(a):
    for k, v in groupby(a, key=itemgetter(2)):
        v = list(v)
        yield [v[0][0],  v[-1][1], v[0][2]]


print(list(gps(a)))
输出:

[[265, 3719, 'S1'], [3719, 6790, 'S2']]
或者,如果您只想在不调用列表的情况下拉取第一个和最后一个,则可以进行轻微的更改:

from itertools import groupby
from operator import itemgetter
from collections import deque


def gps(a):
    for k, v in groupby(a, key=itemgetter(2)):
        start, end = next(v), deque(v, maxlen=1).pop()
        yield [start[0],  end[1], end[2]]
如果您的数据碰巧无序,您可以使用dict:

def gps(a):
    d = defaultdict(lambda: {"mn":float("inf"),"mx":float("-inf")})
    for sub in a:
        key = sub[-1]
        if d[key]["mn"] > sub[0]:
            d[key]["mn"] = sub[0]
        elif d[key]["mx"] < sub[1]:
            d[key]["mx"]  = sub[1]
    return d

for k,v in gps(a).items():
    print([v["mn"], v["mx"], k])
def gps(a):
d=defaultdict(lambda:{“mn”:float(“inf”),“mx”:float(“-inf”)})
对于a中的sub:
键=子[-1]
如果d[key][“mn”]>sub[0]:
d[键][“mn”]=sub[0]
elif d[key][“mx”]
源自Padraic的答案,在我看来更具可读性,并解决了1000个差异:

def nextSpeech(segments):
    it = iter(segments)
    start = end = next(it)
    def isSameSpeech(element): return element[2] == start[2] and end[0] - start[1] < 1000
    def getSpeech(): return start[0], end[1], end[2]

    for element in it:
        if isSameSpeech(element):
            end = element
        else:
            yield getSpeech()
            start = end = element
    yield getSpeech()

list(nextSpeech(a))
如果输入段未排序,您可以运行
list(nextSpeech(sorted(a)))
或修改函数的前两行,默认情况下使用“sort”参数对输入进行排序:

def nextSpeech(segments, sort=True):
    it = iter(sorted(segments) if sort else segments)
    ...
请注意,
sorted()
可以由您喜欢的任何其他排序函数(或lambda)替换。

很容易找到会话之间的(索引)中断:

breaks = [i + 1 
          for (i, (a0, a1)) in enumerate(zip(a, a[1:]))
          if (a1[0] - a0[1]) >= 1000 or (a0[2] != a1[2])]
然后查找要合并的会话:

sessions = zip([0] + breaks, b + [len(breaks)-1])
因此答案是:

answer = [[a[start][0], a[end][1], a[start][2]] 
          for (start, end) in sessions]

我们可以不使用索引:

 breaks = [b for b in zip(a, a[1:])
           if (a1[0] - a0[1]) >= 1000 or (a0[2] != a1[2])]
 sessions = zip([(None, a[0])] + breaks, 
                 breaks + [(a[-1], None)])
 answer = [[p[1][0], n[0][1], p[1][2]] 
           for (p,n) in sessions]

为什么你认为你需要递归呢?很抱歉使用了实际情况,列表比这个大很多,还有很多其他信息,为了便于使用,我删除了不相关的数据。我不一定需要使用递归,我只是使用if条件的for循环,但没有得到必要的输出格式是[[2653719,'S1']其中265是开始时间,3719是结束时间。实际的想法是将特定演讲者的开始时间和结束时间结合起来。这就是为什么265到3719是s1的理想输出。感谢您帮助我理解流程。事实上,我正要评论我检查1000的差异的原因是新演讲者的开始时间可能没有与最后一个发言者的结束时间不同。@AyushChordia,不用担心,第二个和第三个示例将处理这个问题,通过使用prev逻辑使用当前发言者链中的最后一个值,我们将始终在最后一次拉取该发言者。这不适用于此数据,我猜1000的差异条件没有考虑在内:尝试使用此数据:[[493,2244,“S1”],[4741,6526,“S1”],[6526,7729,“S1”],[7729,9476,“S1”],[0,493,“S0”],[2244,3521,“S4”],[3521,4716,“S4”]]输出应为:[4932244,“S1”],[47419476,“S1”],[0493,“S0],[2244,4716,“S4”]代码中有错误。仅捕获两个元素。请尝试获取此数据:[[4932244,“S1”],[47416526,“S1”],[65267729,“S1”],[77299476,“S1”],[0493,”S0“],[22443521,“S4”],[35214716,“S4”]]您没有提到输入未排序。您可以简单地运行:
list(nextSpeech(sorted(a))
或根据您的喜好对其排序,然后运行:
list(nextSpeech)(mysort(a))< /代码>如果我的答案有效,请考虑接受我的回答。)我也用这个解决方案编辑了我的答案,另外一个通过修改函数来编辑我的答案。
answer = [[a[start][0], a[end][1], a[start][2]] 
          for (start, end) in sessions]
 breaks = [b for b in zip(a, a[1:])
           if (a1[0] - a0[1]) >= 1000 or (a0[2] != a1[2])]
 sessions = zip([(None, a[0])] + breaks, 
                 breaks + [(a[-1], None)])
 answer = [[p[1][0], n[0][1], p[1][2]] 
           for (p,n) in sessions]