Bir listeden birden fazla öğeyi silme


160

Bir listeden aynı anda birden çok öğeyi silmek mümkün müdür? 0 ve 2 dizinindeki öğeleri silmek ve del somelist[0]ardından del somelist[2]gelen bir ifadeyi denemek istersem , ikinci ifade gerçekten silinir somelist[3].

Her zaman önce daha yüksek numaralı öğeleri silebilirim ama daha iyi bir yol olduğunu umuyorum.

Yanıtlar:


111

enumerateDizini kaldırmak istediğiniz dizinlerle eşleşen değerleri kullanabilir ve kaldırabilirsiniz:

indices = 0, 2
somelist = [i for j, i in enumerate(somelist) if j not in indices]

2
Neredeyse, yalnızca listenin tamamını silerseniz. len (indeksler) * len (somelist) olacaktır. Ayrıca istenebilecek veya istenmeyecek bir kopya oluşturur
Richard Levasseur

bir listede bir değer olup olmadığını kontrol ediyorsanız, öyle. 'in' operatörü bir listenin değerleri üzerinde çalışırken bir diktenin anahtarları üzerinde çalışır. Eğer yanılıyorsam, lütfen beni pep / başvuruya yönlendirin
Richard Levasseur

5
endeksler için bir seçim yapmamın sebebi sadece kaydın basitliğiydi. set () için O (n) vermek için mükemmel bir iş olurdu
SilentGhost

18
Bu, somelistlerden hiç bir şey silmek değil, yepyeni bir liste oluşturmaktır. Orijinal listeye bir referans tutan bir şey varsa, yine de içindeki tüm öğeler olacaktır.
Tom Future

2
@SilentGhost Numaralandırma yapmak için gerekli değildir. Buna ne dersin somelist = [ lst[i] for i in xrange(len(lst)) if i not in set(indices) ]?
ToolmakerSteve

183

Nedense buradaki cevaplardan hiç hoşlanmıyorum. Evet, çalışıyorlar, ancak kesinlikle birçoğu bir listedeki öğeleri silmiyor, değil mi? (Ancak bir kopyasını oluşturun ve orijinal olanı düzenlenmiş kopyayla değiştirin).

Neden önce daha yüksek dizini silmiyorsunuz?

Bunun bir sebebi var mı? Sadece yapardım:

for i in sorted(indices, reverse=True):
    del somelist[i]

Gerçekten öğeleri geri silmek istemiyorsanız, sanırım son silinen dizinden daha büyük olan indeks değerlerini kaldırmanız gerekir (farklı bir listeye sahip olduğunuz için aynı dizini gerçekten kullanamazsınız) veya listenin bir kopyası ('silme' değil, orijinali düzenlenmiş bir kopyayla değiştirme).

Burada bir şey eksik mi, ters sırada SİLMEMEK için herhangi bir neden var mı?


1
Bunun neden kabul edilen cevap olarak seçilmediğini bilmiyorum !. Bunun için teşekkürler.
swathis

4
Bunun iki nedeni var. (a) Bir liste için, zaman karmaşıklığı "kopya oluştur" yönteminden (bir dizi indeks kullanarak) ortalamadan (rastgele indeksler varsayarak) daha yüksek olacaktır çünkü bazı elemanların birden fazla kez ileri kaydırılması gerekir. (b) En azından benim için okumak zor, çünkü gerçek bir program mantığına karşılık gelmeyen ve sadece teknik nedenlerle var olan bir sıralama işlevi var. Şimdiye kadar mantığı iyice anlasam da, hala okumanın zor olacağını hissediyorum .
Kusursuz Gece

1
@ImperishableGece (a) hakkında ayrıntılı bilgi verebilir misiniz? "Bazı unsurların kaydırılması gerekiyor" anlamıyorum. (B) için okuma netliğine ihtiyacınız varsa bir işlev tanımlayabilirsiniz.
tglaria

109

Bitişik olmayan birden fazla öğeyi siliyorsanız, açıkladığınız en iyi yoldur (ve evet, en yüksek dizinden başladığınızdan emin olun).

Öğeleriniz bitişikse, dilim ataması sözdizimini kullanabilirsiniz:

a[2:10] = []

95
del a[2:10]Aynı etkiyle de söyleyebilirsiniz .
STH

8
@sth İlginçtir ki del, atamadan biraz daha hızlıdır.
thefourtheye

25

Aşağıdaki gibi kullanabilirsiniz numpy.delete:

import numpy as np
a = ['a', 'l', 3.14, 42, 'u']
I = [0, 2]
np.delete(a, I).tolist()
# Returns: ['l', '42', 'u']

numpySonunda bir dizi ile bitirmek sakıncası yoksa, dışarıda bırakabilirsiniz .tolist(). Bazı daha büyük hız geliştirmeleri de görmelisiniz, bu da daha ölçeklenebilir bir çözümdür. Ben karşılaştırmak değil, ama numpyoperasyonlar C veya Fortran yazılı kod derlenmiştir.


1
elemanlar ardışık olmadığında genel çözüm +1
noɥʇʎԀʎzɐɹƆ

1
burada soru silmeye ne dersiniz ['a', 42].
evanhutomo

Hız için bu çözüm için diğerlerine kıyasla BÜYÜK bonus puan. Söyleyebileceğim şey, çok büyük bir veri seti için, sadece birkaç saniye süren iyi bir şeyle başarabilmem birkaç dakika sürüyordu.
legel

18

Greg'in cevabının bir uzmanlığı olarak, genişletilmiş dilim sözdizimini bile kullanabilirsiniz. Örneğin. 0 ve 2 öğelerini silmek istiyorsanız:

>>> a= [0, 1, 2, 3, 4]
>>> del a[0:3:2]
>>> a
[1, 3, 4]

Bu, elbette herhangi bir keyfi seçimi kapsamaz, ancak herhangi iki öğeyi silmek için kesinlikle işe yarayabilir.


16

İşlev olarak:

def multi_delete(list_, *args):
    indexes = sorted(list(args), reverse=True)
    for index in indexes:
        del list_[index]
    return list_

Çalıştırır n log (n) henüz hızlı, doğru çözüm yapmalıdır zaman.


1
Args.sort (). Reverse () içeren sürüm kesinlikle daha iyidir. Ayrıca atmak veya daha da kötüsü sessizce bozmak yerine dikte ile çalışır.

tuple için sort () tanımlanmamışsa, önce listeye dönüştürmeniz gerekir. sort (), None değerini döndürür, böylece üzerinde reverse () kullanamazsınız.
SilentGhost

@ R. Pate: Bu nedenle ilk sürümü kaldırdım. Teşekkürler. @ SilentGhost: Düzeltildi.
Nikhil Chelliah

@Nikhil: hayır yapmadım;) args = list (args) args.sort () args.reverse () ama daha iyi bir seçenek olurdu: args =
sorted

2
n log n? Gerçekten mi? Bence del list[index]O (1).
user202729

12

Yani, esasen bir geçişte birden çok elemanı silmek mi istiyorsunuz? Bu durumda, silinecek bir sonraki öğenin konumu daha önce silindi ancak birçoğu silindi.

Hedefimiz, 1, 4 ve 7 endeksleri olarak önceden hesaplanan tüm sesli harfleri silmek. Önemli olan to_delete endekslerinin artan sırada olduğunu, aksi takdirde çalışmadığını unutmayın.

to_delete = [1, 4, 7]
target = list("hello world")
for offset, index in enumerate(to_delete):
  index -= offset
  del target[index]

Elemanları herhangi bir sırayla silmek isteseniz daha karmaşık olurdu. IMO, sıralama, to_deletene zaman çıkarmanız veya çıkarmamanız gerektiğini anlamaktan daha kolay olabilir index.


8

Ben Python toplam bir acemi ve şu anda benim programlama en az söylemek kaba ve kirli, ama benim çözüm erken öğreticiler öğrendim temel komutların bir kombinasyonunu kullanmak oldu:

some_list = [1,2,3,4,5,6,7,8,10]
rem = [0,5,7]

for i in rem:
    some_list[i] = '!' # mark for deletion

for i in range(0, some_list.count('!')):
    some_list.remove('!') # remove
print some_list

Açıkçası, bir "silme işareti" karakteri seçmek zorunda olduğu için, bunun sınırlamaları vardır.

Listenin boyutu ölçeklendikçe performansa gelince, çözümümün optimal olmadığından eminim. Ancak, bu basittir, umarım diğer yeni başlayanlara hitap eder ve some_listiyi bilinen bir formatta, örneğin her zaman sayısal olan basit durumlarda çalışacaktır ...


2
kullanmak yerine '!' özel karakteriniz olarak Yok'u kullanın. Bu, her karakteri geçerli tutar ve olasılıklarınızı serbest bırakır
portforwardpodcast

5

İşte tumles oluşturmak için enumerate () kullanmayan bir alternatif var (SilentGhost'un orijinal cevabında olduğu gibi).

Bu benim için daha okunaklı görünüyor. (Belki numaralandırma alışkanlığı içinde olsaydım farklı hissederdim.) CAVEAT: İki yaklaşımın performansını test etmedim.

# Returns a new list. "lst" is not modified.
def delete_by_indices(lst, indices):
    indices_as_set = set(indices)
    return [ lst[i] for i in xrange(len(lst)) if i not in indices_as_set ]

Not: Python 2.7 sözdizimi. Python 3 için xrange=> range.

Kullanımı:

lst = [ 11*x for x in xrange(10) ]
somelist = delete_by_indices( lst, [0, 4, 5])

somelist:

[11, 22, 33, 66, 77, 88, 99]

--- İLAVE ---

Bir listeden birden fazla değeri silin. Yani, silmek istediğimiz değerlere sahibiz:

# Returns a new list. "lst" is not modified.
def delete__by_values(lst, values):
    values_as_set = set(values)
    return [ x for x in lst if x not in values_as_set ]

Kullanımı:

somelist = delete__by_values( lst, [0, 44, 55] )

somelist:

[11, 22, 33, 66, 77, 88, 99]

Bu öncekiyle aynı cevap, ama bu sefer silinecek DEĞERLERİ sağladık [0, 44, 55].


@ SilentGhost enumerate sonucu için kullanılan tanımlayıcı olmayan değişken isimleri nedeniyle, sadece okumak zor olduğuna karar verdim. Ayrıca, parens okumayı kolaylaştırırdı. Yani burada nasıl ben (performans için, katma "set" ile) kelimesi onun çözümünü olur: [ value for (i, value) in enumerate(lst) if i not in set(indices) ]. Ama cevabımı burada bırakacağım, çünkü değerlere göre nasıl silineceğini de göstereceğim. Bu daha kolay bir durumdur, ancak birine yardımcı olabilir.
ToolmakerSteve

@ Veedrac- teşekkür ederim; Öncelikle seti kurmak için yeniden yazdım. Ne düşünüyorsun - SilentGhost'unkinden daha hızlı çözüm? (Sadece fikrini soran, aslında zaman o kadar önemli yeterince dikkate almaz.) Aynı şekilde, ben olarak yeniden yazma SilentGhost versiyonu olur indices_as_set = set(indices), [ value for (i, value) in enumerate(lst) if i not in indices_as_set ]bunu hızlandırmak için.
ToolmakerSteve

Çift alt çizgi için bir stil nedeni var mı delete__by_values()?
Tom

5

Liste dizini değerlerini kullanan alternatif bir liste anlama yöntemi:

stuff = ['a', 'b', 'c', 'd', 'e', 'f', 'woof']
index = [0, 3, 6]
new = [i for i in stuff if stuff.index(i) not in index]

Bu döndürür:

['b', 'c', 'e', 'f']

iyi cevap, ancak indexliste yineleyici yöntemi kullanıldığından dizinlerin listesini yanıltıcı olarak adlandırmakindex()
Joe

4

burada öğeleri kaldıran başka bir yöntem var. ayrıca listeniz gerçekten uzunsa, daha hızlıdır.

>>> a = range(10)
>>> remove = [0,4,5]
>>> from collections import deque
>>> deque((list.pop(a, i) for i in sorted(remove, reverse=True)), maxlen=0)

>>> timeit.timeit('[i for j, i in enumerate(a) if j not in remove]', setup='import random;remove=[random.randrange(100000) for i in range(100)]; a = range(100000)', number=1)
0.1704120635986328

>>> timeit.timeit('deque((list.pop(a, i) for i in sorted(remove, reverse=True)), maxlen=0)', setup='from collections import deque;import random;remove=[random.randrange(100000) for i in range(100)]; a = range(100000)', number=1)
0.004853963851928711

+1: Bir "for ..:" bloğu gerektirmek yerine, bir ifadenin parçası olarak bir eylem gerçekleştirmek için ilginç deque kullanımı. Ancak, bu basit dava için, Nikhil blok için daha okunabilir buluyorum.
ToolmakerSteve

4

Bundan bahsedildi, ancak bir şekilde kimse gerçekten doğru yapmayı başaramadı.

O(n)Çözüm üzerinde :

indices = {0, 2}
somelist = [i for j, i in enumerate(somelist) if j not in indices]

Bu, SilentGhost'un sürümüne çok yakın , ancak iki ayraç ekliyor.


Bu, her bir yineleme için O(n)alınan aramaları sayarsanız değil log(len(indices)).
Mad Physicist

@MadPhysicist j not in indicesolduğunu O(1).
Veedrac

Bu numarayı nasıl aldığınızdan emin değilim. Endeksler bir küme olduğundan, j not in indiceshala arama gerektirir O(log(len(indices))). Ben 2 elemanlı bir sette bir arama olarak nitelendirmek kabul ederken O(1), genel durumda olacak O(log(N)). Her iki durumda da O(N log(N))yine atıyor O(N^2).
Mad Physicist


Ve iki diş teli tam olarak ne yaptı?
Nükleer03020704

4
l = ['a','b','a','c','a','d']
to_remove = [1, 3]
[l[i] for i in range(0, len(l)) if i not in to_remove])

Temelde en çok oy alan cevapla aynı, sadece farklı bir şekilde yazmanın yolu. Listedeki yinelenen öğeleri işleyemediğinden, l.index () kullanmanın iyi bir fikir olmadığını unutmayın.


2

Kaldırma yöntemi, liste öğelerinin çok fazla kaydırılmasına neden olur. Bence bir kopya yapmak daha iyi:

...
new_list = []
for el in obj.my_list:
   if condition_is_true(el):
      new_list.append(el)
del obj.my_list
obj.my_list = new_list
...

2

teknik olarak, cevap HAYIR, AYNI ZAMAN iki nesneyi silmek mümkün değildir. Bununla birlikte, güzel bir pitonun bir satırındaki iki nesneyi silmek mümkündür.

del (foo['bar'],foo['baz'])

tekrar tekrar silinecek foo['bar'], sonrafoo['baz']


Bu, bir listeden değil, dikte edilen bir nesneden silinir, ancak hala + 1'iyorum çünkü çok güzel!
Ulf Aslak

Uygun sözdizimi ile liste için de geçerlidir. Ancak iddia, aynı anda iki nesnenin silinmesinin mümkün olmadığıdır; Cevabı gör @bobince
Pedro Gimeno

2

bunu, dizin listesini azalan düzende sıraladıktan sonra dizinler üzerinde bir yineleme yinelemesi kullanarak yapabiliriz

mylist=[66.25, 333, 1, 4, 6, 7, 8, 56, 8769, 65]
indexes = 4,6
indexes = sorted(indexes, reverse=True)
for i in index:
    mylist.pop(i)
print mylist

2

A listesindeki 0 ve 2 endeksleri için:

for x in (2,0): listA.pop(x)

ListA'dan kaldırılacak bazı rastgele indeksler için:

indices=(5,3,2,7,0) 
for x in sorted(indices)[::-1]: listA.pop(x)

2

Düğmeleri çevirmeyi kolaylaştıran farklı çözümleri karşılaştırmak istedim.

Önce verilerimi oluşturdum:

import random

N = 16 * 1024
x = range(N)
random.shuffle(x)
y = random.sample(range(N), N / 10)

Sonra fonksiyonlarımı tanımladım:

def list_set(value_list, index_list):
    index_list = set(index_list)
    result = [value for index, value in enumerate(value_list) if index not in index_list]
    return result

def list_del(value_list, index_list):
    for index in sorted(index_list, reverse=True):
        del(value_list[index])

def list_pop(value_list, index_list):
    for index in sorted(index_list, reverse=True):
        value_list.pop(index)

Sonra timeitçözümleri karşılaştırırdım:

import timeit
from collections import OrderedDict

M = 1000
setup = 'from __main__ import x, y, list_set, list_del, list_pop'
statement_dict = OrderedDict([
    ('overhead',  'a = x[:]'),
    ('set', 'a = x[:]; list_set(a, y)'),
    ('del', 'a = x[:]; list_del(a, y)'),
    ('pop', 'a = x[:]; list_pop(a, y)'),
])

overhead = None
result_dict = OrderedDict()
for name, statement in statement_dict.iteritems():
    result = timeit.timeit(statement, number=M, setup=setup)
    if overhead is None:
        overhead = result
    else:
        result = result - overhead
        result_dict[name] = result

for name, result in result_dict.iteritems():
    print "%s = %7.3f" % (name, result)

Çıktı

set =   1.711
del =   3.450
pop =   3.618

Yani bir endeksler ile jeneratör setkazanan oldu. Ve delo zaman biraz daha hızlı pop.


Bu karşılaştırma için teşekkür ederim, bu benim kendi testlerimi yapmamı sağladı (aslında sadece kodunu ödünç aldı) ve kaldırılacak az sayıda öğe için, bir SET oluşturmanın yükü onu en kötü çözüm haline getiriyor (10, 100, 500 için uzunluğu 'y' ve göreceksiniz). Çoğu zaman, bu uygulamaya bağlıdır.
tglaria

2

Bu mantığı kullanabilirsiniz:

my_list = ['word','yes','no','nice']

c=[b for i,b in enumerate(my_list) if not i in (0,2,3)]

print c

2

En yüksek endeksten kaldırma fikrinin bir başka uygulaması.

for i in range(len(yourlist)-1, -1, -1):
    del yourlist(i)

1

Aslında bunu yapmanın iki yolunu düşünebilirim:

  1. listeyi dilimleyin (1., 3. ve 8. elemanları siler)

    somelist = somelist [1: 2] + somelist [3: 7] + somelist [8:]

  2. bunu yerinde yapın, ama her seferinde bir tane:

    somelist.pop (2) somelist.pop (0)


1

Bu şekilde bir listede değil, bir dikte üzerinde yapabilirsiniz. Bir listede öğeler sırayladır. Bir dikte içinde sadece endekse bağlıdırlar.

Basit kod sadece bunu anlatmaya yaparak :

>>> lst = ['a','b','c']
>>> dct = {0: 'a', 1: 'b', 2:'c'}
>>> lst[0]
'a'
>>> dct[0]
'a'
>>> del lst[0]
>>> del dct[0]
>>> lst[0]
'b'
>>> dct[0]
Traceback (most recent call last):
  File "<pyshell#19>", line 1, in <module>
    dct[0]
KeyError: 0
>>> dct[1]
'b'
>>> lst[1]
'c'

Bir sözlükte bir listeyi "dönüştürmenin" yolu:

>>> dct = {}
>>> for i in xrange(0,len(lst)): dct[i] = lst[i]

Tersi:

lst = [dct[i] for i in sorted(dct.keys())] 

Her neyse, dediğin gibi daha yüksek endeksten silmeye başlamak daha iyi.


Python [dct [i] in dct için] her zaman artan i değerleri kullanacağını garanti eder mi? Öyleyse, list (dct.values ​​()) kesinlikle daha iyidir.

Bunu düşünmüyordum. Haklısın. [Burada] [1] okuduğumda, öğelerin sırayla veya en azından beklenen siparişin alınacağının garantisi yoktur. Ben düzenledim. [1]: docs.python.org/library/stdtypes.html#dict.items
Andrea Ambu

2
Bu cevap sözlüklerden temelde yanlış bir şekilde bahseder. Bir sözlükte KEYS (INDICES değil) vardır. Evet, anahtar / değer çiftleri birbirinden bağımsızdır. Hayır, girişleri hangi sırayla sildiğiniz önemli değildir. Listedeki bazı öğeleri silmek için sözlüğe dönüştürmek aşırıya kaçar.
ToolmakerSteve

1

@ Sth'den yorumu genelleştirmek için . Uygular herhangi sınıfta Öğe silme, abc.MutableSequence ve de listözellikle aracılığıyla yapılır __delitem__sihirli yöntemle. Bu yöntem benzer şekilde çalışır __getitem__, yani tamsayı veya dilim kabul edebilir. İşte bir örnek:

class MyList(list):
    def __delitem__(self, item):
        if isinstance(item, slice):
            for i in range(*item.indices(len(self))):
                self[i] = 'null'
        else:
            self[item] = 'null'


l = MyList(range(10))
print(l)
del l[5:8]
print(l)

Bu çıktı

[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[0, 1, 2, 3, 4, 'null', 'null', 'null', 8, 9]

1

Sadece bu nedenle içe aktarmak aşırı olabilir, ancak pandasyine de kullanıyorsanız , çözüm basit ve basittir:

import pandas as pd
stuff = pd.Series(['a','b','a','c','a','d'])
less_stuff = stuff[stuff != 'a']  # define any condition here
# results ['b','c','d']

1
some_list.remove(some_list[max(i, j)])

Sıralama maliyetini ve listeyi açıkça kopyalamak zorunda kalmaz.


0

Bunlardan birine ne dersin (Python için çok yeniyim, ama iyi görünüyorlar ):

ocean_basin = ['a', 'Atlantic', 'Pacific', 'Indian', 'a', 'a', 'a']
for i in range(1, (ocean_basin.count('a') + 1)):
    ocean_basin.remove('a')
print(ocean_basin)

['Atlantik', 'Pasifik', 'Hint']

ob = ['a', 'b', 4, 5,'Atlantic', 'Pacific', 'Indian', 'a', 'a', 4, 'a']
remove = ('a', 'b', 4, 5)
ob = [i for i in ob if i not in (remove)]
print(ob)

['Atlantik', 'Pasifik', 'Hint']


0

Şimdiye kadar sunulan cevapların hiçbiri silinmesini gerçekleştirir yerinde silmek endeksleri keyfi bir sayı için listenin uzunluğuna O (n) 'de, işte benim sürümü:

def multi_delete(the_list, indices):
    assert type(indices) in {set, frozenset}, "indices must be a set or frozenset"
    offset = 0
    for i in range(len(the_list)):
        if i in indices:
            offset += 1
        elif offset:
            the_list[i - offset] = the_list[i]
    if offset:
        del the_list[-offset:]

# Example:
a = [0, 1, 2, 3, 4, 5, 6, 7]
multi_delete(a, {1, 2, 4, 6, 7})
print(a)  # prints [0, 3, 5]

0

Kaldır'ı da kullanabilirsiniz.

delete_from_somelist = []
for i in [int(0), int(2)]:
     delete_from_somelist.append(somelist[i])
for j in delete_from_somelist:
     newlist = somelist.remove(j)

0

Hepsini list_diffilk listenin orijinal sırasını korurken giriş olarak iki listeyi alan ve farklarını döndüren bir işleve koydum .

def list_diff(list_a, list_b, verbose=False):

    # returns a difference of list_a and list_b,
    # preserving the original order, unlike set-based solutions

    # get indices of elements to be excluded from list_a
    excl_ind = [i for i, x in enumerate(list_a) if x in list_b]
    if verbose:
        print(excl_ind)

    # filter out the excluded indices, producing a new list 
    new_list = [i for i in list_a if list_a.index(i) not in excl_ind]
    if verbose:
        print(new_list)

    return(new_list)

Örnek kullanım:

my_list = ['a', 'b', 'c', 'd', 'e', 'f', 'woof']
# index = [0, 3, 6]

# define excluded names list
excl_names_list = ['woof', 'c']

list_diff(my_list, excl_names_list)
>> ['a', 'b', 'd', 'e', 'f']
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.