Se você realmente quiser fazer isso em sed
/ awk
, é possível:
Como mencionado por Joe , usando o valor de dados SPACE
como separador de campo & é um problema em awk
.
É por isso que sugiro usar sed
para reformatar os dados primeiro:
sed 's/ *$//'
remove SPACE
s no final da linha (todas, exceto a primeira das suas linhas de entrada, terminam em SPACE
, portanto, isso padroniza a entrada e remove possíveis valores ausentes no final de cada linha) .
Em seguida, sed 's/ / . /g/'
insere um .
entre cada par de SPACE
s adjacentes (preenchendo possíveis valores ausentes que não estão no final de uma linha).
Como isso irá inserir% adicionalSPACE
s no caso de valores ausentes adjacentes, sed 's/ / /g'
tem que ser usado para removê-los novamente.
Em seguida, awk
pode usar a primeira linha (ou seja, cabeçalho) para aprender os nomes & número de leituras, adicione possíveis valores omissos no final de cada linha (todos os outros já foram atendidos por sed
), sum & contabilizar todas as leituras mantendo o nome correspondente & instrumento, e produzir os meios (se houver) na orientação / ordem desejada:
sed -e 's/ *$//' -e 's/ / . /g' -e 's/ / /g' <<< 'Name Instrument Rep R1 R2 R3
N1 I1 1 1 2 3
N2 I1 1 1 3 4
N1 I1 2 2 3 4
N3 I1 2 3 4 5
N1 I2 1 1 2 3
N2 I2 1 1 3 4
N2 I2 2 2 3 4
N3 I2 1 3 4 5
N1 I3 1 1 4
N2 I3 1 2 5
N3 I3 1 6
N3 I3 2 1' | awk '
# get number of readings/fields
NR==1{for(i=4;i<=NF;++i)readings[i-4]=$i;fields=NF;next}
# add missing fields in the end
{for(i=NF+1;i<=fields;++i)$i="."}
# keep track of names & instruments
names[$1];instruments[$2]
# sum & count readings per name/instrument (ignoring missing ["."] values)
{for(i=4;i<=NF;++i)if($i!="."){sum[readings[i-4] FS $2 FS $1]+=$i;++count[readings[i-4] FS $2 FS $1]}}
# after reading all data:
END{
# print header
printf "Reading"FS"Instrument";for(name in names)printf FS name;print ""
# sort output rows by instrument
for(instrument in instruments){
# keep order of readings
for(i=0;i<length(readings);++i){
# print first two columns
printf readings[i] FS instrument
# remaining columns (i.e. names):
for(name in names){
# if data available:
if(count[readings[i] FS instrument FS name]){
# print average
printf FS sum[readings[i] FS instrument FS name]/count[readings[i] FS instrument FS name]
# otherwise:
}else{
# print missing value ["."]
printf FS "."
}
# proceed with next row
}print ""
}
}
}
'
Observação: na minha opinião, usar FS
como separador em índices de matriz multidimensional é a melhor escolha na maioria dos casos, pois todos os campos têm a garantia de não contê-lo (no caso de você precisar iterar sobre o array & ; dividir as 'dimensões' dos índices). Embora isso não seja necessário aqui, eu criei um hábito.
editar : Joe apontou que a forma como os nomes / instrumentos eram mantidos em uma versão anterior desta resposta poderia usar algumas explicações adicionais. Isso inspirou a versão simplificada usada acima: Ao contrário de k in a
que verifica a existência da chave k
na matriz a
sem criar essa entrada, a[k]
irá atribuir > em> um valor vazio para essa entrada (e retorná-lo).
Para mim, o código acima produz a saída que você solicitou:
Reading Instrument N1 N2 N3
R1 I1 1.5 1 3
R2 I1 2.5 3 4
R3 I1 3.5 4 5
R1 I2 1 1.5 3
R2 I2 2 3 4
R3 I2 3 4 5
R1 I3 1 2 .
R2 I3 . 5 6
R3 I3 4 . 1
Observação: a sintaxe <<<
que uso é uma HERE-STRING e pode não funcionar em todos os shells (no entanto, bash
oferece suporte a ela). Basta passar o caminho do arquivo de entrada para o sed
e ele deve funcionar em todos os shells (até onde eu sei).
Nota: Isso só funcionará se todos os seus dados couberem na memória. Se esse não for o caso, deve haver uma solução com menos intensidade de memória para resumir os dados com base na classificação da entrada primeiro. Transpor a matriz pode ser mais complicado nesse caso.
editar:
Nota: Minha saída não contém SPACE
no final de qualquer linha, ao contrário do resultado do seu exemplo, já que não consegui descobrir, quando você coloca um SPACE
e quando não o faz. Se isso tiver algum significado, ajuste a pergunta e atualizarei a resposta de acordo. Caso contrário, considere remover os SPACE
s da sua saída esperada.