web-dev-qa-db-fra.com

Obtenir la différence entre deux listes

J'ai deux listes en Python, comme celles-ci:

temp1 = ['One', 'Two', 'Three', 'Four']
temp2 = ['One', 'Two']

Je dois créer une troisième liste avec des éléments de la première liste qui ne sont pas présents dans la seconde. De l'exemple je dois obtenir:

temp3 = ['Three', 'Four']

Existe-t-il des moyens rapides sans cycles et sans vérification?

696
Max Frai
In [5]: list(set(temp1) - set(temp2))
Out[5]: ['Four', 'Three']

Attention ça

In [5]: set([1, 2]) - set([2, 3])
Out[5]: set([1]) 

où vous pouvez vous attendre/vouloir que cela soit égal à set([1, 3]). Si vous voulez que set([1, 3]) soit votre réponse, vous devrez utiliser set([1, 2]).symmetric_difference(set([2, 3])).

1062
ars

Les solutions existantes offrent toutes l'une ou l'autre des caractéristiques suivantes:

  • Plus rapide que O (n * m) performances.
  • Préserver l'ordre de la liste de saisie.

Mais jusqu'à présent, aucune solution n'a les deux. Si vous voulez les deux, essayez ceci:

s = set(temp2)
temp3 = [x for x in temp1 if x not in s]

test de performance

import timeit
init = 'temp1 = list(range(100)); temp2 = [i * 2 for i in range(50)]'
print timeit.timeit('list(set(temp1) - set(temp2))', init, number = 100000)
print timeit.timeit('s = set(temp2);[x for x in temp1 if x not in s]', init, number = 100000)
print timeit.timeit('[item for item in temp1 if item not in temp2]', init, number = 100000)

Résultats:

4.34620224079 # ars' answer
4.2770634955  # This answer
30.7715615392 # matt b's answer

La méthode que j'ai présentée ainsi que la préservation de l'ordre est également (légèrement) plus rapide que la soustraction d'ensemble parce qu'elle ne nécessite pas la construction d'un ensemble inutile. La différence de performances serait plus perceptible si la première liste est considérablement plus longue que la seconde et si le hachage est coûteux. Voici un deuxième test démontrant ceci:

init = '''
temp1 = [str(i) for i in range(100000)]
temp2 = [str(i * 2) for i in range(50)]
'''

Résultats:

11.3836875916 # ars' answer
3.63890368748 # this answer (3 times faster!)
37.7445402279 # matt b's answer
438
Mark Byers
temp3 = [item for item in temp1 if item not in temp2]
64
matt b

La différence entre deux listes (par exemple, list1 et list2) peut être trouvée en utilisant la fonction simple suivante.

def diff(list1, list2):
    c = set(list1).union(set(list2))  # or c = set(list1) | set(list2)
    d = set(list1).intersection(set(list2))  # or d = set(list1) & set(list2)
    return list(c - d)

ou

def diff(list1, list2):
    return list(set(list1).symmetric_difference(set(list2)))  # or return list(set(list1) ^ set(list2))

En utilisant la fonction ci-dessus, la différence peut être trouvée en utilisant diff(temp2, temp1) ou diff(temp1, temp2). Les deux donneront le résultat ['Four', 'Three']. Vous n'avez pas à vous soucier de l'ordre de la liste ou de la liste à donner en premier.

référence de document Python

19
arulmr

Au cas où vous voudriez la différence de manière récursive, j'ai écrit un paquet pour python: https://github.com/seperman/deepdiff

Installation

Installer depuis PyPi:

pip install deepdiff

Exemple d'utilisation

Importation

>>> from deepdiff import DeepDiff
>>> from pprint import pprint
>>> from __future__ import print_function # In case running on Python 2

Le même objet retourne vide

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

Le type d'un article a changé

>>> t1 = {1:1, 2:2, 3:3}
>>> t2 = {1:1, 2:"2", 3:3}
>>> pprint(DeepDiff(t1, t2), indent=2)
{ 'type_changes': { 'root[2]': { 'newtype': <class 'str'>,
                                 'newvalue': '2',
                                 'oldtype': <class 'int'>,
                                 'oldvalue': 2}}}

La valeur d'un article a changé

>>> t1 = {1:1, 2:2, 3:3}
>>> t2 = {1:1, 2:4, 3:3}
>>> pprint(DeepDiff(t1, t2), indent=2)
{'values_changed': {'root[2]': {'newvalue': 4, 'oldvalue': 2}}}

Article 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)
{'dic_item_added': ['root[5]', 'root[6]'],
 'dic_item_removed': ['root[4]'],
 'values_changed': {'root[2]': {'newvalue': 4, 'oldvalue': 2}}}

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, indent = 2)
{ 'values_changed': { 'root[2]': {'newvalue': 4, 'oldvalue': 2},
                      "root[4]['b']": { 'newvalue': 'world!',
                                        'oldvalue': '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, indent = 2)
{ 'values_changed': { "root[4]['b']": { 'diff': '--- \n'
                                                '+++ \n'
                                                '@@ -1,5 +1,4 @@\n'
                                                '-world!\n'
                                                '-Goodbye!\n'
                                                '+world\n'
                                                ' 1\n'
                                                ' 2\n'
                                                ' End',
                                        'newvalue': 'world\n1\n2\nEnd',
                                        'oldvalue': 'world!\n'
                                                    'Goodbye!\n'
                                                    '1\n'
                                                    '2\n'
                                                    'End'}}}

>>> 
>>> print (ddiff['values_changed']["root[4]['b']"]["diff"])
--- 
+++ 
@@ -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, indent = 2)
{ 'type_changes': { "root[4]['b']": { 'newtype': <class 'str'>,
                                      'newvalue': 'world\n\n\nEnd',
                                      'oldtype': <class 'list'>,
                                      'oldvalue': [1, 2, 3]}}}

Différence de liste

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

Différence de liste 2:

>>> 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, 3]}}
>>> ddiff = DeepDiff(t1, t2)
>>> pprint (ddiff, indent = 2)
{ 'iterable_item_added': {"root[4]['b'][3]": 3},
  'values_changed': { "root[4]['b'][1]": {'newvalue': 3, 'oldvalue': 2},
                      "root[4]['b'][2]": {'newvalue': 2, 'oldvalue': 3}}}

Liste des différences en ignorant l'ordre ou les doublons: (avec les mêmes dictionnaires que ci-dessus)

>>> 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, 3]}}
>>> ddiff = DeepDiff(t1, t2, ignore_order=True)
>>> print (ddiff)
{}

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, indent = 2)
{ 'dic_item_removed': ["root[4]['b'][2][2]"],
  'values_changed': {"root[4]['b'][2][1]": {'newvalue': 3, 'oldvalue': 1}}}

Ensembles:

>>> t1 = {1, 2, 8}
>>> t2 = {1, 2, 3, 5}
>>> ddiff = DeepDiff(t1, t2)
>>> pprint (DeepDiff(t1, t2))
{'set_item_added': ['root[3]', 'root[5]'], 'set_item_removed': ['root[8]']}

Tuples nommés:

>>> from collections import namedtuple
>>> Point = namedtuple('Point', ['x', 'y'])
>>> t1 = Point(x=11, y=22)
>>> t2 = Point(x=11, y=23)
>>> pprint (DeepDiff(t1, t2))
{'values_changed': {'root.y': {'newvalue': 23, 'oldvalue': 22}}}

Objets personnalisés:

>>> class ClassA(object):
...     a = 1
...     def __init__(self, b):
...         self.b = b
... 
>>> t1 = ClassA(1)
>>> t2 = ClassA(2)
>>> 
>>> pprint(DeepDiff(t1, t2))
{'values_changed': {'root.b': {'newvalue': 2, 'oldvalue': 1}}}

Attribut d'objet ajouté:

>>> t2.c = "new attribute"
>>> pprint(DeepDiff(t1, t2))
{'attribute_added': ['root.c'],
 'values_changed': {'root.b': {'newvalue': 2, 'oldvalue': 1}}}
17
Seperman

Si vous recherchez vraiment la performance, utilisez numpy!

Voici le carnet complet sous forme de Gist sur github avec une comparaison entre list, numpy et pandas.

https://Gist.github.com/denfromufa/2821ff59b02e9482be15d27f2bbd4451

enter image description here

15
denfromufa

je vais me lancer car aucune des solutions actuelles ne donne un tuple:

temp3 = Tuple(set(temp1) - set(temp2))

alternativement:

#edited using @Mark Byers idea. If you accept this one as answer, just accept his instead.
temp3 = Tuple(x for x in temp1 if x not in set(temp2))

Comme les autres réponses non-Tuple donnant dans cette direction, il préserve l'ordre

13
aaronasterling

manière la plus simple,

use 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])

peut imprimer une liste,

print list(set(list_a).difference(set(list_b)))
13

Peut être fait en utilisant l'opérateur python XOR.

  • Cela supprimera les doublons dans chaque liste
  • Cela montrera la différence entre temp1 et temp2 et temp2 et temp1.

set(temp1) ^ set(temp2)
12
SuperNova

Je voulais quelque chose qui prenne deux listes et puisse faire ce que diff dans bash fait. Puisque cette question apparaît en premier lorsque vous recherchez "python diff two lists" et qu’elle n’est pas très spécifique, je publierai ce que j’ai proposé.

En utilisant SequenceMather à partir de difflib, vous pouvez comparer deux listes comme le fait diff. Aucune des autres réponses ne vous dira où se situe la différence, mais celle-ci le fait. Certaines réponses donnent la différence dans une seule direction. Certains réorganisent les éléments. Certains ne gèrent pas les doublons. Mais cette solution vous donne une vraie différence entre deux listes:

a = 'A quick fox jumps the lazy dog'.split()
b = 'A quick brown mouse jumps over the dog'.split()

from difflib import SequenceMatcher

for tag, i, j, k, l in SequenceMatcher(None, a, b).get_opcodes():
  if tag == 'equal': print('both have', a[i:j])
  if tag in ('delete', 'replace'): print('  1st has', a[i:j])
  if tag in ('insert', 'replace'): print('  2nd has', b[k:l])

Cela génère:

both have ['A', 'quick']
  1st has ['fox']
  2nd has ['brown', 'mouse']
both have ['jumps']
  2nd has ['over']
both have ['the']
  1st has ['lazy']
both have ['dog']

Bien entendu, si votre application repose sur les mêmes hypothèses que les autres réponses, vous en tirerez le meilleur parti. Mais si vous recherchez une vraie fonctionnalité diff, c’est la seule façon de procéder.

Par exemple, aucune des autres réponses ne pourrait traiter:

a = [1,2,3,4,5]
b = [5,4,3,2,1]

Mais celui-ci fait:

  2nd has [5, 4, 3, 2]
both have [1]
  1st has [2, 3, 4, 5]
11
arekolek

Essaye ça:

temp3 = set(temp1) - set(temp2)
10
Maciej Kucharz

cela pourrait être encore plus rapide que la compréhension de la liste de Mark:

list(itertools.filterfalse(set(temp2).__contains__, temp1))
9
Mohammed

Voici une réponse Counter pour le cas le plus simple.

C'est plus court que celui ci-dessus qui fait des différences bidirectionnelles car il ne fait que répondre exactement à la question: générer une liste de ce qui est dans la première liste mais pas de la seconde.

from collections import Counter

lst1 = ['One', 'Two', 'Three', 'Four']
lst2 = ['One', 'Two']

c1 = Counter(lst1)
c2 = Counter(lst2)
diff = list((c1 - c2).elements())

Alternativement, en fonction de vos préférences de lisibilité, il s’agit d’un one-liner décent:

diff = list((Counter(lst1) - Counter(lst2)).elements())

Sortie:

['Three', 'Four']

Notez que vous pouvez supprimer l'appel list(...) si vous ne faites que l'itérer.

Comme cette solution utilise des compteurs, elle gère correctement les quantités par rapport aux nombreuses réponses basées sur des ensembles. Par exemple sur cette entrée:

lst1 = ['One', 'Two', 'Two', 'Two', 'Three', 'Three', 'Four']
lst2 = ['One', 'Two']

La sortie est:

['Two', 'Two', 'Three', 'Three', 'Four']
6
Taylor Edmiston

Vous pouvez utiliser une méthode naïve si les éléments de la liste de diff sont triés et ensembles.

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

print list1[len(list2):]

ou avec des méthodes de set natives:

subset=set(list1).difference(list2)

print subset

import timeit
init = 'temp1 = list(range(100)); temp2 = [i * 2 for i in range(50)]'
print "Naive solution: ", timeit.timeit('temp1[len(temp2):]', init, number = 100000)
print "Native set solution: ", timeit.timeit('set(temp1).difference(temp2)', init, number = 100000)

Solution naïve: 0.0787101593292

Solution d'ensemble natif: 0.998837615564

5
soundcorner

Je suis un peu trop tard dans le jeu pour cela, mais vous pouvez comparer les performances de certains des codes mentionnés ci-dessus avec ceci, deux des candidats les plus rapides sont,

list(set(x).symmetric_difference(set(y)))
list(set(x) ^ set(y))

Je m'excuse pour le niveau élémentaire de codage.

import time
import random
from itertools import filterfalse

# 1 - performance (time taken)
# 2 - correctness (answer - 1,4,5,6)
# set performance
performance = 1
numberoftests = 7

def answer(x,y,z):
    if z == 0:
        start = time.clock()
        lists = (str(list(set(x)-set(y))+list(set(y)-set(y))))
        times = ("1 = " + str(time.clock() - start))
        return (lists,times)

    Elif z == 1:
        start = time.clock()
        lists = (str(list(set(x).symmetric_difference(set(y)))))
        times = ("2 = " + str(time.clock() - start))
        return (lists,times)

    Elif z == 2:
        start = time.clock()
        lists = (str(list(set(x) ^ set(y))))
        times = ("3 = " + str(time.clock() - start))
        return (lists,times)

    Elif z == 3:
        start = time.clock()
        lists = (filterfalse(set(y).__contains__, x))
        times = ("4 = " + str(time.clock() - start))
        return (lists,times)

    Elif z == 4:
        start = time.clock()
        lists = (Tuple(set(x) - set(y)))
        times = ("5 = " + str(time.clock() - start))
        return (lists,times)

    Elif z == 5:
        start = time.clock()
        lists = ([tt for tt in x if tt not in y])
        times = ("6 = " + str(time.clock() - start))
        return (lists,times)

    else:    
        start = time.clock()
        Xarray = [iDa for iDa in x if iDa not in y]
        Yarray = [iDb for iDb in y if iDb not in x]
        lists = (str(Xarray + Yarray))
        times = ("7 = " + str(time.clock() - start))
        return (lists,times)

n = numberoftests

if performance == 2:
    a = [1,2,3,4,5]
    b = [3,2,6]
    for c in range(0,n):
        d = answer(a,b,c)
        print(d[0])

Elif performance == 1:
    for tests in range(0,10):
        print("Test Number" + str(tests + 1))
        a = random.sample(range(1, 900000), 9999)
        b = random.sample(range(1, 900000), 9999)
        for c in range(0,n):
            #if c not in (1,4,5,6):
            d = answer(a,b,c)
            print(d[1])
5
Alex Jacob

Si vous rencontrez TypeError: unhashable type: 'list', vous devez transformer des listes ou des ensembles en nuplets, par exemple.

set(map(Tuple, list_of_lists1)).symmetric_difference(set(map(Tuple, list_of_lists2)))

Voir aussi Comment comparer une liste de listes/ensembles en python?

4
guaka

C'est une autre solution:

def diff(a, b):
    xa = [i for i in set(a) if i not in b]
    xb = [i for i in set(b) if i not in a]
    return xa + xb
4
manhgd

Voici quelques méthodes simples préservant l'ordre permettant de différencier deux listes de chaînes.

Code

Une approche inhabituelle utilisant pathlib :

_import pathlib


temp1 = ["One", "Two", "Three", "Four"]
temp2 = ["One", "Two"]

p = pathlib.Path(*temp1)
r = p.relative_to(*temp2)
list(r.parts)
# ['Three', 'Four']
_

Cela suppose que les deux listes contiennent des chaînes avec des débuts équivalents. Voir le docs pour plus de détails. Notez que ce n'est pas particulièrement rapide par rapport aux opérations définies.


Une implémentation simple utilisant itertools.Zip_longest :

_import itertools as it


[x for x, y in it.Zip_longest(temp1, temp2) if x != y]
# ['Three', 'Four']
_
4
pylang

version simple ligne de arulmr solution

def diff(listA, listB):
    return set(listA) - set(listB) | set(listA) -set(listB)
3
sreemanth pulagam

si vous voulez quelque chose qui ressemble plus à un changeset ... pourrait utiliser Counter

from collections import Counter

def diff(a, b):
  """ more verbose than needs to be, for clarity """
  ca, cb = Counter(a), Counter(b)
  to_add = cb - ca
  to_remove = ca - cb
  changes = Counter(to_add)
  changes.subtract(to_remove)
  return changes

lista = ['one', 'three', 'four', 'four', 'one']
listb = ['one', 'two', 'three']

In [127]: diff(lista, listb)
Out[127]: Counter({'two': 1, 'one': -1, 'four': -2})
# in order to go from lista to list b, you need to add a "two", remove a "one", and remove two "four"s

In [128]: diff(listb, lista)
Out[128]: Counter({'four': 2, 'one': 1, 'two': -1})
# in order to go from listb to lista, you must add two "four"s, add a "one", and remove a "two"
3
Nick Franceschina

Cela peut être résolu avec une seule ligne. Deux listes (temp1 et temp2) sont renvoyées à la question et renvoient leur différence dans une troisième liste (temp3).

temp3 = list(set(temp1).difference(set(temp2)))
2
fgaim

Nous pouvons calculer l'intersection moins l'union des listes:

temp1 = ['One', 'Two', 'Three', 'Four']
temp2 = ['One', 'Two', 'Five']

set(temp1+temp2)-(set(temp1)&set(temp2))

Out: set(['Four', 'Five', 'Three']) 
2
Mohammad Etemaddar

Disons que nous avons deux listes

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

nous pouvons voir dans les deux listes ci-dessus que les éléments 1, 3, 5 existent dans list2 et que les éléments 7, 9 ne le sont pas. D'autre part, les éléments 1, 3, 5 existent dans list1 et les éléments 2, 4 ne le sont pas.

Quelle est la meilleure solution pour renvoyer une nouvelle liste contenant les éléments 7, 9 et 2, 4?

Toutes les réponses ci-dessus trouvent la solution, quelle est maintenant la plus optimale?

def difference(list1, list2):
    new_list = []
    for i in list1:
        if i not in list2:
            new_list.append(i)

    for j in list2:
        if j not in list1:
            new_list.append(j)
    return new_list

versus

def sym_diff(list1, list2):
    return list(set(list1).symmetric_difference(set(list2)))

En utilisant timeit, nous pouvons voir les résultats

t1 = timeit.Timer("difference(list1, list2)", "from __main__ import difference, 
list1, list2")
t2 = timeit.Timer("sym_diff(list1, list2)", "from __main__ import sym_diff, 
list1, list2")

print('Using two for loops', t1.timeit(number=100000), 'Milliseconds')
print('Using two for loops', t2.timeit(number=100000), 'Milliseconds')

résultats

[7, 9, 2, 4]
Using two for loops 0.11572412995155901 Milliseconds
Using symmetric_difference 0.11285737506113946 Milliseconds

Process finished with exit code 0
2
Carson

Voici un moyen simple de distinguer deux listes (quel que soit le contenu), vous pouvez obtenir le résultat comme indiqué ci-dessous:

>>> from sets import Set
>>>
>>> l1 = ['xvda', False, 'xvdbb', 12, 'xvdbc']
>>> l2 = ['xvda', 'xvdbb', 'xvdbc', 'xvdbd', None]
>>>
>>> Set(l1).symmetric_difference(Set(l2))
Set([False, 'xvdbd', None, 12])

J'espère que cela vous sera utile.

0
S.K. Venkat