Установить порядок изменений элементов?

Недавно я заметил, что при преобразовании списка для установки порядок или элементы изменяются и сортируются по символам.

Рассмотрим этот пример:

x=[1,2,20,6,210]
print x 
# [1, 2, 20, 6, 210] # the order is same as initial order

set(x)
# set([1, 2, 20, 210, 6]) # in the set(x) output order is sorted

Мои вопросы -

  1. Почему это происходит?
  2. Как я могу выполнять операции с сетами (особенно Set Difference), не теряя первоначальный порядок?

16 ответов

Решение
  1. set неупорядоченная структура данных

  2. Не используйте set, скорее collections.OrderedDict:

    >>> a = collections.OrderedDict.fromkeys([1, 2, 20, 6, 210])
    >>> b = collections.OrderedDict.fromkeys([6, 20, 1])
    >>> collections.OrderedDict.fromkeys(x for x in a if x not in b)
    OrderedDict([(2, None), (210, None)])
    

    Обратите внимание, что порядок b не имеет значения, поэтому он может быть любым итеративным, но он должен быть итерируемым, который поддерживает тесты членства O(1).

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

>>> a = [1, 2, 20, 6, 210]
>>> b = set([6, 20, 1])
>>> [x for x in a if x not in b]
[2, 210]

Это теряет порядок b, не разрешает быстрые членские тесты на a и результат. Наборы позволяют быстро проверять членство, а списки поддерживают порядок. Если вам нужны обе эти функции в одной коллекции, используйте collections.OrderedDict,

В Python 3.6 set() Теперь следует сохранить порядок, но есть еще одно решение для Python 2 и 3:

>>> x = [1, 2, 20, 6, 210]
>>> sorted(set(x), key=x.index)
[1, 2, 20, 6, 210]

Удалить дубликаты и сохранить порядок с помощью функции ниже

def unique(sequence):
    seen = set()
    return [x for x in sequence if not (x in seen or seen.add(x))]

проверьте эту ссылку

Отвечая на ваш первый вопрос, множество - это структура данных, оптимизированная для операций над множествами. Подобно математическому набору, он не обеспечивает или не поддерживает какой-либо конкретный порядок элементов. Абстрактная концепция набора не обеспечивает порядок, поэтому реализация не обязательна. Когда вы создаете набор из списка, Python может изменять порядок элементов для нужд внутренней реализации, которую он использует для набора, который способен эффективно выполнять операции над множествами.

В математике есть множества и упорядоченные множества (oset).

  • set: неупорядоченный контейнер уникальных элементов (Реализовано)
  • oset: упорядоченный контейнер уникальных элементов (NotImplemented)

В Python напрямую реализованы только наборы. Мы можем эмулировать oset с помощью обычных клавиш dict (3.7+).

Дано

a = [1, 2, 20, 6, 210, 2, 1]
b = {2, 6}

Код

oset = dict.fromkeys(a).keys()
# dict_keys([1, 2, 20, 6, 210])

Демо

Реплики удаляются, порядок вставки сохраняется.

list(oset)
# [1, 2, 20, 6, 210]

Операции, подобные множеству, над клавишами dict.

oset - b
# {1, 20, 210}

oset | b
# {1, 2, 5, 6, 20, 210}

oset & b
# {2, 6}

oset ^ b
# {1, 5, 20, 210}

Детали

Примечание: неупорядоченная структура не препятствует упорядоченным элементам. Напротив, поддержание порядка не гарантируется. Пример:

assert {1, 2, 3} == {2, 3, 1}                    # sets (order is ignored)

assert [1, 2, 3] != [2, 3, 1]                    # lists (order is guaranteed)

Можно с удовольствием обнаружить, что список и мультимножество (mset) - это еще две увлекательные математические структуры данных:

  • список: упорядоченный контейнер элементов, разрешающий репликацию (реализовано)
  • mset: неупорядоченный контейнер элементов, разрешающий репликацию (NotImplemented)*

Резюме

Container | Ordered | Unique | Implemented
----------|---------|--------|------------
set       |    n    |    y   |     y
oset      |    y    |    y   |     n
list      |    y    |    n   |     y
mset      |    n    |    n   |     n*  

* Мультимножество можно косвенно эмулировать с помощью collections.Counter(), dict-подобное отображение кратностей (counts).

Вы можете удалить повторяющиеся значения и сохранить порядок вставки списка с помощью одной строки кода, Python 3.8.2.

mylist = ['b', 'b', 'a', 'd', 'd', 'c']


результаты = список ({значение:"" для значения в моем списке})

печать (результаты)

>>> ['b', 'a', 'd', 'c']

результаты = список (dict.fromkeys(mylist))

печать (результаты)

>>> ['b', 'a', 'd', 'c']

Как указано в других ответах, наборы - это структуры данных (и математические понятия), которые не сохраняют порядок элементов -

Однако, используя комбинацию наборов и словарей, возможно, что вы сможете достичь того, чего захотите - попробуйте использовать следующие фрагменты:

# save the element order in a dict:
x_dict = dict(x,y for y, x in enumerate(my_list) )
x_set = set(my_list)
#perform desired set operations
...
#retrieve ordered list from the set:
new_list = [None] * len(new_set)
for element in new_set:
   new_list[x_dict[element]] = element

Основываясь на ответе Свена, я обнаружил, что с помощью коллекций.OrderedDict вроде как помог мне выполнить то, что вы хотите, плюс позволил мне добавить больше элементов к диктату:

import collections

x=[1,2,20,6,210]
z=collections.OrderedDict.fromkeys(x)
z
OrderedDict([(1, None), (2, None), (20, None), (6, None), (210, None)])

Если вы хотите добавить элементы, но по-прежнему относитесь к ним как к набору, вы можете просто сделать:

z['nextitem']=None

И вы можете выполнить такую ​​операцию, как z.keys(), и получить набор:

z.keys()
[1, 2, 20, 6, 210]

Еще одним более простым способом может быть два создания пустого списка, скажем, «unique_list» для добавления уникальных элементов из исходного списка, например:

      unique_list=[]

for i in original_list:
    if i not in unique_list:
        unique_list.append(i)
    else:
        pass

Это даст вам все уникальные элементы, а также сохранит порядок.

Поздно отвечать, но вы можете использовать Pandas, pd.Series для преобразования списка при сохранении порядка:

      import pandas as pd
x = pd.Series([1, 2, 20, 6, 210, 2, 1])
print(pd.unique(x))

Вывод: массив ([1, 2, 20, 6, 210])

Работает для списка строк

      x = pd.Series(['c', 'k', 'q', 'n', 'p','c', 'n'])
print(pd.unique(x))

Вывод ['c' 'k' 'q' 'n' 'p']

Реализация концепции наивысшего балла, приведенной выше, которая возвращает его в список:

def SetOfListInOrder(incominglist):
    from collections import OrderedDict
    outtemp = OrderedDict()
    for item in incominglist:
        outtemp[item] = None
    return(list(outtemp))

Протестировано (кратко) на Python 3.6 и Python 2.7.

Интересно, что люди всегда используют "проблему реального мира", чтобы пошутить над определением в теоретической науке.

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

Кроме того, set намного быстрее при поиске определенного ключа, что очень хорошо для работы с наборами (и поэтому вам нужен набор, а не список).

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

def indx_dic(l):
    dic = {}
    for i in range(len(l)):
        if l[i] in dic:
            dic.get(l[i]).append(i)
        else:
            dic[l[i]] = [i]
    return(dic)

a = [1,2,3,4,5,1,3,2]
set_a  = set(a)
dic_a = indx_dic(a)

print(dic_a)
# {1: [0, 5], 2: [1, 7], 3: [2, 6], 4: [3], 5: [4]}
print(set_a)
# {1, 2, 3, 4, 5}

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

# initial lists on which you want to do set difference
>>> nums = [1,2,2,3,3,4,4,5]
>>> evens = [2,4,4,6]
>>> evens_set = set(evens)
>>> result = []
>>> for n in nums:
...   if not n in evens_set and not n in result:
...     result.append(n)
... 
>>> result
[1, 3, 5]

Его временная сложность не так хороша, но он аккуратный и легко читаемый.

Вы можете удалить повторяющиеся значения и сохранить порядок вставки списка, если хотите

      lst = [1,2,1,3]
new_lst = []

for num in lst :
    if num not in new_lst :
        new_lst.append(num)

# new_lst = [1,2,3]

не используйте «наборы» для удаления дубликатов, если вам нужен «порядок»,

использовать наборы для поиска, т.е.
x в списке
занимает O(n) времени ,
где
x в наборе
занимает O(1) времени * в большинстве случаев

Для этого мы можем использовать collections.Counter :

      # tested on python 3.7
>>> from collections import Counter
>>> lst = ["1", "2", "20", "6", "210"]

>>> for i in Counter(lst):
>>>     print(i, end=" ")
1 2 20 6 210 

>>> for i in set(lst):
>>>     print(i, end=" ")
20 6 2 1 210

Вот простой способ сделать это:

x=[1,2,20,6,210]
print sorted(set(x))
Другие вопросы по тегам