awk计数发生次数

时间:2016-08-12 17:12:55

标签: bash shell awk

我在shell脚本中创建了这个awk命令来计算$ 4和$ 5的总出现次数。

awk -F" " '{if($4=="A" && $5=="G") {print NR"\t"$0}}' file.txt > ag.txt && cat ag.txt | wc -l
awk -F" " '{if($4=="C" && $5=="T") {print NR"\t"$0}}' file.txt > ct.txt && cat ct.txt | wc -l

awk -F" " '{if($4=="T" && $5=="C") {print NR"\t"$0}}' file.txt > tc.txt && cat ta.txt | wc -l
awk -F" " '{if($4=="T" && $5=="A") {print NR"\t"$0}}' file.txt > ta.txt && cat ta.txt | wc -l

输出是shell中的####(number)。但是我想摆脱> ag.txt && cat ag.txt | wc -l而不是像AG = ####那样在shell中输出。

这是输入格式:

>seq1 284 284 A G 27 100 16 11 16 11
>seq1 266 266 C T 27 100 16 11 16 11
>seq1 185 185 T - 24 100 10 14 10 14
>seq1 194 194 T C 24 100 12 12 12 12
>seq1 185 185 T AAA 24 100 10 14 10 14
>seq1 194 194 A G 24 100 12 12 12 12
>seq1 185 185 T A 24 100 10 14 10 14

我想在shell或文件中输出这样的输出,而不是其他模式。

AG 2
CT 1
TC 1
TA 1

2 个答案:

答案 0 :(得分:4)

是的,您尝试做的所有事情都可能在awk脚本中完成。以下是我根据条件计算行数的方法:

awk -F" " '$4=="A" && $5=="G" {n++} END {printf("AG = %d\n", n)}' file.txt
  • Awk脚本由condition { statement }对组成,因此您可以完全取消if - 它是隐含的。
  • 只要条件匹配,
  • n++会递增一个计数器。
  • 处理完最后一行输入后,魔术条件END为真。

这就是你要追求的吗?如果您想要的只是行数,为什么要在输出中添加NR

哦,你可能想确认一下你是否真的需要-F" "。默认情况下,awk在空格上分割。如果您的字段包含嵌入的标签,我们只需要此选项。

根据编辑过的问题

更新#1 ......

如果你真正想要的是一个配对计数器,那么awk 数组可能就是你的选择。像这样:

awk '{a[$4 $5]++} END {for (pair in a) printf("%s %d\n", pair, a[pair])}' file.txt

这是故障。

  • 第一个语句在每一行上运行,并递增一个计数器,该计数器是一个数组(a[])的索引,该数组的键是从$4$5构建的。
  • END块中,我们在for循环中逐步执行数组,并为每个索引打印索引名称和值。

输出不会按任何特定顺序排列,因为awk不保证数组顺序。如果这对您没问题,那么这应该足够了。它也应该非常高效,因为它的最大内存使用量是基于可用组合的总数,这是一个有限的集合。

示例:

$ cat file
>seq1 284 284 A G 27 100 16 11 16 11
>seq1 266 266 C T 27 100 16 11 16 11
>seq1 227 227 T C 25 100 13 12 13 12
>seq1 194 194 A G 24 100 12 12 12 12
>seq1 185 185 T A 24 100 10 14 10 14
$ awk '/^>seq/ {a[$4 $5]++} END {for (p in a) printf("%s %d\n", p, a[p])}' file
CT 1
TA 1
TC 1
AG 2

更新#2 基于修订后的输入数据和以前未记录的要求。

使用额外的数据,您仍然可以通过一次运行awk来执行此操作,但当然awk脚本随着每个新需求变得越来越复杂。让我们试试这个更长的单行:

$ awk 'BEGIN{v["G"]; v["A"]; v["C"]; v["T"]} $4 in v && $5 in v {a[$4 $5]++} END {for (p in a) printf("%s %d\n", p, a[p])}' i
CT 1
TA 1
TC 1
AG 2

首先(在魔术BEGIN块中)定义一个数组v[]来记录“有效”记录。计数器上的条件只是验证$4$5都包含数组的成员。其他一切都是一样的。

此时,无论如何,脚本运行到多行,我可能会将其分成一个小文件。它甚至可以是一个独立的脚本。

#!/usr/bin/awk -f

BEGIN {
  v["G"]; v["A"]; v["C"]; v["T"]
}

$4 in v && $5 in v {
  a[$4 $5]++
}

END {
  for (p in a)
    printf("%s %d\n", p, a[p])
}

这种方式更容易阅读。

如果您的目标只是计算您在问题中提到的组合,那么您可以略微区别地处理数组。

#!/usr/bin/awk -f

BEGIN {
  a["AG"]; a["TA"]; a["CT"]; a["TC"]
}

($4 $5) in a {
  a[$4 $5]++
}

END {
  for (p in a)
    printf("%s %d\n", p, a[p])
}

这只验证已经有数组索引的内容,每个BEGIN为NULL。

增量条件中的括号不是必需的,仅为了清楚起见而包括在内。

答案 1 :(得分:0)

只需计算所有内容然后打印出您关心的内容:

$ awk '{cnt[$4$5]++} END{split("AG CT TC TA",t); for (i=1;i in t;i++) print t[i], cnt[t[i]]+0}' file
AG 2
CT 1
TC 1
TA 1

请注意,对于您的输入中不会出现的任何目标对,这将产生零计数,例如:如果你想要计算" XY" s:

$ awk '{cnt[$4$5]++} END{split("AG CT TC TA XY",t); for (i=1;i in t;i++) print t[i], cnt[t[i]]+0}' file
AG 2
CT 1
TC 1
TA 1
XY 0

如果需要,请检查其他解决方案是否也这样做。

实际上,这可能是你真正想要的,只是为了确保$ 4和$ 5是单个大写字母:

$ awk '$4$5 ~ /^[[:upper:]]{2}$/{cnt[$4$5]++} END{for (i in cnt) print i, cnt[i]}' file
TA 1
AG 2
TC 1
CT 1