【问题标题】:Removing an item from a list of lists based on each of the lists first element根据每个列表的第一个元素从列表列表中删除一个项目
【发布时间】:2018-09-14 22:21:15
【问题描述】:

给定:

a = [[1,2],[3,4],[5,6],[7,8]]
b = 3

我想删除a 的第一项具有b 的项目。所以在这种情况下,我们将删除 [3,4] 给:

a = [[1,2],[5,6],[7,8]]

我当前的代码是:

if b in [i[0] for i in a]:
    pos = [i[0] for i in a].index(b)
       del a[pos]

这可行,但速度很慢。有什么更好的方法来做到这一点?

编辑: 我之前没有测试过性能,所以我可能做错了,但我明白了:

def fun1():
    lst = [[x, 2*x] for x in range(1000000)]
    lst = [x for x in lst if x[0] != 500]
    return lst

def fun2():
    lst = [[x, 2*x] for x in range(1000000)]
    for i in reversed(range(len(lst))):
        if lst[i][0] == 500:
            del lst[i]
    return lst

cProfile.runctx('fun1()', None, locals())
        6 function calls in 0.460 seconds

cProfile.runctx('fun2()', None, locals())
        6 function calls in 0.502 seconds

【问题讨论】:

  • “相当大”有多大?足够大,值得考虑重新排列代码,以便拥有一个 numpy 数组而不是列表?或者使用 PyPy 而不是 CPython?或者使用 Cython 构建一个快速的 C 扩展来执行 Coldspeed 的解决方案?
  • @abarnert 这在我的头上,但它不是那么大。

标签: python python-3.x list


【解决方案1】:

您可以使用列表推导:

>>> a = [[1,2],[3,4],[5,6],[7,8]]
>>> b = 3
>>> a = [x for x in a if x[0] != b]
>>> a
[[1, 2], [5, 6], [7, 8]]

【讨论】:

  • 这里有一个时空权衡 - 我发现这个答案也更适合大型列表,因为 CS 在 Python 的数组支持列表上的就地删除意味着将每个元素从已删除的元素移动到结束一个,如果列表很长并且有很多删除,我希望它需要更长的时间。
【解决方案2】:
for i in a[:-1]:
    if i[0]==b:
        a.remove(i)

这个怎么样?

输出是

[[1, 2], [5, 6], [7, 8]]

【讨论】:

  • 什么意思?
  • 试试a = [[1,2],[3,4],[3,6],[7,8]]
  • 我改了。现在怎么样? :)
  • 拒绝投票已撤回,但请记住,这会创建一个效率低下的列表副本,因为您正在修改原始 无论如何
  • 那么最好的方法是什么? :-)
【解决方案3】:

反向删除a,就地修改:

for i in reversed(range(len(a))):
    if a[i][0] == 3:
        del a[i]

就地修改意味着这更有效,因为它不会创建新列表(就像列表推导式那样)。


由于 OP 要求提供高效的解决方案,因此这里是 timeit 两个投票最多的答案之间的比较。

设置 -

a = np.random.choice(4, (100000, 2)).tolist()

print(a[:5])
[[2, 1], [2, 2], [3, 2], [3, 3], [3, 1]]

列表理解 -

%timeit [x for x in a if x[0] != b]
11.1 ms ± 685 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

反向删除-

%%timeit
for i in reversed(range(len(a))):
    if a[i][0] == 3:
        del a[i]

10.1 ms ± 146 µs per loop (mean ± std. dev. of 7 runs, 1 loop each)

它们非常接近,但 reverse delete 的性能提高了 1UP,因为它不必像列表解析那样在内存中生成新列表。

【讨论】:

  • 如果你不想建立一个新的列表,a[:] = [x for x in a if x[0] != b] 怎么办?
  • @Matthias 请注意,[x for x in a if x[0] != b] 创建了一个新列表并将其重新分配回a
  • @cᴏʟᴅsᴘᴇᴇᴅ:花了我一些时间,但我越看你的解决方案就越喜欢这个主意。
  • @Matthias 我没有写一个列表理解答案的原因是因为这是一个很棒的成语,没有足够的开发人员知道/似乎使用它,所以我想我会对此有所了解。感谢 wim 很久以前就向我介绍了这个。
  • @AndrewK 这取决于许多因素,大小是其中之一,python 版本是另一个。列表推导被优化到它们实际上比循环本身稍微快一点。反向删除确实在其内存效率方面大放异彩。还有一点,这是在python3.6上计时的,你的里程可能会有所不同。
【解决方案4】:

如果您的列表很小,那么您也可以尝试过滤,

a = [[1,2],[3,4],[5,6],[7,8]]
b = 3

print(list(filter(lambda x:x[0]!=b,a)))

输出:

[[1, 2], [5, 6], [7, 8]]

【讨论】:

    猜你喜欢
    • 2015-09-20
    • 1970-01-01
    • 1970-01-01
    • 2019-06-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-11-13
    相关资源
    最近更新 更多