如何在表的每一列上循环awk命令并输出到单个输出文件?

时间:2013-02-05 22:15:17

标签: linux shell awk

我有一个由单个单元1s,2s和3s组成的多列文件。每列中有很多重复的单元,有时它会从一个单元切换到另一个单元。我想计算每一列发生这种切换的次数。例如,在第1列中,开关从1变为2到3变为1,因此有3个开关,输出应为3.在第二列中,整个列有2个,因此更改为0,输出为0 。

我的输入文件有4000列,因此无法手动完成。该文件是空格分隔的。

例如:

输入:

1 2 3 1 2 
1 2 2 1 3
1 2 3 1 2
2 2 2 1 2
2 2 2 1 2    ......
3 2 2 1 2 
3 2 2 1 1
1 2 2 1 1
1 2 2 1 2
1 2 2 1 1

期望的输出:

3    ## column 1 switch times
0    ## column 2 switch times
3    .....
0    
5    

我正在使用:

awk '{print $1}' <inputfile> | uniq | wc -l
awk '{print $2}' <inputfile> | uniq | wc -l
awk '{print $3}' <inputfile> | uniq | wc -l
....

一次执行一列。它将给出第一列的输出“4”,稍后我将计算4-1 = 3以获得我想要的输出。但有没有办法可以将这个awk命令写入循环并在每个列上执行并输出到一个文件?

谢谢!

3 个答案:

答案 0 :(得分:3)

awk告诉您变量NF中给定行中有多少个字段,因此您可以创建两个数组来跟踪所需的信息。一个数组将保留给定列中最后一行的值。另一个将计算给定列中的开关数。您还将跟踪最大列数(并将新列的计数设置为零,以便在该列的开关数为0时在末尾的输出中正确打印它们)。您还将确保不计算从空字符串到非空字符串的转换 - 这是在第一次遇到列时发生的。

实际上,如果文件的列数统一相同,那么只会影响第一行数据。如果后续行实际上有比第一行更多的列,则会添加它们。如果列停止出现一点,我认为它应该从它停止的地方恢复(好像缺失的列与之前的值相同)。您可以决定不同的算法;这可能算作两个转换(从数字到空白,从空白到数字也是如此。如果是这种情况,你必须修改计数代码。或者,更明智的是,你可以决定不允许不规则数量的列,在这种情况下,如果当前行中的列数与上一行中的列数不同,您可以提前纾困(当心空行,或者它们也是非法的?)。

你不会尝试将整个程序写在一行上,因为它是不可理解的,而且实际上没有必要。

awk '{   if (NF > maxNF)
         {
             for (i = maxNF + 1; i <= NF; i++)
                 count[i] = 0;
             maxNF = NF;
         }
         for (i = 1; i <= NF; i++)
         {
             if (col[i] != "" && $i != col[i])
                 count[i]++;
             col[i] = $i;
         }
     }
     END {
         for (i = 1; i <= maxNF; i++)
             print count[i];
     }' data-file-with-4000-columns

根据您的样本数据(删除了点),脚本的输出符合要求:

3
0
3
0
5

这个带有锯齿状行的替代数据文件:

1 2 3 1 2
1 2 2 1 3
1 2 3 1 2
2 2 2 1 2
2 2 2 1 2 1 1 1
3 2 2 1 2 2 1
3 2 2 1 1
1 2 2 1 1 2 2 1
1 2 2 1
1 2 2 1 1 3

产生输出:

3
0
3
0
3
2
1
0

根据我制定的规则,这是正确的 - 但如果你决定要不同的规则来覆盖数据,你可能会得到不同的答案。

如果在最后一个循环中使用printf("%d\n", count[i]);,则不需要在循环中将计数值设置为零。你付钱并拿走你的选择。

答案 1 :(得分:2)

使用循环并为每个列当前值保留一个数组,并为相应的计数保留另一个数组:

awk '{for(i=0;i<5;i++) if(c[i]!=$(i+1)) {c[i]=$(i+1); t[i]++}} END{for(i=0;i<5;i++)print t[i]-1}' filename

请注意,这假设列的值不为零。如果您碰巧有零值,那么只需将数组c初始化为某个唯一值,该值将不会出现在文件中。

答案 2 :(得分:0)

为了便于查看而编写代码,SaveColx,CountColx应该是数组。我会在结果中打印列号本身至少用于检查: - )

BEGIN { 
    SaveCol1 = " "
    CountCol1 = 0
    CountCol2 = 0
    CountCol3 = 0
    CountCol4 = 0
    CountCol5 = 0
}
{
if ( SaveCol1 == " " ) {
    SaveCol1 = $1
    SaveCol2 = $2
    SaveCol3 = $3
    SaveCol4 = $4
    SaveCol5 = $5
    next
    }
if ( $1 != SaveCol1 ) {
    CountCol1++
    SaveCol1 = $1
    }
if ( $2 != SaveCol2 ) {
    CountCol2++
    SaveCol2 = $2
    }
if ( $3 != SaveCol3 ) {
    CountCol3++
    SaveCol3 = $3
    }
if ( $4 != SaveCol4 ) {
    CountCol4++
    SaveCol4 = $4
    }
if ( $5 != SaveCol5 ) {
    CountCol5++
    SaveCol5 = $5
    }
}
END {
    print CountCol1
    print CountCol2 
    print CountCol3
    print CountCol4
    print CountCol5
}