リストから複数の要素を削除する


160

リストから複数の要素を同時に削除することはできますか?インデックス0と2の要素を削除しdel somelist[0]、の後にを続けたい場合del somelist[2]、2番目のステートメントは実際にを削除しsomelist[3]ます。

私は常に最初に大きい番号の要素を削除することができると思いますが、もっと良い方法があることを望んでいます。

回答:


110

おそらくこの問題の最善の解決策ではありません:

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

2
ほとんどの場合、リスト全体を削除した場合のみです。len(indices)* len(somelist)になります。また、または所望されない場合がありますコピー、作成
リチャードルヴァスール

リストの値をチェックしている場合は、そうです。'in'演算子はリストの値を処理しますが、dictのキーを処理します。私が間違っている場合は、pep / referenceをポイントしてください
Richard Levasseur

5
インデックスにタプルを選択した理由は、レコードの単純さだけでした。それはO(n)を与えるset()の完璧な仕事です
SilentGhost 2009年

18
これは、somelistからアイテムを削除するのではなく、まったく新しいリストを作成することです。元のリストへの参照を保持しているものがある場合でも、すべてのアイテムが含まれています。
トムフューチャー

2
@SilentGhost列挙を行う必要はありません。これはどうsomelist = [ lst[i] for i in xrange(len(lst)) if i not in set(indices) ]ですか?
ToolmakerSteve

183

どういうわけか、私はここでの答えのどれも好きではありません。はい、機能しますが、厳密に言うと、それらのほとんどはリスト内の要素を削除していません。(ただし、コピーを作成してから、元のコピーを編集したコピーで置き換えます)。

なぜ最初に高いインデックスを削除しないのですか?

これには理由がありますか?私はただやります:

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

アイテムを逆方向に削除したくない場合は、最後に削除したインデックスより大きいインデックス値をデクリメントする必要があると思います(別のリストがあるため、実際には同じインデックスを使用できません)。リストのコピー(これは「削除」ではなく、オリジナルを編集されたコピーで置き換えます)。

ここに何か不足していますか?逆の順序で削除しない理由はありますか?


1
なぜこれが受け入れられた答えに選ばれなかったのか分かりません!。これをありがとう。
swathis 2016年

4
理由は2つあります。(a)リストの場合、一部の要素を複数回前方にシフトする必要があるため、時間の複雑さは平均で(インデックスのセットを使用する)「コピーの作成」メソッドよりも高くなります(ランダムインデックスを想定)。(b)少なくとも私にとっては、実際のプログラムロジックに対応せず、技術的な理由でのみ存在するソート関数があるため、読みにくいです。もう論理はよくわかったけど、読みづらい気がする。
不可抗力な夜、

1
@ImperishableNight詳しく説明できますか(a)?「いくつかの要素をシフトする必要がある」というのがわかりません。(b)については、明確に読む必要がある場合は、関数を定義するだけで済みます。
tglaria 2018年

109

隣接していない複数のアイテムを削除する場合は、説明することが最善の方法です(そして、はい、必ず、最も高いインデックスから開始してください)。

アイテムが隣接している場合は、スライス割り当て構文を使用できます。

a[2:10] = []

95
del a[2:10]同じ効果で言うこともできます。
STH

8
@sth興味深いことに、delは割り当てよりも少し高速です。
thefourtheye 2013年

24

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

numpy最後に配列で終わることを気にしない場合は、を省略できます.tolist()。また、速度が大幅に改善されているため、これをよりスケーラブルなソリューションにできます。ベンチマークはしていませんが、numpy操作はCまたはFortranで書かれたコンパイル済みコードです。


1
要素が+1連続していない一般的な解決策
noɥʇʎԀʎzɐɹƆ

1
ここで質問、['a'、42]を削除する方法は?
evanhutomo 2017年

このソリューションのスピードは、他のソリューションと比較して大きなボーナスポイントです。私が言えることは、非常に大規模なデータセットの場合、数分かかって何かを達成するのに数分かかっていたということです。
レゲル

18

Gregの回答の専門分野として、拡張スライス構文を使用することもできます。例えば。アイテム0と2を削除したい場合:

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

もちろん、これは任意の選択をカバーするものではありませんが、2つのアイテムを削除する場合には確実に機能します。


16

関数として:

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

n log(n)時間で実行されます。これにより、これが最速の正しいソリューションになるはずです。


1
args.sort()。reverse()のバージョンは間違いなく優れています。それはまた、投げる代わりに、またはひどく、静かに腐敗させるのではなく、ディクテーションで動作することも起こります。

タプルにはsort()が定義されていないため、最初にリストに変換する必要があります。sort()はNoneを返すため、reverse()を使用できませんでした。
SilentGhost 2009年

@ R. Pate:そのため、最初のバージョンを削除しました。ありがとう。@ SilentGhost:修正しました。
Nikhil Chelliah、2009年

@Nikhil:いいえ、しませんでした;)args = list(args)args.sort()args.reverse()しかし、より良いオプションは次のとおりです:args =
Sorted

2
n log n?本当に?del list[index]O(1)だとは思わない。
user202729

12

それで、あなたは本質的に一度のパスで複数の要素を削除したいですか?その場合、次に削除する要素の位置は、以前に削除された要素の数だけオフセットされます。

私たちの目標は、インデックス1、4、および7になるように事前計算されたすべての母音を削除することです。重要なのは、to_deleteインデックスが昇順であることに注意してください。昇順でないと機能しません。

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

要素を任意の順序で削除する場合は、さらに複雑になります。IMO、並べ替えのto_delete方が、から差し引く必要がある場合と差し引かない場合を理解するよりも簡単な場合がありますindex


8

私はPythonの完全な初心者であり、現在のプログラミングは控えめに言っても粗雑で汚いですが、私の解決策は、初期のチュートリアルで学習した基本的なコマンドの組み合わせを使用することでした。

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

明らかに、「削除マーク」文字を選択する必要があるため、これには制限があります。

リストのサイズが拡大するときのパフォーマンスについては、私のソリューションは最適ではないと確信しています。ただし、それは簡単で、他の初心者にアピールすることを望みsome_list、よく知られているフォーマット(たとえば、常に数値)の単純なケースで機能することを期待しています...


2
「!」を使用する代わりに 特殊文字として、Noneを使用します。これにより、すべてのキャラクターが有効になり、可能性が解放されます
portforwardpodcast 2015年

5

これは、(SilentGhostの元の回答のように)タプルの作成にenumerate()を使用しない代替方法です。

これは私には読みやすいようです。(enumerateを使用する癖があったとしたら、私は違った感じになるかもしれません。)警告:2つのアプローチのパフォーマンスをテストしていません。

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

注:Python 2.7構文。Python 3の場合xrange== range

使用法:

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

somelist:

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

---ボーナス---

リストから複数の値を削除します。つまり、削除する値があります。

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

使用法:

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

somelist:

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

これは以前と同じ答えですが、今回は削除するVALUESを指定しました[0, 44, 55]


列挙の結果に使用されている説明的でない変数名のため、@ SilentGhostは読みにくいと判断しました。また、括弧を使用すると読みやすくなります。したがって、ここに私が彼のソリューションをどのように表現するかを示します(パフォーマンスのために「セット」が追加されています)[ value for (i, value) in enumerate(lst) if i not in set(indices) ]。ただし、値で削除する方法も示すので、ここでは答えをここに残しておきます。これは簡単なケースですが、誰かを助けるかもしれません。
ToolmakerSteve

@ Veedrac-ありがとうございます。最初にセットをビルドするように書き直しました。あなたはどう思いますか-SilentGhostのより速い解決策?(私はあなたの意見を求め、その実際の時間、それには重要十分に考慮していない。)同様に、私はのように再書き込みSilentGhostのバージョンだろうindices_as_set = set(indices)[ value for (i, value) in enumerate(lst) if i not in indices_as_set ]それをスピードアップします。
ToolmakerSteve 2014年

二重下線のスタイル上の理由はありdelete__by_values()ますか?
トム

5

リストインデックス値を使用する代替リスト内包法:

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]

これは次を返します:

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

良い答えですがindex、リストの反復子がメソッドを使用しているため、インデックスのリストに誤解を招くような名前を付けるindex()
Joe

4

要素を削除する別の方法を次に示します。また、リストが本当に長い場合は、より高速です。

>>> 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: "for ..:"ブロックを必要とするのではなく、式の一部としてforアクションを実行するためのdequeの興味深い使用。ただし、この単純なケースでは、Nikhilのブロックの方が読みやすくなっています。
ToolmakerSteve

4

これは言及されましたが、どういうわけか実際にそれを正しく理解することができた人はいません。

上のO(n)解決策のようになります。

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

これはSilentGhostのバージョンに非常に似ていますが、2つのブレースが追加されています。


これは、反復ごとに実行O(n)するルックアップをカウントする場合ではありませんlog(len(indices))
Mad Physicist、

@MadPhysicist j not in indicesO(1)です。
Veedrac、2015年

どのようにその番号を取得するのかわかりません。インデックスはセットであるj not in indicesため、まだルックアップが必要ですO(log(len(indices)))。2要素セットでのルックアップはに該当することに同意しますO(1)が、一般的なケースではになりますO(log(N))。どちらにしてO(N log(N))も、まだ勝つO(N^2)
Mad Physicist


そして、2つのブレースは正確に何をしましたか?
Nuclear03020704

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

基本的にはトップ投票の回答と同じですが、書き方が異なります。l.index()はリスト内の重複した要素を処理できないため、使用はお勧めできません。


2

メソッドを削除すると、リスト要素が大幅にシフトします。私はコピーを作成する方が良いと思います:

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

技術的には、答えはNOです。2つのオブジェクトを同時に削除することはできません。ただし、美しいpythonの1行で2つのオブジェクトを削除することは可能です。

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

再帰的に削除しfoo['bar']foo['baz']


これはリストではなくdictオブジェクトから削除しますが、私はまだ+1しているので、かなりきれいです!
Ulf Aslak 2016

これは、適切な構文でリストにも適用されます。ただし、2つのオブジェクトを同時に削除することはできないという主張は誤りです。@bobinceの回答を参照
Pedro Gimeno

2

これを行うには、インデックスリストを降順で並べ替えた後、forループを使用してインデックスを反復処理します。

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

listAのインデックス0および2の場合:

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

一部のランダムなインデックスをlistAから削除する場合:

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

2

ノブを簡単に回せるようにするさまざまなソリューションを比較する方法を探していました。

まず、データを生成しました。

import random

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

次に、関数を定義しました。

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)

次にtimeit、ソリューションを比較するために使用しました:

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)

出力

set =   1.711
del =   3.450
pop =   3.618

したがって、インデックスがaのジェネレータsetが勝者でした。そしてdel、少し速くなりpopます。


この比較をありがとう、これは私自身のテスト(実際にはコードを借りただけ)を行うことにつながり、少数の項目を削除するために、SETを作成するためのオーバーヘッドがそれを最悪のソリューションにします(10、100、500を使用) 「y」の長さを見るとわかります)。ほとんどの場合、これはアプリケーションに依存します。
tglaria 2017年

2

このロジックを使用できます。

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

最高のインデックスから削除するアイデアの別の実装。

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

1

実際には、次の2つの方法を考えることができます。

  1. リストを次のようにスライスします(これにより、1番目、3番目、8番目の要素が削除されます)

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

  2. それをその場で行いますが、一度に1つ:

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


1

リストではなく、辞書でそのようにすることができます。リストの要素は順番に並んでいます。辞書では、それらはインデックスのみに依存します。

簡単なコードだけでそれを説明することによって

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

辞書内のリストを「変換」する方法は次のとおりです。

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

逆は:

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

とにかく、あなたが言ったように、より高いインデックスから削除を開始する方が良いと思います。


Pythonは、[dct [i] for i in dct]が常に増加するiの値を使用することを保証しますか?もしそうなら、list(dct.values())は確かにより良いです。

私はそれについて考えていませんでした。あなたが正しい。私が[ここ] [1]を読んだとき、アイテムが順番に選ばれる、または少なくとも予想される順番になるという保証はありません。編集しました。[1]:docs.python.org/library/stdtypes.html#dict.items
Andrea Ambu

2
この回答は、基本的に間違った方法で辞書について語っています。辞書にはKEYSがあります(INDICESではありません)。はい、キーと値のペアは互いに独立しています。いいえ、エントリを削除する順序は関係ありません。リストから一部の要素を削除するためだけに辞書に変換するのはやり過ぎです。
ToolmakerSteve

1

@sthからのコメントを一般化するため。任意のクラス内の項目の削除は、その実装abc.MutableSequence、とにlist特には、を介して行われる__delitem__魔法の方法。このメソッドはと同様に機能__getitem__します。つまり、整数またはスライスを受け入れることができます。次に例を示します。

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)

これは出力されます

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

1

この理由だけでインポートするのはやり過ぎかもしれませんが、pandasとにかく使用している場合、解決策はシンプルで簡単です。

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

ソートコストを避け、リストを明示的にコピーする必要がありません。


0

これらの1つについてはどうですか(私はPythonを初めて使用しましたが、問題はないようです)。

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)

[「大西洋」、「太平洋」、「インド」]

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)

[「大西洋」、「太平洋」、「インド」]


0

これまでに提供された回答のいずれも、削除するインデックスの任意の数について、リストの長さでO(n)の適切な場所で削除実行しないため、これが私のバージョンです。

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

削除も使用できます。

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

list_diff最初のリストの元の順序を維持しながら、2つのリストを入力として受け取り、それらの差を返す関数にすべてまとめました。

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)

使用例:

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']
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.