Python знайде елементи в одному списку, яких немає в іншому [дублікат]


136

Мені потрібно порівняти два списки, щоб створити новий список конкретних елементів, знайдених в одному списку, а не в іншому. Наприклад:

main_list=[]
list_1=["a", "b", "c", "d", "e"]
list_2=["a", "f", "c", "m"] 

Я хочу проглянути список_1 та додати до головного списку всі елементи зі списку_2, які не знайдені в списку_1.

Результатом має бути:

main_list=["f", "m"]

Як я можу це зробити за допомогою пітона?


2
Шукаєте елементи, list_2які ніде не з’являються, list_1або елементи list_2, які не мають того самого індексу list_1?
Патрік Хог

Відповіді:


98

TL; DR:
РІШЕННЯ (1)

import numpy as np
main_list = np.setdiff1d(list_2,list_1)
# yields the elements in `list_2` that are NOT in `list_1`

РІШЕННЯ (2) Ви хочете відсортований список

def setdiff_sorted(array1,array2,assume_unique=False):
    ans = np.setdiff1d(array1,array2,assume_unique).tolist()
    if assume_unique:
        return sorted(ans)
    return ans
main_list = setdiff_sorted(list_2,list_1)




ПОЯСНЕННЯ:
(1) Ви можете використовувати NumPy - х setdiff1d( array1, array2, assume_unique= False).

assume_uniqueзапитує користувача, ЯКЩО масиви НАДІЛЬНО УНІКАЛЬНІ.
Якщо False, то спочатку визначаються унікальні елементи.
Якщо Trueфункція припускає, що елементи вже унікальні, І функція пропускатиме визначення унікальних елементів.

Це дає унікальні значення в array1тому , що НЕ в array2. assume_uniqueце Falseза замовчуванням.

Якщо ви переймаєтесь унікальними елементами (на основі відповіді Chinny84 ), просто використовуйте (де assume_unique=False=> значення за замовчуванням):

import numpy as np
list_1 = ["a", "b", "c", "d", "e"]
list_2 = ["a", "f", "c", "m"] 
main_list = np.setdiff1d(list_2,list_1)
# yields the elements in `list_2` that are NOT in `list_1`


(2) Для тих, хто хоче сортувати відповіді, я зробив власну функцію:

import numpy as np
def setdiff_sorted(array1,array2,assume_unique=False):
    ans = np.setdiff1d(array1,array2,assume_unique).tolist()
    if assume_unique:
        return sorted(ans)
    return ans

Щоб отримати відповідь, запустіть:

main_list = setdiff_sorted(list_2,list_1)

Сторонні примітки:
(a) Рішення 2 (спеціальна функція setdiff_sorted) повертає список (порівняно з масивом у рішенні 1).

(b) Якщо ви не впевнені, що елементи унікальні, просто використовуйте налаштування NumPy за замовчуванням setdiff1dв обох рішеннях A і B. Що може бути прикладом ускладнення? Див. Примітку (с).

(c) Речі будуть різними, якщо жоден із двох списків не є унікальним.
Скажімо list_2не унікальна: list2 = ["a", "f", "c", "m", "m"]. Зберігати list1як: list_1 = ["a", "b", "c", "d", "e"]
Установка значення за замовчуванням для assume_uniqueвиходів ["f", "m"](в обох розчинах). ЗАРАЗ, якщо встановити assume_unique=True, дають обидва рішення ["f", "m", "m"]. Чому? Це пояснюється тим, що користувач ВІДПОВІДАв, що елементи унікальні). Отже, НАЙКРАЩЕ ЗБЕРІГАТИassume_uniqueдо значення за замовчуванням. Зауважте, що обидві відповіді сортовані.


Якщо ваші списки вже замовлені, це також поверне замовлений список. Нативне рішення перетворення на набори, а потім отримання різниці (рішення показано нижче) повертає не упорядкований список, що може ускладнити візуальне вивчення результатів.
Подвоєння

1
Привіт, @Doubledown! Ваша стурбованість була вирішена в редагованій публікації. Сподіваюся, це допомагає!
jcoderepo

183

Ви можете використовувати набори:

main_list = list(set(list_2) - set(list_1))

Вихід:

>>> list_1=["a", "b", "c", "d", "e"]
>>> list_2=["a", "f", "c", "m"]
>>> set(list_2) - set(list_1)
set(['m', 'f'])
>>> list(set(list_2) - set(list_1))
['m', 'f']

За коментарем @JonClements, ось охайніша версія:

>>> list_1=["a", "b", "c", "d", "e"]
>>> list_2=["a", "f", "c", "m"]
>>> list(set(list_2).difference(list_1))
['m', 'f']

2
Це добре, якщо ми дбаємо лише про uniqueелементи, але що, якщо у нас їх декілька, m'sнаприклад, це не сприймає.
Chinny84

Це правда. Я припускав, що плакат шукає унікальні елементи. Я думаю, це залежить від того, що він означає під "конкретним".
nrlakin

Справді, пс я не проголосував вашу відповідь, особливо на незрозуміле оригінальне запитання.
Chinny84

13
Ви можете написати це так, щоб list(set(list_2).difference(list_1))уникнути явного setперетворення ...
Джон Клементс

Не хвилюйтесь! Дякуємо @leaf за допомогу з форматування.
nrlakin

61

Не впевнені, чому вищезгадані пояснення настільки складні, коли у вас є наявні методи:

main_list = list(set(list_2)-set(list_1))

6
Причиною може бути збереження порядку
Кіт

57

Використовуйте таке розуміння списку :

main_list = [item for item in list_2 if item not in list_1]

Вихід:

>>> list_1 = ["a", "b", "c", "d", "e"]
>>> list_2 = ["a", "f", "c", "m"] 
>>> 
>>> main_list = [item for item in list_2 if item not in list_1]
>>> main_list
['f', 'm']

Редагувати:

Як зазначено в коментарях нижче, з великими списками, вищезгадане не є ідеальним рішенням. У такому випадку кращим варіантом буде перетворення list_1на setперше:

set_1 = set(list_1)  # this reduces the lookup time from O(n) to O(1)
main_list = [item for item in list_2 if item not in set_1]

3
Примітка. Для великих розмірів list_1потрібно попередньо конвертувати в set/ frozenset, наприклад set_1 = frozenset(list_1), потім main_list = [item for item in list_2 if item not in set_1], скоротивши час перевірки з O(n)кожного елемента на (приблизно) O(1).
ShadowRanger

@ettanany Будь ласка, будьте обережні, якщо ви спробуєте рішення, як розміщено в ettanany Я спробував рішення Ettanany як є, і це дійсно дуже повільно для більшого списку. Чи можете ви оновити відповідь, щоб включити пропозицію тінейджера?
Подвоєння

Чи можливо отримати індекс, а не рядок?
JareBear

@JareBear Ви можете використовувати enumerate()для цього:[index for (index, item) in enumerate(list_2) if item not in list_1]
ettanany

@ ettanany дуже дякую !! Я реалізую це якнайшвидше, я це зробив. Але ваш код виглядає набагато чистіше.
JareBear

5

Якщо ви хочете однолінійне рішення (ігноруючи імпорт), яке вимагає O(max(n, m))роботи лише для введення довжини, nа mне для O(n * m)роботи, ви можете зробити це за допомогою itertoolsмодуля :

from itertools import filterfalse

main_list = list(filterfalse(set(list_1).__contains__, list_2))

Це використовує переваги функціональних функцій, які приймають функцію зворотного виклику під час побудови, дозволяючи їй створювати зворотний виклик один раз і використовувати його для кожного елемента, не потребуючи його зберігання десь (тому що filterfalse зберігає його всередині); список розумінь та генераторних виразів може це зробити, але це некрасиво. †

Це отримує ті ж результати в одному рядку, що і:

main_list = [x for x in list_2 if x not in list_1]

зі швидкістю:

set_1 = set(list_1)
main_list = [x for x in list_2 if x not in set_1]

Звичайно, якщо порівняння мають бути позиційними, так:

list_1 = [1, 2, 3]
list_2 = [2, 3, 4]

повинен виробляти:

main_list = [2, 3, 4]

(оскільки значення у list_2має збіг з тим самим індексом list_1), ви обов'язково повинні відповідати відповіді Патріка , що не передбачає тимчасових lists або sets (навіть якщо sets є приблизно O(1), вони мають більш високий "постійний" коефіцієнт на перевірку, ніж прості перевірки рівності ) і передбачає O(min(n, m))роботу, менше, ніж будь-яка інша відповідь, і якщо ваша проблема є чутливою до позиції, це єдино правильна рішення, коли відповідні елементи з’являються при невідповідних зміщеннях.

†: Спосіб зробити те ж саме із розумінням списку, як однолінійний, - це зловживання вкладеним циклом для створення та кешування значень (ів) у "зовнішньому" циклі, наприклад:

main_list = [x for set_1 in (set(list_1),) for x in list_2 if x not in set_1]

що також надає незначну перевагу продуктивності на Python 3 (тому що зараз set_1локально зафіксовано код розуміння, а не шукає з вкладеної області для кожної перевірки; на Python 2 це не має значення, оскільки Python 2 не використовує закриття для перелічіть розуміння; вони діють у тому ж обсязі, в якому вони використовуються)


4
main_list=[]
list_1=["a", "b", "c", "d", "e"]
list_2=["a", "f", "c", "m"]

for i in list_2:
    if i not in list_1:
        main_list.append(i)

print(main_list)

вихід:

['f', 'm']

Як і еквівалентне рішення , що базується на розумінні списку , воно буде повільним, якщо list_1воно велике, і list_2має нетривіальний розмір, оскільки воно включає len(list_2) O(n)сканування list_1, виготовлення його O(n * m)(де nі mдовжини list_2і list_1відповідно). Якщо ви перетворюєтесь list_1на фронтальний set/ frozensetвперед, перевірки на вміст можна виконати O(1), зробивши загальну роботу O(n)по довжині list_2(технічно O(max(n, m)), оскільки ви робите O(m)роботу з виготовлення set).
ShadowRanger

1

Я хотів би zipсписки разом порівнювати їх по елементам.

main_list = [b for a, b in zip(list1, list2) if a!= b]

Якщо ОП хоче порівнювати елемент за елементами (незрозуміло, приклад міг би піти в будь-якому випадку), це набагато ефективніше, ніж інші відповіді, оскільки це єдиний дешевий перехід над обома lists з новим новим listбудується, без додаткових тимчасових періодів , жодних дорогих стримуючих перевірок тощо
ShadowRanger

1
@ShadowRanger це спрацювало б лише за стихійною різницею, що є ключовим моментом
префект форд

@fordprefect: Так. Моя власна відповідь охоплює незалежні від позиції відмінності.
ShadowRanger

1

Я використав два методи, і я знайшов один метод кориснішим за інший. Ось моя відповідь:

Мої вхідні дані:

crkmod_mpp = ['M13','M18','M19','M24']
testmod_mpp = ['M13','M14','M15','M16','M17','M18','M19','M20','M21','M22','M23','M24']

Метод 1: np.setdiff1dМені подобається такий підхід перед іншими, оскільки він зберігає свою позицію

test= list(np.setdiff1d(testmod_mpp,crkmod_mpp))
print(test)
['M15', 'M16', 'M22', 'M23', 'M20', 'M14', 'M17', 'M21']

Метод 2: Хоча він дає таку ж відповідь, як у Методі1, але порушує порядок

test = list(set(testmod_mpp).difference(set(crkmod_mpp)))
print(test)
['POA23', 'POA15', 'POA17', 'POA16', 'POA22', 'POA18', 'POA24', 'POA21']

Метод1 ідеально np.setdiff1dвідповідає моїм вимогам. Ця відповідь для інформації.


0

Якщо кількість випадків слід враховувати, вам, ймовірно, потрібно використовувати щось на кшталт collections.Counter:

list_1=["a", "b", "c", "d", "e"]
list_2=["a", "f", "c", "m"] 
from collections import Counter
cnt1 = Counter(list_1)
cnt2 = Counter(list_2)
final = [key for key, counts in cnt2.items() if cnt1.get(key, 0) != counts]

>>> final
['f', 'm']

Як було обіцяно, це також може сприймати різну кількість випадків як "різницю":

list_1=["a", "b", "c", "d", "e", 'a']
cnt1 = Counter(list_1)
cnt2 = Counter(list_2)
final = [key for key, counts in cnt2.items() if cnt1.get(key, 0) != counts]

>>> final
['a', 'f', 'm']

-1

З ser1 видаліть елементи, наявні в ser2.

Вхідні дані

ser1 = pd.Series ([1, 2, 3, 4, 5]) ser2 = pd.Series ([4, 5, 6, 7, 8])

Рішення

ser1 [~ ser1.isin (ser2)]


Ласкаво просимо до переповнення стека. На це питання є вісім інших відповідей, один з яких був прийнятий оригінальним плакатом. Опишіть, будь ласка, як покращується ваша відповідь на те, що вже було представлено.
чб
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.