我有一个矩阵(大约10,000x10,000),我想找到包含'0'的列号。
矩阵(test.txt):1 1 1 1 1 1 1 1 1 1
1 0 1 1 1 0 1 1 1 1
1 1 1 0 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1
3 2 2 3 3 0 3 2 2 2
3 2 1 3 3 0 3 2 2 0
3 2 2 3 3 2 3 2 2 2
1 1 1 1 1 1 1 1 1 1
输出(示例):
2 4 6 10
我是LINUX SHELL的新手,在类似的例子中没有发现太多。任何帮助都将非常感激!!
我只知道如何使用代码找到行号:grep -nw '0' test.txt|cut -f1 -d':'
,也许我可以先转置矩阵(像这样)?然后使用上面的代码,对吧?有更简单的方法吗?
在任何Unix系统的任何shell中使用任何awk:
$ awk '
/(^| )0( |$)/ {
for ( i=1; i<=NF; i++ ) {
if ( $i == 0 ) {
cols[i]
}
}
}
END {
for ( i in cols ) {
printf "%s%d", sep, i
sep = OFS
}
print ""
}
' file
2 4 6 10
由于使用in
操作符的循环,上面的输出不保证是数字(或任何其他)顺序,参见https://www.gnu.org/software/gawk/manual/gawk.html#Scanning-an-Array了解详细信息。
如果您需要按递增顺序打印字段编号,则将脚本更改为稍微慢一点的:
awk '
/(^| )0( |$)/ {
for ( i=1; i<=NF; i++ ) {
if ( $i == 0 ) {
cols[i]
}
}
}
END {
for ( i=1; i<=NF; i++ ) {
if ( i in cols ) {
printf "%s%d", sep, i
sep = OFS
}
}
print ""
}
' file
为什么不使用矩阵语言进行矩阵运算,例如GNU Octave:
<infile octave --silent --eval "
[row, col] = find( dlmread(0) == 0 );
dlmwrite(1, unique(col))"
输出:
2
4
6
10
给dlm*命令的0和1分别是指标准输入和标准输出。
如果您想在一行上输出,请调换位置并指定分隔符,例如将dlmwrite(...)
更改为dlmwrite(1, unique(col)', ' ')"
也许我可以转置矩阵
是的,只要使用可以做到的工具,例如下面的GNUdatamash
,让file.txt
的内容为
1 1 1 1 1 1 1 1 1 1
1 0 1 1 1 0 1 1 1 1
1 1 1 0 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1
3 2 2 3 3 0 3 2 2 2
3 2 1 3 3 0 3 2 2 0
3 2 2 3 3 2 3 2 2 2
1 1 1 1 1 1 1 1 1 1
然后
datamash --field-separator=' ' transpose < file.txt
给输出
1 1 1 1 1 1 1 1 1 1
1 0 1 1 1 0 1 1 1 1
1 1 1 0 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1
3 2 2 3 3 0 3 2 2 2
3 2 1 3 3 0 3 2 2 0
3 2 2 3 3 2 3 2 2 2
1 1 1 1 1 1 1 1 1 1
解释:我通知GNUdatamash
文件是空格分隔的,并指示它转置。免责声明:此解决方案假设每行的字段数完全相等。
(在GNU datamash 1.7中测试)
一种方法是预先扫描是否存在任何未与其他数字配对的"0"
,
然后将列简化为类似于ASCII
位串的东西,使用高速gsub()
将所有非零列设置为"1"
,然后使用新的delimFS = "0"
拆分它:
1101011111 —> NF = 3
^ ^
11
-[0]-1
-[0]-11111
不必每行循环10,000 columns
,只需在每个新列(e.g. (2,1,5) in this example
)中跟踪1
的#字符串长度的和和差,从而推断位于$3
和$5
的0