使用 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