Mesclando 2 arquivos baseados em uma única coluna

2

Eu gostaria de mesclar dois arquivos. Eu olhei para as perguntas e respostas anteriores, mas nenhuma delas corresponde à minha saída desejada.

Eu tenho dois arquivos separados por vírgula, de diferentes comprimentos, file1.csv e file2.csv .

Eu preciso mesclar esses arquivos com base no primeiro campo deles. Se o primeiro campo de file1.csv estiver presente em file2.csv , a linha correspondente de file2.csv deve ser anexada à de file1.csv . Se o primeiro campo não estiver presente, a linha de file1.csv deve ser impressa e no match anexada a ele.

file1.csv (4 colunas):

Contig_Spider_Gland_98_1_1,>Contig_Spider_Gland_98_1_1 [1169 - 963] (REVERSE SENSE),MQGHRRKLATPRQRAPRKERQRALLLRLQWRIGLQPCSRRNKSLDRKNIYWRYLVEYGSWKGRTHISDV,C#
Contig_Spider_Gland_98_7_3,>Contig_Spider_Gland_98_17965_1 [90 - 278],MADVEKTSCCTETKECCKDETCCENGQGACHTGKEECKDTCHKKACGCKAGEDCKCSDGKCGC,CC#CC#CC#C#C#C#C#C#C#C#C#C#

file2.csv (7 colunas):

Contig_Spider_Gland_98_1_1, SignalP-4.1, SIGNAL, 1, 22, 0.808, YES
Contig_Spider_Gland_98_8_2, SignalP-4.1, SIGNAL 1, 20, 0.877, YES

Saída desejada:

Contig_Spider_Gland_98_1_1,>Contig_Spider_Gland_98_1_1 [1169 - 963] (REVERSE SENSE),MQGHRRKLATPRQRAPRKERQRALLLRLQWRIGLQPCSRRNKSLDRKNIYWRYLVEYGSWKGRTHISDV,C#,Contig_Spider_Gland_98_1_1, SignalP-4.1, SIGNAL, 1, 22, 0.808, YES
Contig_Spider_Gland_98_7_3,>Contig_Spider_Gland_98_17965_1 [90 - 278],MADVEKTSCCTETKECCKDETCCENGQGACHTGKEECKDTCHKKACGCKAGEDCKCSDGKCGC,CC#CC#CC#C#C#C#C#C#C#C#C#C#,no match
    
por Trupti 12.10.2015 / 12:05

3 respostas

3

Isso deve ser feito:

awk -F, -vOFS=, '(NR==FNR){a[$1]=$0; next}
                   {
                      if(a[$1]){print $0,a[$1]}
                      else{print $0,"no match"}
                   }' file2.csv file1.csv

Explicação

  • awk -F, -vOFS=, : run awk , definindo os separadores de campo de entrada ( -F ) e saída ( -vOFS=, ) para , .
  • (NR==FNR){a[$1]=$0; next} : NR e FNR são variáveis especiais que significam o número da linha atual e o número da linha atual do arquivo atual , respectivamente. Ao passar mais de um nome de arquivo, os dois serão iguais apenas enquanto o primeiro arquivo estiver sendo lido. Então, isso significa "ao ler o primeiro arquivo, salve cada linha em uma matriz cuja chave é o primeiro campo e vá para a próxima linha".
  • if(a[$1]){print $0,a[$1]} : agora estamos no segundo arquivo. Se o primeiro campo da linha atual também estava no primeiro arquivo, imprima a linha atual e a linha do primeiro arquivo.
  • else{print $0,"no match"} : se o primeiro campo não estava no primeiro arquivo, imprima a linha atual e "sem correspondência"

Observe que estou passando file2.csv como o primeiro arquivo e file1.csv como o segundo. Isso ocorre porque um dos dois arquivos precisa ser armazenado na memória, por isso é melhor armazenar o menor dos dois.

    
por terdon 12.10.2015 / 13:27
1

Isso deve ser feito (encurtado (R) e fixo (TM) por terdon):

#!/usr/bin/perl

use strict;
use warnings;

@ARGV==2 || die;

open(my $file1, $ARGV[0]) || die("Could not open \"$ARGV[0]\": $!");
open(my $file2, $ARGV[1]) || die("Could not open \"$ARGV[1]\": $!");

$"=","; #" (this comment exists only to prevent syntax hilighting from breaking)

while(my $l1 = <$file1>) {
    chomp($l1);
    my @f1 = split(",", $l1);
    if(my $l2 = <$file2>) {
        chomp($l2);
        my @f2 = split(",", $l2);
        if($f1[0] eq $f2[0]) {
            print("@f1,@f2\n");
        }
        else {
            push(@f1, "no_match");
            seek($file2, -length($l2), 1);
            print("@f1\n");
        }
    }
    else {
        push(@f1, "no_match");
        print("@f1\n");
    }
}

close($file1);
close($file2);

exit;

Como os arquivos são classificados, "file1.csv" é superconjunto de "file2.csv" e não há linhas duplicadas em nenhum desses arquivos:

  • Compara as próximas linhas nos dois arquivos;
  • Se o primeiro campo da linha de "file1.csv" corresponder ao primeiro campo da linha de "file2.csv", acrescenta a linha de "file2.csv" à linha de "file1.csv" (vírgula -separada) e imprime a linha gerada; caso contrário, anexa um campo "no_match" à linha de "file1.csv", retorna uma linha em "file2.csv" e imprime a linha modificada de "file1.csv";
  • Se "file2.csv" não tiver mais linhas, anexará um campo "no_match" à linha de "file1.csv" e imprimirá a linha modificada de "file1.csv".
por kos 12.10.2015 / 14:24
0

Outra opção é usar join:

join -t, file1.csv file2.csv -a 1 -o auto -e 'no match'

Se os arquivos de entrada ainda não estiverem classificados, você poderá fazer isso de uma só vez:

join -t, <(sort file1.csv) <(sort file2.csv) -a 1 -o auto -e 'no match'

Explanação

  • -t, define o separador de campo
  • -a 1 garante que todas as linhas não parciais sejam impressas também
  • -o auto define o formato de saída
  • -e 'no match' substitui campos de entrada ausentes por 'sem correspondência'
  • o operador <(list) conecta a saída dos comandos de classificação a pipes nomeados que são usados como arquivos para o comando de união (chamado Substituição de processo)
por Bram 17.10.2015 / 12:38