Python находит элементы в одном списке, которых нет в другом [дубликат]


137

Мне нужно сравнить два списка, чтобы создать новый список конкретных элементов, найденных в одном списке, но не найденных в другом. Например:

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

Я хочу перебрать list_1 и добавить в main_list все элементы из list_2, которых нет в list_1.

Результат должен быть:

main_list=["f", "m"]

Как я могу это сделать с помощью Python?


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).

(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значение по умолчанию. Обратите внимание, что оба ответа отсортированы.


Если ваши списки уже упорядочены, это также вернет упорядоченный список. Собственное решение преобразования в наборы с последующим получением разницы (решения, показанные ниже) возвращает неупорядоченный список, что может затруднить визуальную проверку результатов.
Doubledown

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

Действительно ps Я не голосовал против вашего ответа, особенно на неясный исходный вопрос.
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 как есть, и оно действительно очень медленное для большего списка. Можете ли вы обновить ответ, чтобы включить предложение shadowranger?
Doubledown

Можно ли получить индекс вместо строки?
JareBear,

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

@ ettanany большое спасибо !! Я сделаю это как можно скорее, я это сделал. Но ваш код выглядит намного чище.
JareBear,

6

Если вам нужно однострочное решение (без учета импорта), которое требует 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]

Если OP хочет сравнить элемент за элементом (неясно, пример может быть любым), это намного эффективнее, чем другие ответы, поскольку это один дешевый проход по обоим lists с одним созданным новым list, без дополнительных временных модулей , никаких дорогостоящих проверок содержания и т. д.
ShadowRanger

1
@ShadowRanger, это будет работать только для элементарной разницы, которая является ключевым моментом,
префект

@fordprefect: Ага. Мой собственный ответ касается различий, не зависящих от позиции.
ShadowRanger 06

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']

Method1 полностью 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)]


Добро пожаловать в Stack Overflow. На этот вопрос есть еще восемь ответов, один из которых был принят исходным автором. Пожалуйста, опишите, как ваш ответ улучшает то, что уже было представлено.
chb
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.