回答:
おそらくこの問題の最善の解決策ではありません:
indices = 0, 2
somelist = [i for j, i in enumerate(somelist) if j not in indices]
somelist = [ lst[i] for i in xrange(len(lst)) if i not in set(indices) ]
ですか?
どういうわけか、私はここでの答えのどれも好きではありません。はい、機能しますが、厳密に言うと、それらのほとんどはリスト内の要素を削除していません。(ただし、コピーを作成してから、元のコピーを編集したコピーで置き換えます)。
なぜ最初に高いインデックスを削除しないのですか?
これには理由がありますか?私はただやります:
for i in sorted(indices, reverse=True):
del somelist[i]
アイテムを逆方向に削除したくない場合は、最後に削除したインデックスより大きいインデックス値をデクリメントする必要があると思います(別のリストがあるため、実際には同じインデックスを使用できません)。リストのコピー(これは「削除」ではなく、オリジナルを編集されたコピーで置き換えます)。
ここに何か不足していますか?逆の順序で削除しない理由はありますか?
隣接していない複数のアイテムを削除する場合は、説明することが最善の方法です(そして、はい、必ず、最も高いインデックスから開始してください)。
アイテムが隣接している場合は、スライス割り当て構文を使用できます。
a[2:10] = []
del a[2:10]
同じ効果で言うこともできます。
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で書かれたコンパイル済みコードです。
関数として:
def multi_delete(list_, *args):
indexes = sorted(list(args), reverse=True)
for index in indexes:
del list_[index]
return list_
n log(n)時間で実行されます。これにより、これが最速の正しいソリューションになるはずです。
n log n
?本当に?del list[index]
O(1)だとは思わない。
それで、あなたは本質的に一度のパスで複数の要素を削除したいですか?その場合、次に削除する要素の位置は、以前に削除された要素の数だけオフセットされます。
私たちの目標は、インデックス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
。
私は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
、よく知られているフォーマット(たとえば、常に数値)の単純なケースで機能することを期待しています...
これは、(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]
。
[ value for (i, value) in enumerate(lst) if i not in set(indices) ]
。ただし、値で削除する方法も示すので、ここでは答えをここに残しておきます。これは簡単なケースですが、誰かを助けるかもしれません。
indices_as_set = set(indices)
、[ value for (i, value) in enumerate(lst) if i not in indices_as_set ]
それをスピードアップします。
delete__by_values()
ますか?
要素を削除する別の方法を次に示します。また、リストが本当に長い場合は、より高速です。
>>> 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
これは言及されましたが、どういうわけか実際にそれを正しく理解することができた人はいません。
上の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))
。
j not in indices
はO(1)
です。
j not in indices
ため、まだルックアップが必要ですO(log(len(indices)))
。2要素セットでのルックアップはに該当することに同意しますO(1)
が、一般的なケースではになりますO(log(N))
。どちらにしてO(N log(N))
も、まだ勝つO(N^2)
。
j not in indices
はO(1)
真面目です。
技術的には、答えはNOです。2つのオブジェクトを同時に削除することはできません。ただし、美しいpythonの1行で2つのオブジェクトを削除することは可能です。
del (foo['bar'],foo['baz'])
再帰的に削除しfoo['bar']
、foo['baz']
ノブを簡単に回せるようにするさまざまなソリューションを比較する方法を探していました。
まず、データを生成しました。
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
ます。
実際には、次の2つの方法を考えることができます。
リストを次のようにスライスします(これにより、1番目、3番目、8番目の要素が削除されます)
somelist = somelist [1:2] + somelist [3:7] + somelist [8:]
それをその場で行いますが、一度に1つ:
somelist.pop(2)somelist.pop(0)
リストではなく、辞書でそのようにすることができます。リストの要素は順番に並んでいます。辞書では、それらはインデックスのみに依存します。
簡単なコードだけでそれを説明することによって:
>>> 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())]
とにかく、あなたが言ったように、より高いインデックスから削除を開始する方が良いと思います。
@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]
この理由だけでインポートするのはやり過ぎかもしれませんが、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つについてはどうですか(私は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)
[「大西洋」、「太平洋」、「インド」]
これまでに提供された回答のいずれも、削除するインデックスの任意の数について、リストの長さで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]
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']