我在 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