计算字段中 TRUE 的数量,报告字段编号、TRUE 数量和标签(大或小)计数

计算字段中 TRUE 的数量,报告字段编号、TRUE 数量和标签(大或小)计数

我在 Linux 机器上运行代码,计算给定列/字段中找到的 TRUE 数量,打印列号和该列中 TRUE 的数量作为输出。

在新输入中,行(输入中的最后一列)被指定为“大”或“小”(各三行)。

我想计算每列中具有 2 个或更多 TRUE 的“小”和“大”的数量。

用于查找具有 2 个或更多 TRUE 的列的代码(我知道下面的代码忽略输入的第一列):

awk -vtc=2 'NR==1{next};
                NR==2{for(i=2;i<=NF;i++){t[i]=0}};
                {for(i=2;i<=NF;i++){if($i=="TRUE"){t[i]++}}}
                END{
                    for(j in t)
                    if(t[j]>=tc){print(j,t[j])}
                }' input.tsv > output.tsv

输入.tsv:

MT MT MT MT MT MT MT MT MT MT
FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE TRUE FALSE
FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
FALSE FALSE FALSE FALSE FALSE TRUE FALSE FALSE FALSE FALSE
FALSE FALSE FALSE FALSE FALSE TRUE FALSE FALSE FALSE TRUE
FALSE FALSE FALSE FALSE FALSE TRUE FALSE FALSE TRUE TRUE

输出.tsv:

(第一列:列号;第二列:TRUE 的数量)

3 3
6 3
9 2
10 2

新输入.tsv

MT MT MT MT MT MT MT MT MT MT CAT
FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE LARGE        
FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE TRUE FALSE SMALL         
FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE SMALL        
FALSE FALSE FALSE FALSE FALSE TRUE FALSE FALSE FALSE FALSE SMALL        
FALSE FALSE FALSE FALSE FALSE TRUE FALSE FALSE FALSE TRUE LARGE     
FALSE FALSE FALSE FALSE FALSE TRUE FALSE FALSE TRUE TRUE LARGE

所需的输出.tsv:

(第三列:分配为小的 TRUE 数量;第四列:分配为大的 TRUE 数量)

3 3 2 1
6 3 1 2
9 2 1 1
10 2 0 2

非常感谢各位 Linux 向导的帮助!

答案1

使用(伪)多维数组的解决方案awk

awk '
BEGIN {
    b["TRUE"] = 1
    b["FALSE"] = 0
}
FNR > 1 {
    for (i=1; i < NF; ++i)
        a[i, $NF] += b[$i]
}
END {
    s = "SMALL"
    l = "LARGE"
    for (j=1; j<=i; ++j)
        if (a[j, s] || a[j, l])
            print j, a[j, s] + a[j, l],
            a[j, s] + 0,
            a[j, l] + 0
}' input.tsv

或者使用 GNU awk 中提供的真正的多维数组

awk '
FNR > 1 {
    for (i=1; i < NF; ++i)
        if ($i == t)
            ++a[i][$NF] 
}
END {
    for (j in a)
        print j, a[j][s] + a[j][l],
        +a[j][s],
        +a[j][l]
}' t=TRUE s=SMALL l=LARGE input.tsv

答案2

不优雅的大锤,但似乎有用

#!/bin/bash

cols=$(echo $(head -n 1 file) | awk '{print gsub(/ /, "")}')
sed -e "1d" -e "s/TRUE/1/g" -e "s/FALSE/0/g" -e "s/ /,/g" file > tmp1
sed "/,S.*/d" tmp1 > tmp2
for s in $(seq 1 $cols); do
    tr=$(cut -d, -f$s tmp1 | paste -s -d+ | bc --)
    if [ $tr -gt 0 ]; then
        trl=$(cut -d, -f$s tmp2 | paste -s -d+ | bc --)
        echo $s $tr $(( $tr-$trl )) $trl
    fi
done | column -t -N Col,True,Small,Large
rm tmp1 tmp2

输出

Col  True  Small  Large
3    3     2      1
6    3     1      2
9    2     1      1
10   2     0      2

编辑

攻击性稍弱一些awk

#!/bin/bash

sed -e "1d" -e "s/TRUE/1/g" -e "s/FALSE/0/g" file | awk '{
    for (i=1; i<NF; i++)
        {sumall[i]+= $i; if ($NF == "LARGE") {sumlarge[i]+= $i}};
    } END {
    for (x in sumall)
        if (sumall[x] > 0)
            { print x, sumall[x], sumall[x]-sumlarge[x], sumlarge[x]}
    }' | column -t -N Col,True,Small,Large

答案3

这使用了调用不同实用程序的管道:

$ sed -E '1d;s/FALSE/0/g;/LARGE$/s/TRUE/L/g;s/TRUE/S/g' input.tsv |
  datamash transpose |
  perl -F'\t' -lane '$,="\t"; my %h;
    my $c = grep { /^([LS])$/ && ++$h{$1} } @F;
    print $., $c, $h{S}||0, $h{L}||0 if $c > 1;
  '
3   3   2   1
6   3   1   2
9   2   1   1
10  2   0   2

相关内容