3

I checked this comparing lists, Only one answer is relative to what I am trying to do. I have to lists with some similiar elements, I want to get the non -matching elements.

len(h) = 1973182  #h[0] = 'B00006J8F4F2', y[0] = 'B0075Y2X2GO6'
len(y) = 656890

I am doing

new_list = [i for i in h if i not in y],however this takes about 13 minutes to do, Is there a faster way of doing this?

In refer to "duplicate" question, Finding elements not in a list, I use the same code, What I am looking for is a faster way of doing it.

programmerwiz32
  • 529
  • 1
  • 5
  • 20

4 Answers4

2

You can use sets to more efficiently find the difference between both lists. If you need to keep the order in the original list you can use sorted with a key.

We want to sort the elements in the set according to their appearance in the original list, so one way is to build a lookup dictionary. We can use enumerate for that. Then we only need to lookup on the dictionary as a key function:

d = {j:i for i,j in enumerate(h)}
new_list  = sorted(list((set(h) - set(y))), key = lambda x: d[x])

Let's try with a simple example:

y = range(5)
h = range(7)
d = {j:i for i,j in enumerate(h)}
sorted(list((set(h) - set(y))), key = lambda x: d[x])
# [5, 6]

Timings -

import random
y = random.sample(range(1, 10001), 10000)
h = random.sample(range(1, 20001), 10000)

%timeit [i for i in h if i not in y]
# 1.28 s ± 37.8 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

def using_sets(a,b):
    d = {j:i for i,j in enumerate(a)}
    sorted(list((set(a) - set(b))), key = lambda x: d[x])

%timeit using_sets(h,y)
# 6.16 ms ± 373 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

So there's a clear improvement, with the proposed approach performing up to 200 times faster.

yatu
  • 86,083
  • 12
  • 84
  • 139
1

The answer you linked to suggests using sets, because they use hashes to look thing up quickly. With lists, and in, like

new_list = [i for i in h if i not in y]

the whole of list y needs checking each time for each i in h.

You could use sets, but as has been pointed out need to be careful with duplicates getting lost.

You could use a Counter:

from collections import Counter

the with two lists, say

l1 = [1,1,2,3,4]
l2 = [3,3,4,5,6]

for examples' sake, can use fed into a Counter each

>>> Counter(l1)
Counter({1: 2, 2: 1, 3: 1, 4: 1})
>>> Counter(l2)
Counter({3: 2, 4: 1, 5: 1, 6: 1})

This just walks each list once. Subtracting them gives what's in the first but not the second:

>>> Counter(l1)-Counter(l2)
Counter({1: 2, 2: 1})

The elements tell you what you want

>>> diff = Counter(l1)-Counter(l2)
>>> list(diff.elements())
[1, 1, 2]
doctorlove
  • 18,872
  • 2
  • 46
  • 62
0

using programmatically and keep order and handle duplicate in list1

def function(list1, list2):
    dic2={}   
    for i in list2:
        try:
            if i in dic2.keys():
                pass
        except KeyError:
            dic2[i]=1           

    result =[]
    for i in list1:
        try:
            if i in dic2.keys():
                pass
        except:
            result.append(i)
    return result



list1=[1,2,2,3]
list2=[3,4,5]

solution = function(list1,list2)
print(solution)

output

[1, 2, 2]

using @yatu h,y list, here is time result

%timeit function(h,y)
2.75 ms ± 22.2 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
sahasrara62
  • 10,069
  • 3
  • 29
  • 44
  • how is this faster than `new_list = sorted((set(h) - set(y)), key = h.index)` ? – programmerwiz32 May 31 '19 at 15:06
  • @programmerwiz32 because i didn't sort the object which will took O(nlogn) time and then do calculation to preserve index, here i just hashed value of list2 in dictionary so O(1) time to access it , and O(n) TIME to go through list1, so complexity becomes `O(n)`, – sahasrara62 May 31 '19 at 15:14
  • @programmerwiz32 see current one solution , check speed and if you find it good one than accept and upvote – sahasrara62 May 31 '19 at 15:56
0

You can use the Counter class from collections:

list1 = [1,1,2,3,4]
list2 = [3,3,4,5,6]

from collections import Counter
result = list((Counter(list1)-Counter(list2)).elements())

# [1, 1, 2]

Or, if you want mutual exclusion:

count1 = Counter(list1)
count2 = Counter(list2)
r = list((count1-count2+(count2-count1)).elements()) 

# [1, 1, 2, 3, 5, 6]
Alain T.
  • 40,517
  • 4
  • 31
  • 51