Операция вычитания списка Python



Я хочу сделать что-то похожее на это:



>>> x = [1,2,3,4,5,6,7,8,9,0]  
>>> x
[1, 2, 3, 4, 5, 6, 7, 8, 9, 0]
>>> y = [1,3,5,7,9]
>>> y
[1, 3, 5, 7, 9]
>>> y - x # (should return [2,4,6,8,0])


но это не поддерживается списками python
Каков наилучший способ сделать это?

3174   10  

10 ответов:

использовать список понимание:

[item for item in x if item not in y]

если вы хотите использовать - синтаксис инфикса, вы можете просто сделать:

class MyList(list):
    def __init__(self, *args):
        super(MyList, self).__init__(args)

    def __sub__(self, other):
        return self.__class__(*[item for item in self if item not in other])

затем вы можете использовать его как:

x = MyList(1, 2, 3, 4)
y = MyList(2, 5, 2)
z = x - y   

но если вам абсолютно не нужны свойства списка (например, порядок), просто используйте наборы, как рекомендуют другие ответы.

использовать установить разницу

>>> z = list(set(x) - set(y))
>>> z
[0, 8, 2, 4, 6]

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

это операция" set subtraction". Используйте для этого заданную структуру данных.

В Python 2.7:

x = {1,2,3,4,5,6,7,8,9,0}
y = {1,3,5,7,9}
print x - y

выход:

>>> print x - y
set([0, 8, 2, 4, 6])

Если дублировать и заказывать детали проблема:

[i for i in a if not i in b or b.remove(i)]

a = [1,2,3,3,3,3,4]
b = [1,3]
result: [2, 3, 3, 3, 4]

для многих случаев использования, ответ вы хотите:

ys = set(y)
[item for item in x if item not in ys]

это гибрид между aaronasterling это и ответ quantumSoup.

версия aaronasterling делает len(y) сравнение элементов для каждого элемента в x, так что это занимает квадратичное время. версия quantumSoup использует наборы, поэтому она выполняет один поиск набора констант для каждого элемента в x-но, потому что он преобразует иx и y в наборы, он теряет порядок ваших элементов.

путем преобразования только y в набор, и перебор x для того, чтобы вы получили лучшее из обоих миров-линейное время и сохранение порядка.*


однако у этого все еще есть проблема с версией quantumSoup: она требует, чтобы ваши элементы были хэшируемыми. Это в значительной степени встроено в природу наборов.** Если вы пытаетесь, например, вычесть список диктов из другого списка диктов, но список чтобы вычесть большой, что вы делаете?

если вы можете украсить ваши ценности в некотором роде, что они hashable, что решает проблему. Например, с плоским словарем, значения которого сами хешируются:

ys = {tuple(item.items()) for item in y}
[item for item in x if tuple(item.items()) not in ys]

если видов немного более сложных (например, часто вы имеете дело с JSON-совместимые значения, которые hashable, или списки или словарь, значения которых являются рекурсивно тот же тип), вы все еще можете использовать это решение. Но некоторые типы просто не могут быть преобразуется во что-нибудь хэшируемое.


если ваши элементы не, и не может быть сделано, hashable, но они сопоставимы, можно как минимум получить лог-линейного времени (O(N*log M), что намного лучше, чем O(N*M) время решения списка, но не так хорошо, как O(N+M) время заданного решения) путем сортировки и использования bisect:

ys = sorted(y)
def bisect_contains(seq, item):
    index = bisect.bisect(seq, item)
    return index < len(seq) and seq[index] == item
[item for item in x if bisect_contains(ys, item)]

если ваши товары не hashable, ни сравнима, то вы застряли с квадратичной решение.


* обратите внимание, что вы также можете сделать это с помощью пары OrderedSet объекты, для которых можно найти рецепты и сторонних модулей. Но я думаю, что это проще.

** причина, по которой поиск настроек является постоянным временем, заключается в том, что все, что ему нужно сделать, это хэшировать значение и посмотреть, есть ли запись для этого хэша. Если он не может хэшировать значение, это не будет работать.

поиск значений в наборах выполняется быстрее, чем поиск их в списках:

[item for item in x if item not in set(y)]

Я считаю, что это будет масштаб чуть лучше:

[item for item in x if item not in y]

оба сохраняют порядок списков.

попробуйте это.

def subtract_lists(a, b):
    """ Subtracts two lists. Throws ValueError if b contains items not in a """
    # Terminate if b is empty, otherwise remove b[0] from a and recurse
    return a if len(b) == 0 else [a[:i] + subtract_lists(a[i+1:], b[1:]) 
                                  for i in [a.index(b[0])]][0]

>>> x = [1,2,3,4,5,6,7,8,9,0]
>>> y = [1,3,5,7,9]
>>> subtract_lists(x,y)
[2, 4, 6, 8, 0]
>>> x = [1,2,3,4,5,6,7,8,9,0,9]
>>> subtract_lists(x,y)
[2, 4, 6, 8, 0, 9]     #9 is only deleted once
>>>

ответ, предоставленный @aaronasterling, выглядит хорошо, однако он не совместим с интерфейсом по умолчанию list:x = MyList(1, 2, 3, 4) vs x = MyList([1, 2, 3, 4]). Таким образом, приведенный ниже код может быть использован в качестве более дружественного python-list:

class MyList(list):
    def __init__(self, *args):
        super(MyList, self).__init__(*args)

    def __sub__(self, other):
        return self.__class__([item for item in self if item not in other])

пример:

x = MyList([1, 2, 3, 4])
y = MyList([2, 5, 2])
z = x - y

в этом примере вычитаются два списка:

# List of pairs of points
list = []
list.append([(602, 336), (624, 365)])
list.append([(635, 336), (654, 365)])
list.append([(642, 342), (648, 358)])
list.append([(644, 344), (646, 356)])
list.append([(653, 337), (671, 365)])
list.append([(728, 13), (739, 32)])
list.append([(756, 59), (767, 79)])

itens_to_remove = []
itens_to_remove.append([(642, 342), (648, 358)])
itens_to_remove.append([(644, 344), (646, 356)])

print("Initial List Size: ", len(list))

for a in itens_to_remove:
    for b in list:
        if a == b :
            list.remove(b)

print("Final List Size: ", len(list))

Я думаю, что это быстрее:

In [1]: a = [1,2,3,4,5]

In [2]: b = [2,3,4,5]

In [3]: c = set(a) ^ set(b)

In [4]: c
Out[4]: {1}

Comments

    Ничего не найдено.