in reply to match and mismatch

There is a neat trick for finding difference between similar equal length ASCII strings: xor them together and any non-zero bytes are different. Consider:

use strict; use warnings; my $one= "AGCTGATCGAGCTAGTACCCTAGCTC"; my $two= "AGCTGATCGAGCTAGTACCCTATCTC"; my $diff = $one ^ $two; $diff =~ tr/\0/x/c; my $start = -1; while (-1 < ($start = index $diff, 'x', ++$start)) { print "Difference at $start\n"; }

Prints:

Difference at 22

The tr/// changes non-zero bytes to x. The while loop then uses index to search through the difference string for the x bytes and reports their index (0 based position).


Perl reduces RSI - it saves typing

Replies are listed 'Best First'.
Re^2: match and mismatch
by johngg (Canon) on Nov 14, 2008 at 10:35 UTC
    The tr/// changes non-zero bytes to x. The while loop then uses index to search through the difference string for the x bytes and reports their index (0 based position).

    I think it might be simpler to use a global regex match for non-nulls and pos. You can just match them for 1-based counting or put them in a look-ahead for 0-based counting. Note that I added another difference in the third characters to check that it was working as I had hoped.

    use strict; use warnings; my $one = "AGCTGATCGAGCTAGTACCCTAGCTC"; my $two = "AGATGATCGAGCTAGTACCCTATCTC"; # Diffs here ^ ^ my $diff = $one ^ $two; my @posns = (); push @posns, pos $diff while $diff =~ m{[^\0]}g; print qq{Differences, 1-based counting, at - @posns\n}; @posns = (); push @posns, pos $diff while $diff =~ m{(?=[^\0])}g; print qq{Differences, 0-based counting, at - @posns\n};

    The output.

    Differences, 1-based counting, at - 3 23 Differences, 0-based counting, at - 2 22

    I hope this is of interest.

    Cheers,

    JohnGG

Re^2: match and mismatch
by heidi (Sexton) on Nov 17, 2008 at 03:25 UTC
    i will write the complete problem i am facing. Here is the input file i am using.
    sxoght: #query hit score probability qstart qend qorien +tation tstart tend matches mismatches gapOpening gap +s @SNPSTER4_104_308EFAAXX:1:1:1694:128 GGGATAAGAGAGGTGCATGTTGGTATTTAAGGTAGT 1 alignment(s) -- reports limited to 10 alignment(s) sxoght: SNPSTER4_104_308EFAAXX:1:1:1694:128 gi|122939163|ref|NM_00 +0165.3| -10 1.000000 1 36 + 1595 163 +0 35 1 00 Score = -10, P(A|R) = 1.000000 Query: 1 GGGATAAGAGAGGTGCATGTTGGTATTTAAGGTAGT 36 |||||||||||||||||||||||||||||| ||||| Sbjct: 1595 GGGATAAGAGAGGTGCATGTTGGTATTTAAAGTAGT 1630 @SNPSTER4_104_308EFAAXX:1:1:1608:94 GCAGTTTTAAGTTATTAGTTTTTAAAATCAGTACTT 14 alignment(s) -- reports limited to 10 alignment(s) sxoght: SNPSTER4_104_308EFAAXX:1:1:1608:94 gi|113412254|ref|XR_01 +8775.1| 0 0.090884 1 36 + 1578 161 +3 36 0 00 Score = 0, P(A|R) = 0.090884 Query: 1 GCAGTTTTAAGTTATTAGTTTTTAAAATCAGTACTT 36 ||| |||||||||||||||||||||||||||||| | Sbjct: 1578 GCATTTTTAAGTTATTAGTTTTTAAAATCAGTACGT 1613
    this is a big file, though the whole file looks like this. What i am trying to do exactly is to grep the header (sxoght) for display in columns and also to display where there is a mismatch in the alignment between query and sbjct. for this input file, the expected results should look like:
    >gi|122939163|ref|NM_000165.3| 1595 1630 SNPSTER4_104_308EFAA +XX:1:1:1694:128 1 36 36 -10 1 1.000000 35 mismatch : 1625.GA >gi|113412254|ref|XR_018775.1| 1578 1613 SNPSTER4_104_308EFAA +XX:1:1:1608:94 1 36 36 0 1 0.090884 36 mismatch : 1581.GT 1612.TG
    the code which i have written is :
    #!/usr/bin/perl open(FILE,"align.input") or die "can not open file"; while($var=<FILE>){ $str1=();$str2=(); if($var=~/^sxoght:/){ @ar=split(/\s+/,$var); print ">$ar[2]\t$ar[8]\t$ar[9]\t$ar[1]\t$ar[5]\t$ar[10]\t$ar[6]\t$ +ar[3]\t$ar[4]\t$ar[11]\n"; } if($var=~/^Query:/){ $str1=$var; $str1=~s/^Query:\s+//g; $str1=~s/\d+\s+//g; $str1=~s/\s+//g; } if($var=~/^Sbjct:/){ $str2=$var; $str2=~s/^Sbjct:\s+//g; $str2=~s/\d+\s+//g; $str2=~s/\s+//g; } for($i=0;$i<=length($str1);$i++) { if(substr($str1,$i,1) ne substr($str2,$i,1)){ print substr($str1,$i,1); print substr($str2,$i,1); print "$i\n"; } } }
    I am not able to use "strict and warning" because using it doesnt allow me to access the scalar variable outside the loop. In my code, i m trying to extract the positions first, so that i will subtract it from the already stored @arr values of beginning and start. I am having problems with the for loop. I know where i am going wrong, but dont know how to correct it. PLEASEEEE HELP !!!
      You should only print the output if $str1 and $str2 are both set, and then, clear them so you don't print them again.

      The next is very close to your code, but kind of works. You only need to format the output as desired (I don't know where the large numbers come from):

      #!/usr/bin/perl open(FILE,"align.input") or die "can not open file: $!"; while($var=<FILE>){ if($var=~/^sxoght:/){ ($str1,$str2)=(); @ar=split(/\s+/,$var); print ">$ar[2]\t$ar[8]\t$ar[9]\t$ar[1]\t$ar[5]\t$ar[10]\t$ar[6 +]\t$ar[3]\t$ar[4]\t$ar[11]\n"; } if($var=~/^Query:/){ $str1=$var; $str1=~s/^Query:\s+//g; $str1=~s/\d+\s+//g; $str1=~s/\s+//g; } if($var=~/^Sbjct:/){ $str2=$var; $str2=~s/^Sbjct:\s+//g; $str2=~s/\d+\s+//g; $str2=~s/\s+//g; } if(defined $str1 and defined $str2) { for($i=0;$i<=length($str1);$i++) { if(substr($str1,$i,1) ne substr($str2,$i,1)){ # this is not in the desired format, yet print substr($str1,$i,1); print substr($str2,$i,1); print "$i\n"; } } ($str1,$str2)=(); } }
      For the format, I propose to store the results in an array, and print "mismatch:" only if the array isn't empty at the end.
      my @mismatch; for($i=0;$i<=length($str1);$i++) { if(substr($str1,$i,1) ne substr($str2,$i,1)){ push @mismatch, "$i." . substr($str1,$i,1) . substr($s +tr2,$i,1); } } if(@mismatch) { print "mismatch: @mismatch\n"; }
      After that modification, the output I get for this file is
      >hit tstart tend #query qstart matches qend score + probability mismatches >gi|122939163|ref|NM_000165.3| 1595 1630 SNPSTER4_104_308EFAA +XX:1:1:1694:128 1 35 36 -10 1.000000 1 mismatch: 30.GA >gi|113412254|ref|XR_018775.1| 1578 1613 SNPSTER4_104_308EFAA +XX:1:1:1608:94 1 36 36 0 0.090884 0 mismatch: 3.GT 34.TG

      p.s. There's a possible speed improvement if you XOR (^) the two strings, you'll get a string of null bytes for where they are the same and non null where they are not:

      my $xor = $str1 ^ $str2; while($xor =~ /[^\0]/g) { my $i = pos($xor) - 1; # or: $-[0] push @mismatch, "$i." . substr($str1,$i,1) . substr($str2, +$i,1); }
        hi bart, thank u sooo much for the help.. now i know where all i went wrong. will try to avoid such mistakes next time. i am learning a lot from perlmonks.org. thanks again :)
      but u did not define here @var and @ar