与えられた列/フィールドで見つかった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:
(最初の列:列番号、2番目の列: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:
(3番目の列:少数の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