Text-Processing

根據另一個矩陣的值建構一個矩陣

  • December 25, 2017

今天我在其他站點發現了一個關於 shell 腳本的有趣問題。問題是根據另一個矩陣的值建構一個矩陣

https://askubuntu.com/questions/884372/count-number-in-two-columns-and-generate-matrix

原始矩陣是這樣的:

joe it 9
wolf it 10
wolf pr 9
mark pm 6
jack pr 20
anton pm 5
joe pm 20
mark sa 35

輸出矩陣必須是這樣的

0 anton jack joe mark wolf
it 0 0 9 0 10
pm 5 0 20 6 0
pr 0 20 0 0 9
sa 0 0 0 35 0

如您所見,第二個矩陣是第一個矩陣的摘要

我嘗試按如下方式解決問題:

我將第一個矩陣保存在名為 test01 的文件中。我使用空格作為分隔符

從第一列中提取不同的元素,將其轉換為一行並將其保存到名為 new 的文件中

cut -d ' ' -f1-1 test01 |sort |uniq |awk  '{ printf( "%s ", $1 ); } END { printf( "\n" ); }' > new

提取第二列的不同元素並保存在名為 new2 的文件中

cut -d ' ' -f2-2 test01|sort|uniq > new2

添加一個 0 作為 new 的第一個元素並將其保存為 new1

while read line; do echo "0 $line"; done < new > new1

由於文件的第一列有 5 個不同的元素,因此在 new2 文件的每一行中添加了 5 個零,以便進行交叉比較

while read line; do echo "$line 0 0 0 0 0"; done < new2 > new3

並且 new3 的內容被添加到文件 new1 的末尾

while read line; do echo $line |awk '{print $1,$2}'; done < new1

現在new1的內容是:

0 anton jack joe mark wolf
it 0 0 0 0 0
pm 0 0 0 0 0
pr 0 0 0 0 0
sa 0 0 0 0 0

那是我被卡住的時候。

我不知道如何瀏覽 new1 中的矩陣並將每個元素與 test01 的行進行比較,以便在必要時替換 0。最終結果必須是:

0 anton jack joe mark wolf
it 0 0 9 0 10
pm 5 0 20 6 0
pr 0 20 0 0 9
sa 0 0 0 35 0

也許有一種比我到目前為止使用的方法更有效的方法來獲得結果而無需那麼多中間文件

我為文章的長度道歉

沒有 AWK 的多通道方法

這首先讀取文件以提取行標籤和列標籤。然後它列印一個 0,後跟第一行的每個列標籤。

循環處理非標籤行。首先它列印行標籤,然後搜尋文件中與(行、列)對匹配的所有條目。此結果第三列中的條目與 相加dc,以防返回多行。

這種方法的一個明顯問題是文件在結果矩陣中的*每個條目都被讀取一次。*因此,計算最初的兩次傳遞以獲得行標籤和列標籤,您的範例將被讀取 22 次!

呼叫為./contingency-table input-file

#!/bin/sh
# file: contingency-table

columns=$(cut -d' ' -f 1 "$1" | sort | uniq)
rows=$(cut -d' ' -f 2 "$1" | sort | uniq)

printf '0'
printf ' %s' ${columns}
printf '\n'

for row in ${rows}; do
 printf "${row} "
 for col in ${columns}; do
   (grep "${col} ${row}" "${1}" \
    | cut -d' ' -f 3            \
    | tr '\n' '+'
    printf '\n')                \
   | sed -e 's/^/0 /'           \
         -e 's/$/pq/'           \
   | dc                         \
   | tr '\n' ' '
 done
 printf '\n'
done

使用 AWK 的更有效方法

#!/usr/bin/awk -f

function max(val1, val2) {
   return ((val1 > val2) ? val1 : val2)
}

BEGIN {
   name_length = 0
   department_length = 0
   # This line influences sorting in GNU awk
   PROCINFO["sorted_in"] = "@ind_str_asc"
}

(!($1 in names)) {
   names[$1]
   name_length = max(length($1), name_length)
}

(!($2 in departments)) {
   departments[$2]
   department_length = max(length($2), department_length)
}

{
   hours[$2, $1] += $3
}

END {
   printf "%" department_length "s", 0
   for (name in names) {
       printf " %" name_length "s", name
   }
   printf "\n"
   for (department in departments) {
       printf "%" department_length "s", department
       for (name in names) {
           printf " %" name_length "d", hours[department, name]
       }
       printf "\n"
   }
}

begin 塊設置一些變數並配置 GNU awk 對數組遍歷進行排序。接下來的兩個塊根據需要添加名稱和部門,同時掃描輸入。第三個塊計算每個執行總計。

如果您不想要“人類可讀”格式,請註釋掉這些…_length = max(…行。

END塊是所有輸出和格式化發生的地方,通過遍歷之前創建的數組。這允許對輸入文件進行單次傳遞,而不是輸出表中的每個條目。

引用自:https://unix.stackexchange.com/questions/345879