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?
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]))
.
Les solutions existantes offrent toutes l'une ou l'autre des caractéristiques suivantes:
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
temp3 = [item for item in temp1 if item not in temp2]
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.
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
Installer depuis PyPi:
pip install deepdiff
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}}}
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
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
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)))
Peut être fait en utilisant l'opérateur python XOR.
set(temp1) ^ set(temp2)
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]
Essaye ça:
temp3 = set(temp1) - set(temp2)
cela pourrait être encore plus rapide que la compréhension de la liste de Mark:
list(itertools.filterfalse(set(temp2).__contains__, temp1))
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']
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
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])
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?
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
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']
_
version simple ligne de arulmr solution
def diff(listA, listB):
return set(listA) - set(listB) | set(listA) -set(listB)
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"
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)))
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'])
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
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.