web-dev-qa-db-ja.com

同じリストの次のアイテムに基づいてリストからアイテムを削除します

私はpythonを学び始めたばかりで、ここにタンパク質配列(合計59,000配列)の並べ替えられたリストがあり、それらのいくつかは重複しています。ここにおもちゃのリストを作成しました:

ABCDE
ABCDEFG
ABCDEFGH
ABCDEFGHIJKLMNO
CEST
DBTSFDE
DBTSFDEO
EOEUDNBNUW
EOEUDNBNUWD
EAEUDNBNUW
FEOEUDNBNUW
FG
FGH

これらの短いオーバーラップを削除して、最も長いオーバーラップを保持したいので、目的の出力は次のようになります。

ABCDEFGHIJKLMNO
CEST
DBTSFDEO
EAEUDNBNUW
FEOEUDNBNUWD
FGH

どうすればできますか?私のコードは次のようになります:

with open('toy.txt' ,'r') as f:
    pattern = f.read().splitlines()
    print pattern

    for i in range(0, len(pattern)):
        if pattern[i] in pattern[i+1]:
            pattern.remove(pattern[i])
        print pattern

そして、私はエラーメッセージを受け取りました:

['ABCDE', 'ABCDEFG', 'ABCDEFGH', 'ABCDEFGHIJKLMNO', 'CEST', 'DBTSFDE', 'DBTSFDEO', 'EOEUDNBNUW', 'EAEUDNBNUW', 'FG', 'FGH']
['ABCDEFG', 'ABCDEFGH', 'ABCDEFGHIJKLMNO', 'CEST', 'DBTSFDE', 'DBTSFDEO', 'EOEUDNBNUW', 'EAEUDNBNUW', 'FG', 'FGH']
['ABCDEFG', 'ABCDEFGHIJKLMNO', 'CEST', 'DBTSFDE', 'DBTSFDEO', 'EOEUDNBNUW', 'EAEUDNBNUW', 'FG', 'FGH']
['ABCDEFG', 'ABCDEFGHIJKLMNO', 'CEST', 'DBTSFDE', 'DBTSFDEO', 'EOEUDNBNUW', 'EAEUDNBNUW', 'FG', 'FGH']
['ABCDEFG', 'ABCDEFGHIJKLMNO', 'CEST', 'DBTSFDEO', 'EOEUDNBNUW', 'EAEUDNBNUW', 'FG', 'FGH']
['ABCDEFG', 'ABCDEFGHIJKLMNO', 'CEST', 'DBTSFDEO', 'EOEUDNBNUW', 'EAEUDNBNUW', 'FG', 'FGH']
['ABCDEFG', 'ABCDEFGHIJKLMNO', 'CEST', 'DBTSFDEO', 'EOEUDNBNUW', 'EAEUDNBNUW', 'FG', 'FGH']
['ABCDEFG', 'ABCDEFGHIJKLMNO', 'CEST', 'DBTSFDEO', 'EOEUDNBNUW', 'EAEUDNBNUW', 'FGH']
Traceback (most recent call last):
  File "test.py", line 8, in <module>
    if pattern[i] in pattern[i+1]:
IndexError: list index out of range
21
Kenny

他にも有効な答えはありますが、実際の問題を説明するものはありません。あなたは実際には有効な解決策に本当に近づいており、私の意見では、最も読みやすい答えは何ですか。

エラーは、あなたがrange()。を使用してインデックスをチェックしているときに同じリストを変更していたという事実に起因しました。

したがって、i変数を増やしている間、リストから項目を削除していたため、ある時点で必然的にindex errorが発生しました。

したがって、これはいくつかの変更を加えた初期コードの作業バージョンです。

pattern = ["ABCDE","ABCDEFG","ABCDEFGH","ABCDEFGHIJKLMNO","CEST","DBTSFDE","DBTSFDEO","EOEUDNBNUW","EAEUDNBNUW","FG","FGH"]
output_pattern = []


for i in range(0, (len(pattern)-1)):
    if not pattern[i] in pattern[i+1]:
        output_pattern.append(pattern[i]) 

# Adding the last item
output_pattern.append(pattern[-1])   
print (output_pattern)

>>>> ['ABCDEFGHIJKLMNO', 'CEST', 'DBTSFDEO', 'EOEUDNBNUW', 'EAEUDNBNUW', 'FGH']    

このコードは、コメントセクションで言及したようにリストが以前に並べ替えられている場合に機能することに注意してください。

このコードは何をしていますか?

基本的には、最初の回答と同じロジックを使用して、リストで反復し、次のアイテムに現在のアイテムが含まれているかどうかを確認します。ただし、別のリストを使用し、 before last 項目まで反復すると、インデックスの問題が修正されます。しかし今、質問が来ます、

最後のアイテムはどうすればよいですか?

リストはソートされているため、最後のアイテムは常に一意であると見なすことができます。これが私が使用している理由です

output_pattern.append(pattern[-1])

最初のリストの最後のアイテムを追加します。

重要な注意

この回答は、OPの最初の質問への回答として書かれたもので、彼はより長い重複を維持したいと思っていました。同じリストの次の項目に基づいてを引用します @Chris_Randsが述べたように、懸念が生物学的タスクに関連していて、anyの重複を見つける必要がある場合、このソリューションはニーズに適していません。

このコードが重複の可能性を認識できない例

pattern = ["ACD", "AD", "BACD"]

可能な"ACD"オーバーラップを削除せずに同じ結果を出力します。さて、説明として、-これははるかに複雑なアルゴリズムを意味するであり、最初は質問の要件の範囲外であると思った。もしこれがあなたのケースであるなら、私はここで完全に間違っているかもしれませんが、C++実装がより適切であるように思えますコメントセクションで@Chris_Randsによって提案されたCD-Hitアルゴリズムをご覧ください。

15
scharette

groupby() およびmax()を使用すると、ここで役立ちます。

_from itertools import groupby

with open('toy.txt') as f_input:
    for key, group in groupby(f_input, lambda x: x[:2]):
        print(max(group, key=lambda x: len(x)).strip())
_

これは表示されます:

_ABCDEFGHIJKLMNO
CEST
DBTSFDEO
EOEUDNBNUW
EAEUDNBNUW
FGH
_

groupby()は、関数に基づいて一致する項目のリストを返すことで機能します。この場合、最初の2文字が同じ連続する行です。次に、max()関数がこのリストを受け取り、最も長いリスト項目を返します。

5
Martin Evans
# assuming list is sorted:
pattern = ["ABCDE",
"ABCDEFG",
"ABCDEFGH",
"ABCDEFGHIJKLMNO",
"CEST",
"DBTSFDE",
"DBTSFDEO",
"EOEUDNBNUW",
"EAEUDNBNUW",
"FG",
"FGH"]

pattern = list(reversed(pattern))

def iterate_patterns():
    while pattern:
        i = pattern.pop()
        throw_it_away = False
        for p in pattern:
            if p.startswith(i):
                throw_it_away = True
                break
        if throw_it_away == False:
            yield i

print(list(iterate_patterns()))

出力:

['ABCDEFGHIJKLMNO'、 'C​​EST'、 'DBTSFDEO'、 'EOEUDNBNUW'、 'EAEUDNBNUW'、 'FGH']

4
Andrej Kesely

これはあなたがなりたい場所を取得します:

with open('toy.txt' ,'r') as f:
    lines = f.readlines()
    data = set(lines)
    print(sorted([i for i in lines if len([j for j in data if j.startswith(i)])==1]))

#['ABCDEFGHIJKLMNO', 'CEST', 'DBTSFDEO', 'EAEUDNBNUW', 'EOEUDNBNUW', 'FGH']

同じテキストが複数回出現する場合に備えて、setを追加しました。

1
zipa

コード

import collections as ct


def read_file(filepath):
    """Yield a generator of lines from a file."""
    with open(filepath, "r") as f:
        for line in f:
            yield line.strip()


def find_longest_sequences(seqs):
    """Return a dict of the long common sequences."""
    seqs = Tuple(seqs) 
    dd = ct.defaultdict(list)
    [dd[k].append(seq) for seq in seqs for k in seqs if k in seq]
    return {max(v, key=len) for v in dd.values()}


data = read_file("test.txt")
find_longest_sequences(data)

出力

{'ABCDEFGHIJKLMNO',
 'CEST',
 'DBTSFDEO',
 'EAEUDNBNUW',
 'EOEUDNBNUWD',
 'FEOEUDNBNUW'}

詳細

を使用しております read_fileは、ファイルの各行を生成します。

find_longest_sequencesは、類似のシーケンスをグループ化する defaultdict を作成します。 2つのループでデータを反復処理します。

  1. 最初のループは、一意のシーケンスをキーとして持つ空のリストの辞書を作成します。
  2. 2番目のループは、キーと同様の文字列を値として追加します。

結果の辞書から値のセットが作成され、最も長いシーケンスが返されます。

予想される出力とのいくつかの不一致に注意してください:

  1. FGHABCDEFGHIJKLMNOと重複するため、有効な出力ではありません。
  2. FEOEUDNBNUWDは元のシーケンスではありません。シーケンスのオーバーラップには後処理が必要です。
1
pylang

期待と完全に一致するわけではありませんが、並べ替えられている(そしてEOEUDNBNUWD EAEUDNBNUWの近くではない)と述べ、あなたが欠落している理由がわかりませんEOEUDNBNUWDわかりませんあなたの期待が正しく述べられている場合、または私があなたの質問を誤って読んだ場合。

(ああ、そうですoverlapの概念がsortstartswithアプローチにレンチを投げます)

OPがその特定の側面を述べるのがいいかもしれません。私は彼の懸念を本当に理解せずに@DSMコメントを読みました。今私はそうします

li = sorted([i.strip() for i in """
ABCDE
ABCDEFG
ABCDEFGH
ABCDEFGHIJKLMNO
CEST
DBTSFDE
DBTSFDEO
EOEUDNBNUW
EOEUDNBNUWD
EAEUDNBNUW
FEOEUDNBNUW
FG
FGH""".splitlines() if i.strip()])

def get_iter(li):
    prev = ""
    for i in li:
        if not i.startswith(prev):
            yield(prev)
        prev = i
    yield prev

for v in get_iter(li):
    print(v)

出力:

ABCDEFGHIJKLMNO
CEST
DBTSFDEO
EAEUDNBNUW
EOEUDNBNUWD
FEOEUDNBNUW
FGH
1
JL Peyret

簡単な方法は、一度に1行ずつ入力ファイルを処理し、各行を前の行と比較し、現在の行に含まれていない場合はprevious 1を保持します。

コードは次のように単純にすることができます。

with open('toy.txt' ,'r') as f:
    old = next(f).strip()               # keep first line after stripping EOL 

    for pattern in f:
        pattern = pattern.strip()       # strip end of line...
        if old not in pattern:
            print old                   # keep old if it is not contained in current line
        old = pattern                   # and store current line for next iteration
    print old                           # do not forget last line
1
Serge Ballesta

ケニー、ほとんどわかったが、@ scharetteが指摘した2つの問題がある。

  1. forループとリスト項目の削除は一緒に行われるべきではありません。修正は、whileループを使用して、明示的にインデックスを増やすことです。 whileループは、len()を1回ではなく数回呼び出すため、効率が低下しますが、これが正しい結果を得るのに必要なことです。
  2. IndexError。これは最後の行でのみ発生します。この問題に対処する私の方法は、エラーを無視することです。

それで、コードを次のように変更しました。

with open('toy.txt' ,'r') as f:
    pattern = f.read().splitlines()
    print pattern

    try:
        i = 0
        while i < len(pattern):
            if pattern[i] in pattern[i+1]:
                pattern.remove(pattern[i])
            print pattern
            i += 1
    except IndexError:
        pass
1
Hai Vu
with open('demo.txt') as f:
    lines = f.readlines()

l_lines = len(lines)

n_lst = []

for i, line in enumerate(lines):
    line = line.strip()
    if i == l_lines - 1:
        if lines[-2] not in line:
            n_lst.append(line)
        break
    if line not in lines[i + 1]:
        n_lst.append(line)

print(n_lst)

出力

['ABCDEFGHIJKLMNO', 'CEST', 'DBTSFDEO', 'EOEUDNBNUW', 'EAEUDNBNUW', 'FGH']
1
Druta Ruslan

挿入プロセスが値の前にあるノードを見つけようとするバイナリツリーを使用できます。

class Tree:
  def __init__(self, val=None):
    self.left, self.value, self.right = None, val, None
  def insert_val(self, _val):
    if self.value is None or _val.startswith(self.value):
       self.value = _val
    else:
       if _val < self.value:
          getattr(self.left, 'insert_val', lambda x:setattr(self, 'left', Tree(x)))(_val)
       else:
          getattr(self.right, 'insert_val', lambda x:setattr(self, 'right', Tree(x)))(_val)
  def flatten(self):
     return [*getattr(self.left, 'flatten', lambda :[])(), self.value, *getattr(self.right, 'flatten', lambda :[])()]

t = Tree()
for i in open('filename.txt'):
  t.insert_val(i.strip('\n'))
print(t.flatten())

出力:

['ABCDEFGHIJKLMNO', 'CEST', 'DBTSFDEO', 'EAEUDNBNUW', 'EOEUDNBNUW', 'FGH']
0
Ajax1234

他の回答で述べたように、エラーは、最初に入力の長さを計算し、リストを短くしても更新されないことに起因します。

これが実際の解決策の別の見方です:

with open('toy.txt', 'r') as infile:
    input_lines = reversed(map(lambda s: s.strip(), infile.readlines()))

output = []
for pattern in input_lines:
    if len(output) == 0 or not output[-1].startswith(pattern):        
        output.append(pattern)

print('\n'.join(reversed(output)))
0
jfg