web-dev-qa-db-ja.com

リストからサブリストをすべて削除する方法

2つのリストがあります。

_big_list = [2, 1, 2, 3, 1, 2, 4]
sub_list = [1, 2]
_

Big_listのすべてのsub_listオカレンスを削除します。

結果は_[2, 3, 4]_である必要があります

文字列の場合、これを使用できます:

_'2123124'.replace('12', '')
_

ただし、これはリストでは機能しません。

これは リストからサブリストを削除する の複製ではありません。大きなリストからすべてのサブリストを削除するためです。他の質問では、結果は_[5,6,7,1,2,3,4]_になります。

更新:簡単にするために、この例では整数を使用しました。ただし、リスト項目は任意のオブジェクトにすることができます。

Update2:

_big_list = [1, 2, 1, 2, 1]_および_sub_list = [1, 2, 1]_の場合、結果を_[2, 1]_にしたい('12121'.replace('121', '')のように)

Update3:

StackOverflowからソースコードをコードにコピーして貼り付けるのは好きではありません。だからこそ、ソフトウェアの推奨事項で2番目の質問を作成しました: https://softwarerecs.stackexchange.com/questions/51273/library-to-remove-every-occurrence-of-sub-list-from-list-python

Update4:この1つのメソッド呼び出しを行うライブラリを知っている場合は、これを回答として書いてください。これは私の推奨ソリューションです。

テストはこのテストに合格する必要があります。

_def test_remove_sub_list(self):
    self.assertEqual([1, 2, 3], remove_sub_list([1, 2, 3], []))
    self.assertEqual([1, 2, 3], remove_sub_list([1, 2, 3], [4]))
    self.assertEqual([1, 3], remove_sub_list([1, 2, 3], [2]))
    self.assertEqual([1, 2], remove_sub_list([1, 1, 2, 2], [1, 2]))
    self.assertEquals([2, 1], remove_sub_list([1, 2, 1, 2, 1], [1, 2, 1]))
    self.assertEqual([], remove_sub_list([1, 2, 1, 2, 1, 2], [1, 2]))
_
45
guettli

自分で実装する必要があります。基本的な考え方は次のとおりです。

def remove_sublist(lst, sub):
    i = 0
    out = []
    while i < len(lst):
        if lst[i:i+len(sub)] == sub:
            i += len(sub)
        else:
            out.append(lst[i])
            i += 1
    return out

これは、元のリストのすべての要素に沿って進み、サブセットのメンバーではない場合、出力リストに追加します。このバージョンはあまり効率的ではありませんが、サブセットを含まない新しいリストを作成するという意味で、指定した文字列の例のように機能します。 ==をサポートしている限り、任意の要素タイプでも機能します。 [1,1,1]から[1,1,1,1]を削除すると、文字列に関しては[1]になります。

ここに IDEOneリンク の結果が表示されています

>>> remove_sublist([1, 'a', int, 3, float, 'a', int, 5], ['a', int])
[1, 3, <class 'float'>, 5]
25
Mad Physicist

delslicingを試してください。最悪の時間の複雑さはO(N^2)です。

sub_list=['a', int]
big_list=[1, 'a', int, 3, float, 'a', int, 5]
i=0
while i < len(big_list):
    if big_list[i:i+len(sub_list)]==sub_list:
        del big_list[i:i+len(sub_list)]
    else:
        i+=1

print(big_list)

結果:

[1, 3, <class 'float'>, 5]
14

再帰的アプローチ:

def remove(lst, sub):
    if not lst:
        return []
    if lst[:len(sub)] == sub:
        return remove(lst[len(sub):], sub)
    return lst[:1] + remove(lst[1:], sub)
print(remove(big_list, sub_list))

この出力:

[2, 3, 4]
8
blhsing

lst[i:i+len(sub)] < len(lst)かどうかを確認する改善されたバージョン

def remove_sublist(lst, sub):
    i = 0
    out = []
    sub_len = len(sub)
    lst_len = len(lst)
    while i < lst_len:
        if (i+sub_len) < lst_len:
            if lst[i: i+sub_len] == sub:
                i += sub_len
            else:
                out.append(lst[i])
                i += 1
        else:
            out.append(lst[i])
            i += 1

    return out
6
mingganz

これはどう:

def remove_sublist(lst, sub):
    max_ind_sub = len(sub) - 1
    out = []
    i = 0
    tmp = []

    for x in lst:
        if x == sub[i]:
            tmp.append(x)
            if i < max_ind_sub: # partial match 
                i += 1
            else:  # found complete match
                i = 0
                tmp = []
        else:
            if tmp:  # failed partial match 
                i = 0
                out += tmp
            if x == sub[0]:  # partial match
                i += 1
                tmp = [x]
            else:
                out.append(x)

    return out

性能:

lst = [2, 1, 2, 3, 1, 2, 4]
sub = [1, 2]
%timeit remove_sublist(lst, sub)  # solution of Mad Physicist
%timeit remove_sublist_new(lst, sub)
>>> 2.63 µs ± 112 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
>>> 1.77 µs ± 13.7 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

更新

私の最初の解決策にはバグがありました。修正できました(上記のコードを更新しました)が、この方法は今ではもっと複雑に見えます。パフォーマンスの点では、ローカルマシンのMad Physicistのソリューションよりも優れています。

6
RandomDude

_itertools.Zip_longest_を使用して、n個の要素タプル(nはsub_listの長さ)を作成し、要素の1つがsub_listに一致したときに現在の要素と次のn-1個の要素をフィルタリングします

_>>> from itertools import Zip_longest, islice
>>> itr = Zip_longest(*(big_list[i:] for i in range(len(sub_list))))
>>> [sl[0] for sl in itr if not (sl == Tuple(sub_list) and next(islice(itr, len(sub_list)-2, len(sub_list)-1)))]
[2, 3, 4]
_

効率を改善するために、フィルタリングを開始する前にTuple(sub_list)およびlen(sub_list)を計算できます

_>>> l = len(sub_list)-1
>>> tup = Tuple(sub_list)
>>> [sl[0] for sl in itr if not (sl == tup and next(islice(itr, l-1, l)))]
[2, 3, 4]
_
5
Sunitha

Update_more_itertools_ ライブラリがリリースされました _more_itertool.replace_ 、aこの特定の問題を解決するツール(オプション3を参照)。

まず、一般的なイテラブル(リスト、文字列、イテレータなど)で機能する他のオプションを次に示します。

コード

オプション1-ライブラリなし

_def remove(iterable, subsequence):
    """Yield non-subsequence items; sans libraries."""
    seq = Tuple(iterable)
    subsequence = Tuple(subsequence)
    n = len(subsequence)
    skip = 0

    for i, x in enumerate(seq):
        slice_ = seq[i:i+n]
        if not skip and (slice_ == subsequence):
            skip = n
        if skip:
            skip -= 1
            continue
        yield x   
_

オプション2- _more_itertools_

_import more_itertools as mit


def remove(iterable, subsequence):
    """Yield non-subsequence items."""
    iterable = Tuple(iterable)
    subsequence = Tuple(subsequence)
    n = len(subsequence)
    indices = set(mit.locate(mit.windowed(iterable, n), pred=lambda x: x == subsequence))

    it_ = enumerate(iterable)
    for i, x in it_:
        if i in indices:
            mit.consume(it_, n-1)
        else:
            yield x
_

デモ

_list(remove(big_list, sub_list))
# [2, 3, 4]

list(remove([1, 2, 1, 2], sub_list))
# []

list(remove([1, "a", int, 3, float, "a", int, 5], ["a", int]))
# [1, 3, float, 5]

list(remove("11111", "111"))
# ['1', '1']

list(remove(iter("11111"), iter("111")))
# ['1', '1']
_

オプション3- _more_itertools.replace_を使用:

デモ

_pred = lambda *args: args == Tuple(sub_list)
list(mit.replace(big_list, pred=pred, substitutes=[], window_size=2))
# [2, 3, 4]

pred=lambda *args: args == Tuple(sub_list)
list(mit.replace([1, 2, 1, 2], pred=pred, substitutes=[], window_size=2))
# []

pred=lambda *args: args == Tuple(["a", int])
list(mit.replace([1, "a", int, 3, float, "a", int, 5], pred=pred, substitutes=[], window_size=2))
# [1, 3, float, 5]

pred=lambda *args: args == Tuple("111")
list(mit.replace("11111", pred=pred, substitutes=[], window_size=3))
# ['1', '1']

pred=lambda *args: args == Tuple(iter("111"))
list(mit.replace(iter("11111"), pred=pred, substitutes=[], window_size=3))
# ['1', '1']
_

詳細

これらすべての例では、メインウィンドウを小さなウィンドウスライスでスキャンしています。スライスに見つからないものはすべて生成し、スライスにあるものはすべてスキップします。

オプション1-ライブラリなし

列挙されたシーケンスを繰り返し、サイズn(サブシーケンスの長さ)のスライスを評価します。次のスライスがサブシーケンスと等しい場合、skipをリセットしてアイテムを生成します。それ以外の場合は、それを過ぎて繰り返します。 skipは、ループを何回進めるかを追跡します。 sublistのサイズは_n=2_であるため、一致ごとに2回スキップします。

注:最初の2つのTuple割り当てを削除し、iterableパラメーターをseqに置き換えることにより、このオプションを sequences のみで動作するように変換できます。 def remove(seq, subsequence):

オプション2- _more_itertools_

インデックスは、イテラブルの一致するすべてのサブシーケンスに配置されます。列挙型イテレータの反復中に、indicesでインデックスが見つかった場合、イテレータから次の_n-1_要素を消費することにより、残りのサブシーケンスがスキップされます。それ以外の場合、アイテムが生成されます。

このライブラリを_> pip install more_itertools_経由でインストールします。

オプション3- _more_itertools.replace_を使用:

このツールは、述語で定義されたアイテムのサブシーケンスを代替値に置き換えます。アイテムを削除するには、空のコンテナ、たとえば_substitutes=[]_。置き換えられるアイテムの長さは、_window_size_パラメーターで指定されます(この値は、サブシーケンスの長さに等しい)。

5
pylang

上記よりも読みやすく、追加のメモリフットプリントなし:

def remove_sublist(sublist, mainlist):

    cursor = 0

    for b in mainlist:
        if cursor == len(sublist):
            cursor = 0
        if b == sublist[cursor]:
            cursor += 1
        else:
            cursor = 0
            yield b

    for i in range(0, cursor):
        yield sublist[i]

これは、ライブラリの関数が必要な場合にオンラインで使用するためのものです。

[x for x in remove_sublist([1, 2], [2, 1, 2, 3, 1, 2, 4])]
4
Dmitry Dyachkov

Python 2.x!

from more_itertools import locate, windowed
big_list = [1, 2, 1, 2, 1]
sub_list = [1, 2, 1]

"""
Fetching all starting point of indexes (of sub_list in big_list)
to be removed from big_list. 
"""

i = list(locate(windowed(big_list, len(sub_list)), pred=lambda x: x==Tuple(sub_list)))

""" 
Here i comes out to be [0, 2] in above case. But index from 2 which 
includes 1, 2, 1 has last 1 from the 1st half of 1, 2, 1 so further code is
to handle this case.
PS: this won't come for-
big_list = [2, 1, 2, 3, 1, 2, 4]
sub_list = [1, 2]
as here i comes out to be [1, 4]
"""

# The further code.
to_pop = []
for ele in i:
    if to_pop:
        if ele == to_pop[-1]:
            continue
    to_pop.extend(range(ele, ele+len(sub_list)))

# Voila! to_pop consists of all the indexes to be removed from big_list.

# Wiping out the elements!
for index in sorted(to_pop, reverse=True):
    del big_list[index]

後続のインデックスを破棄しないように、逆の順序で削除する必要があることに注意してください。

Python3では、locate()の署名が異なります。

3
user5319825

(最終的なアプローチについては、最後のコードスニペットを参照)

単純な文字列変換で十分だと思っていました。

big_list = [2, 1, 2, 3, 1, 2, 4]
sub_list = [1, 2]

new_list = list(map(int, list((''.join(map(str, big_list))).replace((''.join(map(str, sub_list))), ''))))

私は本質的に、リストの文字列に相当するもので検索/置換を行っています。変数の元の型が保持されるように、後で整数にマッピングします。これは、任意のサイズの大きなリストとサブリストで機能します。

ただし、テキスト表現がない場合に任意のオブジェクトで呼び出すと、これは機能しない可能性があります。さらに、この方法では、オブジェクトのテキストバージョンのみが保持されます。元のデータ型を維持する必要がある場合、これは問題です。

このために、私は別のアプローチでソリューションを作成しました:

new_list = []
i = 0
while new_list != big_list:
    if big_list[i:i+len(sub_list)] == sub_list:
        del big_list[i:i+len(sub_list)]
    else:
        new_list.append(big_list[i])
        i += 1

本質的に、sub_listの重複を見つけるとそれらをすべて削除し、重複の一部ではない要素を見つけるとnew_listに追加します。 new_listとbig_listが等しい場合、すべての重複が見つかりました。これが停止します。インデックス作成エラーはないはずなので、try-exceptを使用していません。

これは@MadPhysicistの答えに似ており、ほぼ同じ効率ですが、私はメモリの消費量が少ないです。

この2番目のアプローチは、任意のサイズのリストを持つ任意のタイプのオブジェクトに対して機能するため、最初のアプローチよりもはるかに柔軟性があります。ただし、リストが整数の場合、最初のアプローチは高速です。

しかし、私はまだ終わっていません!私は、2番目のアプローチと同じ機能を持つ1行のリストの理解を作り上げました!

import itertools
new_list = [big_list[j] for j in range(len(big_list)) if j not in list(itertools.chain.from_iterable([ list(range(i, i+len(sub_list))) for i in [i for i, x in enumerate(big_list) if x == sub_list[0]] if big_list[i:i+len(sub_list)] == sub_list ]))]

初めは、これは気が重いように思えますが、非常に簡単だと確信しています!最初に、サブリストの最初の要素が発生したインデックスのリストを作成します。次に、これらの各インデックスについて、次の要素がサブリストを形成しているかどうかを確認します。その場合、サブリストの複製を形成するインデックスの範囲が別のリストに追加されます。その後、itertoolsの関数を使用して、結果のリストのリストをフラット化します。このフラット化されたリストのすべての要素は、サブリストの複製にあるインデックスです。最後に、big_listのすべての要素で構成されるnew_listを作成します。この要素には、フラット化されたリストにないインデックスがあります。

この方法は他の答えにはないと思います。どのように機能し、非常に効率的であるかを理解すると、非常にきちんとしているので最も気に入っています(リスト内包の性質のため) )。

1
Adi219

ジェネレータで再帰を使用できます:

def remove(d, sub_list):
   if d[:len(sub_list)] == sub_list and len(sub_list) <= len(d[:len(sub_list)]):
      yield from [[], remove(d[len(sub_list):], sub_list)][bool(d[len(sub_list):])]
   else:
      yield d[0]
      yield from [[], remove(d[1:], sub_list)][bool(d[1:])]

tests = [[[2, 1, 2, 3, 1, 2, 4], [1, 2]], [[1, 2, 1, 2], [1, 2]], [[1, 'a', int, 3, float, 'a', int, 5], ['a', int]], [[1, 1, 1, 1, 1], [1,1,1]]]
for a, b in tests:
  print(list(remove(a, b)))

出力:

[2, 3, 4]
[]
[1, 3, <class 'float'>, 5]
[1, 1]
0
Ajax1234

あなたが達成しようとしていることは、それを文字列のリストに変換し、再度置き換えた後に整数型に変換することによって行うことができます。

1行で次のようにできます

map(int,list(("".join(map(str, big_list))).replace("".join(map(str, sub_list)),'').replace(''.join((map(str, sub_list))[::-1]),'')))

入力

big_list = [1, 2, 1, 2, 1]
sub_list = [1, 2, 1]

出力

[2、1]

入力

big_list = [2, 1, 2, 3, 1, 2, 4]
sub_list = [1, 2]

出力

[2、3、4]

0
HimanshuGahlot

楽しみのために、ここに1ライナーに最も近い近似を示します。

from functools import reduce

big_list = [2, 1, 2, 3, 1, 2, 4]
sub_list = [1, 2]
result = reduce(lambda r, x: r[:1]+([1]+r[2:-r[1]],[min(len(r[0]),r[1]+1)]+r[2:])[r[-r[1]:]!=r[0]]+[x], big_list+[0], [sub_list, 1])[2:-1]

それが機能することを信用しませんか?それを確認してください IDEoneで

もちろん、効率的とは言えず、うんざりするほど謎めいていますが、OPが @ Mad Physicist's answer を受け入れるように説得するのに役立つはずです。

0
Leon