Um método multi-pass sem AWK
Isso começa lendo o arquivo para extrair os rótulos de linha e coluna. Em seguida, ele imprime um 0 seguido por cada rótulo de coluna para a primeira linha.
O loop cuida das linhas sem rótulo. Primeiro, imprime o rótulo da linha e, em seguida, pesquisa todas as entradas no arquivo que correspondem a esse par (linha, coluna). As entradas na terceira coluna desse resultado são somadas com dc
, no caso de várias linhas serem retornadas.
Um problema evidente com essa abordagem é que o arquivo é lido uma vez por entrada na matriz de resultados. Então, contando os dois passes iniciais para obter os rótulos de linha e coluna, seu exemplo seria lido 22 vezes!
Ligue como ./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
Um método mais eficiente usando o 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"
}
}
O bloco inicial configura algumas variáveis e configura o GNU awk para ordenar travessias de matriz. Os próximos dois blocos adicionam nomes e departamentos conforme necessário, enquanto examinam a entrada. O terceiro bloco calcula cada total em execução.
Se você não quiser a formatação "humana legível", comente as …_length = max(…
linhas.
O bloco END
é onde toda a saída e formatação acontece, percorrendo as matrizes criadas anteriormente. Isso permite uma única passagem sobre o arquivo de entrada, em vez de um por entrada na tabela de saída.