我有一个文件,如您所见,是制表符分隔的数据,大约 4,000 行,十列。
文件的第二列记录了不同的组织。
samples tissue_s tissue_e tissue_d tissue_category tissue_visa sex study tissue_f age
samples1 ear CNS ear CNS CNS male 1 ear 365
samples2 ear CNS ear CNS CNS male 1 ear 365
samples3 ear CNS ear CNS CNS male 1 ear 365
samples4 ear CNS ear CNS CNS male 1 ear 365
samples5 ear CNS ear CNS CNS male 1 ear 365
samples6 stomach CNS ear CNS CNS male 1 ear 365
samples7 stomach CNS ear CNS CNS male 1 ear 365
samples8 stomach CNS ear CNS CNS male 1 ear 365
samples9 stomach CNS ear CNS CNS male 1 ear 365
...
...
希望能够将出现过十次以上的纸巾信息全部打印出来
但我认为这样做并生成中间文件效率很低。有没有更简洁高效的方法呢?
cat file | awk '{print $2}' | awk '{a[$0]++}END{for(i in a){if(a[i] > 10){print i}}}' > tmp.txt
grep -wFf tmp.txt file.txt > resule.txt
答案1
一种方法是处理输入文件两次:
awk -F'\t' -v frq=10 -v colId=2 '
NR==FNR{ count[$colId]++; next }
count[$colId] >frq
' infile infile
注意:用户定义的 awk 变量frq
用于colId
设置和指定应输出记录的目标 columnId 中元素的最小重复频率。
另一种方法是处理输入文件一次并且只有缓冲几行如果您的 ipnut 数据在第二个字段上排序,如下所示:
awk -F'\t' -v frq=10 -v colId=2 '
function prnt() { if(c>frq) printf("%s", buf); buf=c="" }
prev!=$colId{ prnt() }
{ c++; prev=$colId; buf = buf $0 ORS }
END{ prnt() }' infile
如果它没有在第二个字段上排序,那么首先对其进行排序,然后将其传递给 awk。
<infile sort -t$'\t' -k2,2 |
awk -F'\t' -v frq=10 -v colId=2 '
function prnt() { if(c>frq) printf("%s", buf); buf=c="" }
prev!=$colId{ prnt() }
{ c++; prev=$colId; buf = buf $0 ORS }
END{ prnt() }'
答案2
使用乐(以前称为 Perl_6)
~$ raku -e 'my %h; do for lines.skip() {%h.push: .words.[1] => .words}; \
for %h.kv -> $k,@v {(put $k; .put for @v) if @v.elems > 4};' file
您可能有兴趣尝试 Perl 家族语言,即 Raku。优点之一是对内置 Unicode 的高级支持,如果您与使用不同语言排序规则的同事交换数据的话。
上面声明了一个散列,并对其进行了%h
自动剪切lines
(skip
ping 标题行) ,以(第二列)作为键,(所有列)作为值。由于散列中不能存在重复的键,因此在第二列中找到的每个单独组织下添加行。处理完所有行后,散列将被键值处理为标量键和数组内的值。仅打印(即 OP 样本输入的 4 行以上)。push
.words.[1]
.words
%h
%h.kv
$k
@a
@v.elems > 4
输入示例:
samples tissue_s tissue_e tissue_d tissue_category tissue_visa sex study tissue_f age
samples1 ear CNS ear CNS CNS male 1 ear 365
samples2 ear CNS ear CNS CNS male 1 ear 365
samples3 ear CNS ear CNS CNS male 1 ear 365
samples4 ear CNS ear CNS CNS male 1 ear 365
samples5 ear CNS ear CNS CNS male 1 ear 365
samples6 stomach CNS ear CNS CNS male 1 ear 365
samples7 stomach CNS ear CNS CNS male 1 ear 365
samples8 stomach CNS ear CNS CNS male 1 ear 365
samples9 stomach CNS ear CNS CNS male 1 ear 365
示例输出(来自上面的代码):
ear
samples1 ear CNS ear CNS CNS male 1 ear 365
samples2 ear CNS ear CNS CNS male 1 ear 365
samples3 ear CNS ear CNS CNS male 1 ear 365
samples4 ear CNS ear CNS CNS male 1 ear 365
samples5 ear CNS ear CNS CNS male 1 ear 365
调整输出以满足您的需要相当容易。put $k;
如果您不想要单独的“组织”标题,请放弃呼叫。此外,将@a
行输出更改为.join("\t").put for @v
重新构成\t
制表符分隔行。
请注意,上面的答案假设每个列条目都没有空格,因为.words
在空格上分割(\t
或不分割)。如果您不能保证每个列条目都是单个空格分隔的元素,请.split("\t")
改为使用。将它们放在一起(提供与上面相同的输出,但现在以制表符分隔):
~$ raku -e 'my \%h; do for lines.skip() {\%h.push: .split("\t").[1] => .split("\t")}; \
for \%h.kv -> $k,@v {($k.put; .join("\t").put for @v) if @v.elems > 4};' file