Home > Mobile >  grep variable pattern and output match and sequence position
grep variable pattern and output match and sequence position

Time:08-12

Given the following string,

>Q07092
MWVSWAPGLWLLGLWATFGHGANTGAQCPPSQQEGLKLEHSSSLPANVTGFNLIHRLSLMKTSAIKKIRNPKGPLILRLGAAPVTQPTRRVFPRGLPEEFALVLTLLLKKHTHQKTWYLFQVTDANGYPQISLEVNSQERSLELRAQGQDGDFVSCIFPVPQLFDLRWHKLMLSVAGRVASVHVDCSSASSQPLGPRRPMRPVGHVFLGLDAEQGKPVSFDLQQVHIYCDPELVLEEGCCEILPAGCPPETSKARRDTQSNELIEINPQSEGKVYTRCFCLEEPQNSEVDAQLTGRISQKAERGAKVHQETAADECPPCVHGARDSNVTLAPSGPKGGKGERGLPGPPGSKGEKGARGNDCVRISPDAPLQCAEGPKGEKGESGALGPSGLPGSTGEKGQKGEKGDGGIKGVPGKPGRDGRPGEICVIGPKGQKGDPGFVGPEGLAGEPGPPGLPGPPGIGLPGTPGDPGGPPGPKGDKGSSGIPGKEGPGGKPGKPGVKGEKGDPCEVCPTLPEGFQNFVGLPGKPGPKGEPGDPVPARGDPGIQGIKGEKGEPCLSCSSVVGAQHLVSSTGASGDVGSPGFGLPGLPGRAGVPGLKGEKGNFGEAGPAGSPGPPGPVGPAGIKGAKGEPCEPCPALSNLQDGDVRVVALPGPSGEKGEPGPPGFGLPGKQGKAGERGLKGQKGDAGNPGDPGTPGTTGRPGLSGEPGVQGPAGPKGEKGDGCTACPSLQGTVTDMAGRPGQPGPKGEQGPEGVGRPGKPGQPGLPGVQGPPGLKGVQGEPGPPGRGVQGPQGEPGAPGLPGIQGLPGPRGPPGPTGEKGAQGSPGVKGATGPVGPPGASVSGPPGRDGQQGQTGLRGTPGEKGPRGEKGEPGECSCPSQGDLIFSGMPGAPGLWMGSSWQPGPQGPPGIPGPPGPPGVPGLQGVPGNNGLPGQPGLTAELGSLPIEQHLLKSICGDCVQGQRAHPGYLVEKGEKGDQGIPGVPGLDNCAQCFLSLERPRAEEARGDNSEGDPGCVGSPGLPGPPGLPGQRGEEGPPGMRGSPGPPGPIGPPGFPGAVGSPGLPGLQGERGLTGLTGDKGEPGPPGQPGYPGATGPPGLPGIKGERGYTGSAGEKGEPGPPGSEGLPGPPGPAGPRGERGPQGNSGEKGDQGFQGQPGFPGPPGPPGFPGKVGSPGPPGPQAEKGSEGIRGPSGLPGSPGPPGPPGIQGPAGLDGLDGKDGKPGLRGDPGPAGPPGLMGPPGFKGKTGHPGLPGPKGDCGKPGPPGSTGRPGAEGEPGAMGPQGRPGPPGHVGPPGPPGQPGPAGISAVGLKGDRGATGERGLAGLPGQPGPPGHPGPPGEPGTDGAAGKEGPPGKQGFYGPPGPKGDPGAAGQKGQAGEKGRAGMPGGPGKSGSMGPVGPPGPAGERGHPGAPGPSGSPGLPGVPGSMGDMVNYDEIKRFIRQEIIKMFDERMAYYTSRMQFPMEMAAAPGRPGPPGKDGAPGRPGAPGSPGLPGQIGREGRQGLPGVRGLPGTKGEKGDIGIGIAGENGLPGPPGPQGPPGYGKMGATGPMGQQGIPGIPGPPGPMGQPGKAGHCNPSDCFGAMPMEQQYPPMKTMKGPFG

I want to first grep for pattern matching 6 or more xGx repeats, where x is any character. This, I can easily do,

grep -EIho -B1 '([^G]G[^G]){6,}' file

which outputs

>Q07092
KGERGLPGPPGSKGEKGARGN
EGPKGEKGESGALGPSGLPGSTGEKGQKGEKGD
IGPKGQKGDPGFVGPEGLAGEPGPPGLPGPPGI
PGPKGDKGSSGIPGKEGP
FGLPGLPGRAGVPGLKGEKGNFGEAGPAGSPGPPGPVGPAGIKGAKGE
FGLPGKQGKAGERGLKGQKGDAGNPGDPGTPGTTGRPGLSGEPGVQGPAGPKGEKGD
AGRPGQPGPKGEQGPEGV
PGKPGQPGLPGVQGPPGLKGVQGEPGPPGR
QGPQGEPGAPGLPGIQGLPGPRGPPGPTGEKGAQGSPGVKGATGPVGPPGA
SGPPGRDGQQGQTGLRGTPGEKGPRGEKGEPGE
PGPQGPPGIPGPPGPPGVPGLQGVPGNNGLPGQPGL
EGDPGCVGSPGLPGPPGLPGQRGEEGPPGMRGSPGPPGPIGPPGFPGAVGSPGLPGLQGERGLTGLTGDKGEPGPPGQPGYPGATGPPGLPGIKGERGYTGSAGEKGEPGPPGSEGLPGPPGPAGPRGERGPQGNSGEKGDQGFQGQPGFPGPPGPPGFPGKVGSPGPPGP
KGSEGIRGPSGLPGSPGPPGPPGIQGPAGLDGLDGKDGKPGLRGDPGPAGPPGLMGPPGFKGKTGHPGLPGPKGDCGKPGPPGSTGRPGAEGEPGAMGPQGRPGPPGHVGPPGPPGQPGPAGI
VGLKGDRGATGERGLAGLPGQPGPPGHPGPPGEPGTDGAAGKEGPPGKQGFYGPPGPKGDPGAAGQKGQAGEKGRAGM
PGKSGSMGPVGPPGPAGERGHPGAPGPSGSPGLPGVPGSMGD
PGRPGPPGKDGAPGRPGAPGSPGLPGQIGREGRQGLPGVRGLPGTKGEKGDIGI
AGENGLPGPPGPQGPPGY
MGATGPMGQQGIPGIPGPPGPMGQPGKAGH

Now, I want to find the character position of all G's when they occur in 'TGA' or 'SGA'. The character positions should be based on the input and NOT the output.

Expected output,

$ some-grep-awk-code
>Q07092
TGA: 573
SGA: 384

The awk solution,

awk -v str='TGA' '{ off=0; while (pos=index(substr($0,off 1),str)) { printf("%d: %d\n", NR, pos off); off =length(str) pos } }' file

outputs TGA both at character position 25 and 573. However, I want to only identify the character position of G in SGA/TGA when they occur in the midst of six or more xGx repeats.

Really appreciate any help!

CodePudding user response:

With your shown samples please try following awk code. Written and tested in GNU awk should work in any POSIX awk. In this code we could pass how many strings/variables into the function and can get their ALL present index values in the line.

awk '
function checkValue(value){
  max=""
  len=length($0)
  num=split(value,arr,",")
  for(i=1;i<=num;i  ){
     line=$0
     while(index(line,arr[i])){
        val =index(line,arr[i])
        line=substr(line,val 1)
        if(val<=(len/2)){
           finalVal=arr[i]": "val 1
        }
     }
     if(finalVal){
        print finalVal
     }
  }
}
/^>[0-9a-zA-Z]{6}$/{print; next}
checkValue("SGA,TGA")
'   Input_file

Output with shown samples will be as follows:

>Q07092
SGA: 384
TGA: 573

CodePudding user response:

Here's a complete awk solution:

  • Each sequence must span a single line
  • The results are relatives to the start of the line
awk '
    BEGIN {
        regexp = "([^G]G[^G]){6,}"
        search["SGA"]
        search["TGA"]
    }
    /^>/ {
        print
        next
    }
    {
        i0 = 1
        s0 = $0
        while ( match( s0, regexp ) ) {
            head = substr(s0,RSTART,RLENGTH)
            tail = substr(s0,RSTART RLENGTH)
            i0  = RSTART - 1
            for (s in search) {
                s1 = head
                i1 = i0
                while ( i = index(s1, s) ) {
                    s1 = substr(s1, i 1)
                    i1  = i
                    search[s] =  search[s] " " i1-1
                }
            }
            s0 = tail
            i0  = RLENGTH
        }
        for (s in search) {
            print s ":" search[s]
            search[s] = ""
        }
    }
'

The algorithm searches for the occurrences of SGA and TGA in all the parts of the line that match the regex [^G]G[^G]{6,}. The implementation is a little tedious though, as there's no offset option for the match() and index() functions of awk.

TODO
  • parameterize the regex and the search strings
  • prevent the infinite loops that occur when a search string is empty or when the regex can match 0-length strings
  • allow multi-line sequences
  • allow overlapping matches for the regex. Basically, it means to try matching the regex at each position in the line; there will be a lot of duplicate indexes for the search strings and they will need to be discarded one way or an other.
Example

Input

>TEST1
SGA.G..G.TGATGA.G..G..G.SGA.....TGA.....SGA.....G..G.SGA.G..G..G.
>TEST2
.G..G.TGAG..G..G.....G..G..G..G.SGA.G.

Output

>TEST1
SGA: 1 25 54
TGA: 10 13
>TEST2
SGA: 33
TGA:
  • Related