Reputation: 127
I have previously used awk to reduce an enormous data table which has mostly zeros, to a smaller table with just the interesting rows (those with not too many zeros), with something like this:
awk -F '\t' '{count=0} {for(i=2; i<30; i++) if($i==0) count++} {if(count<5) print $0}' BigTable > SmallerTable
Now I would like to filter a similar table, to find rows with non-zero values in most of the "female" columns and zeros in most of the "male" columns. I tried to use the same awk logic, but my code returns all lines of the input file.
#! /usr/bin/awk -f
FS="\t"
{countF=0} {for(i=2; i<7; i++) if($i==0) countF++}
# count zeros in female columns 2-6
{countM=0} {for(i=7; i<12; i++) if($i==0) countM++}
# count zeros in male columns 7-12
{if (countF<2 && countM>3) {print $0}}
# if fewer than 2/5 females AND more than 3/5 males are zero, print line
My input file starts like this:
NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN F_CR1 F_CR2 F_CR3 F_CR4 F_CR6 M_CR10 M_CR5 M_CR7 M_CR8 M_CR9
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 14727 13526 13318 13862 11040 18975 21411 20079 16285 15611
CCGGTGTGACAACTGTAGTGAACTCAGCTCA 23 32 26 15 28 28 42 29 8 22
AACCAAATCTACAAACAGGAGATGTTGTTCT 107 110 118 106 95 100 121 132 92 90
GAAATAGAACAGGCCTGGAAGCCATGTCAAA 15 15 16 12 11 31 23 19 9 28
Have I messed up the syntax in the print line? Any advice much appreciated!
Upvotes: 0
Views: 126
Reputation: 203324
Change FS="\t"
to BEGIN{FS="\t"}
. Right now the result of that assignment is a true condition which invokes the default action of printing every line.
Then change your shell script to:
/usr/bin/awk '
BEGIN { FS="\t" }
{
# count zeros in female columns
countF=0
for(i=2; i<=6; i++) {
if ($i==0) {
countF++
}
}
# count zeros in male columns
countM=0
for(; i<=NF; i++) {
if ($i==0) {
countM++
}
}
}
# if fewer than 2/5 females AND more than 3/5 males are zero, print line
countF<2 && countM>3
' "$@"
so it's more awk-ish and easier to enhance later if/when you need to separate shell args into awk args and awk variable assignments (shebangs are not useful for this).
Also consider abbreviating it and removing the hard-coded Male/Female limits but get them from the header line instead:
/usr/bin/awk '
BEGIN { FS="\t" }
FNR==1 {
for (i=2; i<=NF; i++) {
sub(/_.*/,"",$i)
gender[i] = $i
}
next
}
{
for (i=2; i<=NF; i++) {
count[gender[i]] += ($i==0)
}
}
count["F"]<2 && count["M"]>3
' "$@"
The above is untested since you didn't provide the expected output for us to test with.
Upvotes: 3