使用 bash 重塑 table 并使用 NA(或 -999)完成空隙
Reshape table and complete voids with NA (or -999) using bash
我正在尝试创建一个基于 ASCII 波纹管的 table。我需要的是将第 2 列的数字排列成矩阵。 ASCII 的第一列和第三列给出了新矩阵中的列和行。新矩阵需要完全填充,所以需要用NA(或-999)补全新table上缺失的位置。
这就是我的
$ cat infile.txt
1 68 2
1 182 3
1 797 4
2 4 1
2 70 2
2 339 3
2 1396 4
3 12 1
3 355 3
3 1854 4
4 7 1
4 85 2
4 333 3
5 9 1
5 68 2
5 182 3
5 922 4
6 10 1
6 70 2
以及我想要的:
NA 4 12 7 9 10
68 70 NA 85 68 70
182 339 355 333 182 NA
797 1396 1854 NA 922 NA
我只能使用标准的 UNIX 命令(例如 awk、sed、grep 等)。
那么到目前为止我所拥有的...
我可以在 bash
中模拟二维数组
irows=(`awk '{print }' infile.txt`) # rows positions
jcols=(`awk '{print }' infile.txt`) # columns positions
values=(`awk '{print }' infile.txt`) # values
declare -A matrix # the new matrix
nrows=(`sort -k3 -n in.txt | tail -1 | awk '{print }'`) # numbers of rows
ncols=(`sort -k1 -n in.txt | tail -1 | awk '{print }'`) # numbers of columns
nelem=(`echo "${#values[@]}"`) # number of elements I want to pass to the new matrix
# Creating a matrix (i,j) with -999
for ((i=0;i<=$((nrows-1));i++)) do
for ((j=0;j<=$((ncols-1));j++)) do
matrix[$i,$j]=-999
done
done
甚至在屏幕上打印
for ((i=0;i<=$((nrows-1));i++)) do
for ((j=0;j<=$((ncols-1));j++)) do
printf " %i" ${matrix[$i,$j]}
done
echo
done
但是当我尝试分配元素时,出现了错误
for ((i=0;i<=$((nelem-1));i++)) do
matrix[${irows[$i]},${jcols[$i]}]=${values[$i]}
done
在此先感谢您的帮助,真的。
这是让您入门的一种方法。请注意,这并不是“答案”,而是鼓励您尝试学习该工具包。
$ join -a1 -e NA -o2.2 <(printf "%s\n" {1..4}"_"{1..6}) \
<(awk '{print "_",}' file | sort -n) |
pr -6at
NA 4 12 7 9 10
68 70 NA 85 68 70
182 339 355 333 182 NA
797 1396 1854 NA 922 NA
有效,但是,行数和列数是硬编码的,这不是正确的方法。
首选解决方案是用数据填充一个 awk
二维数组,最后以矩阵形式打印。
任何时候你发现自己在 shell 中编写一个循环只是为了操作文本,你的方法是错误的。请参阅 why-is-using-a-shell-loop-to-process-text-considered-bad-practice 了解许多原因。
在每个 UNIX 机器上的任何 shell 中使用任何 awk:
$ cat tst.awk
{
vals[,] =
numRows = ( > numRows ? : numRows)
numCols =
}
END {
OFS = "\t"
for (rowNr=1; rowNr<=numRows; rowNr++) {
for (colNr=1; colNr<=numCols; colNr++) {
val = ((rowNr,colNr) in vals ? vals[rowNr,colNr] : "NA")
printf "%s%s", val, (colNr < numCols ? OFS : ORS)
}
}
}
.
$ awk -f tst.awk infile.txt
NA 4 12 7 9 10
68 70 NA 85 68 70
182 339 355 333 182 NA
797 1396 1854 NA 922 NA
通过使用关联数组模拟二维数组的简单 bash 解决方案可能是这样的(请注意,行数和列数 不是 硬编码并且该代码适用于输入行的任何排列,前提是每一行都具有问题中指定的格式):
$ cat printmat
#!/bin/bash
declare -A mat
nrow=0
ncol=0
while read -r col elem row; do
mat[$row,$col]=$elem
if ((row > nrow)); then nrow=$row; fi
if ((col > ncol)); then ncol=$col; fi
done
for ((row = 1; row <= nrow; ++row)); do
for ((col = 1; col <= ncol; ++col)); do
elem=${mat[$row,$col]}
if [[ -z $elem ]]; then elem=NA; fi
if ((col == ncol)); then elem+=$'\n'; else elem+=$'\t'; fi
printf "%s" "$elem"
done
done
$ ./printmat < infile.txt
打印出来
NA 4 12 7 9 10
68 70 NA 85 68 70
182 339 355 333 182 NA
797 1396 1854 NA 922 NA
我正在尝试创建一个基于 ASCII 波纹管的 table。我需要的是将第 2 列的数字排列成矩阵。 ASCII 的第一列和第三列给出了新矩阵中的列和行。新矩阵需要完全填充,所以需要用NA(或-999)补全新table上缺失的位置。
这就是我的
$ cat infile.txt
1 68 2
1 182 3
1 797 4
2 4 1
2 70 2
2 339 3
2 1396 4
3 12 1
3 355 3
3 1854 4
4 7 1
4 85 2
4 333 3
5 9 1
5 68 2
5 182 3
5 922 4
6 10 1
6 70 2
以及我想要的:
NA 4 12 7 9 10
68 70 NA 85 68 70
182 339 355 333 182 NA
797 1396 1854 NA 922 NA
我只能使用标准的 UNIX 命令(例如 awk、sed、grep 等)。
那么到目前为止我所拥有的...
我可以在 bash
中模拟二维数组irows=(`awk '{print }' infile.txt`) # rows positions
jcols=(`awk '{print }' infile.txt`) # columns positions
values=(`awk '{print }' infile.txt`) # values
declare -A matrix # the new matrix
nrows=(`sort -k3 -n in.txt | tail -1 | awk '{print }'`) # numbers of rows
ncols=(`sort -k1 -n in.txt | tail -1 | awk '{print }'`) # numbers of columns
nelem=(`echo "${#values[@]}"`) # number of elements I want to pass to the new matrix
# Creating a matrix (i,j) with -999
for ((i=0;i<=$((nrows-1));i++)) do
for ((j=0;j<=$((ncols-1));j++)) do
matrix[$i,$j]=-999
done
done
甚至在屏幕上打印
for ((i=0;i<=$((nrows-1));i++)) do
for ((j=0;j<=$((ncols-1));j++)) do
printf " %i" ${matrix[$i,$j]}
done
echo
done
但是当我尝试分配元素时,出现了错误
for ((i=0;i<=$((nelem-1));i++)) do
matrix[${irows[$i]},${jcols[$i]}]=${values[$i]}
done
在此先感谢您的帮助,真的。
这是让您入门的一种方法。请注意,这并不是“答案”,而是鼓励您尝试学习该工具包。
$ join -a1 -e NA -o2.2 <(printf "%s\n" {1..4}"_"{1..6}) \
<(awk '{print "_",}' file | sort -n) |
pr -6at
NA 4 12 7 9 10
68 70 NA 85 68 70
182 339 355 333 182 NA
797 1396 1854 NA 922 NA
有效,但是,行数和列数是硬编码的,这不是正确的方法。
首选解决方案是用数据填充一个 awk
二维数组,最后以矩阵形式打印。
任何时候你发现自己在 shell 中编写一个循环只是为了操作文本,你的方法是错误的。请参阅 why-is-using-a-shell-loop-to-process-text-considered-bad-practice 了解许多原因。
在每个 UNIX 机器上的任何 shell 中使用任何 awk:
$ cat tst.awk
{
vals[,] =
numRows = ( > numRows ? : numRows)
numCols =
}
END {
OFS = "\t"
for (rowNr=1; rowNr<=numRows; rowNr++) {
for (colNr=1; colNr<=numCols; colNr++) {
val = ((rowNr,colNr) in vals ? vals[rowNr,colNr] : "NA")
printf "%s%s", val, (colNr < numCols ? OFS : ORS)
}
}
}
.
$ awk -f tst.awk infile.txt
NA 4 12 7 9 10
68 70 NA 85 68 70
182 339 355 333 182 NA
797 1396 1854 NA 922 NA
通过使用关联数组模拟二维数组的简单 bash 解决方案可能是这样的(请注意,行数和列数 不是 硬编码并且该代码适用于输入行的任何排列,前提是每一行都具有问题中指定的格式):
$ cat printmat
#!/bin/bash
declare -A mat
nrow=0
ncol=0
while read -r col elem row; do
mat[$row,$col]=$elem
if ((row > nrow)); then nrow=$row; fi
if ((col > ncol)); then ncol=$col; fi
done
for ((row = 1; row <= nrow; ++row)); do
for ((col = 1; col <= ncol; ++col)); do
elem=${mat[$row,$col]}
if [[ -z $elem ]]; then elem=NA; fi
if ((col == ncol)); then elem+=$'\n'; else elem+=$'\t'; fi
printf "%s" "$elem"
done
done
$ ./printmat < infile.txt
打印出来
NA 4 12 7 9 10
68 70 NA 85 68 70
182 339 355 333 182 NA
797 1396 1854 NA 922 NA