使用unix命令和awk重新格式化表(groupby)

时间:2017-08-26 08:14:56

标签: unix awk

以下5列表格中包含cols1-3 位置信息, col4保持位置内类型的命中(有3种可能的类型 - 序列号:1,2,3),col5保存特定的命中数类型(可以是零或任意数量的命中)。

请注意,有几个地点有多种类型的点击。 对于前者location [chrX 93554661 94234661]有5个字及'' 2', 和#3;'类型的3次点击。

chrX    93554661    94234661    2   5
chrX    93554661    94234661    3   3
chrX    94234661    94674661    1   1
chrX    95044661    96804661    1   4
chrX    95044661    96804661    2   1
chrX    95044661    96804661    3   6
chrX    96804661    97684661    1   4
chrX    97684661    98964661    1   3
chrX    98964661    99724661    0   0
chrX    99724661    101124661   1   4
chrX    101124661   101524661   1   4
chrX    101524661   103124661   1   5
chrX    101524661   103124661   3   1
chrX    103124661   103444661   3   2
chrX    103444661   104044661   1   1
chrX    103444661   104044661   2   4

我想重新格式化表格,以便在新表格中,最右边的三列代表每个位置中每种类型的点击总数,这样第4,5,6列将代表连续命中类型1,2,3,分别为。

理想的格式是:

chrX    93554661    94234661    0   5   3
chrX    94234661    94674661    1   0   0
chrX    95044661    96804661    4   1   6
chrX    96804661    97684661    4   0   0
chrX    97684661    98964661    3   0   0
chrX    98964661    99724661    0   0   0
chrX    99724661    101124661   4   0   0
chrX    101124661   101524661   4   0   0
chrX    101524661   103124661   5   0   1
chrX    103124661   103444661   0   0   2
chrX    103444661   104044661   1   4   0

我目前的解决方案可以完成这项工作 - 但我怀疑它非常繁琐,而且我对学习更优雅的解决方案非常感兴趣。请将您的解决方案仅限于UNIX系统,并使用更简单的awk版本而不是gawk。

这是我的解决方案:

awk -F"\t" 'NF>1{a[$1"\t"$2"\t"$3] = a[$1"\t"$2"\t"$3]","$4"~"$5};END{for(i in a) {n=split(a[i],g,",")-1; printf i"\t" ;for(z=2;z<=n+1;z++){split(g[z],w,"~"); if(w[1]!=""){printf "%s",w[1]"\t"w[2]"\t";}}printf "\n"}}'|sort -t$'\t' -k1,1 -k2,2n|awk '{printf "%s",$0;for(i=NF+1;i<=9;i++){printf "%s",0"\t"};printf "\n"}' |awk -F"\t" '{OFS="\t"}{$($4+9)=$5;$($6+9)=$7;$($8+9)=$9;print $0}'|awk 'BEGIN { FS = OFS = "\t" } { for(i=1; i<=12; i++) if($i ~ /^ *$/) $i = 0 }; 1'|awk -F "\t" '{OFS="\t"}{print $1,$2,$3,$10,$11,$12}' 

4 个答案:

答案 0 :(得分:2)

如果您的Input_file与显示的示例相同,并且您不担心输出的顺序应该与Input_file相同,那么以下内容可能对您有帮助。

awk '
{
  a[$1 FS $2 FS $3 FS $4]+=$5;
  b[$1 FS $2 FS $3]
}
END{
  for(i in b){
    printf("%s %s %s %s\n",i,a[i FS "1"]?a[i FS "1"]:0,a[i FS "2"]?a[i FS "2"]:0, a[i FS "3"]?a[i FS "3"]:0)
}
}
'   Input_file

编辑:如果您需要输出与Input_file的顺序相同,那么以下内容可能对您有帮助。

awk '
!b[$1 FS $2 FS $3]{
  c[++i]=$1 FS $2 FS $3
}
{
a[$1 FS $2 FS $3 FS $4]+=$5;
b[$1 FS $2 FS $3]=$1 FS $2 FS $3
}
END{
  for(j=1;j<=i;j++){
    printf("%s %s %s %s\n",c[j],a[b[c[j]] FS "1"]?a[b[c[j]] FS "1"]:0,a[b[c[j]] FS "2"]?a[b[c[j]] FS "2"]:0, a[b[c[j]] FS "3"]?a[c[j] FS "3"]:0)
}
}
'   Input_file

输出如下。

chrX 93554661 94234661 0 5 3
chrX 94234661 94674661 1 0 0
chrX 95044661 96804661 4 1 6
chrX 96804661 97684661 4 0 0
chrX 97684661 98964661 3 0 0
chrX 98964661 99724661 0 0 0
chrX 99724661 101124661 4 0 0
chrX 101124661 101524661 4 0 0
chrX 101524661 103124661 5 0 1
chrX 103124661 103444661 0 0 2
chrX 103444661 104044661 1 4 0

答案 1 :(得分:2)

这个怎么样:我假设您的数据是以制表符分隔的

sort -k1,1 -k2,3n file | awk -F'\t' -v OFS='\t' '
    function init()   { v[1] = v[2] = v[3] = 0 }
    function output() { print prev, v[1], v[2], v[3] }
                { key = $1 FS $2 FS $3 }
    NR == 1     { prev = key; init() }
    key != prev { output(); init() }
                { v[$4] = $5; prev = key }
    END         { output() }
'

答案 2 :(得分:1)

$ cat tst.awk
BEGIN { FS=OFS="\t" }
{ curr = $1 FS $2 FS $3 }
curr != prev { if (NR>1) prt(); prev=curr }
{ cnt[$4] += $5 }
END { prt() }
function prt() {
    print prev, cnt[1]+0, cnt[2]+0, cnt[3]+0
    delete cnt
}

$ awk -f tst.awk file
chrX    93554661        94234661        0       5       3
chrX    94234661        94674661        1       0       0
chrX    95044661        96804661        4       1       6
chrX    96804661        97684661        4       0       0
chrX    97684661        98964661        3       0       0
chrX    98964661        99724661        0       0       0
chrX    99724661        101124661       4       0       0
chrX    101124661       101524661       4       0       0
chrX    101524661       103124661       5       0       1
chrX    103124661       103444661       0       0       2
chrX    103444661       104044661       1       4       0

答案 3 :(得分:0)

不假设输入是有序的:

$ cat test.awk
BEGIN { FS=OFS="\t" }
!sum[$1 FS $2 FS $3]{
   sum[$1 FS $2 FS $3] = "000"
}
{
   values = sum[$1 FS $2 FS $3]
   if ($4 > 0)
      sum[$1 FS $2 FS $3] = substr(values, 1, $4 - 1) substr(values, $4, 1) + $5 substr(values, $4 + 1, 3 - $4)
}
END {
   for (i in sum) {
      print i, substr(sum[i],1,1), substr(sum[i],2,1), substr(sum[i],3,1)
   }
}

$ awk -f test.awk input.txt
chrX    103444661   104044661   1   4   0
chrX    99724661    101124661   4   0   0
chrX    95044661    96804661    4   1   6
chrX    93554661    94234661    0   5   3
chrX    96804661    97684661    4   0   0
chrX    101124661   101524661   4   0   0
chrX    103124661   103444661   0   0   2
chrX    94234661    94674661    1   0   0
chrX    98964661    99724661    0   0   0
chrX    101524661   103124661   5   0   1
chrX    97684661    98964661    3   0   0