Como combinar dois arquivos combinando dois parâmetros com awk
Eu tenho um arquivo A.txt(sep = \t):
Cycle Well Value Target
1 A1 5.07368111264623 EC
1 A1 3.06982862746599 FT
1 A1 2.46545646544623 EC
E um segundo arquivo B.txt(sep = \t, a primeira coluna está vazia ):
Well Fluor Target Content Sample
A1 Cy5 EC Unkn-01 2060563935
A1 Cy5 FT Unkn-09 2156515156
Quero adicionar a coluna Contentde B.txtem A.txtse ambos Well e Targetforem os mesmos dados dos dois arquivos e enviar o resultado para C.txt(sep = \t):
Cycle Well Value Target Content
1 A1 5.07368111264623 EC Unkn-01
1 A1 3.06982862746599 FT Unkn-09
1 A1 2.46545646544623 EC Unkn-01
Eu tento coisas como:
awk -F"\t" 'FNR==NR{if (a[$2]) {a[$2]=a[$2] "\t" $7} else {a[$2]=$7}} NR>FNR{split($0,f,"\t"); if (a[f[4]]) $0=$0 "\t" a[f[4]]; print}'
Mas não funcionou. Alguma ideia de como fazer isso?
Precisão:
- No primeiro arquivo usado como modelo (A.txt), várias linhas com o mesmo Poço e Alvo estão presentes.
- Em B.txt, há apenas uma linha com a mesma combinação Poço / alvo.
- Não é possível não ter um padrão correspondente no arquivo A do arquivo B.
Respostas
Primeira solução usando GNU awk ou POSIX awk
Edit: Como Ed Morton escreveu em seu comentário, a resposta original estava errada sobre o que é suportado awkapenas pelo GNU . (O texto na documentação GNU vs. a documentação POSIX é um pouco confuso.)
O que a awkdocumentação GNU chama de Arrays multidimensionais é compatível com POSIX awk. Vejohttps://pubs.opengroup.org/onlinepubs/000095399/utilities/awk.htmle pesquise por "multidimensional" ou SUBSEP. Essas matrizes são, na verdade, unidimensionais.
GNU awktambém suporta Arrays of Arrays que são verdadeiros arrays multidimensionais.
Esta versão do comando requer GNU awk:
awk -F"\t" 'NR == FNR { a[$2][$4] = $5; next } { print $0, a[$2][$4] }' B.txt A.txt > C.txt
A variante compatível com POSIX (*), que deve funcionar com qualquer awké
awk -F"\t" 'NR == FNR { a[$2,$4] = $5; next } { print $0, a[$2,$4] }' B.txt A.txt > C.txt
Ambos imprimem
Cycle Well Value Target
1 A1 5.07368111264623 EC Unkn-01
1 A1 3.06982862746599 FT Unkn-09
1 A1 2.46545646544623 EC Unkn-01
Os dados do arquivo B.txtsão salvos no array aporque, de acordo com a pergunta, a chave Well / Target é única neste arquivo. Em seguida, esses dados são anexados aos dados do arquivo A.txt.
O separador de campo deve ser especificado explicitamente. Caso contrário, awkiria ignorar colunas / valores vazios.
Esta solução usa números de coluna fixos para identificar as colunas a serem correspondidas ou impressas.
Editar: A seguinte solução de solução que combina explicitamente as expressões de índice com \to separador não tem nenhuma vantagem em comparação com a solução compatível com POSIX (*) mostrada acima.
awk -F"\t" 'NR == FNR { a[$2 "\t" $4] = $5; next } { print $0, a[$2 "\t" $4] }' B.txt A.txt > C.txt
Isso é equivalente a definir SUBSEP = "\t"e usar a sintaxe .a[$2, $4]
Segunda solução usando q
A ferramenta q pode ser usada para realizar consultas de banco de dados em arquivos CSV.
Vejo http://harelba.github.io/q/ ou https://github.com/harelba/q
Esta solução tem um problema com o título da coluna vazia em B.txt. Como solução alternativa, adicionei um título Emptyà linha de cabeçalho deste arquivo.
Então, eu uso estes arquivos:
A.txt
Cycle Well Value Target
1 A1 5.07368111264623 EC
1 A1 3.06982862746599 FT
1 A1 2.46545646544623 EC
B.txt
Empty Well Fluor Target Content Sample
A1 Cy5 EC Unkn-01 2060563935
A1 Cy5 FT Unkn-09 2156515156
O comando
q -H -t "select a.Cycle,a.Well,a.Value,a.Target,b.Content from A.txt as a inner join B.txt as b on a.Well=b.Well and a.Target=b.Target"
estampas
1 A1 5.07368111264623 EC Unkn-01
1 A1 3.06982862746599 FT Unkn-09
1 A1 2.46545646544623 EC Unkn-01
Para imprimir o cabeçalho, você pode adicionar um comando printfou echo.
printf "Cycle\tWell\tValue\tTarget\tContent\n" > C.txt
q -H -t "select a.Cycle,a.Well,a.Value,a.Target,b.Content from A.txt as a inner join B.txt as b on a.Well=b.Well and a.Target=b.Target" >> C.txt
Para automatizar a modificação do arquivo, B.txtvocê pode usar
printf "Empty" > B1.txt
cat B.txt >> B1.txt
printf "Cycle\tWell\tValue\tTarget\tContent\n" > C.txt
q -H -t "select a.Cycle,a.Well,a.Value,a.Target,b.Content from A.txt as a inner join B1.txt as b on a.Well=b.Well and a.Target=b.Target" >> C.txt
Esta solução usou colunas nomeadas da linha do cabeçalho para identificar as colunas a serem correspondidas ou impressas.
Supondo que você tenha GNU awkpara matrizes bidimensionais, o seguinte programa fará a tarefa:
awk -F'\t' 'NR==FNR&&FNR>1{map[$2][$4]=$5}\ NR>FNR{if (FNR==1) {$5="Content"} else {$5=map[$2][$4]}} NR>FNR' B.txt A.txt > C.txt
Primeiro, esse processo B.txtcriará um mapeamento de valores de "Conteúdo" para uma combinação específica de "Poço" e "Alvo". Ao processar A.txtposteriormente (indicado por FNR, o contador de linha por arquivo agora sendo menor do que NRo contador de linha global), o programa procura a combinação específica de "Poço" e "Alvo" na linha atual e substitui o " Conteúdo "valor do mapa criado anteriormente. Ele imprime a saída e depois processa o segundo arquivo (a NR>FNRcondição "perdida" ).