2017-09-11 7 views
18

我特別關注R,Perl和shell。但任何其他編程語言也可以。根據返回的結果和以前的正則表達式的規則創建一個新的正則表達式|索引正則表達式並查看正則表達式如何匹配子字符串

問題

有沒有一種方法,以在視覺上或程序檢查和指數基於正則表達式匹配的字符串?這是爲了引用第一個正則表達式及其第二個正則表達式的結果,以便能夠修改匹配字符串的一部分併爲該特定部分編寫新規則。

https://regex101.com確實可視化某個字符串如何匹配正則表達式。但它遠非完美,對於我的龐大數據集效率不高。

問題

我有我的第一個正則表達式各地12000所匹配的字符串(DNA序列),我想處理這些字符串和基於一些嚴格的規則找到一個文件裏其他一些字符串順利以及那些基於嚴格規則的12000場比賽。

簡單的例子

這是我第一次正則表達式(簡化,較短的版本我原來的正則表達式)通過我第一個文本文件運行。

[ACGT]{1,12000}(AAC)[AG]{2,5}[ACGT]{2,5}(CTGTGTA) 

讓我們假設它發現在我的大文本文件中的以下三個子字符串:

1. AAACCCGTGTAATAACAGACGTACTGTGTA 
2. TTTTTTTGCGACCGAGAAACGGTTCTGTGTA 
3. TAACAAGGACCCTGTGTA 

現在我有一個第二個文件其中包括一個非常大的字符串。從這第二個文件中,我只對提取匹配新的(第二個)正則表達式的那些子字符串感興趣,該新的正則表達式本身依賴於我的第一個正則表達式在幾個部分中。 因此,這第二個正則表達式必須考慮第一個文件中匹配的子字符串,並查看它們與第一個正則表達式匹配的方式!

請允許我,爲了簡單起見,指數我的第一個正則表達式的緣故以這種方式更好地說明:

first.regex.p1 = [ACGT]{1,12000} 
first.regex.p2 = (AAC) 
first.regex.p3 = [AG]{2,5} 
first.regex.p4 = [ACGT]{2,5} 
first.regex.p5 = (CTGTGTA) 

現在我第二(新)的正則表達式將搜索第二個文本文件並將取決於第一個正則表達式的結果(以及第一個文件返回的子串如何與第一個正則表達式匹配)將按以下方式定義:

second.regex = (CTAAA)[AC]{5,100}(TTTGGG){**rule1**} (CTT)[AG]{10,5000}{**rule2**} 

在這裏規則1規則2依賴於從第一個文件的第一個正則表達式來匹配。因此,

rule1 = look at the matched strings from file1 and complement the pattern of first.regex.p3 that is found in the matched substring from file1 (the complement should of course have the same length) 
rule2 = look at the matched strings from file1 and complement the pattern of first.regex.p4 that is found in the matched substring from file1 (the complement should of course have the same length) 

你可以看到,第二個正則表達式有一個屬於自己的部分(即它們是獨立於任何其他的文件/正則表達式),但它也有依賴於第一個文件的結果和規則部分的第一個正則表達式以及第一個文件中的每個子字符串如何匹配第一個正則表達式!

現在再次爲了簡單起見,我使用file1中第三個匹配的子字符串(因爲它比其他兩個短),以向您展示第二個文件的可能匹配以及它如何滿足第二個正則表達式:

這是我們從我們的第一個正則表達式的實驗,獲得通過的第一個文件:

3. TAACAAGGACCCTGTGTA 

所以在這場比賽中,我們看到:

T has matched first.regex.p1 
AAC has matched first.regex.p2 
AAGGA has matched first.regex.p3 
CC first.regex.p4 
CTGTGTA has matched first.regex.p5 

現在在第二個文件的第二個正則表達式中,我們看到當查找與第二個正則表達式匹配的子字符串時,我們依賴於來自第一個文件(與第一個正則表達式匹配)的結果。特別是我們需要查看匹配的子串並補充匹配first.regex.p3和first.regex.p4(rule1rule2 from second.regex)的部分。

complement means: 
A will be substituted by T 
T -> A 
G -> C 
C -> G 

所以,如果你有TAAA,補充將是ATTT。

因此,回到這個例子:

  • TAACAAGGACCCTGTGTA
  • 我們需要補充以下以滿足第二正則表達式的要求:

    AAGGA has matched first.regex.p3 
    CC first.regex.p4 
    

    並補充如下:

    TTCCT (based on rule1) 
    GG (based on rule2) 
    

    所以匹配second.regex子串的例子是這樣的:

    CTAAAACACCTTTGGG TTCCT CTTAAAAAAAAAGGGGGAGAGAGAAGAAAAAAAGAGAG GG

    這只是一個例子!但在我的情況下,我有12000匹配的子字符串!我無法弄清如何解決這個問題。我試圖寫純正則表達式,但我完全沒有實現任何正確遵循這個邏輯的東西..也許我不應該使用正則表達式?

    是否有可能完全使用正則表達式完成此操作?或者我應該看看另一種方法?是否有可能索引一個正則表達式,並在第二個正則表達式引用回到第一個正則表達式,並強制正則表達式考慮第一個正則表達式返回的匹配子串?

    +0

    問題中有一點不清楚。假設你將在first.regex.p3中匹配'AAGGA',那麼第二個文件中的相應匹配應該是什麼:完全是'TTCCT'還是[CT] {2,5}?甚至是[CT] {5}? –

    +0

    @MarcLambrichs我真的很抱歉,我的文本是複雜和混亂。我在嘗試簡化原始正則表達式和結果時也迷失在自己的文本中。回答你的問題:是的!它應該完全是'TTCCT',因爲第二個文件中該正則表達式特定相關部分的匹配長度應該與第一個文件中找到的匹配長度完全相同。另一個例子:如果'first.regex.p4'匹配某個子字符串的第一個文件中的'GGGG',那麼第二個文件(第二個正則表達式)中的相應匹配肯定應該是'CCCC'。 –

    +0

    這是因爲這些基本上是兩條DNA鏈,並且在某些時候它們部分形成了一個鹼基對(它們聚集在一起並相互粘在一起)。所以第二個文件中的TTCCT從第一個文件中粘貼到AAGGA。 http://www.michaelwosnick.com/CancerResearch/wp-content/uploads/DNA-replication-from-visionlearning.jpg –

    回答

    1

    這個問題真的讓人想起the old saying about regular expressions,雖然在這種情況下,您匹配的語言是正常的,所以RE非常適合這種情況。

    不幸的是,我的Perl有點欠缺,但從根本上說,這聽起來像是Regex問題,而不是R或Perl問題,所以我會盡力在此基礎上回答它。

    Perl的正則表達式引擎支持捕獲組。匹配您正則表達式的子表達式括號中的子可以匹配之後公佈:

    use feature qw(say); 
    
    $foo = 'foo'; 
    'aaa' =~ /(a)(a+)/; 
    say($1); # => 'a' 
    say($2); # => 'aa' 
    say("Matched!") if 'aaaa' =~ /${2}/; 
    

    我會建議做的是正確地包圍你的正則表達式時,匹配後採摘除了捕獲組,然後粘在一起他們入新的正則表達式,說...

    use feature qw(say); 
    
    'ACGTAACAGAGATCTGTGTA' =~ /([ACGT]{1,12000})(AAC)([AG]{2,5})([ACGT]{2,5})(CTGTGTA)/ ; # Note that I've added a lot of (s and)s here so that the results get sorted into nice groups 
    say($1); # => 'ACGT' 
    say($2); # => 'AAC' 
    say($3); # => 'AGAG' 
    say($4); # => 'AT' 
    say($5); # => 'CTGTGTA' 
    
    $complemented_3 = complement($3); # You can probably implement these yourself... 
    $complemented_4 = complement($4); 
    
    $new_regex = /${complemented_3}[ACGT]+${complemented_4}/; 
    

    如果部分有實際意義的話,我也想建議你了named capture groups,並給予成績不俗的名字,而不是$1, $2, $3...

    +2

    我很確定Damian Conway是否遇到過這個問題,他會寫出一個很長的正則表達式來解決它。 :) – simbabque

    10

    這可以用Perl或任何其他語言以編程方式完成。

    由於您需要輸入來自兩個不同的文件,因此您無法在純正則表達式中執行此操作,因爲正則表達式無法讀取文件。你甚至不能用一種模式來做,因爲沒有正則表達式引擎記住你之前在不同的輸入字符串上匹配的東西。它必須在圍繞你的比賽的程序中完成,這應該是正則表達式,因爲這正是regex的意思。

    您可以逐步構建第二個模式。我已經在Perl中實現了一個更高級的版本,可以很容易地調整以適應其他模式組合,而無需更改實際工作的代碼。

    代替文件1,我將使用DATA部分。它包含全部三個示例輸入字符串。而不是文件2,我使用您的示例輸出作爲第三個輸入字符串。

    這背後的主要思想是將兩種模式分成子模式。對於第一個,我們可以簡單地使用一組模式。對於第二個,我們創建匿名函數,我們將調用第一個模式的匹配結果來構造第二個完整模式。他們中的大多數只是返回一個固定的字符串,但實際上有兩個從參數中獲取值來構建補充。

    use strict; 
    use warnings; 
    
    sub complement { 
        my $string = shift; 
        $string =~ tr/ATGC/TACG/; # this is a transliteration, faster than s/// 
        return $string; 
    } 
    
    # first regex, split into sub-patterns 
    my @first = ( 
        qr([ACGT]{1,12000}), 
        qr(AAC), 
        qr([AG]{2,5}), 
        qr([ACGT]{2,5}), 
        qr(CTGTGTA), 
    ); 
    
    # second regex, split into sub-patterns as callbacks 
    my @second = (
        sub { return qr(CTAAA) }, 
        sub { return qr([AC]{5,100}) }, 
        sub { return qr(TTTGGG) }, 
        sub { 
         my (@matches) = @_; 
    
         # complement the pattern of first.regex.p3 
         return complement($matches[3]); 
        }, 
        sub { return qr(CTT) }, 
        sub { return qr([AG]{10,5000}) }, 
        sub { 
         my (@matches) = @_; 
    
         # complement the pattern of first.regex.p4 
         return complement($matches[4]); 
        }, 
    ); 
    
    my $file2 = "CTAAAACACCTTTGGGTTCCTCTTAAAAAAAAAGGGGGAGAGAGAAGAAAAAAAGAGAGGG"; 
    
    while (my $file1 = <DATA>) { 
    
        # this pattern will match the full thing in $1, and each sub-section in $2, $3, ... 
        # @matches will contain (full, $2, $3, $4, $5, $6) 
        my @matches = ($file1 =~ m/(($first[0])($first[1])($first[2])($first[3])($first[4]))/g); 
    
        # iterate the list of anonymous functions and call each of them, 
        # passing in the match results of the first match 
        my $pattern2 = join q{}, map { '(' . $_->(@matches) . ')' } @second; 
    
        my @matches2 = ($file2 =~ m/($pattern2)/); 
    } 
    
    __DATA__ 
    AAACCCGTGTAATAACAGACGTACTGTGTA 
    TTTTTTTGCGACCGAGAAACGGTTCTGTGTA 
    TAACAAGGACCCTGTGTA 
    

    這些是爲您的三個輸入子字符串生成的第二個模式。

    ((?^:CTAAA))((?^:[AC]{5,100}))((?^:TTTGGG))(TCT)((?^:CTT))((?^:[AG]{10,5000}))(GCAT) 
    ((?^:CTAAA))((?^:[AC]{5,100}))((?^:TTTGGG))(CC)((?^:CTT))((?^:[AG]{10,5000}))(AA) 
    ((?^:CTAAA))((?^:[AC]{5,100}))((?^:TTTGGG))(TTCCT)((?^:CTT))((?^:[AG]{10,5000}))(GG) 
    

    如果你不熟悉,這是如果你print一個圖案是用quoted regex operator qr//構造會發生什麼。

    該模式與第三種情況下的示例輸出匹配。使用Data::Printer傾銷時產生的@matches2看起來像這樣。

    [ 
        [0] "CTAAAACACCTTTGGGTTCCTCTTAAAAAAAAAGGGGGAGAGAGAAGAAAAAAAGAGAGGG", 
        [1] "CTAAA", 
        [2] "ACACC", 
        [3] "TTTGGG", 
        [4] "TTCCT", 
        [5] "CTT", 
        [6] "AAAAAAAAAGGGGGAGAGAGAAGAAAAAAAGAGAG", 
        [7] "GG" 
    ] 
    

    我不能說這個實現的速度,但我相信它會合理快速。

    如果您想查找其他模式組合,則只需替換這兩個數組中的sub { ... }條目即可。如果第一場比賽的數量不同於其中的五場,那麼您還需要以編程方式構建該模式。我沒有做到這一點,以保持簡單。這是它的樣子。

    my @matches = ($file1 =~ join q{}, map { "($_)" } @first); 
    

    如果您想了解更多關於這種策略,我建議你閱讀馬克·傑森·多明斯優秀高階的Perl,這是available for free as a PDF here

    2

    R中使用stringr

    提取匹配regex_1:"[ACGT]{1,12000}(AAC)[AG]{2,5}[ACGT]{2,5}(CTGTGTA)"

    reg_1_matches = stringr::str_extract_all(sequences, "[ACGT]{1,12000}(AAC)[AG]{2,5}[ACGT]{2,5}(CTGTGTA)") 
    reg_1_matches = unlist(reg_1_matches) 
    

    讓我們假設的比賽是:

    reg_1_matches = c("TTTTTTTGCGACCGAGAAACGGTTCTGTGTA", "TAACAAGGACCCTGTGTA") 
    

    使用stringr :: str_match與捕獲組(... )

    df_ps = stringr::str_match(reg_1_matches, "[ACGT]{1,12000}AAC([AG]{2,5})([ACGT]{2,5})CTGTGTA") 
    
    p3 = df_ps[,2] 
    p4 = df_ps[,3] 
    

    rule_1 = chartr(old= "ACGT", "TGCA", p3) 
    rule_2 = chartr(old= "ACGT", "TGCA", p4) 
    

    構建regex_2

    paste("(CTAAA)[AC]{5,100}(TTTGGG)", rule_1, "(CTT)[AG]{10,5000}", rule_2, sep="") 
    

    都一氣呵成:

    reg_1_matches = stringr::str_extract_all(sequences, "[ACGT]{1,12000}(AAC)[AG]{2,5}[ACGT]{2,5}(CTGTGTA)") 
    df_ps = stringr::str_match(reg_1_matches, "[ACGT]{1,12000}AAC([AG]{2,5})([ACGT]{2,5})CTGTGTA") 
    p3 = df_ps[,2] 
    p4 = df_ps[,3] 
    rule_1 = chartr(old= "ACGT", "TGCA", p3) 
    rule_2 = chartr(old= "ACGT", "TGCA", p4) 
    paste("(CTAAA)[AC]{5,100}(TTTGGG)", rule_1, "(CTT)[AG]{10,5000}", rule_2, sep="") 
    
    +0

    輝煌!我真的很喜歡這個答案,簡單來說,它是一個非常聰明的實現。我也一直主要使用stringi/stringr來解決這個問題,因爲我發現它非常高效,而且我需要的許多軟件包都可以在R中找到,這使得它不那麼麻煩。我已經嘗試過你的解決方案,它確實是一個聰明的解決方案,但我似乎無法理解爲什麼'df_ps = stringr :: str_match'節只能產生'reg_1_matches'中第一個匹配的字符串的結果。它爲所有其他找到的字符串返回NA ..你知道爲什麼會發生這種情況嗎? https://pastebin.com/XS7wB4bc –

    +1

    'stringr :: str_extract_all'產生一個列表,在答案中我假設它產生一個向量。在我的簡單示例中,需要執行'reg_1_matches = unlist(reg_1_matches)'並繼續執行'stringr :: str_extract_all'。我正在下載'BSgenome.Hsapiens.UCSC.hg38_1.4'來檢查它,但它是一個700 MB的文件。我在測試時會編輯評論。 – missuse

    +0

    是的!!!謝謝。這正好解決了這個問題。 –

    1

    AWK解決方案。 要求並不複雜:一個簡單的腳本可以做到這一點。只有一個複雜因素:每個來自第一個匹配結果的正則表達式必須與第二個文件的全部行匹配。這裏是我們使用xargs來解決這個問題。

    現在,無論您選擇哪種語言,看起來所做匹配的數量都會很大,所以有關正則表達式的一些評論需要先做出。

    第一個文件的正則表達式將是緩慢的,因爲在

    [ACGT]{1,12000}(AAC)[AG]{2,5}[ACGT]{2,5}(CTGTGTA) 
    

    爲第一部分[AGCT]{1,12000}可能性的數量是巨大的。實際上,它只是說從A,C,G,T中選取任何元素,並選擇1到12000次之間的元素。然後匹配其餘的。難道我們不能做一個

    AAC([AG]{2,5})([ACGT]{2,5})CTGTGTA$ 
    

    改爲?速度增益相當可觀。

    可以對第二個文件的正則表達式做類似的註釋。如果您有

    (CTAAA)[AC]{5,100}(TTTGGG){**rule1**}(CTT)[AG]*{**rule2**}$ 
    

    更換

    (CTAAA)[AC]{5,100}(TTTGGG){**rule1**}(CTT)[AG]{10,5000}{**rule2**} 
    

    可能會有所改善。

    因爲我開始了這個答案與要求的低複雜因素,讓我們來看看一些代碼:

    $ cat tst.awk 
    match($0, /AAC([AG]{2,5})([ACGT]{2,5})CTGTGTA$/, a) { 
        r = sprintf("(CTAAA)[AC]{5,100}(TTTGGG)(%s)(CTT)[AG]*(%s)$", 
           translate(a[1]), 
           translate(a[2])); 
        print r 
    } 
    
    function translate(word) { 
        cmd = "echo '" word "' | tr 'ACGT' 'TGCA'"; 
        res = ((cmd | getline line) > 0 ? line : ""); 
        close(cmd); 
        return res 
    } 
    

    這將完成是產生於你的第二個文件中的正則表達式。 (我爲演示目的添加了額外的分組)。現在,讓我們來看看第二個腳本:

    $ cat tst2.awk 
    match($0, regex, a){ printf("Found matches %s and %s\n", a[3], a[5]) } 
    

    這將完成是得到一個regex,並從第二個輸入文件中讀取每一行進行匹配。我們需要爲regex提供這個腳本的值,就像這樣:

    $ awk -f tst.awk input1.txt | xargs -I {} -n 1 awk -v regex={} -f tst2.awk input2.txt 
    

    的awk -v選項讓我們定義一個正則表達式,它被送入該呼叫通過的第一個腳本。

    $ cat input1.txt 
    TAACAAGGACCCTGTGTA 
    
    $ cat input2.txt 
    CTAAAACACCTTTGGGTTCCTCTTAAAAAAAAAGGGGGAGAGAGAAGAAAAAAAGAGAGGG 
    

    ,其結果是:

    $ awk -f tst.awk input1.txt | xargs -I {} -n 1 awk -v regex={} -f tst2.awk input2.txt 
    Found matches TTCCT and GG 
    

    結論:你應該使用正則表達式來解決問題了嗎?是的,但你需要不太雄心勃勃,無法一次匹配整個字符串。無論您選擇哪種語言,{1,12000}等量詞都會讓您減慢速度。

    相關問題