【问题标题】:Insert missing elements in a nested list based on a condition - Python根据条件在嵌套列表中插入缺失的元素 - Python
【发布时间】:2013-12-11 10:04:23
【问题描述】:

我有嵌套列表,

a = [(2,0),(3,0),(4,2),(10,3),(11,5)]

我要做的是在n 位置添加内部元组(0,n),其中na 中缺失元素的位置。每个内部列表中的第二个元素应以 1 为增量增加,如果存在间隙,则应在该间隙处插入 (0,n)

所以列表a 的预期结果是:

a_out = [(2,0),(3,0),(0,1),(4,2),(10,3),(0,4),(11,5)]

即因为a 中的第一个和第二个元素是(3,0)(4,2),所以在它们之间插入了(0,1)

我的解决方案有效,但我想知道是否有更 Pythonic 的方式来解决它?我一直在查找 Python 的 itertools 库,但找不到简洁的解决方案。

到目前为止我的代码是:

l1 = [n[1] for n in a]
l2 = range(max(l1)+1)
l3 = [n for n in l2 if not in l1]


zeros = [0]*len(l3)
inserts = zip(zeros,l3)
a_full = a + inserts

a_out = sorted(a_full, key = itemgetter(1))

谁能提出更好的解决方案?

编辑:

通常可能有许多元素具有相同的第二个内部元素(例如,a 中出现的(2,0)(3,0))。但是,我可以将它们组合在一起而不失一般性。

嵌套列表a 可以表示为,

a_sum = [(5,0),(4,2),(10,3),(11,5)]

通过使用代码,

a_group = [sum([x for x, y in group]) for key, group in groupby(a, key=itemgetter(1))]

a_sum = zip(output,list(set(l1)))

编辑二:

a 的长度始终为 600,但根据研究的进展情况,这可能会增加到 10**3 的顺序。

【问题讨论】:

  • 您确定要将(2, 0) 放在列表的开头吗?无法通过插入项目来修复 (2, 0)(3, 0) 一起出现的问题。
  • 我刚刚添加了一个编辑来解决您的评论。我可以通过将具有相同第二个内部元素的元素分组然后将它们相加来解决这个问题;单独的元素(2,0)(3,0) 可以组合成(5,0) wlog。

标签: python insert nested tuples


【解决方案1】:

您可以在 O(n) 的嵌套列表推导中执行此操作。只需在嵌套部分中添加任何缺少的条目即可。

>>> a = [(2,0),(3,0),(4,2),(10,3),(11,5)]
>>> [k for i,j in enumerate(a, 1) for k in [j] + [(0,n) for n in range(j[1]+1, a[min(i, len(a)-1)][1])]]
[(2, 0), (3, 0), (0, 1), (4, 2), (10, 3), (0, 4), (11, 5)]

>>> [k for i,j in zip(a, a[1:]) for k in [i] + [(0,n) for n in range(i[1]+1, j[1])]] + a[-1:]
[(2, 0), (3, 0), (0, 1), (4, 2), (10, 3), (0, 4), (11, 5)]

如果a 很大,您可以通过在其上使用额外的迭代器来避免a[1:] 切片

>>> a_iter = iter(a); next(a_iter)
(2, 0)
>>> [k for i,j in zip(a, a_iter) for k in [i] + [(0,n) for n in range(i[1]+1, j[1])]] + a[-1:]
[(2, 0), (3, 0), (0, 1), (4, 2), (10, 3), (0, 4), (11, 5)]

【讨论】:

    【解决方案2】:

    此版本在 cmets 允许的情况下将 (2,0) 和 (3,0) 组合成 (5,0)

    >>> from collections import defaultdict
    >>> D = defaultdict(int)
    >>> a = [(2,0),(3,0),(4,2),(10,3),(11,5)]
    >>> for i,j in a:
    ...     D[j]+=i
    ...
    >>> [(D[n], n) for n in range(a[0][1], a[-1][1]+1)]
    [(5, 0), (0, 1), (4, 2), (10, 3), (0, 4), (11, 5)]
    

    【讨论】:

    • 您建议我使用哪种解决方案?您以前的解决方案和我的分组方法(在编辑中) - 还是这个解决方案自己?
    • 我希望嵌套列表理解更快,但更难阅读。使用最适合您的。
    • 非常感谢@gnibbler。我想我会选择嵌套列表理解,因为这个过程可能是可选的,所以将它分开可能更容易编码。
    【解决方案3】:
    a = [(2,0),(3,0),(4,2),(10,3),(11,5)]
    i = 1
    while i < len(a):
      if a[i-1][1] + 1 < a[i][1]:
        a.insert(i, (0, a[i-1][1]+1))
      i += 1
    

    但您可能需要考虑使用不同的数据类型,可能是defaultdict,它似乎在所有没有存储任何内容的地方都有默认值(在您的情况下为 0) .

    【讨论】:

    • 同意。唉,最 Pythonic 的版本并不总是性能最好的版本。
    【解决方案4】:

    为什么不只使用一个小而易读的函数:

    def fill(seq):
        """
        >>> list(fill([(2, 0), (3, 0), (4, 2), (10, 3), (11, 5)]))
        [(2, 0), (3, 0), (0, 1), (4, 2), (10, 3), (0, 4), (11, 5)]
        """
        prev = None
        for value, key in seq:
            if prev != None:
                while key > prev + 1:
                    prev += 1
                    yield (0, prev)
            yield (value, key)
            prev = key
    
    if __name__ == '__main__':
        import doctest
        doctest.testmod()
    

    【讨论】:

    • 感谢您的回复。不过,我在解释您的代码时遇到了一些困难,如果您能提供一个简短的解释,是否有可能?非常感谢。
    • 单步执行序列中的每个值/键对。在每次迭代时将prev 设置为key。如果当前key与之前key的差值大于1,则用(0, "missing_key")填写缺失值。
    【解决方案5】:
    import operator
    a = [(2,0),(3,0),(4,2),(10,3),(11,5)]
    seen = set([item[1] for item in a])
    inserts = [(0, item) for item in range(max(seen)) if item not in seen]
    a_out = sorted(a + inserts, key=operator.itemgetter(1))
    print(a_out)
    

    产量

    [(2, 0), (3, 0), (0, 1), (4, 2), (10, 3), (0, 4), (11, 5)]
    

    上述O(n log n) 解决方案保留了您发布的代码的行为。如果我们还可以假设a 的元组中的第二项始终不递减,那么有更好的O(n)(一次性)解决方案,例如:

    a = [(2,0),(3,0),(4,2),(10,3),(11,5)]
    result = a[:1]
    for item in a[1:]:
        result.extend(
            [(0,i) for i in range(result[-1][1]+1, item[1])] + [item])
    

    【讨论】:

    • 是的,我可以假设a 的元组中的第二项始终是非递减的。都在增加,但是,我不知道这种增加模式的行为。鉴于此 - 更好的 O(n) 解决方案是什么?
    • @Holtz,任何使用 sort 的东西都不是 O(n)
    • @gnibbler,我不需要使用sort - 进来的数据已经排序了。
    • @Holtz,我的意思是任何使用sort 的答案都不是 O(n)。并不是说必须使用排序
    • 啊,我明白了,我的错误。
    【解决方案6】:
    >>> from collections import defaultdict
    >>> D = defaultdict(list)
    >>> a = [(2,0),(3,0),(4,2),(10,3),(11,5)]
    >>> for i,j in a:
    ...     D[j].append(i)
    ...
    >>> [(z, n) for n in range(a[0][1], a[-1][1]+1) for z in D[n] or [0]]
    [(2, 0), (3, 0), (0, 1), (4, 2), (10, 3), (0, 4), (11, 5)]
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-04-25
      • 2023-03-17
      • 2023-01-23
      • 1970-01-01
      • 2019-01-16
      • 2022-11-30
      • 2019-08-21
      相关资源
      最近更新 更多