2

私は、タンパク質の非常に大きな .txt ファイル データベースを使用して Java で作業しています。タンパク質には一般的な構造がありますが、「これを startIndex から endIndex に変換し、逆にして置換する」をハードコードするほど均一な構造ではありません。唯一の真の統一性は、それらが で区切られていることです>

...WERINWETI>gi|230498 [Bovine Albumin]ADFIJWOENAONFOAIDNFKLSADNFATHISDATFDAIFJ>sp|234235 (human) AGP1 QWIQWONOQWNROIWQRNOQWIRNSWELLE>gi|...等々。

ご覧のとおり、実際のタンパク質配列 (すべての大文字の長い鎖) は大文字の鎖であるという点で統一されていますが、それ以外にも、前述の説明はほとんど何でもかまいません (多くの場合、スペースではありません)。説明とシーケンスの間)。私のプログラムが行う必要があるのは、元のテキストを新しいファイルにコピーしてから、通過し、r-それぞれの後に>(例: ...EERFDS>r-gi|23423...) を追加し、大文字のチェーンのみを逆にすることです。そのプロセスが完了したら、元のテキストの末尾に追加する必要があります。

私はr-機能を完了しました。実際に反転と追加も完了しましたが、十分に効率的ではありません。この処理を受けているデータベースは大規模であり、私のプログラムは時間がかかりすぎます。実際、私はそれを終わらせたことがないので、どれくらいの時間がかかるかわかりません. 1時間待って終了。正規表現 (組み込みの Pattern クラス) を使用した反転のアルゴリズムは次のとおりです (計算量の多い部分)。

Pattern regexSplit = Pattern.compile(">");
String[] splits = regexSplit.split(rDash.toString());
StringBuilder rDashEdited = new StringBuilder();
Pattern regexProtein = Pattern.compile("[A-Z]{5,}");

for (int splitIndex = 1; splitIndex < splits.length; splitIndex++) {
    Matcher rDashMatcher = regexProtein.matcher(splits[splitIndex]);
    rDashMatcher.find();
    StringBuffer reverser = new StringBuffer(rDashMatcher.group());
    rDashEdited.append(rDashMatcher.replaceAll(reverser.reverse().toString()) + ">");
}
System.out.println(">" + rDashEdited);

したがって、基本的にはrDash(元のすべてのタンパク質を含む StringBuilder ですが、>r-まだ反転していません) を個々のタンパク質に分割し、それらを String 配列に追加します。次に、配列内の各文字列を調べて、5 文字を超える一連の大文字を探し、一致するものを StringBuffer に追加し、それを逆にして、順方向バージョンを逆方向バージョンに置き換えます。このアルゴリズムは、小さなテキスト ファイルに対して意図したとおりに機能することに注意してください。

配列を分割/トラバースする必要をなくす、より強力な正規表現はありますか? 私が試したとき、replaceAll()コールは下流のすべてのタンパク質をセット内の最初のタンパク質の逆に置き換えました. 楽しみのためにチェックしたところ、セット内の各タンパク質のSystem.out.println(rDashMatcher.groupCount())aが出力されました。0より効率的/強力な正規表現を手伝ってくれる人はいますか? これは私にとってかなり新しい概念ですが、MATLAB でのベクトル化 (文字のみ) を思い出させます。

4

4 に答える 4

2

これで 10,000,000 レコード (約 379MB のテキスト ファイル) を投げたところ、1:06 分かかりました (4core athlon、数年前)。

大きな if ツリーは、区切り文字が要素の途中にあるために半分しか得られない端を処理します。

public void readProteins(BufferedReader br, BufferedWriter bw) throws IOException
{     
  Pattern regexSplit = Pattern.compile(">");
  Pattern proteinPattern = Pattern.compile("(.*?)([A-Z]{5,})");
  Matcher m;
  Scanner s = new Scanner(br);
  s.useDelimiter(regexSplit);         
  while (s.hasNext())
  {
      StringBuffer sb = new StringBuffer();
      String protein = s.next();
      m = proteinPattern.matcher(protein);            
      if (m.find())
          sb.append(m.group(2)).reverse().append(">r-").insert(0, m.group(1));
      else
          sb.append(protein);
      );          
  }
  bw.flush();
  bw.close();
}
于 2012-06-30T00:22:05.920 に答える
1

より強力な正規表現は必要ありません。同じテキストのビットを何度も処理し続けないように、プロセスを合理化する必要があるだけです。ほとんどの場合、これは Java の下位レベルの正規表現 API、つまりappendReplacement()andを使用することを意味しますappendTail()。そして、空の文字列を に渡すことappendReplacement()で、後方参照の自動処理を回避しました。

の使い方にfind()も注目してください。find()(またはmatches()またはlookingAt()) を呼び出して、その戻り値をチェックしていないことに気付いた場合は、何か間違ったことをしています。それが、試合が成功したかどうかを知る方法です。

public static void main(String[] args) throws Exception
{
  // this I/O code is bare-bones so as not to distract from the fun stuff
  BufferedWriter bw = new BufferedWriter(new FileWriter("test_out.txt"));

  // I use a lookahead so the ">" doesn't get discarded
  Scanner sc = new Scanner(new File("test.txt")).useDelimiter("(?=>)");
  while (sc.hasNext())
  {
    bw.write(reverseCapBlocks(sc.next()));
  }
  sc.close();
  bw.close();
}

// cache these because recompiling them is fairly expensive
static final Pattern CAPS_PATTERN = Pattern.compile("\\b[A-Z]{5,}\\b");
static final Pattern BRACKET_PATTERN = Pattern.compile("^>");

static String reverseCapBlocks(String s)
{
  StringBuffer sb = new StringBuffer();
  Matcher m = CAPS_PATTERN.matcher(s);
  while (m.find())
  {
    // appends whatever was between the last match and this one
    // but hole off on appending the current match
    m.appendReplacement(sb, "");
    String temp = m.group();

    // do the reversing manually because it's trivial and it avoids
    // creating a new StringBuilder every time
    for (int i = temp.length() - 1; i >= 0; i--)
    {
      sb.append(temp.charAt(i));
    }
  }
  // append whatever was left after the last match
  m.appendTail(sb);

  // if the chunk began with ">", add the "r-"
  return BRACKET_PATTERN.matcher(sb).replaceFirst(">r-");
}

私は StringBuilder の代わりに StringBuffer を使用します。これは API が必要とするためですが、大したことではありません。StringBuffer の非効率性に関する報告は、真実ではありますが、非常に誇張されている傾向があります。

于 2012-06-30T05:29:42.040 に答える
1

最適化のアイデア:

  • StringBuffer は避けてください。StringBuilder は同じ機能を提供し、より高速です。
  • replaceAll ではなく、stringBuilder.replace(int start, int end, String str) を使用できます。これにより、文字列全体でパターンを再度一致させようとするのを回避できます。
  • アプローチ #b を使用すると、分割をスキップして、タンパク質を検索し続け、それらを置き換えることもできます。

プロファイラーを実行して、推測するよりも時間を消費しているものを確認することをお勧めします。たとえば、プログラムのメモリを増やしたり、特定の遅いファイル システムを回避したりすることで、パフォーマンスを改善できる場合があります。

于 2012-06-29T21:37:33.587 に答える
0

コメントで述べたように、ファイル全体をメモリにロードしないでください。これにより、メモリがスワップインおよびスワップアウトされ、プログラムが遅くなります。

「タンパク質」のサイズ、つまり>区切られた文字列がメモリ内で管理できる場合、これでうまくいくはずです

    Scanner scanner = null;
    BufferedWriter writer = null;
    try {
        writer = new BufferedWriter(new FileWriter("output.txt"));
        scanner  = new Scanner(new BufferedReader(new FileReader("input.txt")));
        scanner.useDelimiter(">");
        while ( scanner.hasNext() ) {
           doReverseAndWriteToFile(scanner.next(), writer);
        }
    } finally {
        if ( scanner != null) {
            scanner.close();
        }
        if ( writer != null ) {
            writer.flush();
            writer.close();
        }
    }

doReverseAndWriteToFile()あなたのプログラムの2番目の部分を入れるべきです(私はあまり注意を払いませんでした:-))。この関数では、作業が進むにつれて、新しいファイルにも書き込む必要があります。

これを使用すると、一度に「bufferSize」+「1 つのタンパク質の長さ」だけがメモリに保持されます。

これにより速度が上がるかどうかを確認してください..そうでない場合は、他の場所を探す必要があります。

于 2012-06-29T22:46:16.770 に答える