私は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
他にも有効な答えはありますが、実際の問題を説明するものはありません。あなたは実際には有効な解決策に本当に近づいており、私の意見では、最も読みやすい答えは何ですか。
エラーは、あなたが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アルゴリズムをご覧ください。
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()
関数がこのリストを受け取り、最も長いリスト項目を返します。
# 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'、 'CEST'、 'DBTSFDEO'、 'EOEUDNBNUW'、 'EAEUDNBNUW'、 'FGH']
これはあなたがなりたい場所を取得します:
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
を追加しました。
コード
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つのループでデータを反復処理します。
結果の辞書から値のセットが作成され、最も長いシーケンスが返されます。
予想される出力とのいくつかの不一致に注意してください:
FGH
はABCDEFGHIJKLMNO
と重複するため、有効な出力ではありません。FEOEUDNBNUWD
は元のシーケンスではありません。シーケンスのオーバーラップには後処理が必要です。期待と完全に一致するわけではありませんが、並べ替えられている(そしてEOEUDNBNUWD EAEUDNBNUW
の近くではない)と述べ、あなたが欠落している理由がわかりませんEOEUDNBNUWD
わかりませんあなたの期待が正しく述べられている場合、または私があなたの質問を誤って読んだ場合。
(ああ、そうですoverlapの概念がsort
とstartswith
アプローチにレンチを投げます)
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行ずつ入力ファイルを処理し、各行を前の行と比較し、現在の行に含まれていない場合は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
ケニー、ほとんどわかったが、@ scharetteが指摘した2つの問題がある。
for
ループとリスト項目の削除は一緒に行われるべきではありません。修正は、while
ループを使用して、明示的にインデックスを増やすことです。 while
ループは、len()
を1回ではなく数回呼び出すため、効率が低下しますが、これが正しい結果を得るのに必要なことです。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
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']
挿入プロセスが値の前にあるノードを見つけようとするバイナリツリーを使用できます。
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']
他の回答で述べたように、エラーは、最初に入力の長さを計算し、リストを短くしても更新されないことに起因します。
これが実際の解決策の別の見方です:
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)))