Измените форму таблицы и заполните пустоты с NA (или -999), используя bash - PullRequest
2 голосов
/ 11 июля 2020

Я пытаюсь создать таблицу на основе ASCII ниже. Мне нужно расположить числа из 2-го столбца в матрице. Первый и третий столбцы ASCII дают столбцы и строки в новой матрице. Новая матрица должна быть полностью заполнена, поэтому необходимо заполнить недостающие позиции в новой таблице с помощью NA (или -999).

Это то, что у меня

$ 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, et c).

Итак, что у меня так далеко ...

Я могу скопировать c 2-мерный массив в bash

irows=(`awk '{print $1 }'  infile.txt`) # rows positions 
jcols=(`awk '{print $3 }'  infile.txt`) # columns positions
values=(`awk '{print $2 }' infile.txt`) # values

declare -A matrix                                         # the new matrix
nrows=(`sort -k3 -n in.txt | tail -1 | awk '{print $3}'`) # numbers of rows
ncols=(`sort -k1 -n in.txt | tail -1 | awk '{print $1}'`) # 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

Заранее спасибо за любую помощь с этим, правда.

Ответы [ 3 ]

2 голосов
/ 11 июля 2020

Простое решение bash путем моделирования 2D-массива с ассоциативным массивом может быть примерно таким (обратите внимание, что количество строк и столбцов не жестко закодировано, и код работает с любой перестановкой входных данных строк при условии, что каждая строка имеет формат, указанный в вопросе):

$ 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
1 голос
/ 11 июля 2020

Каждый раз, когда вы обнаруживаете, что пишете al oop в оболочке только для того, чтобы манипулировать текстом, у вас неправильный подход. См. why-is-using-a-shell-l oop -to-process-text-separated-bad-practice по многим причинам.

Использование любого awk в любая оболочка на каждом UNIX поле:

$ cat tst.awk
{
    vals[$3,$1] = $2
    numRows = ($3 > numRows ? $3 : numRows)
    numCols = $1
}
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
1 голос
/ 11 июля 2020

вот один из способов начать работу. Обратите внимание, что это не является «ответом», но призвано побудить вас попробовать изучить набор инструментов.

$ join -a1 -e NA -o2.2 <(printf "%s\n" {1..4}"_"{1..6})           \
                       <(awk '{print $3"_"$1,$2}' 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 2D-массива данными и печать его в матричной форме в конце.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...