주어진 열/필드에서 발견된 TRUE 수를 계산하고 해당 열의 TRUE 수와 열 번호를 출력으로 인쇄하는 코드를 Linux 시스템에서 실행하고 있습니다.
새 입력에서 행(입력의 마지막 열)은 "큰" 또는 "작은"(각각 3개 행)으로 지정됩니다.
각 열에서 TRUE가 2개 이상인 "소형"과 "대형"의 수를 계산하고 싶습니다.
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
새로운 input.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에 할당됨, 4열: 많은 수의 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