【问题标题】:Removing duplicates from a list of lists based on a comparison of an element of the inner lists基于内部列表元素的比较从列表列表中删除重复项
【发布时间】:2015-12-18 22:56:02
【问题描述】:

我有大量列表,需要根据特定条件删除重复元素:

  1. 唯一性由列表的第一个元素确定。
  2. 重复项的删除是通过比较重复列表的第二个元素的值来确定的,即保留具有最低第二个元素的列表。

[[1, 4, 5], [1, 3, 4], [1, 2, 3]]

所有上述列表都被认为是重复的,因为它们的第一个元素是相等的。第三个列表需要保留,因为它的第二个元素是最小的。请注意,列表的实际列表有超过 400 万个元素,是双重排序的,需要保留顺序。

列表首先根据内部列表的第二个元素并以反向(降序)顺序排序,然后是基于第一个元素的正常(升序)顺序:

sorted(sorted(the_list, key=itemgetter(1), reverse=True), key=itemgetter(0))

实际排序中三个重复列表的示例:

[...
[33554432, 50331647, 1695008306],
[33554432, 34603007, 1904606324],
[33554432, 33554687, 2208089473],
...]

目标是为平分搜索准备列表。有人可以向我提供有关如何使用 Python 实现这一点的见解吗?

【问题讨论】:

  • 如果第一个和第二个元素相等会发生什么?
  • 第一个和第二个元素分别代表一个范围的开始和结束。所有重复项要么是超集,要么是另一个范围的子集。不应该有两个或多个第一个元素相等且第二个元素相等的重复项。有一些单独的子列表具有相同的第一个和第二个元素,但即表示范围为 1,但它们从不会出现两次,因为特定范围始终是唯一的。

标签: python


【解决方案1】:

您可以使用 dict 对元素进行分组,始终将子列表与较小的第二个元素保持在一起:

l = [[1, 2, 3], [1, 3, 4], [1, 4, 5], [2, 4, 3], [2, 5, 6], [2, 1, 3]]
d = {}
for sub in l:
    k = sub[0]
    if k not in d or sub[1] < d[k][1]:
        d[k] = sub

你也可以传递两个键来排序,你不需要调用两次排序:

In [3]:  l = [[1,4,6,2],[2,2,4,6],[1,2,4,5]]
In [4]: sorted(l,key=lambda x: (-x[1],x[0]))
Out[4]: [[1, 4, 6, 2], [1, 2, 4, 5], [2, 2, 4, 6]]

如果您想按照保持字典中的顺序,则需要保留顺序。

from collections import OrderedDict

l = [[1, 2, 3], [1, 3, 4], [1, 4, 5], [2, 4, 3], [2, 5, 6], [2, 1, 3]]
d = OrderedDict()
for sub in l:
    k = sub[0]
    if k not in d or sub[1] < d[k][1]:
        d[sub[0]] = sub

但不确定这是否适合您对数据进行排序,因此您将丢失任何顺序。

sortedcontainers.sorteddict:您可能会发现非常有用:

SortedDict 提供与 dict 相同的方法。此外,SortedDict 有效地按排序顺序维护其键。因此,keys 方法将按排序顺序返回键,popitem 方法将删除具有最高键的项目,等等。

一个可选的 key 参数定义了一个可调用的,就像 Python 的 sorted 函数的 key 参数一样,从每个 dict 键中提取一个比较键。如果未指定函数,则默认直接比较 dict 键。关键参数必须作为位置参数提供,并且必须位于所有其他参数之前。

from sortedcontainers import SortedDict

l = [[1, 2, 3], [1, 3, 4], [1, 4, 5], [2, 4, 3], [2, 5, 6], [2, 1, 3]]
d = SortedDict()
for sub in l:
    k = sub[0]
    if k not in d or sub[1] < d[k][1]:
        d[k] = sub


print(list(d.values()))

它有你想要的所有方法bisectbisect_left 等等。

【讨论】:

  • 谢谢,您的回答让我朝着正确的方向前进,关于 sortedcontainers 的信息也很有用。调用排序两次:由于第一次排序是相反的,第二次是正常的,我很确定需要两次调用。也就是说,您不能在一次调用中为不同的键指定单独的顺序。
  • @Aaron,你实际上可以使用 lambda 完成它,我编辑了答案
【解决方案2】:

如果我理解正确,解决方案可能是这样的:

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

ordering = []
newdata = {}

for a, b, c in mylist:
    if a in newdata:
        if b < newdata[a][1]:
            newdata[a] = [a, b, c]
    else:
        newdata[a] = [a, b, c]
        ordering.append(a)

newlist = [newdata[v] for v in ordering]

所以在newlist 中,我们将收到[[1, 2, 3], [7, 1, 8]] 的精简列表。

【讨论】:

  • 在字典中存储密钥时为什么要使用列表?
  • 我没有进行测量,但我认为list 可能比OrderedDict 工作得更快。见stackoverflow.com/questions/8176513/…
  • 另一方面,在 dict 的键中查找元素会更快,感谢您的想法,将稍微更改代码
  • 一个字典查找是 O(1) 一个列表扫描是 O(n),你已经拥有了所有的键来进行查找,如果它在字典中它在列表中
  • 现在没关系,因为它应该是输出中的一个列表。
猜你喜欢
  • 1970-01-01
  • 2019-03-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-11-02
  • 2020-08-02
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多