【问题标题】:How to remove duplicates of huge lists of objects in Python如何在 Python 中删除大量对象的重复项
【发布时间】:2016-08-19 07:38:22
【问题描述】:

我有包含许多重复项的庞大对象列表(我说的是数千个列表,每个列表包含数千个对象,最多包含大约 1000 万个单独的对象(已经没有重复项)。

我需要遍历它们并删除每个列表中的所有重复项(无需在列表之间进行比较,只在每个列表中进行比较)。

当然,我可以浏览这些列表并与已多次发布的任何重复数据删除算法进行比较,但我想这将花费我很长时间。

我认为我可以使用精心设计的 __hash__ 方法创建一个对象并使用 list(set(obj)) 删除它们,但首先:我不知道这是否可行,其次:我仍然需要循环列表以将元素转换为新对象。

我知道 Python 不是我想要实现的最佳解决方案,但在这种情况下,它必须在 Python 中完成。我想知道以最佳性能实现这一目标的最佳方法是什么。

编辑:澄清一下:我有大约 2k 个对象列表,每个对象中大约有 5k 个对象(粗略估计)。复制的对象是副本,而不是对同一内存位置的引用。列表(dicts)基本上是转换后的 JSON 数组


编辑 2:很抱歉没有说清楚,我会改写。

这是针对 django 数据迁移的,尽管我的问题仅适用于数据“格式化”,而不适用于框架本身或数据库插入。 我将一大堆数据作为 JSON 插入到表中以供以后分析。现在我需要对其进行规范化并正确保存。我创建了新表,需要迁移数据。

所以当我从数据库中检索数据时,我有大约 2000 个 JSON 数组。应用json.loads(arr)(通过文档)我得到了 2000 个对象列表(字典)。每个 dict 只有字符串、数字和布尔值作为每个键的值,没有嵌套的对象/数组,所以像这样:

[
  {
    a: 'aa',
    b: 2,
    c: False,
    date: <date_as_long> // ex: 1471688210
  },
  {
    a: 'bb',
    b: 4,
    c: True,
    date: <date_as_long> // ex: 1471688210
  }
]

我需要遍历每个列表并删除重复项。如果列表中除日期之外的所有字段都匹配(这不在原始问题中,因为我没有预测到),则认为某些内容是重复的。如果它们在不同的列表中匹配,则不会被视为重复。

在对内容进行更好的分析后,我发现我有接近 200 万条个人记录(而不是之前所说的 1000 万条)。 我面临的性能问题是因为每个 dict 都需要进行某种数据格式化(例如转换日期)并将其“包装”在模型对象中以进行数据库插入:ModelName(a='aaa', b=2, c=True, date=1471688210)

数据库本身的插入是由bulk_create完成的。

注意:很抱歉未能对原始问题做出澄清。我对此进行的深入研究越多,我就越了解必须做什么以及如何处理数据。


我接受了@tuergeist 的回答,因为它指出了我需要的东西,即使我的细节不好。

鉴于 dicts 不能被散列,因此我不能将它们添加到 set() 中,我的解决方案是为重复数据创建一个 set() 元组,并用它验证重复项。如果重复项在列表中,这可以防止额外的迭代。

原来是这样的:

data = [lots of lists of dicts]
formatted_data = []
duplicates = set()

for my_list in data:
  for element in my_list:
    a = element['a']
    b = convert_whatever(element['b'])
    c = element['c']

    d = (a, b, c) # Notice how only the elements that count for checking if it's a duplicate are here (not the date)

    if d not in duplicates:
      duplicates.add(d)
      normalized_data = {
        a: a,
        b: b,
        c: c,
        date: element['date']
      }
      formatted_data.append(MyModel(**normalized_data)

  duplicates.clear()

之后,为了更好的内存管理,我使用了生成器:

data = [lots of lists of dicts]
formatted_data = []
duplicates = set()

def format_element(el):
  a = el['a']
  b = convert_whatever(el['b'])
  c = el['c']

  d = (a, b, c)

  if d not in duplicates:
    duplicates.add(d)
    normalized_data = {
      'a': a,
      'b': b,
      'c': c,
      'date': el['date']
    }
    formatted_data.append(MyModel(**normalized_data))

def iter_list(l):
  [format_element(x) for x in l]
  duplicates.clear()

[iter_list(my_list) for my_list in data]

这里的工作代码:http://codepad.org/frHJQaLu

注意:我完成的代码与这个有点不同(并且在功能风格上)。这只是我如何解决问题的一个例子。


编辑 3: 对于数据库插入,我使用了 bulk_create。最后,正确格式化所有内容(150 万个唯一条目,22.5 万个重复项)需要 1 分钟,将所有内容插入数据库需要 2 分钟。

谢谢大家!

【问题讨论】:

  • 你能把你的问题说得更清楚些吗?你说的重复是什么意思?它们是指相同的内存位置还是副本?你说列表中的列表,所以它们像树一样。我认为时间复杂度不会低于 O(n^2)。您是在寻找一种算法还是在寻找任何现有的功能供您这样做?
  • 抱歉,刚刚编辑了细节。它不是树状的,它是单独的对象列表。每个列表都有很多重复项,我需要清除它们。好吧,如果 Python 为它提供一个函数,那就更好了。但我只想以最好的方式解决问题:)
  • 我不认为嵌套列表的大小使得这对于普通的 python 方法和循环来说是不可行的。如果顺序无关紧要,请使用set(),否则请参阅:stackoverflow.com/questions/480214/…
  • “我想这会带我永远”,“我不知道这是否可行” 如果可行,我建议您尝试其中一种方法 - 很好。如果没有,您可以在此处发布您的代码。
  • 顺便说一句:我已经在从0-100 获取的 2k 个 5k 随机整数列表上尝试了 list(set(..)) 解决方案。 timeit 模块报告 142 毫秒。如果对象不是简单的整数而是更复杂的对象,这将花费更多时间,但我仍然怀疑它会“永远”。

标签: python python-3.x


【解决方案1】:

(可散列项)的快速、不保留顺序的解决方案是

def unify(seq):
    # Not order preserving
    return list(set(seq))

完成编辑

我假设您在list 中有dicts。你有很多清单。从单个列表中删除重复项的解决方案是:

def remove_dupes(mylist):
    newlist = [mylist[0]]
    for e in mylist:
        if e not in newlist:
            newlist.append(e)
    return newlist

此处的列表包含以下字典。 (但都是随机的)

{"firstName":"John", "lastName":"Doe"}, 
{"firstName":"Anna", "lastName":"Smith"}, 
{"firstName":"Peter","lastName":"Jones"}

运行此程序,我的 MacBook(2,4GHz,i5)上 2000 个 dicts 需要 8 秒

完整代码:http://pastebin.com/NSKuuxUe

【讨论】:

  • 列表中的元素不需要是可散列的吗?基本上这是我转换为字典的 JSON 对象数组
  • 您可以提供示例数据而不是投票
  • 我没有对你投反对票,我什至不确定我评论了什么,因此我问:((除非我在没有注意到的情况下点击它,但在这里它没有显示出来)我做到了)
  • 我也没有对你投反对票,但是:1. 将它用于 Python 3,因为问题标记为 Python 3。2. 它不适用于 dict 实例作为列表元素。
  • 在 Py3 上你是对的。 “dict”编辑比我的答案更新!仍然没有给出示例
【解决方案2】:

我建议有一个排序列表(如果可能的话),这样当你想要比较项目时可以更精确(比如我的意思是字典)。一个散列(或不是)列表可以实现这一点。

如果您能够管理列表中的“添加和删除”,那就更好了!每次添加/删除时对新项目进行排序。 (如果你有哈希列表,IMO 很好,如果你有链表就忘了)。

复杂性当然取决于你的结构(fifo/filo 列表、链表、哈希...)

【讨论】:

    【解决方案3】:

    这是排序列表的解决方案:

    class Solution:
    def removeDuplicates(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        if (len(nums) == 0):
            return 0;
        j = 0
        for i in range(len(nums)):
            if (nums[i] != nums[j]):
                j = j+1
                nums[j] = nums[i];
    
        return j + 1
    

    【讨论】:

      【解决方案4】:

      对于复合对象(如列表中的列表),下面的代码就足够了:

      def unique(a):
      i = 0
      while i < len(a):
          p = a[i+1:]
          j = 0
          while j < len(p):
              if p[j]!=a[i]:
                  j = j+1
              else:
                  p.remove(p[j])
          a = a[:i+1] + p
      
          i = i+1
      return a
      

      【讨论】:

        猜你喜欢
        • 2020-04-29
        • 2020-07-22
        • 2019-08-08
        • 1970-01-01
        • 2020-09-08
        • 2022-12-05
        • 1970-01-01
        • 2011-05-09
        • 1970-01-01
        相关资源
        最近更新 更多