Annotate every other column according with matching condition

Viewed 97

I have a tab delim file

NC_044998.1     4013    0       TT      2       GG      0       TT      0       TT      0       TT      
NC_044998.1     4016    0       TT      0       TT      0       TT      2       GG      0       TT      
NC_044998.1     4018    0       TT      0       TA      0       TT      0       TT      0       TT      
NC_044998.1     4019    2       CG      2       CC      2       CC      2       CG      2       CC      

I need to compare the two letters in every other column

If they are match add "homo" else add "het"

The result of each comparison can be appended to the last column, as in the output bellow.

    NC_044998.1     4013    0       TT      2       GG      0       TT      0       TT      0       TT  homo homo homo homo homo  
    NC_044998.1     4016    0       TT      0       TT      0       TT      2       GG      0       TT  homo homo homo homo homo           
    NC_044998.1     4018    0       TT      0       TA      0       TT      0       TT      0       TT  homo het homo homo homo     
    NC_044998.1     4019    2       CG      2       CC      2       CC      2       CG      2       CC  het homo homo het  homo 

another example

NC_044998.1     3803    0       GG      1       GA      0       GG      1       GA      1       GA      0       GG      0       GG      var     heterozygous    varvar  8       0.727273        3       0.272727
NC_044998.1     3806    0       TT      1       TA      1       TA      0       TT      0       TT      0       TT      0       TT     var     heterozygous    varvar  9       0.818182        2       0.181818 

output would be

NC_044998.1     3803    0       GG      1       GA      0       GG      1       GA      1       GA      0       GG      0       GG      var     heterozygous    varvar  8       0.727273        3       0.272727 homo het homo het het homo homo
NC_044998.1     3806    0       TT      1       TA      1       TA      0       TT      0       TT      0       TT      0       TT     var     heterozygous    varvar  9       0.818182        2       0.181818 homo het het homo homo homo homo

have tried

awk 'BEGIN {FS=OFS="\t"} {for (i=4; length($i)==2 && i<=12; i+=2) substr($i,1,1) == substr($i,2,1); print $0, "homo"}'

and

awk 'BEGIN {FS=OFS="\t"} { for (i=4; i<=NF; i+=2); for (j=1; j<=2; ++j); $0 = $0 " " (substr($i,j,1) == substr($i,2,1) ? "homo" : "het")} 1'

But both will only print "homo" in the end of each line

3 Answers

EDIT: Since OP's requirement got more clear now after adding more samples but keeping my first answer here too.

awk '
{
  for(i=4;i<=NF;i+=2){
    if(length($i)==2){
      if(substr($i,1,1) == substr($i,2,1)){
        val=(val?val OFS:"")"homo"
      }
      else{
        val=(val?val OFS:"")"het"
      }
    }
  }
  printf("%s%s\n",$0,(val!=""?OFS val:""))
  val=""
}' Input_file

OR if you don't want to print those lines where NO field is having 2 letter length(basically no value of homo or het) and you want to skip that line from printing then do following.

awk '
{
  for(i=4;i<=NF;i+=2){
    if(length($i)==2){
      if(substr($i,1,1) == substr($i,2,1)){
        val=(val?val OFS:"")"homo"
      }
      else{
        val=(val?val OFS:"")"het"
      }
    }
  }
  if(val!=""){
    print $0,val
  }
  val=""
}' Input_file


Could you please try following, written as per your shown samples only. your Input_file doesn't look to me tab delimited in case its tab delimited the add a BEGIN section like BEGIN{FS=OFS="\t"} after awk '` line in following solution.

awk  '
{
  for(i=6;i<=NF;i+=2){
    if($i==$(i-2)){
      val=(val?val OFS:"")"homo"
    }
    else{
      val=(val?val OFS:"")"het"
    }
  }
  print $0,val
  val=""
}' Input_file

Explanation: Adding detailed explanation for above.

awk  '                                ##Starting awk program from here.
{
  for(i=6;i<=NF;i+=2){                ##Starting a for loop from 6th field to last field which will go every 2nd field from 6th one.
    if($i==$(i-2)){                   ##Checking condition if current field is equals to current-2 field value.
      val=(val?val OFS:"")"homo"      ##if its equal then add homo in val variable here and keep appending value to it.
    }
    else{                             ##else part, in case current field is NOT equal to current-2 field then do following.
      val=(val?val OFS:"")"het"       ##if its NOT equal then add net to val value here.
    }
  }
  print $0,val                        ##Printing current line and val when for loop is completed here.
  val=""                              ##Nullifying val here.
}' Input_file                         ##Mentioning Input_file name here.

You are fairly close with your attempt. You may use:

awk 'BEGIN {
   FS=OFS="\t"
}
{
   s = ""
   for (i=4; length($i)==2 && i<=NF; i+=2)
      s = s " " (substr($i,1,1) == substr($i,2,1) ? "homo" : "het")
   $0 = $0 OFS s
} 1' file
NC_044998.1 4013    0   TT  2   GG  0   TT  0   TT  0   TT   homo homo homo homo homo
NC_044998.1 4016    0   TT  0   TT  0   TT  2   GG  0   TT   homo homo homo homo homo
NC_044998.1 4018    0   TT  0   TA  0   TT  0   TT  0   TT   homo het homo homo homo
NC_044998.1 4019    2   CG  2   CC  2   CC  2   CG  2   CC   het homo homo het homo
NC_044998.1 3803    0   GG  1   GA  0   GG  1   GA  1   GA  0   GG  0   GG  var heterozygous    varvar  8   0.727273    3   0.272727     homo het homo het het homo homo
NC_044998.1 3806    0   TT  1   TA  1   TA  0   TT  0   TT  0   TT  0   TT  var heterozygous    varvar  9   0.818182    2   0.181818     homo het het homo homo homo homo

PS: I have combined both of your input files into single file.

Another example to add to the two other very good answers, that depending on whether the field numbering may change, simply loops over the fields looking for those with a length of 2 characters that are both [[:alpha:]] characters where the first equals the second character for "homo" otherwise if the two [[:alpha:]] characters are not equal "het" that relies on a ternary and string concatenation could be:

awk '{
    for (i=1;i<=NF;i++)
        if (length($i)==2 && $i~/^[[:alpha:]]+$/)
            s = (substr($i,1,1)==substr($i,2,1)) ? s" homo" : s" het"
        print $0 " " s
        s=""
    }
' file

The strings of "homo" and "het" are appended together in s and then output at the end of the record. Thereafter s is emptied.

Example Use/Output

$ awk '{
>     for (i=1;i<=NF;i++)
>         if (length($i)==2 && $i~/^[[:alpha:]]+$/)
>             s = (substr($i,1,1)==substr($i,2,1)) ? s" homo" : s" het"
>         print $0 " " s
>         s=""
>     }
> ' file
NC_044998.1     4013    0       TT      2       GG      0       TT      0       TT      0       TT        homo homo homo homo homo
NC_044998.1     4016    0       TT      0       TT      0       TT      2       GG      0       TT        homo homo homo homo homo
NC_044998.1     4018    0       TT      0       TA      0       TT      0       TT      0       TT        homo het homo homo homo
NC_044998.1     4019    2       CG      2       CC      2       CC      2       CG      2       CC        het homo homo het homo

If you did want to limit the beginning field to field-4 and then only check every-other, then as done in the other two answers, you can loop from field-4 with a loop increment of 2, e.g.

awk '{
    for (i=4;i<=NF;i+=2)
        if (length($i)==2 && $i~/^[[:alpha:]]+$/)
            s = (substr($i,1,1)==substr($i,2,1)) ? s" homo" : s" het"
        print $0 " " s
        s=""
    }
' file

There are a number of ways to put the strings together and whether you use a ternary or if .. else .. is largely a matter of taste. Look things over an let me know if you have further questions.

Related