Bir listeyi bir kümeye dönüştürmek, öğe sırasını değiştirir


119

Son zamanlarda ben bir dönüştürme ediyorum ne zaman fark listetmek setelementlerin sırayla değiştirilir ve karakteri göre sıralanır.

Şu örneği düşünün:

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

Sorularım -

  1. Bu neden oluyor?
  2. İlk sırayı kaybetmeden set işlemlerini (özellikle Set Farkı) nasıl yapabilirim?

8
Özellikle set işlemleri yapıyorsanız, neden ilk sırayı kaybetmek istemiyorsunuz? "Düzen" sadece Python'da değil matematikte de kümeler için anlamsız bir kavramdır.
Karl Knechtel

131
@KarlKnechtel - Evet "matematikte düzen, kümeler için anlamsız bir kavramdır" ama benim gerçek dünya sorunlarım var :)
d.putto

CPython 3.6+ üzerinde unique = list(dict.fromkeys([1, 2, 1]).keys()). Bu işe yarıyor çünkü dictkampanya siparişini şimdi koruyor.
Boris

Yanıtlar:


106
  1. A set, sıralanmamış bir veri yapısıdır, bu nedenle ekleme sırasını korumaz.

  2. Bu, gereksinimlerinize bağlıdır. Normal bir listeniz varsa ve listenin sırasını korurken bazı öğeleri kaldırmak istiyorsanız, bunu bir liste anlayışı ile yapabilirsiniz:

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

    Her iki destekleyen bir veri yapısını gerekiyorsa hızlı üyelik testleri ve yerleştirme düzeninin korunması , ekleme düzeni korumak için garanti edilir Python 3.7 başlayarak bir Python sözlük, anahtarlarını kullanabilirsiniz:

    >>> a = dict.fromkeys([1, 2, 20, 6, 210])
    >>> b = dict.fromkeys([6, 20, 1])
    >>> dict.fromkeys(x for x in a if x not in b)
    {2: None, 210: None}

    bburada sipariş edilmesine gerçekten gerek yok - bir de kullanabilirsiniz set. Not a.keys() - b.keys()bir şekilde grubu sayısını gönderir setsokma sırasını korumak olmayacak şekilde.

    Python'un eski sürümlerinde, collections.OrderedDictbunun yerine şunları kullanabilirsiniz :

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

3
Hiçbir nesnenin maliyeti 16 bayt. Yalnızca varsayılan bir OrderedSet () varsa. :(
Sean

2
@Sean hayır, yok. Nonedil garantili bir singletondur. CPython'da, gerçek maliyet yalnızca göstericidir (bu maliyet her zaman oradadır, ancak bir dikte için, neredeyse Noneve diğer tekilleri veya paylaşılan referansları "ücretsiz" olarak değerlendirebilirsiniz), dolayısıyla bir makine kelimesi, muhtemelen modern bilgisayarlarda 8 bayt . Ama evet, bir setin olabileceği kadar verimli değil.
juanpa.arrivillaga

2
CPython 3.6+ üzerinde bunu yapabilirsiniz dict.fromkeys([1, 2, 1]).keys()çünkü normal dicts düzeni de korur.
Boris

@Boris Bu, Python 3.7'den başlayarak dil belirtiminin yalnızca bir parçası olmuştur. CPython uygulaması halihazırda sürüm 3.6'daki ekleme sırasını korurken, bu, diğer Python uygulamaları tarafından takip edilmeyen bir uygulama ayrıntısı olarak kabul edilir .
Sven Marnach

@Sven CPython dedim. Bunu her yerde yayınlıyorum, sadece "CPython 3.6 veya Python 3.7 ile başlayan başka herhangi bir uygulama" yazmaktan sıkılmaya başladım. Fark etmez, herkes CPython kullanıyor
Boris

53

Python 3.6 olarak set()şimdi gerektiğini düzeni sağlamak, ancak Python 2 ve 3 için başka bir çözüm yoktur:

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

8
Sipariş korumayla ilgili iki not: yalnızca Python 3.6'dan itibaren ve orada bile, bir uygulama ayrıntısı olarak kabul edilir, bu yüzden ona güvenmeyin. Bunun dışında kodunuz çok verimsizdir çünkü her x.indexçağrıldığında doğrusal bir arama yapılır. İkinci dereceden karmaşıklıkta sorun yoksa, setilk etapta a kullanmak için bir neden yoktur .
Thijs van Dien

27
@ThijsvanDien Bu yanlış, set()Python 3.6'da sıralanmadı, uygulama detayı olarak bile değil, düşünüyorsun dicts
Chris_Rands

8
@ThijsvanDien Hayır, sıralanmamışlar, ancak bazen öyle görünseler de, intgenellikle kendi kendilerine
hashing yaptıkları

3
Deneyin x=[1,2,-1,20,6,210]ve bir set yapın. Python 3.6'da hiç sipariş edilmediğini ve test edildiğini göreceksiniz.
GabrielChu

3
Bu cevabın neden bu kadar çok oy aldığını anlayamıyorum, ekleme sırasını tutmuyor ve bir set döndürmüyor.
Igor Rodriguez

20

İlk sorunuzu yanıtlayan bir set, set işlemleri için optimize edilmiş bir veri yapısıdır. Matematiksel bir küme gibi, elementlerin belirli bir sırasını zorlamaz veya sürdürmez. Bir kümenin soyut kavramı düzeni zorlamaz, bu nedenle uygulama zorunlu değildir. Bir listeden bir set oluşturduğunuzda, Python, set işlemlerini verimli bir şekilde gerçekleştirebilen bir set için kullandığı dahili uygulamanın ihtiyaçları için öğelerin sırasını değiştirme özgürlüğüne sahiptir.



8

Matematikte kümeler ve sıralı kümeler (osetler) vardır.

  • set : benzersiz öğelerden oluşan sırasız bir kap (Uygulandı)
  • oset : benzersiz öğelerden oluşan sıralı bir kap (NotImplemented)

Python'da yalnızca kümeler doğrudan uygulanır. Normal dikt anahtarlarıyla ( 3.7+ ) oset'leri taklit edebiliriz .

verilmiş

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

kod

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

gösteri

Kopyalar kaldırılır, ekleme sırası korunur.

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

Dikte tuşlarında set benzeri işlemler.

oset - b
# {1, 20, 210}

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

oset & b
# {2, 6}

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

ayrıntılar

Not: sırasız bir yapı, sıralı elemanları engellemez. Aksine, sürdürülen düzen garanti edilmez. Misal:

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

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

Bir liste ve çoklu kümenin (mset) iki tane daha büyüleyici matematiksel veri yapısı olduğunu keşfetmekten memnun olabiliriz :

  • liste : kopyalara izin veren sıralı bir öğe kabı (Uygulandı)
  • mset : çoğaltmalara izin veren sırasız bir öğe kabı (NotImplemented) *

özet

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

* Bir çoklu set, çoklukların (sayımların) collections.Counter()dikte benzeri bir eşlemesi ile dolaylı olarak benzetilebilir .


4

Diğer yanıtlarda da belirtildiği gibi, kümeler, öğe sırasını korumayan veri yapılarıdır (ve matematiksel kavramlardır) -

Bununla birlikte, kümeler ve sözlüklerin bir kombinasyonunu kullanarak istediğinizi başarmanız mümkündür - şu parçacıkları kullanmayı deneyin:

# 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

1

Sven'in cevabına dayanarak, koleksiyonları kullandığımı buldum.

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

Öğe eklemek istiyor ancak yine de bir set gibi davranmak istiyorsanız, şunları yapabilirsiniz:

z['nextitem']=None

Ve diktede z.keys () gibi bir işlem gerçekleştirebilir ve seti elde edebilirsiniz:

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

list(z.keys())liste çıktısını almak için yapmanız gerekenler .
jxn

Python 3'te evet. Python 2'de değil, belirtmeliydim.
jimh

0

Yukarıdaki en yüksek puan kavramının uygulanması, onu bir listeye geri getirir:

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

Python 3.6 ve Python 2.7'de test edildi (kısaca).


0

İlk iki listenizde üzerinde set fark işlemi yapmak istediğiniz az sayıda öğe varsa collections.OrderedDict, uygulamayı karmaşıklaştıran ve daha az okunabilir kılanı kullanmak yerine , kullanabilirsiniz:

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

Zaman karmaşıklığı o kadar iyi değil ama derli toplu ve okunması kolay.


0

İnsanların teorik bilimdeki tanıma şaka yapmak için her zaman 'gerçek dünya problemini' kullanması ilginçtir.

Setin düzeni varsa, önce aşağıdaki sorunları bulmanız gerekir. Listenizde yinelenen öğeler varsa, onu bir kümeye dönüştürdüğünüzde sıra ne olmalıdır? İki seti birleştirirsek, sıra nedir? Aynı elemanlar üzerinde farklı sıralı iki kümeyi kesişirsek sıra nedir?

Ayrıca set, belirli bir anahtarı aramada çok daha hızlıdır, bu da set işlemlerinde çok iyidir (bu yüzden bir sete ihtiyacınız vardır, ancak listeye ihtiyacınız yoktur).

Dizini gerçekten önemsiyorsanız, bir liste olarak tutun. Yine de birçok listedeki öğeler üzerinde set işlemi yapmak istiyorsanız, en basit yol, orijinal listedeki anahtarın tüm indeksini içeren bir liste değeriyle birlikte kümedeki aynı anahtarlara sahip her liste için bir sözlük oluşturmaktır.

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}

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.