如何删除Python中巨大的对象列表的重复项

时间:2016-08-19 07:38:23

标签: python python-3.x

我有很多重复的对象列表(我在谈论数千个列表,每个列表包含数千个对象,占用大约1000万个单独的对象(已经没有重复)。

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

当然,我可以浏览列表并与已经发布很多次的重复数据删除算法进行比较,但我猜这会让我永远。

我以为我可以使用精心设计的__hash__方法创建一个对象并使用list(set(obj))删除它们,但首先:我不知道这是否有用,第二:我还是会必须循环列表以将元素转换为新对象。

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

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

编辑2 :我很抱歉不清楚,我会改写。

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

因此,当我从db检索数据时,我有大约2000个JSON数组。应用json.loads(arr)(通过文档)我得到2000个对象列表(dicts)。每个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万个)。 我遇到的性能问题是因为每个字典都需要进行某种数据格式化(例如转换日期)和&#39; wrap&#39;它在数据库插入的模型对象中: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。最后花了1分钟正确格式化所有内容(150万个唯一条目,225k重复项)和2分钟将所有内容插入到数据库中。

谢谢大家!

4 个答案:

答案 0 :(得分:1)

我建议有一个排序列表(如果可能的话),这样你就可以更精确地想要比较项目(就像我的意思是一个词典)。哈希(或非哈希)列表可以实现该目标。

如果您有能力管理&#34;添加和删除&#34;从你的名单中,它更好!每次添加/删除时对新项目进行排序。 (IMO很好,如果你有哈希列表,忘记你有链表)。

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

答案 1 :(得分:1)

以下是排序列表的解决方案:

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

答案 2 :(得分:0)

(可散列物品)的快速而非订单保留解决方案是

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

完成修改

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

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)上花了8s用于2000 dicts

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

答案 3 :(得分:0)

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

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