web-dev-qa-db-ja.com

リスト内のエレガントなサブリストの検索

ノイズに囲まれた既知のパターンを含むリストがある場合、パターンに等しいすべてのアイテムを取得するエレガントな方法があります。私の粗雑なコードについては以下を参照してください。

list_with_noise = [7,2,1,2,3,4,2,1,2,3,4,9,9,1,2,3,4,7,4,3,1,2,3,5]
known_pattern = [1,2,3,4]
res = []


for i in list_with_noise:
    for j in known_pattern:
        if i == j:
            res.append(i)
            continue

print res

2, 1, 2, 3, 4, 2, 1, 2, 3, 4, 1, 2, 3, 4, 4, 3

ボーナス:完全なパターンが存在しない場合、iの追加を避けます(つまり、1,2,3,4は許可しますが、1,2,3は許可しません)

例:

find_sublists_in_list([7,2,1,2,3,4,2,1,2,3,4,9,9,1,2,3,4,7,4,3,1,2,3,5],[1,2,3,4])

[1,2,3,4],[1,2,3,4],[1,2,3,4]


find_sublists_in_list([7,2,1,2,3,2,1,2,3,6,9,9,1,2,3,4,7,4,3,1,2,6],[1,2,3,4])

[1,2,3],[1,2,3],[1,2,3]

リストには名前付きタプルが含まれています。

25
rikAtee

私はこの質問が5か月前で既に「受け入れられている」ことを知っていますが、非常によく似た問題をグーグルで調べてこの質問に至り、すべての回答にいくつかのかなり重大な問題があるようですでSO答え、だから私は見つけたものをガラガラするつもりです。

私が理解しているように、質問の最初の部分は非常に簡単です。「パターン」に含まれないすべての要素を除外した元のリストを返すだけです。その考えに従って、私が考えた最初のコードはfilter()関数を使用しました:

def subfinder(mylist, pattern):
    return list(filter(lambda x: x in pattern, mylist))

私はこのソリューションが元のソリューションよりも明らかに簡潔であると言いますが、それは高速ではなく、少なくとも認識できるほどではありません。ラムダ式を使用する十分な理由がない場合はラムダ式を回避しようとします。実際、私が思いつくことができる最善の解決策は、単純なリストの理解に関係していました。

def subfinder(mylist, pattern):
    pattern = set(pattern)
    return [x for x in mylist if x in pattern]

このソリューションは、オリジナルよりもエレガントで非常に高速です。理解度はオリジナルよりも約120%高速ですが、テストではパターンを最大320%高速に設定した最初のバンプにキャストします。

ボーナスのために:私はちょうどそれに飛び込みます、私の解決策は次のとおりです:

def subfinder(mylist, pattern):
    matches = []
    for i in range(len(mylist)):
        if mylist[i] == pattern[0] and mylist[i:i+len(pattern)] == pattern:
            matches.append(pattern)
    return matches

これは、Steven Rumbalskiの「非効率な1つのライナー」のバリエーションであり、「mylist [i] == pattern [0]」チェックが追加され、Pythonの短絡評価のおかげで、元のステートメントよりも大幅に高速化されます。 itertoolsバージョン(および私が知る限り他のすべてのソリューション)andは、重複するパターンもサポートします。それであなたは行き​​ます。

37
mintchkin

これにより、質問の「ボーナス」部分が得られます。

pattern = [1, 2, 3, 4]
search_list = [7,2,1,2,3,4,2,1,2,3,4,9,9,1,2,3,4,7,4,3,1,2,3,5]
cursor = 0
found = []
for i in search_list:
    if i == pattern[cursor]:
        cursor += 1
        if cursor == len(pattern):
            found.append(pattern)
            cursor = 0
    else:
        cursor = 0

ボーナス以外の場合:

pattern = [1, 2, 3, 4]
search_list = [7,2,1,2,3,4,2,1,2,3,4,9,9,1,2,3,4,7,4,3,1,2,3,5]
cursor = 0
found = []
for i in search_list:
    if i != pattern[cursor]:
        if cursor > 0:
            found.append(pattern[:cursor])
        cursor = 0
    else:
        cursor += 1

最後に、これはオーバーラップを処理します。

def find_matches(pattern_list, search_list):
    cursor_list = []
    found = []
    for element in search_list:
        cursors_to_kill = []
        for cursor_index in range(len(cursor_list)):
            if element == pattern_list[cursor_list[cursor_index]]:
                cursor_list[cursor_index] += 1
                if cursor_list[cursor_index] == len(pattern_list):
                    found.append(pattern_list)
                    cursors_to_kill.append(cursor_index)
            else:
                cursors_to_kill.append(cursor_index)
        cursors_to_kill.reverse()
        for cursor_index in cursors_to_kill:
            cursor_list.pop(cursor_index)
        if element == pattern_list[0]:
            cursor_list.append(1)
    return found
4
Silas Ray
list_with_noise = [7,2,1,2,3,4,2,1,2,3,4,9,9,1,2,3,4,7,4,3,1,2,3,5]
string_withNoise = "".join(str(i) for i in list_with_noise)
known_pattern = [1,2,3,4]
string_pattern = "".join(str(i) for i in known_pattern)
string_withNoise.count(string_pattern)
1
user850498

与えられた:

a_list = [7,2,1,2,3,4,2,1,2,3,4,9,9,1,2,3,4,7,4,3,1,2,3,5]
pat = [1,2,3,4]

非効率的な1つのライナーを次に示します。

res = [pat for i in range(len(a_list)) if a_list[i:i+len(pat)] == pat]

より効率的なitertoolsバージョンは次のとおりです。

from itertools import izip_longest, islice

res = []
i = 0  

while True:
    try:
        i = a_list.index(pat[0], i)
    except ValueError:
        break
    if all(a==b for (a,b) in izip_longest(pat, islice(a_list, i, i+len(pat)))):
        res.append(pat)
        i += len(pat)
    i += 1
0

問題に対する慣用的で構成可能なソリューション。

まず、 itertoolsレシピconsume(イテレータから指定された数の要素を消費および破棄します。その後、itertoolspairwiseレシピを取得し、それを使用してnwise関数に拡張する必要があります。 consume

import itertools

def nwise(iterable, size=2):
    its = itertools.tee(iterable, size)
    for i, it in enumerate(its):
        consume(it, i)  # Discards i elements from it
    return Zip(*its)

これで、ボーナスの問題を解決するのは本当に簡単です。

def find_sublists_in_list(biglist, searchlist):
    searchtup = Tuple(searchlist)
    return [list(subtup) for subtup in nwise(biglist, len(searchlist)) if subtup == searchtup]

    # Or for more obscure but faster one-liner:
    return map(list, filter(Tuple(searchlist).__eq__, nwise(biglist, len(searchlist))))

同様に、主な問題に対するより簡潔で迅速な(ややきれいではないが)ソリューションが置き換わります。

def subfinder(mylist, pattern):
    pattern = set(pattern)
    return [x for x in mylist if x in pattern]

で:

def subfinder(mylist, pattern):
    # Wrap filter call in list() if on Python 3 and you need a list, not a generator
    return filter(set(pattern).__contains__, mylist)

これは同じように動作しますが、一時的なsetを名前に保存する必要がなくなり、すべてのフィルタリング作業をCにプッシュします。

0
ShadowRanger