Combinando duas colunas principais entre os arquivos; e cole outras colunas no arquivo de saída quando essas colunas principais corresponderem. Manter o tamanho da linha do 1º arquivo intacto

1

Esta é uma pergunta de acompanhamento da minha pergunta anterior, feita há cerca de 24 horas.

Link para a pergunta anterior:

anterior Link da pergunta

G-Man resolveu esse problema com um código útil, mas eu tenho uma pergunta de acompanhamento. Eu já aceitei a resposta, daí esse segundo post ...

Eu tenho 3 arquivos, cada um com um número exclusivo de colunas, todos separados por tabulações, mas algumas colunas são compartilhadas entre os três arquivos. São as colunas compartilhadas entre os três arquivos que eu quero usar para criar algum tipo de arquivo "agregado".

As tabelas abaixo mostram exemplos de como os arquivos podem se parecer. Basicamente, quero corresponder as colunas MAIN1 e MAIN2 entre os arquivos. Ambas as colunas entre os três arquivos precisam corresponder.

Eu quero adicionar a coluna "minor8" do arquivo2 ao lado direito da tabela no arquivo1 para essas linhas quando MAIN1 e MAIN2 entre os dois arquivos corresponderem. Posteriormente, eu quero adicionar "minor9" do arquivo3 no lado direito da tabela file1 para esses casos quando MAIN1 e MAIN2 entre os dois arquivos correspondem. Porque "minor8" deve ir imediatamente ao lado da coluna mais à direita do arquivo1 (nome da coluna: "minor3"), gostaria que "minor9" fosse ao lado de "minor8" no novo arquivo OUTPUT. O arquivo OUTPUT dá uma ideia de como deve ser meu arquivo final ideal.

Basicamente, estes são exemplos de 3 arquivos (as "guias" são um pouco confusas)

arquivo1:

MAIN1   minor1  MAIN2   minor3
1  bla1    a    blabla1
1  bla2    b    blabla2
1  bla3    c    blabla3
2  bla4    a    blabla4
2  bla5    d    blabla5
3  bla6    e    blabla6
4  bla7    f    blabla7
5  bla8    a    blabla8
5  bla9    g    blabla9

arquivo2:

minor8  MAIN1   MAIN2
yes1    2   d
yes2    3   e
yes3    4   f
yes4    5   a
yes5    5   g
yes6    1   a
yes7    1   b
yes8    1   c
yes9    2   a

arquivo3:

MAIN1   MAIN2   minor9
5   a   sure1
5   g   sure2
1   a   sure3
1   b   sure4
1   c   sure5
2   a   sure6
2   d   sure7
3   e   sure8
4   f   sure9

arquivo OUTPUT desejado:

MAIN1   minor1  MAIN2   minor3  minor8  minor9
1   bla1    a   blabla1 yes6    sure3
1   bla2    b   blabla2 yes7    sure4
1   bla3    c   blabla3 yes8    sure5
2   bla4    a   blabla4 yes9    sure6
2   bla5    d   blabla5 yes1    sure7
3   bla6    e   blabla6 yes2    sure8
4   bla7    f   blabla7 yes3    sure9
5   bla8    a   blabla8 yes4    sure1
5   bla9    g   blabla9 yes5    sure2

Como mencionado anteriormente, o G-Man forneceu um código útil que fez exatamente o que eu pedi (por favor, veja o post anterior). Provavelmente vou perguntar ao G-Man (ou a outra pessoa que tenha tempo) algumas perguntas específicas sobre algumas das linhas individuais do código que eu ainda não entendi muito bem, mas até então, tenho essa pergunta de acompanhamento.

O código do G-Man conseguiu recriar o arquivo OUTPUT mencionado acima, então obrigado G-Man!

A pergunta de acompanhamento:

Uma coisa que esqueci de mencionar, que o código não foi capaz de fazer (até onde eu vi), é que ele removerá linhas do arquivo1 se não houver correspondência com as colunas MAIN1 e MAIN2 entre os arquivos. Isso é culpa minha, já que eu não especifiquei isso. Meu objetivo é ter um arquivo OUTPUT no qual nenhuma linha do arquivo1 seja removida.

Basicamente file1 é o meu arquivo de prioridade. Qualquer quantidade de linhas que este arquivo tenha (perto de um milhão), essa é a quantidade de linhas que o arquivo OUTPUT deve ter também. As colunas "menor8" e "menor9" podem estar vazias para algumas linhas se não houver correspondência de coluna MAIN1, MAIN2. Mas eu gostaria de manter essas linhas de arquivo1 quando houver um valor "ausente / vazio" para "menor8" ou "menor9" (ou ambos).

Vou tentar ilustrar isso usando uma versão ligeiramente diferente dos arquivos 2 e 3 mencionados acima (assim o arquivo1 permanece o mesmo).

arquivo2 ajustado (não tem combinação MAIN1, MAIN2: 2, d):

minor8  MAIN1   MAIN2
yes2    3   e
yes3    4   f
yes4    5   a
yes5    5   g
yes6    1   a
yes7    1   b
yes8    1   c
yes9    2   a

arquivo3 ajustado (não tem combinação MAIN1, MAIN2: 5, a):

MAIN1   MAIN2   minor9
5   g   sure2
1   a   sure3
1   b   sure4
1   c   sure5
2   a   sure6
2   d   sure7
3   e   sure8
4   f   sure9

ajustado, SAIDA desejada (isto é, valor vazio na coluna menor8 para MAIN1, MAIN2 combinação 2-d; e valor vazio na coluna menor9 para MAIN1, MAIN2 combinação 5-a):

MAIN1   minor1  MAIN2   minor3  minor8  minor9
1   bla1    a   blabla1 yes6    sure3
1   bla2    b   blabla2 yes7    sure4
1   bla3    c   blabla3 yes8    sure5
2   bla4    a   blabla4 yes9    sure6
2   bla5    d   blabla5     sure7
3   bla6    e   blabla6 yes2    sure8
4   bla7    f   blabla7 yes3    sure9
5   bla8    a   blabla8 yes4    
5   bla9    g   blabla9 yes5    sure2

Espero que minha maneira de explicar isso seja clara o suficiente. Eu vejo que as abas das tabelas estão um pouco confusas. Vocês preferem assim, ou para eu endireitar as mesas visualmente? (O único problema que pode resultar disso, imagino, é que quando você copia e cola meus dados de exemplo, você teria guias adicionais que não deveriam estar lá ...)

De qualquer forma, eu aprecio muito a ajuda de vocês. Espero que em algum momento no futuro próximo eu possa contribuir para este fórum, além de simplesmente pedir ajuda ...

Você tem alguma sugestão sobre como o código do G-Man deve ser editado para tornar isso possível? Ou se você tiver uma sugestão totalmente diferente sobre como um código útil poderia ser escrito e levar em conta esse requisito adicional, por favor, avise-nos.

    
por Noobie123 16.12.2015 / 21:48

1 resposta

2

Crie os seguintes arquivos:

merge21 :

BEGIN {
        FS = "\t"
        OFS = "\t"
}
NR==FNR {               # file2
        key = $2 "," $3
        present[key] = 1
        minor8[key] = $1
        next
}
{                       # file1
        key = $1 "," $3
        if (present[key]) print $1, $2, $3, $4, minor8[key]
        else              print $1, $2, $3, $4, "-"
}

merge312 :

BEGIN {
        FS = "\t"
        OFS = "\t"
}
NR==FNR {               # file3
        key = $1 "," $2
        present[key] = 1
        minor9[key] = $3
        next
}
{                       # file1 + file2
        key = $1 "," $3
        if (present[key]) print $1, $2, $3, $4, $5, minor9[key]
        else              print $1, $2, $3, $4, $5, "-"
}

Eles são quase idênticos; Eu corrijo as diferenças. Agora digite o comando

awk -f merge21 file2 file1 | awk -f merge312 file3 -

Isso pressupõe que nenhum dos seus campos principais inclui vírgula (s) e nenhum dos seus dados inclui hífens mas realmente depende apenas de haver algumas strings que não aparecem nos dados. Seria trivial estender isso para suportar mais colunas; Espero que isso seja óbvio. Isso poderia ser aprimorado para fazer tudo em uma única execução de awk , mas isso seria um pouco mais complexo, e (IMNSHO) não vale o esforço.

Isso produz o que é chamado de "junção externa esquerda" dos dados em seus arquivos; consulte Diferença entre o INNER e o OUTER junta-se ao Stack Overflow para algumas definições. (“Junção externa esquerda” é definida na resposta aceita a essa pergunta como (parafraseada) «Todas as linhas na primeira tabela, mais quaisquer linhas comuns na (s) outra (s) tabela (s)».

Sua saída será

MAIN1   minor1  MAIN2   minor3  minor8  minor9
1       bla1    a       blabla1 yes6    sure3
1       bla2    b       blabla2 yes7    sure4
1       bla3    c       blabla3 yes8    sure5
2       bla4    a       blabla4 yes9    sure6
2       bla5    d       blabla5 -       sure7
3       bla6    e       blabla6 yes2    sure8
4       bla7    f       blabla7 yes3    sure9
5       bla8    a       blabla8 yes4    -
5       bla9    g       blabla9 yes5    sure2

e, obviamente, você pode remover os caracteres - com sed . (E, claro, se os dados reais incluírem hífens, escolha algum caractere não usado ou string como o espaço reservado para dados ausentes.)

Notas

  • FS e OFS são o separador do campo de entrada e o separador de campo de saída, respectivamente. (Aparentemente IFS não tem sentido em awk ; isso foi um erro da minha parte.) Você provavelmente não precisa do FS="\t" - awk reconhece guias como separadores de campo na entrada por padrão. (Permite que você tenha campos que contenham espaços, mas você não parece estar interessado nisso.) OFS="\t" é importante; por causa disso, eu posso dizer print $1, $2, $3, $4 e obtenha os campos de entrada para serem saída com abas entre eles. Se eu não dissesse OFS="\t" , eles seriam separados por espaços, a menos que eu tenha dito print $1 "\t" $2 "\t" $3 "\t" $4 , que é entediante e prejudica a legibilidade.
  • Se você tivesse dado restrições adicionais a MAIN1 e MAIN2 - por exemplo, eles sempre têm apenas um caractere cada ou MAIN1 é sempre um número e MAIN2 sempre começa com uma letra - Eu não precisaria da vírgula ( , ) em key . Mas a versão original da sua primeira pergunta não mostra tal restrição. Considere os seguintes dados:

    MAIN1 ($2)         MAIN2 ($3)         badkey = $2 $3         goodkey = $2 "," $3
        2              34151                  234151                   2,34151
       23               4151                  234151                   23,4151
    

    Se não incluirmos algum caractere separador na chave que não aparecem nos campos principais (MAIN1 e MAIN2), podemos obter o mesmo valor key para linhas diferentes.

  • Correndo o risco de dividir os cabelos, não estou dizendo nada ao Linux; Estou dizendo awk o que fazer.
  • Em relação ao código
    NR==FNR {               # file3
            key = $1 "," $2
            present[key] = 1
            minor9[key] = $3
            next
    }
    Considere a sétima da última linha de file3 , que contém 1 a sure3 . Obviamente, temos $1 = 1 , $2 = a e $3 = sure3 , portanto key = 1,a . present[key] = 1 significa que estou definindo present["1,a"] para 1 como sinalizador para indicar que file3 tem uma 1,a line; ou seja, que existe um valor minor9 para key = 1,a . Como não há 5,a linha em file3 , present["5,a"] não é definido, e assim a parte " file1 + file2 " do código sabe que não há minor9 para key = 5,a , e deve imprimir - . O nome present é apenas uma escolha arbitrária da minha parte; indica que a linha 1,a está presente em file3 (e a linha 5,a não é). É convencional usar 1 para representar "VERDADEIRO".
  • Você pode substituir print $1, $2, $3, $4 com for (n=1; n<=4; n++) printf "%s\t", $n . Você deve terminar a linha usando apenas print (em oposição a printf ) para o último campo ou fazendo printf "\n" . Você pode simplificar ainda mais fazendo algo parecido com

            for (n=1; n<=4; n++) printf "%s\t", $n
            if (present[key]) print minor8[key]
            else              print "-"

Por favor, leia awk (1) , o Especificação de POSIX para awk , O Guia do Usuário do GNU Awk , e veja Awk.info para mais informações.

    
por 16.12.2015 / 22:31