我有大量单独的文件,每个文件包含六列(行数可能有所不同)。举个简单的例子:
1 0 0 0 0 0
0 1 1 1 0 0
我试图确定我有多少个唯一列(即数字及其顺序匹配),在本例中为 3。
有没有一个简单的单行代码可以做到这一点?我知道将一列与另一列进行比较很容易,但如何找到相同的列?
答案1
您可以使用以下管道来计算唯一列:
$ awk '{for (i=1; i<=NF; ++i) a[i]=a[i]$i; } END { for (i in a) print a[i] }' foo \
| sort -u | wc -l
awk 命令转置您的输入,对结果行进行排序,仅保留唯一行 ( -u
),最后对所有(唯一)行(即转置列)进行计数 ( wc -l
)。
请注意,这NF
是一个内置 awk 变量,并自动设置为当前记录中的字段数。$i
引用第 i 个字段并END
保护后面的块,以便在处理所有记录后执行它。 awk 默认使用空白-非空白字段分隔。
答案2
(((...))),但是如何找到相同的列呢?
$ printf '%s\n' '1 0 0 0 0 0' '0 1 1 1 0 0' | awk -vSUBSEP='=' '
{ for (i=1; i<NF; i++)
for (j=i+1; j<=NF; j++)
if ($i==$j)
M[i,j]++
}
END{ for (m in M) if (M[m]==NR) print m }'
5=6
2=3
2=4
3=4
对于i<j
每行的所有列,M[i,j]
每当这些列的值相等时就递增。因此,M[i,j]==NR
在读取NR
行之后,读取的所有行的值都是相同的。
答案3
这个问题让我很感兴趣,我想采用一种我无法确切弄清楚的方法并得到一些很好的帮助在我作为不同的问题发布之后。您可以从我发布的问题中理解我试图遵循的方法。
对于这个问题,我还有另外 2 个解决方案(其中之一来自格努克的答案是珀尔解决方案和另一个来自 约翰的解决方案与我的解决方案相结合)。
#The variable appended_input will remove spaces/tabs and just append the rows.
#Modify the file name in this line. Here I use inputfile as the filename.
appended_input=$(column -s '\t' inputfile | tr -d '[:space:]') ;
#The array variable will store each column-wise value as an array element.
#I use sort to find the number of unique elements.
array=($(
for ((i=0; i<6; i++))
do
new=${appended_input:$i:1}
for ((j=i+6; j<${#appended_input}; j=j+6))
do
new="$new${appended_input:$j:1}"
done
echo "$new"
done
)) | echo "${array[@]}" | tr ' ' '\n' | sort -u | tr '\n' ' '
测试
我的输入文件如下。
1 0 0 1 0 0
0 1 1 0 0 0
1 1 1 1 1 0
1 0 0 1 0 1
1 0 0 1 0 1
运行上面的脚本后,我得到的输出为,
00011 00100 01100 10111
您可以将 awc -w
作为最终管道,并且您将得到仅 4 的输出,而不是如上所述的唯一列值。
答案4
下面是一个gawk
解决方案,它使用协进程将每个列提供给单独的实例,sha256sum
并报告唯一哈希的总数(鉴于哈希冲突可能性在sha256sum
统计上不显着,唯一哈希的数量应与唯一列的数量一致)。虽然有些人可能认为这是一种令人震惊的黑客行为,但与其他一些方法相比,这种方法的一个优点是它不会尝试连接/转置数据,因此内存效率相对较高。
awk 'BEGIN{for(i=1; i<=6; ++i){s=sprintf("%*s", i+1, ""); a[i]="sha256sum"s}}
{for (i=1; i<=6; ++i) print $i |& a[i]}
END{com= "sort | uniq | wc -l"
for (i=1; i<=6; ++i){close(a[i], "to"); a[i] |& getline x;
close(a[i]); print x | com};
close(com)}' file