如何打印值满足范围的列?
How to print columns that have values satisfying a range?
我有一个很大的 table,有数千列和行。但是为了简化起见,假设我有一个 table 包含 11 行和 100 列。 table 单元格包含介于 0 和 1 之间的值。table 如下所示:
Sample1 Sample2 Sample3 Sample4
1 0 0.001 0.002
0.74 0.52 0.654 0.75
0.65 0.64 0.455 0.72
0.24 0.51 0.512 0.78
0.25 0.555 0.557 0.25
0.003 0.454 0.532 0.23
0.02 0.56 0.643 0.22
1 0.495 0.555 0.99
0.992 1 0.999 0.98
0.12 0 0.968 1
现在我想扫描所有单元格以查找 0.80 >= 值 >= 0.70 特定范围内的所有值。任何包含此类值的单元格,将打印整列,包括 headers。
预期输出如下:
Sample1 Sample4
1 0.002
0.74 0.75
0.65 0.72
0.24 0.78
0.25 0.25
0.003 0.23
0.02 0.22
1 0.99
0.992 0.98
0.12 1
使用 awk 的命令会更好,但我不知道它是否最适合这种提取。
请告诉我如何做到这一点。任何帮助将不胜感激。谢谢。
awk
救援!
$ awk 'NR==FNR && NR>1{for(i=1; i<=NF; i++)
if(0.7<=$i && $i<=0.8) col[i]=1; next}
{for(i=1 ;i<=NF; i++)
if(col[i]) printf "%s", $i OFS; print ""}' file{,} | column -t
Sample1 Sample4
1 0.002
0.74 0.75
0.65 0.72
0.24 0.78
0.25 0.25
0.003 0.23
0.02 0.22
1 0.99
0.992 0.98
0.12 1
双扫描算法,第一轮标记过滤后的列,第二轮打印。
不确定这是否适用于您正在处理的 table 大小,但任何解决方案都可能必须将某些内容存储在数组中,或者是 multi-pass。
我的第一个想法是给定 rotate.awk
这样的:
{
for (i=1; i<=NF; i++) {
d[i,NR]=$i
}
}
END {
for (i=1; i<=NF; i++) {
tab=""
for (j=1; j<=NR; j++) {
printf "%s%s", tab, d[i,j]
tab="\t"
}
printf "\n"
}
}
您可以分析两次旋转之间的结果:
$ awk -f rotate.awk file.tsv | awk -v n=0.7 -v m=0.8 '{x=0; for (i=2; i<=NF; i++) if ($i >= n && $i <= m) x=1} x' | awk -f rotate.awk
Sample1 Sample4
1 0.002
0.74 0.75
0.65 0.72
0.24 0.78
0.25 0.25
0.003 0.23
0.02 0.22
1 0.99
0.992 0.98
0.12 1
同样,您可能会受到系统可以分配给 awk 以包含旋转所需数组的内存量的限制。
另一种不使用内存块存储数组的方法是 multi-pass 识别列号的方法,然后将它们用作打印脚本的输入:
$ awk -v n=0.7 -v m=0.8 '{for (i=1; i<=NF; i++) if ($i >= n && $i <= m) print i}' file.tsv |
awk 'NR==FNR{c[];next} {tab="";for (i=1; i<=NF; i++) if (i in c) {printf "%s%s",tab,$i;tab="\t"} printf "\n" }' - file.tsv
这里的想法是第一个 awk 脚本选择要打印的列,并只打印那些列号。第二个 awk 脚本接受两个输入;首先,它从标准输入 (-
) 中读取列号列表,并用它们填充一个数组。然后它遍历输入文件,打印其编号在数组内的列。
$ cat tst.awk
BEGIN { FS=OFS="\t" }
NR==FNR {
if (FNR > 1) {
for (i=1; i<=NF; i++) {
if ( ($i >= 0.7) && ($i <= 0.8) ) {
good[i]
}
}
}
next
}
{
c=0
for (i=1; i<=NF; i++) {
if (i in good) {
printf "%s%s", (c++ ? OFS : ""), $i
}
}
print ""
}
$ awk -f tst.awk file file
Sample1 Sample4
1 0.002
0.74 0.75
0.65 0.72
0.24 0.78
0.25 0.25
0.003 0.23
0.02 0.22
1 0.99
0.992 0.98
0.12 1
我有一个很大的 table,有数千列和行。但是为了简化起见,假设我有一个 table 包含 11 行和 100 列。 table 单元格包含介于 0 和 1 之间的值。table 如下所示:
Sample1 Sample2 Sample3 Sample4
1 0 0.001 0.002
0.74 0.52 0.654 0.75
0.65 0.64 0.455 0.72
0.24 0.51 0.512 0.78
0.25 0.555 0.557 0.25
0.003 0.454 0.532 0.23
0.02 0.56 0.643 0.22
1 0.495 0.555 0.99
0.992 1 0.999 0.98
0.12 0 0.968 1
现在我想扫描所有单元格以查找 0.80 >= 值 >= 0.70 特定范围内的所有值。任何包含此类值的单元格,将打印整列,包括 headers。 预期输出如下:
Sample1 Sample4
1 0.002
0.74 0.75
0.65 0.72
0.24 0.78
0.25 0.25
0.003 0.23
0.02 0.22
1 0.99
0.992 0.98
0.12 1
使用 awk 的命令会更好,但我不知道它是否最适合这种提取。
请告诉我如何做到这一点。任何帮助将不胜感激。谢谢。
awk
救援!
$ awk 'NR==FNR && NR>1{for(i=1; i<=NF; i++)
if(0.7<=$i && $i<=0.8) col[i]=1; next}
{for(i=1 ;i<=NF; i++)
if(col[i]) printf "%s", $i OFS; print ""}' file{,} | column -t
Sample1 Sample4
1 0.002
0.74 0.75
0.65 0.72
0.24 0.78
0.25 0.25
0.003 0.23
0.02 0.22
1 0.99
0.992 0.98
0.12 1
双扫描算法,第一轮标记过滤后的列,第二轮打印。
不确定这是否适用于您正在处理的 table 大小,但任何解决方案都可能必须将某些内容存储在数组中,或者是 multi-pass。
我的第一个想法是给定 rotate.awk
这样的:
{
for (i=1; i<=NF; i++) {
d[i,NR]=$i
}
}
END {
for (i=1; i<=NF; i++) {
tab=""
for (j=1; j<=NR; j++) {
printf "%s%s", tab, d[i,j]
tab="\t"
}
printf "\n"
}
}
您可以分析两次旋转之间的结果:
$ awk -f rotate.awk file.tsv | awk -v n=0.7 -v m=0.8 '{x=0; for (i=2; i<=NF; i++) if ($i >= n && $i <= m) x=1} x' | awk -f rotate.awk
Sample1 Sample4
1 0.002
0.74 0.75
0.65 0.72
0.24 0.78
0.25 0.25
0.003 0.23
0.02 0.22
1 0.99
0.992 0.98
0.12 1
同样,您可能会受到系统可以分配给 awk 以包含旋转所需数组的内存量的限制。
另一种不使用内存块存储数组的方法是 multi-pass 识别列号的方法,然后将它们用作打印脚本的输入:
$ awk -v n=0.7 -v m=0.8 '{for (i=1; i<=NF; i++) if ($i >= n && $i <= m) print i}' file.tsv |
awk 'NR==FNR{c[];next} {tab="";for (i=1; i<=NF; i++) if (i in c) {printf "%s%s",tab,$i;tab="\t"} printf "\n" }' - file.tsv
这里的想法是第一个 awk 脚本选择要打印的列,并只打印那些列号。第二个 awk 脚本接受两个输入;首先,它从标准输入 (-
) 中读取列号列表,并用它们填充一个数组。然后它遍历输入文件,打印其编号在数组内的列。
$ cat tst.awk
BEGIN { FS=OFS="\t" }
NR==FNR {
if (FNR > 1) {
for (i=1; i<=NF; i++) {
if ( ($i >= 0.7) && ($i <= 0.8) ) {
good[i]
}
}
}
next
}
{
c=0
for (i=1; i<=NF; i++) {
if (i in good) {
printf "%s%s", (c++ ? OFS : ""), $i
}
}
print ""
}
$ awk -f tst.awk file file
Sample1 Sample4
1 0.002
0.74 0.75
0.65 0.72
0.24 0.78
0.25 0.25
0.003 0.23
0.02 0.22
1 0.99
0.992 0.98
0.12 1