Python, différence de liste de calcul

195

En Python, quelle est la meilleure façon de calculer la différence entre deux listes?

exemple

A = [1,2,3,4]
B = [2,5]

A - B = [1,3,4]
B - A = [5]
Mike
la source

Réponses:

206

Utilisez setsi vous ne vous souciez pas de la commande ou de la répétition des articles. Utilisez des listes de compréhension si vous le faites:

>>> def diff(first, second):
        second = set(second)
        return [item for item in first if item not in second]

>>> diff(A, B)
[1, 3, 4]
>>> diff(B, A)
[5]
>>> 
Roman Bodnarchuk
la source
31
Envisagez d'utiliser set(b)pour vous assurer que l'algorithme est O (nlogn) au lieu de Theta (n ^ 2)
Neil G
8
@Pencilcheck - pas si vous vous souciez de la commande ou de la duplication dans A. Appliquer setà B est inoffensif, mais l'appliquer à Aet utiliser le résultat au lieu de l'original Ane l'est pas.
Mark Reed
1
@NeilG Considérez-vous le temps consacré à la construction de l'ensemble? Dans mon cas (les deux listes ont environ 10M de chaînes), le temps de construire deux ensembles et de les soustraire est considérablement plus important que de créer un ensemble et d'itérer sur la liste.
dimril
@dimril si c'est ce que vous voulez faire, vous devriez peut-être implémenter quelque chose de plus sophistiqué. Vous pouvez par exemple trier les deux listes O (n log n + m log m) puis parcourir la deuxième liste mais utiliser la recherche binaire pour trouver les éléments de la première liste. Cela reviendrait aux opérations O (n log n + m log m + m log n) (au lieu des opérations O (n * m)), ce qui ne semble pas trop mal. Assurez-vous simplement de vérifier les voisins pour éliminer également les doublons dans vos implémentations de recherche binaire. Il pourrait même y avoir un package qui implémente déjà cela, mais je n'ai pas vérifié.
jaaq
366

Si la commande n'a pas d'importance, vous pouvez simplement calculer la différence définie:

>>> set([1,2,3,4]) - set([2,5])
set([1, 4, 3])
>>> set([2,5]) - set([1,2,3,4])
set([5])
phihag
la source
9
C'est de loin la meilleure solution. Le cas de test sur des listes de ~ 6000 chaînes chacune a montré que cette méthode était presque 100 fois plus rapide que les compréhensions de liste.
perrygeo
15
Dépend de l'application: si la préservation de l'ordre ou de la duplication est importante, Roman Bodnarchuk peut avoir une meilleure approche. Pour la vitesse et le comportement purement défini, celui-ci semble meilleur.
Bryan P
7
Si vous avez plusieurs éléments égaux dans la liste, cette solution ne fonctionnera pas.
karantan
Bien mieux que la compréhension des listes.
Dawei
4
Cette solution semble si évidente mais elle est incorrecte. Je suis désolé. Bien sûr, nous voulons dire qu'une liste peut avoir des éléments égaux répétés. Sinon, nous demandons la différence entre les ensembles, pas la différence de liste.
sergzach
67

Vous pouvez faire un

list(set(A)-set(B))

et

list(set(B)-set(A))
Senthil Kumaran
la source
7
Mais si A = [1,1,1] et B = [0] alors cela renvoie [1]
Mark Bell
1
@Mark Bell: C'est parce qu'un ensemble est une liste distincte. (supprime les doublons)
nuageux
1
@cloudy Alors cela ne répond pas à la question.
samm82
@ samm82 si A = [1,1,1] que set (A) est [1] car set est une liste distincte et supprime les doublons. C'est pourquoi, si A = [1,1,1] et B = [0], il renvoie [1].
nuageux
29

Bon mot:

diff = lambda l1,l2: [x for x in l1 if x not in l2]
diff(A,B)
diff(B,A)

Ou:

diff = lambda l1,l2: filter(lambda x: x not in l2, l1)
diff(A,B)
diff(B,A)
Artsiom Rudzenka
la source
14

Les exemples ci-dessus ont banalisé le problème du calcul des différences. En supposant que le tri ou la déduplication facilite certainement le calcul de la différence, mais si votre comparaison ne peut pas se permettre ces hypothèses, vous aurez besoin d'une implémentation non triviale d'un algorithme diff. Voir difflib dans la bibliothèque standard de python.

from difflib import SequenceMatcher 

squeeze=SequenceMatcher( None, A, B )

print "A - B = [%s]"%( reduce( lambda p,q: p+q, 
                               map( lambda t: squeeze.a[t[1]:t[2]], 
                                    filter(lambda x:x[0]!='equal', 
                                           squeeze.get_opcodes() ) ) ) )

A - B = [[1, 3, 4]]

Kevin
la source
1
vous obtenez +1 pour difflib, que je n'avais jamais vu auparavant. néanmoins, je ne suis pas d'accord que les réponses ci-dessus banalisent le problème comme indiqué .
rbp
Merci d'utiliser difflib - je cherchais une solution en utilisant la bibliothèque standard. Cependant, cela ne fonctionne pas dans Python 3, comme cela printest passé d'une commande à une fonction, et reduce, filteret mapont été déclarés non pythoniques. (Et je pense que Guido a peut-être raison - je ne comprends pas reducenon plus.)
Post169
Pas un gros changement pour le faire fonctionner pour py3. J'ai lu le débat sur le filtre, la carte, la réduction et je suis d'accord avec le choix de pousser la réduction et l'alternance du filtre dans functools. La nature mixte fonctionnelle, OO et procédurale du python a toujours été, l'OMI, l'une de ses forces.
Kevin
14

Python 2.7.3 (par défaut, 27 février 2014, 19:58:35) - IPython 1.1.0 - timeit: (github gist)

def diff(a, b):
  b = set(b)
  return [aa for aa in a if aa not in b]

def set_diff(a, b):
  return list(set(a) - set(b))

diff_lamb_hension = lambda l1,l2: [x for x in l1 if x not in l2]

diff_lamb_filter = lambda l1,l2: filter(lambda x: x not in l2, l1)

from difflib import SequenceMatcher
def squeezer(a, b):
  squeeze = SequenceMatcher(None, a, b)
  return reduce(lambda p,q: p+q, map(
    lambda t: squeeze.a[t[1]:t[2]],
      filter(lambda x:x[0]!='equal',
        squeeze.get_opcodes())))

Résultats:

# Small
a = range(10)
b = range(10/2)

timeit[diff(a, b)]
100000 loops, best of 3: 1.97 µs per loop

timeit[set_diff(a, b)]
100000 loops, best of 3: 2.71 µs per loop

timeit[diff_lamb_hension(a, b)]
100000 loops, best of 3: 2.1 µs per loop

timeit[diff_lamb_filter(a, b)]
100000 loops, best of 3: 3.58 µs per loop

timeit[squeezer(a, b)]
10000 loops, best of 3: 36 µs per loop

# Medium
a = range(10**4)
b = range(10**4/2)

timeit[diff(a, b)]
1000 loops, best of 3: 1.17 ms per loop

timeit[set_diff(a, b)]
1000 loops, best of 3: 1.27 ms per loop

timeit[diff_lamb_hension(a, b)]
1 loops, best of 3: 736 ms per loop

timeit[diff_lamb_filter(a, b)]
1 loops, best of 3: 732 ms per loop

timeit[squeezer(a, b)]
100 loops, best of 3: 12.8 ms per loop

# Big
a = xrange(10**7)
b = xrange(10**7/2)

timeit[diff(a, b)]
1 loops, best of 3: 1.74 s per loop

timeit[set_diff(a, b)]
1 loops, best of 3: 2.57 s per loop

timeit[diff_lamb_filter(a, b)]
# too long to wait for

timeit[diff_lamb_filter(a, b)]
# too long to wait for

timeit[diff_lamb_filter(a, b)]
# TypeError: sequence index must be integer, not 'slice'

@ rom-bodnarchuk list comprehensions function def diff (a, b) semble être plus rapide.

Moreno
la source
9
A = [1,2,3,4]
B = [2,5]

#A - B
x = list(set(A) - set(B))
#B - A 
y = list(set(B) - set(A))

print x
print y 
Saksham Varma
la source
8

Vous voudriez utiliser un setau lieu d'un list.

Le canard communiste
la source
5

Dans le cas où vous souhaitez que la différence rentre profondément dans les éléments de votre liste, j'ai écrit un package pour python: https://github.com/erasmose/deepdiff

Installation

Installer depuis PyPi:

pip install deepdiff

Si vous êtes Python3, vous devez également installer:

pip install future six

Exemple d'utilisation

>>> from deepdiff import DeepDiff
>>> from pprint import pprint
>>> from __future__ import print_function

Le même objet revient vide

>>> t1 = {1:1, 2:2, 3:3}
>>> t2 = t1
>>> ddiff = DeepDiff(t1, t2)
>>> print (ddiff.changes)
    {}

Le type d'un article a changé

>>> t1 = {1:1, 2:2, 3:3}
>>> t2 = {1:1, 2:"2", 3:3}
>>> ddiff = DeepDiff(t1, t2)
>>> print (ddiff.changes)
    {'type_changes': ["root[2]: 2=<type 'int'> vs. 2=<type 'str'>"]}

La valeur d'un article a changé

>>> t1 = {1:1, 2:2, 3:3}
>>> t2 = {1:1, 2:4, 3:3}
>>> ddiff = DeepDiff(t1, t2)
>>> print (ddiff.changes)
    {'values_changed': ['root[2]: 2 ====>> 4']}

Élément ajouté et / ou supprimé

>>> t1 = {1:1, 2:2, 3:3, 4:4}
>>> t2 = {1:1, 2:4, 3:3, 5:5, 6:6}
>>> ddiff = DeepDiff(t1, t2)
>>> pprint (ddiff.changes)
    {'dic_item_added': ['root[5, 6]'],
     'dic_item_removed': ['root[4]'],
     'values_changed': ['root[2]: 2 ====>> 4']}

Différence de chaîne

>>> t1 = {1:1, 2:2, 3:3, 4:{"a":"hello", "b":"world"}}
>>> t2 = {1:1, 2:4, 3:3, 4:{"a":"hello", "b":"world!"}}
>>> ddiff = DeepDiff(t1, t2)
>>> pprint (ddiff.changes, indent = 2)
    { 'values_changed': [ 'root[2]: 2 ====>> 4',
                          "root[4]['b']:\n--- \n+++ \n@@ -1 +1 @@\n-world\n+world!"]}
>>>
>>> print (ddiff.changes['values_changed'][1])
    root[4]['b']:
    --- 
    +++ 
    @@ -1 +1 @@
    -world
    +world!

Différence de chaîne 2

>>> t1 = {1:1, 2:2, 3:3, 4:{"a":"hello", "b":"world!\nGoodbye!\n1\n2\nEnd"}}
>>> t2 = {1:1, 2:2, 3:3, 4:{"a":"hello", "b":"world\n1\n2\nEnd"}}
>>> ddiff = DeepDiff(t1, t2)
>>> pprint (ddiff.changes, indent = 2)
    { 'values_changed': [ "root[4]['b']:\n--- \n+++ \n@@ -1,5 +1,4 @@\n-world!\n-Goodbye!\n+world\n 1\n 2\n End"]}
>>>
>>> print (ddiff.changes['values_changed'][0])
    root[4]['b']:
    --- 
    +++ 
    @@ -1,5 +1,4 @@
    -world!
    -Goodbye!
    +world
     1
     2
     End

Changement de type

>>> t1 = {1:1, 2:2, 3:3, 4:{"a":"hello", "b":[1, 2, 3]}}
>>> t2 = {1:1, 2:2, 3:3, 4:{"a":"hello", "b":"world\n\n\nEnd"}}
>>> ddiff = DeepDiff(t1, t2)
>>> pprint (ddiff.changes, indent = 2)
    { 'type_changes': [ "root[4]['b']: [1, 2, 3]=<type 'list'> vs. world\n\n\nEnd=<type 'str'>"]}

Liste des différences

>>> t1 = {1:1, 2:2, 3:3, 4:{"a":"hello", "b":[1, 2, 3]}}
>>> t2 = {1:1, 2:2, 3:3, 4:{"a":"hello", "b":[1, 2]}}
>>> ddiff = DeepDiff(t1, t2)
>>> pprint (ddiff.changes, indent = 2)
    { 'list_removed': ["root[4]['b']: [3]"]}

Énumérez la différence 2: notez qu'elle ne tient pas compte de l'ordre

>>> # Note that it DOES NOT take order into account
... t1 = {1:1, 2:2, 3:3, 4:{"a":"hello", "b":[1, 2, 3]}}
>>> t2 = {1:1, 2:2, 3:3, 4:{"a":"hello", "b":[1, 3, 2]}}
>>> ddiff = DeepDiff(t1, t2)
>>> pprint (ddiff.changes, indent = 2)
    { }

Liste contenant le dictionnaire:

>>> t1 = {1:1, 2:2, 3:3, 4:{"a":"hello", "b":[1, 2, {1:1, 2:2}]}}
>>> t2 = {1:1, 2:2, 3:3, 4:{"a":"hello", "b":[1, 2, {1:3}]}}
>>> ddiff = DeepDiff(t1, t2)
>>> pprint (ddiff.changes, indent = 2)
    { 'dic_item_removed': ["root[4]['b'][2][2]"],
      'values_changed': ["root[4]['b'][2][1]: 1 ====>> 3"]}
Seperman
la source
5

moyen le plus simple,

utilisez set (). difference (set ())

list_a = [1,2,3]
list_b = [2,3]
print set(list_a).difference(set(list_b))

la réponse est set([1])

Mohideen bin Mohammed
la source
2

Dans le cas d'une liste de dictionnaires , la solution de compréhension de liste complète fonctionne pendant que la setsolution soulève

TypeError: unhashable type: 'dict'

Cas de test

def diff(a, b):
    return [aa for aa in a if aa not in b]

d1 = {"a":1, "b":1}
d2 = {"a":2, "b":2}
d3 = {"a":3, "b":3}

>>> diff([d1, d2, d3], [d2, d3])
[{'a': 1, 'b': 1}]
>>> diff([d1, d2, d3], [d1])
[{'a': 2, 'b': 2}, {'a': 3, 'b': 3}]
joao
la source
0

Code simple qui vous donne la différence avec plusieurs éléments si vous le souhaitez:

a=[1,2,3,3,4]
b=[2,4]
tmp = copy.deepcopy(a)
for k in b:
    if k in tmp:
        tmp.remove(k)
print(tmp)
UN M
la source
-1

Lorsque l'on regarde TimeComplexity of In-operator, dans le pire des cas, cela fonctionne avec O (n). Même pour les ensembles.

Ainsi, lorsque nous comparons deux tableaux, nous aurons une TimeComplexity de O (n) dans le meilleur des cas et O (n ^ 2) dans le pire des cas.

Une solution alternative (mais malheureusement plus complexe), qui fonctionne avec O (n) dans le meilleur et le pire des cas est celle-ci:

# Compares the difference of list a and b
# uses a callback function to compare items
def diff(a, b, callback):
  a_missing_in_b = []
  ai = 0
  bi = 0

  a = sorted(a, callback)
  b = sorted(b, callback)

  while (ai < len(a)) and (bi < len(b)):

    cmp = callback(a[ai], b[bi])
    if cmp < 0:
      a_missing_in_b.append(a[ai])
      ai += 1
    elif cmp > 0:
      # Item b is missing in a
      bi += 1
    else:
      # a and b intersecting on this item
      ai += 1
      bi += 1

  # if a and b are not of same length, we need to add the remaining items
  for ai in xrange(ai, len(a)):
    a_missing_in_b.append(a[ai])


  return a_missing_in_b

par exemple

>>> a=[1,2,3]
>>> b=[2,4,6]
>>> diff(a, b, cmp)
[1, 3]
DerKnorr
la source