24

私は長さ25のDNA配列を扱っています(以下の例を参照)。私は230,000のリストを持っており、ゲノム全体(toxoplasma gondii寄生虫)の各配列を探す必要があります。ゲノムの大きさはわかりませんが、23万配列よりはるかに長いです。

たとえば、(AGCCTCCCATGATTGAACAGATCAT)のように、25文字のシーケンスをそれぞれ探す必要があります。

ゲノムは連続した文字列としてフォーマットされます。つまり、(CATGGGAGGCTTGCGGAGCCTGAGGGCGGAGCCTGAGGTGGGAGGCTTGCGGAGTGCGGAGCCTGAGCCTGAGGGCGGAGCCTGAGGTGGGAGGCTT....

どこに何回あるかは気にせず、見つけられるかどうかだけです。
これは簡単だと思います-

str.find(AGCCTCCCATGATTGAACAGATCAT)

しかし、私はまた、任意の場所で間違っている(不一致)と定義されている厳密な一致を見つけて、その場所を1つの場所だけに記録し、その場所を順番に記録します。これをどのように行うのかわかりません。私が考えることができる唯一のことは、ワイルドカードを使用し、各位置でワイルドカードを使用して検索を実行することです。つまり、25回検索します。

例えば、

AGCCTCCCATGATTGAACAGATCAT    
AGCCTCCCATGATAGAACAGATCAT

位置13での不一致との密接な一致。

スピードは3回しかないので大した問題ではありませんが、速ければいいのですが。

これを行うプログラムがあります-一致と部分一致を検索します-しかし、私はこれらのアプリケーションでは検出できないタイプの部分一致を探しています。

これはperlの同様の投稿ですが、シーケンスを比較しているだけで、連続した文字列を検索していません。

関連記事

4

13 に答える 13

28

読み進める前に、 biopythonを見たことがありますか?

1つの置換エラー、およびゼロの挿入/削除エラー、つまりハミング距離が1の近似一致を見つけたいようです。

ハミング距離一致関数(たとえば、Ignacioが提供するリンクを参照)がある場合は、次のように使用して最初の一致を検索できます。

any(Hamming_distance(genome[x:x+25], sequence) == 1 for x in xrange(len(genome)))

ただし、これはかなり遅くなります。これは、(1)ハミング距離関数が2番目の置換エラー後も(2)失敗後もグラインドし続けるため、カーソルを(ボイヤーのように)見たものに基づいてスキップするのではなく、カーソルを1つ進めます。ムーア検索は行います)。

次のような関数で(1)を克服できます。

def Hamming_check_0_or_1(genome, posn, sequence):
    errors = 0
    for i in xrange(25):
        if genome[posn+i] != sequence[i]:
            errors += 1
            if errors >= 2:
                return errors
    return errors 

注:これは意図的にPythonicではなく、Cicです。妥当な速度を得るには、C(おそらくCython経由)を使用する必要があるためです。

スキップを伴うビット並列近似レーベンシュタイン検索に関するいくつかの作業は、NavarroとRaffinot(google "Navarro Raffinot nrgrep")によって行われ、これはハミング検索に適合させることができます。ビット並列メソッドにはクエリ文字列の長さとアルファベットのサイズに制限がありますが、それぞれ25と4であるため、問題はありません。更新:アルファベットサイズが4の場合、スキップしてもあまり役に立ちません。

ハミング距離検索をグーグルで検索すると、ソフトウェアではなく、ハードウェアでの実装に関する多くのことに気付くでしょう。これは、思いついたアルゴリズムがCまたは他のコンパイル言語で実装されるべきであることを示す大きなヒントです。

更新: ビット並列メソッドの作業コード

また、正確性のチェックを支援するための単純な方法を提供し、いくつかの比較のためにPaulのreコードのバリエーションをパッケージ化しました。re.finditer()を使用すると、重複しない結果が得られることに注意してください。これにより、距離1の一致が完全一致をシャドウする可能性があります。私の最後のテストケースを参照してください。

ビット並列方式には、次の機能があります。保証された線形動作O(N)ここで、Nはテキストの長さです。ナイーブメソッドは正規表現メソッドと同様にO(NM)であることに注意してください(Mはパターンの長さです)。ボイヤームーアスタイルの方法は、最悪の場合のO(NM)であり、予想されるO(N)です。また、ビットパラレル方式は、入力をバッファリングする必要がある場合に簡単に使用できます。一度に1バイトまたは1メガバイトを供給することができます。先読みも、バッファ境界の問題もありません。大きな利点:Cでコーディングした場合の、入力バイトごとの単純なコードの速度。

欠点:パターンの長さは、高速レジスタのビット数(32または64など)に効果的に制限されます。この場合、パターンの長さは25です。問題なし。パターン内の個別の文字の数に比例した追加のメモリ(S_table)を使用します。この場合、「アルファベットサイズ」はわずか4です。問題なし。

このテクニカルレポートからの詳細。レーベンシュタイン距離を使用した近似検索用のアルゴリズムがあります。ハミング距離の使用に変換するために、挿入と削除を処理するステートメント2.1の一部を単純に(!)削除しました。「d」の上付き文字が付いた「R」への参照がたくさんあることに気付くでしょう。「d」は距離です。必要なのは0と1だけです。これらの「R」は、以下のコードでR0変数とR1変数になります。

# coding: ascii

from collections import defaultdict
import re

_DEBUG = 0


# "Fast Text Searching with Errors" by Sun Wu and Udi Manber
# TR 91-11, Dept of Computer Science, University of Arizona, June 1991.
# http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.20.8854

def WM_approx_Ham1_search(pattern, text):
    """Generate (Hamming_dist, start_offset)
    for matches with distance 0 or 1"""
    m = len(pattern)
    S_table = defaultdict(int)
    for i, c in enumerate(pattern):
        S_table[c] |= 1 << i
    R0 = 0
    R1 = 0
    mask = 1 << (m - 1)
    for j, c in enumerate(text):
        S = S_table[c]
        shR0 = (R0 << 1) | 1
        R0 = shR0 & S
        R1 = ((R1 << 1) | 1) & S | shR0
        if _DEBUG:
            print "j= %2d msk=%s S=%s R0=%s R1=%s" \
                % tuple([j] + map(bitstr, [mask, S, R0, R1]))
        if R0 & mask: # exact match
            yield 0, j - m + 1
        elif R1 & mask: # match with one substitution
            yield 1, j - m + 1

if _DEBUG:

    def bitstr(num, mlen=8):
       wstr = ""
       for i in xrange(mlen):
          if num & 1:
             wstr = "1" + wstr
          else:
             wstr = "0" + wstr
          num >>= 1
       return wstr

def Ham_dist(s1, s2):
    """Calculate Hamming distance between 2 sequences."""
    assert len(s1) == len(s2)
    return sum(c1 != c2 for c1, c2 in zip(s1, s2))

def long_check(pattern, text):
    """Naively and understandably generate (Hamming_dist, start_offset)
    for matches with distance 0 or 1"""
    m = len(pattern)
    for i in xrange(len(text) - m + 1):
        d = Ham_dist(pattern, text[i:i+m])
        if d < 2:
            yield d, i

def Paul_McGuire_regex(pattern, text):
    searchSeqREStr = (
        '('
        + pattern
        + ')|('
        + ')|('.join(
            pattern[:i]
            + "[ACTGN]".replace(c,'')
            + pattern[i+1:]
            for i,c in enumerate(pattern)
            )
        + ')'
        )
    searchSeqRE = re.compile(searchSeqREStr)
    for match in searchSeqRE.finditer(text):
        locn = match.start()
        dist = int(bool(match.lastindex - 1))
        yield dist, locn


if __name__ == "__main__":

    genome1 = "TTTACGTAAACTAAACTGTAA"
    #         01234567890123456789012345
    #                   1         2

    tests = [
        (genome1, "ACGT ATGT ACTA ATCG TTTT ATTA TTTA"),
        ("T" * 10, "TTTT"),
        ("ACGTCGTAAAA", "TCGT"), # partial match can shadow an exact match
        ]

    nfailed = 0
    for genome, patterns in tests:
        print "genome:", genome
        for pattern in patterns.split():
            print pattern
            a1 = list(WM_approx_Ham1_search(pattern, genome))
            a2 = list(long_check(pattern, genome))
            a3 = list(Paul_McGuire_regex(pattern, genome))
            print a1
            print a2
            print a3
            print a1 == a2, a2 == a3
            nfailed += (a1 != a2 or a2 != a3)
    print "***", nfailed
于 2010-03-10T23:09:45.410 に答える
25

Python regexライブラリは、ファジー正規表現マッチングをサポートしています。TREに対する1つの利点は、テキスト内の正規表現のすべての一致を検索できることです(重複する一致もサポートします)。

import regex
m=regex.findall("AA", "CAG")
>>> []
m=regex.findall("(AA){e<=1}", "CAAG") # means allow up to 1 error
m
>>> ['CA', 'AG']
于 2012-06-11T19:44:02.757 に答える
6

これらのゲノムファイルのいくつかをオンラインで見つけるために、「toxoplasmagondii寄生虫ゲノム」をグーグルで検索しました。http://toxodb.orgにある「TgondiiGenomic_ToxoDB-6.0.fasta」というタイトルのファイルで、サイズが約158Mbで、近いと思うものを見つけました。次のpyparsing式を使用して遺伝子配列を抽出しました。2分弱かかりました。

fname = "TgondiiGenomic_ToxoDB-6.0.fasta"
fastasrc = open(fname).read()   # yes! just read the whole dang 158Mb!

"""
Sample header:
>gb|scf_1104442823584 | organism=Toxoplasma_gondii_VEG | version=2008-07-23 | length=1448
"""
integer = Word(nums).setParseAction(lambda t:int(t[0]))
genebit = Group(">gb|" + Word(printables)("id") + SkipTo("length=") + 
                "length=" + integer("genelen") + LineEnd() + 
                Combine(OneOrMore(Word("ACGTN")),adjacent=False)("gene"))

# read gene data from .fasta file - takes just under a couple of minutes
genedata = OneOrMore(genebit).parseString(fastasrc)

(驚いたことに、いくつかの遺伝子配列には「N」の実行が含まれています!一体何なのですか?!)

次に、このクラスをpyparsing Tokenクラスのサブクラスとして記述し、厳密な一致を実行しました。

class CloseMatch(Token):
    def __init__(self, seq, maxMismatches=1):
        super(CloseMatch,self).__init__()
        self.name = seq
        self.sequence = seq
        self.maxMismatches = maxMismatches
        self.errmsg = "Expected " + self.sequence
        self.mayIndexError = False
        self.mayReturnEmpty = False

    def parseImpl( self, instring, loc, doActions=True ):
        start = loc
        instrlen = len(instring)
        maxloc = start + len(self.sequence)

        if maxloc <= instrlen:
            seq = self.sequence
            seqloc = 0
            mismatches = []
            throwException = False
            done = False
            while loc < maxloc and not done:
                if instring[loc] != seq[seqloc]:
                    mismatches.append(seqloc)
                    if len(mismatches) > self.maxMismatches:
                        throwException = True
                        done = True
                loc += 1
                seqloc += 1
        else:
            throwException = True

        if throwException:
            exc = self.myException
            exc.loc = loc
            exc.pstr = instring
            raise exc

        return loc, (instring[start:loc],mismatches)

一致するたびに、一致した実際の文字列と不一致の場所のリストを含むタプルが返されます。もちろん、完全一致は2番目の値の空のリストを返します。(私はこのクラスが好きです。次のリリースのpyparsingに追加すると思います。)

次に、このコードを実行して、.fastaファイルから読み取られたすべてのシーケンスで「最大2つの不一致」の一致を検索しました(genedataはParseResultsグループのシーケンスであり、それぞれにID、整数の長さ、およびシーケンス文字列):

searchseq = CloseMatch("ATCATCGAATGGAATCTAATGGAAT", 2)
for g in genedata:
    print "%s (%d)" % (g.id, g.genelen)
    print "-"*24
    for t,startLoc,endLoc in searchseq.scanString(g.gene):
        matched, mismatches = t[0]
        print "MATCH:", searchseq.sequence
        print "FOUND:", matched
        if mismatches:
            print "      ", ''.join(' ' if i not in mismatches else '*' 
                            for i,c in enumerate(searchseq.sequence))
        else:
            print "<exact match>"
        print "at location", startLoc
        print
    print

遺伝子ビットの1つからランダムに検索シーケンスを取得して、完全に一致するものを見つけられるようにしました。好奇心から、1要素と2要素の不一致がいくつあるかを確認しました。

これは実行するのに少し時間がかかりました。45分後、この出力があり、各IDと遺伝子の長さ、および見つかった部分的な一致が一覧表示されました。

scf_1104442825154 (964)
------------------------

scf_1104442822828 (942)
------------------------

scf_1104442824510 (987)
------------------------

scf_1104442823180 (1065)
------------------------
...

私は落胆していました。次の場合まで一致するものが表示されませんでした。

scf_1104442823952 (1188)
------------------------
MATCH: ATCATCGAATGGAATCTAATGGAAT
FOUND: ATCATCGAACGGAATCGAATGGAAT
                *      *        
at location 33

MATCH: ATCATCGAATGGAATCTAATGGAAT
FOUND: ATCATCGAATGGAATCGAATGGAAT
                       *        
at location 175

MATCH: ATCATCGAATGGAATCTAATGGAAT
FOUND: ATCATCGAATGGAATCGAATGGAAT
                       *        
at location 474

MATCH: ATCATCGAATGGAATCTAATGGAAT
FOUND: ATCATCGAATGGAATCGAATGGAAT
                       *        
at location 617

MATCH: ATCATCGAATGGAATCTAATGGAAT
FOUND: ATCATCGAATGGAATCGAATAGAAT
                       *   *    
at location 718

MATCH: ATCATCGAATGGAATCTAATGGAAT
FOUND: ATCATCGAATGGATTCGAATGGAAT
                    *  *        
at location 896

MATCH: ATCATCGAATGGAATCTAATGGAAT
FOUND: ATCATCGAATGGAATCGAATGGTAT
                       *     *  
at location 945

そして最後に私の完全一致:

scf_1104442823584 (1448)
------------------------
MATCH: ATCATCGAATGGAATCTAATGGAAT
FOUND: ATCATCGAATGGACTCGAATGGAAT
                    *  *        
at location 177

MATCH: ATCATCGAATGGAATCTAATGGAAT
FOUND: ATCATCGAATGGAATCAAATGGAAT
                       *        
at location 203

MATCH: ATCATCGAATGGAATCTAATGGAAT
FOUND: ATCATCAAATGGAATCGAATGGAAT
             *         *        
at location 350

MATCH: ATCATCGAATGGAATCTAATGGAAT
FOUND: ATCATCGAATGGAATCGAATGGAAA
                       *       *
at location 523

MATCH: ATCATCGAATGGAATCTAATGGAAT
FOUND: ATCATCAAATGGAATCGAATGGAAT
             *         *        
at location 822

MATCH: ATCATCGAATGGAATCTAATGGAAT
FOUND: ATCATCGAATGGAATCTAATGGAAT
<exact match>
at location 848

MATCH: ATCATCGAATGGAATCTAATGGAAT
FOUND: ATCGTCGAATGGAGTCTAATGGAAT
          *         *           
at location 969

それで、これは速度の記録を設定しませんでしたが、私は仕事を終わらせ、興味があるかもしれない場合に備えて、いくつかの2つの一致も見つけました。

比較のために、これがREベースのバージョンで、1つの不一致の一致のみが検出されます。

import re
seqStr = "ATCATCGAATGGAATCTAATGGAAT"
searchSeqREStr = seqStr + '|' + \
    '|'.join(seqStr[:i]+"[ACTGN]".replace(c,'') +seqStr[i+1:] 
             for i,c in enumerate(seqStr))

searchSeqRE = re.compile(searchSeqREStr)

for g in genedata:
    print "%s (%d)" % (g.id, g.genelen)
    print "-"*24
    for match in searchSeqRE.finditer(g.gene):
        print "MATCH:", seqStr
        print "FOUND:", match.group(0)
        print "at location", match.start()
        print
    print

(最初は、生のFASTAファイルソース自体を検索しようとしましたが、pyparsingバージョンと比較して一致が少ない理由に戸惑いました。その後、fastaファイルの出力がnでラップされるため、一部の一致は改行を越える必要があることに気付きました。文字。)

したがって、照合する遺伝子配列を抽出するための最初のpyparsingパスの後、このREベースのサーチャーは、テキストでラップされていないすべての配列をスキャンして、同じ1つの不一致のエントリをすべて見つけるためにさらに約1分半かかりました。 pyparsingソリューションが行ったこと。

于 2010-03-11T05:59:49.930 に答える
3

Python-Levenshteinでさまざまなルーチンが使用されている場合があります。

于 2010-03-10T20:49:21.517 に答える
3
>>> import re
>>> seq="AGCCTCCCATGATTGAACAGATCAT"
>>> genome = "CATGGGAGGCTTGCGGAGCCTGAGGGCGGAGCCTGAGGTGGGAGGCTTGCGGAGTGCGGAGCCTGAGCCTGAGGGCGGAGCCTGAGGTGGGAGGCTT..."
>>> seq_re=re.compile('|'.join(seq[:i]+'.'+seq[i+1:] for i in range(len(seq))))

>>> seq_re.findall(genome)  # list of matches
[]  

>>> seq_re.search(genome) # None if not found, otherwise a match object

これは最初の一致を停止するため、複数の一致がある場合は少し速くなる可能性があります

>>> print "found" if any(seq_re.finditer(genome)) else "not found"
not found

>>> print "found" if seq_re.search(genome) else "not found" 
not found

>>> seq="CAT"
>>> seq_re=re.compile('|'.join(seq[:i]+'.'+seq[i+1:] for i in range(len(seq))))
>>> print "found" if seq_re.search(genome) else "not found"
found

長さが10,000,000のゲノムの場合、1つのスレッドで230,000のシーケンスをスキャンするために、約2.5日を見ているので、タスクをいくつかのコア/CPUに分割することをお勧めします。

これが実行されている間は、いつでもより効率的なアルゴリズムの実装を開始できます:)

単一の削除または追加された要素を検索する必要がある場合は、正規表現をこれに変更します

>>> seq_re=re.compile('|'.join(seq[:i]+'.{0,2}'+seq[i+1:] for i in range(len(seq))))
于 2010-03-10T20:59:04.977 に答える
1

これは、最長共通部分列問題のヒントです。ここでの文字列の類似性の問題は、230000シーケンスの連続した文字列に対してテストする必要があることです。したがって、25のシーケンスの1つを連続文字列と比較している場合、類似性は非常に低くなります。

25シーケンスと連続文字列の間で最長共通部分列を計算すると、長さが同じである場合、それが文字列内にあるかどうかがわかります。

于 2010-03-10T20:57:35.427 に答える
1

一致させたいすべての異なるシーケンスからトライを作成できます。ここで、最大で1つの不一致を許容する、深さ優先探索関数をトライの下で作成する際の注意が必要な部分です。

この方法の利点は、すべてのシーケンスを一度に検索できることです。これにより、多くの比較を節約できます。たとえば、最上位ノードから開始して「A」ブランチを下に移動すると、「A」で始まるすべてのシーケンスと即座に一致するため、何千もの比較が保存されます。別の議論として、与えられた配列と正確に一致するゲノムのスライスを考えてみましょう。シーケンスのリストに最後のシンボルのみが異なる別のシーケンスがある場合、トライを使用すると23回の比較操作が節約されます。

これを実装する1つの方法があります。'A'、'C'、T'、G'を0,1,2,3またはその変形に変換します。次に、タプルのタプルをトライの構造として使用します。各ノードで、配列の最初の要素は「A」に対応し、2番目の要素は「C」に対応します。'A'がこのノードのブランチである場合、このノードのタプルの最初の項目として4つの要素の別のタプルがあります。'A'ブランチがない場合は、最初の項目を0に設定します。トライの下部には、一致リストに入れることができるように、そのシーケンスのIDを持つノードがあります。

この種のトライに対して1つの不一致を許容する再帰検索関数は次のとおりです。

def searchnomismatch(node,genome,i):
    if i == 25:
        addtomatches(node)
    else:
        for x in range(4):
            if node[x]:
                if x == genome[i]:
                    searchnomismatch(node[x],genome,i+1)
                else:
                    searchmismatch(node[x],genome,i+1,i)

def searchmismatch(node,genome,i,where):
    if i == 25:
        addtomatches(node,where)
    else:
        if node[genome[i]]:
            searchmismatch(node[genome[i]],genome,i+1,where)

ここでは、次のようなものから検索を開始します

searchnomismatch(trie,genome[ind:ind+25],0)

addtomatchesは次のようなものです

def addtomatches(id,where=-1):
    matches.append(id,where)

ここで、-1に等しい場合は、不一致がなかったことを意味します。とにかく、私はあなたが写真を撮れるように私が十分に明確であったことを望みます。

于 2010-03-10T23:28:19.270 に答える
1

いくつかの解決策を試しましたが、すでに書いたように、大量のシーケンス(文字列)を処理する場合は遅くなります。

私は蝶ネクタイを使用して、対象の部分文字列(soi)をFASTA形式の文字列を含む参照ファイルに対してマッピングすることを思いつきました。許可された不一致の数(0..3)を指定すると、許可された不一致が与えられた場合にsoiがマップされた文字列が返されます。これはうまく機能し、かなり高速です。

于 2012-02-07T15:22:14.197 に答える
0

Pythonに組み込まれている機能を使用して、正規表現のマッチングで検索を行うことができます。

Pythonのreモジュール http://docs.python.org/library/re.html

正規表現入門書 http://www.regular-expressions.info/

于 2010-03-10T20:49:15.730 に答える
0

これは少し遅れるかもしれませんが、あなたが望むことを正確に実行するPatMaNという名前のツールがあります:http: //bioinf.eva.mpg.de/patman/

于 2010-05-27T17:46:45.393 に答える
0

「近似マッチング」には、正規表現マッチングライブラリTREを使用できます。Python、Perl、Haskellのバインディングもあります。

import tre

pt = tre.compile("Don(ald)?( Ervin)? Knuth", tre.EXTENDED)
data = """
In addition to fundamental contributions in several branches of
theoretical computer science, Donnald Erwin Kuth is the creator of
the TeX computer typesetting system, the related METAFONT font
definition language and rendering system, and the Computer Modern
family of typefaces.
"""

fz = tre.Fuzzyness(maxerr = 3)
print fz
m = pt.search(data, fz)

if m:
    print m.groups()
    print m[0]

出力されます

tre.Fuzzyness(delcost=1,inscost=1,maxcost=2147483647,subcost=1, maxdel=2147483647,maxerr=3,maxins=2147483647,maxsub=2147483647)
((95, 113), (99, 108), (102, 108))
Donnald Erwin Kuth

http://en.wikipedia.org/wiki/TRE_%28computing%29

http://laurikari.net/tre/

于 2012-06-11T18:45:12.493 に答える
0

以下のコードはシンプルで便利だと思いました。

in_pattern = "";
in_genome = "";
in_mistake = d;
out_result = ""


kmer = len(in_pattern)

def FindMistake(v):
    mistake = 0
    for i in range(0, kmer, 1):
        if (v[i]!=in_pattern[i]):
            mistake+=1
        if mistake>in_mistake:
            return False
    return True


for i in xrange(len(in_genome)-kmer+1):
    v = in_genome[i:i+kmer]
    if FindMistake(v):
        out_result+= str(i) + " "

print out_result

チェックしたいゲノムやセグメントを簡単に挿入したり、ミスマッチの値を設定したりできます。

于 2016-01-30T19:19:33.187 に答える
0

これはかなり古いですが、おそらくこの単純な解決策が機能する可能性があります。25文字のスライスを取得してシーケンスをループします。スライスをnumpy配列に変換します。25char文字列と比較してください(numpy配列としても)。答えを合計し、答えが24の場合は、ループ内の位置と不一致を出力します。

次の数行はそれが機能していることを示しています

numpyをnpとしてインポートします

a = ['A'、'B'、'C']

b = np.array(a)

b

array(['A'、'B'、'C']、dtype = '

c = ['A'、'D'、'C']

d = np.array(c)

b == d

array([True、False、True])

合計(b == d)

2

于 2018-02-24T11:42:58.247 に答える