3

我有一个由单个单元 1s、2s 和 3s 组成的多列文件。每列中有很多重复单元,有时它会从一个单元切换到另一个单元。我想计算在每一列上发生了多少次这种切换。例如在第 1 列中,开关从 1 到 2 到 3 到 1,所以有 3 个开关,输出应该是 3。在第二列中,整列有 2s,所以变化为 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 命令写入一个循环并在每一列上执行它并输出到一个文件?

谢谢!

4

3 回答 3

4

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]);在最终循环中使用,则无需在循环中将计数值设置为零。您付钱并选择。

于 2013-02-06T01:27:47.930 回答
2

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

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为文件中不存在的某个唯一值。

于 2013-02-05T22:27:01.393 回答
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
}
于 2013-02-06T01:45:36.073 回答