0

I'm trying to format some output using printf with awk. Basically, I want the final format to look like this (everything is tab-separated.)

chr10   100000624       100000625       10:100000625_A_G
chr10   100000644       100000645       10:100000645_A_C
chr10   100002463       100002464       10:100002464_C_T
chr10   100003241       100003242       10:100003242_G_T
chr10   100003303       100003304       10:100003304_A_G
chr10   10000337        10000338        10:10000338_C_T
chr10   100003515       100003516       10:100003516_A_G
chr10   100003784       100003785       10:100003785_C_T
chr10   100004359       100004360       10:100004360_A_G
chr10   100004440       100004441       10:100004441_C_G 
...

With the starting file looking like this (the other columns are not important):

10:100000625_A_G        G       A
10:100000645_A_C        C       A
10:100002464_C_T        C       T
10:100003242_G_T        G       T
10:100003304_A_G        G       A
10:10000338_C_T T       C
10:100003516_A_G        A       G
10:100003785_C_T        C       T
10:100004360_A_G        A       G
10:100004441_C_G        C       G ...
...

The second column is supposed to be one less than the first column. Using my original file, I basically did:

awk -F ":" '$1=$1' OFS="\t" <(zcat <filename>) | awk -F "_" '$2=$2' OFS="\t" | awk -v OFMT="%f" 'BEGIN {OFS=FS="\t"} {print "chr"$1, $2-1, $2, $1":"$2"_"$3"_"$4}'


First, I broke apart the id at the beginning at the ":", then at the "_". I think I could have also done awk -F "[:_]"... and broke apart the first column using both those delimiters at once but I don't think that makes a difference at the end.

This works, except in a few cases, the number is reported in scientific notation (numbers like 12000000), which I don't want. printf should be able to allow me to undo the scientific notation, but I'm not able to get it to work.

My first thought was that I want the first and fourth column as strings, and the second and third columns as floating point digits with no decimals. So, I tried the following awk '{printf "%s\t%4.0f\t%4.0f\t%s\n" "chr"$1, $2-1, $2, $1":"$2"_"$3"_"$4}'. However, I get the following error message:


awk: (FILENAME=- FNR=1) fatal: not enough arguments to satisfy format string
        `%s     %4.0f   %4.0f   %s
chr10'
                        ^ ran out for this one

It seems like the format string is too long for how many fields I have in my file, but I'm not sure why this is. In playing around with printf, I found a peculiarity. If I did awk '{printf "\t%s\t%4.0f\t%4.0f\t\n" "chr"$1, $2-1, $2, $1":"$2"_"$3"_"$4}', I would get the following result:

        100000624       100000625         10
chr10   100000644       100000645         10
chr10   100002463       100002464         10
chr10   100003241       100003242         10
chr10   100003303       100003304         10
chr10   10000337        10000338          10
chr10   100003515       100003516         10
chr10   100003784       100003785         10
chr10   100004359       100004360         10
chr10   100004440       100004441         10
chr10 

So, the first column is pushed down one row relative to all the other columns, and the fourth column is cut off. I would very much appreciate if in your response, you could also explain how your printf syntax is working. Thank you so much!

2
  • Ah, I'm not very familiar with the use of AWK. I just needed the output to be tab-separated and also not display those numbers using scientific notation. What would be a more succinct way of doing this? Thank you.
    – Lyn
    Apr 17 at 4:31
  • I wish you had included at least one of the cases that print in scientific notation in your sample input/output so we could use it to reproduce your problem.
    – Ed Morton
    Apr 17 at 12:42
2

You're just missing a comma after the format string, e.g.

awk -F'[:_\t]' '{ printf "%s\t%4.0f\t%4.0f\t%s\n", "chr"$1, $2-1, $2, $1":"$2"_"$3"_"$4 }' file
#                                                ^
#                                                |
#                                                this one
3
  • Thanks so much. That worked! One last question. The fourth column is still rendered with the scientific notation at the spot where $2 is used, so it'll look like 1:12e07_A_G, for example. Is it possible, when constructing the string for the fourth column, to have column 2 incorporated without scientific notation?
    – Lyn
    Apr 17 at 0:27
  • 1
    Sure, depending on how you want to format it, you could use something like $1":"sprintf("%4.0f",$2)"_"$3"_"$4 as the fourth argument.
    – Freddy
    Apr 17 at 0:43
  • Or printf "chr%s\t%4.0f\t%4.0f\t%s:%4.0f_%s_%s\n", $1, $2-1, $2, $1, $2, $3, $4 or similar.
    – Ed Morton
    Apr 17 at 13:00
2

I can't reproduce your problem at all but assuming it does exist this may help you:

$ cat tst.awk
BEGIN { OFS="\t"; OFMT="%d" }
{
    split($1,f,/[:_]/)
    print "chr"f[1], f[2]-1, f[2], $1
}

$ awk -f tst.awk file
chr10   100000624       100000625       10:100000625_A_G
chr10   100000644       100000645       10:100000645_A_C
chr10   100002463       100002464       10:100002464_C_T
chr10   100003241       100003242       10:100003242_G_T
chr10   100003303       100003304       10:100003304_A_G
chr10   10000337        10000338        10:10000338_C_T
chr10   100003515       100003516       10:100003516_A_G
chr10   100003784       100003785       10:100003785_C_T
chr10   100004359       100004360       10:100004360_A_G
chr10   100004440       100004441       10:100004441_C_G

If %d format doesn't do what you need but %4.0f does, then just change OFMT="%d" to OFMT="%4.0f".

That stuff you mention about the first column being pushed down and the 4th column being truncated probably means you have DOS line endings in your input, see https://stackoverflow.com/questions/45772525/why-does-my-tool-output-overwrite-itself-and-how-do-i-fix-it.

0

Using the awk utility, we can do as follows:

printf -v fmt '%s\t' '%s' '%4.0f' '%4.0f' '%s\n'
awk -F '\t' -v fmt="${fmt%?}" '
{
  split($1, a, /[:_]/)
  f1 = "chr" a[1]
  f2 = (f3 = a[2])-1
  f4 = sprintf("%4.0f", f3)
  sub(/:[^_]+/, ":"f4, $1)
  printf fmt, f1, f2, f3, $1
}
' file

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.