Mesclar uma coluna com base na segunda coluna

1

Eu tenho um arquivo parecido com isto:

pw1jc5ssyt6hx618,254343
ysezaratlycpuggl,254333
pht92h4adr3mrbz3,254343
hguvgstqxu3gowfg,254344
gqjp2rsjmk1a2v9c,254333
twdzyi2ddbnrfknd,254333
gcmj7krrx5x6nf8r,254341
tpqorqbyrg1nmm7s,254333
alnac47rt8d4ege3,254343

Eu quero mesclar esse arquivo com base na segunda coluna, com - como delimitador, para que o resultado seja assim:

254343,pw1jc5ssyt6hx618-pht92h4adr3mrbz3-alnac47rt8d4ege3
254333,ysezaratlycpuggl-gqjp2rsjmk1a2v9c-twdzyi2ddbnrfknd-tpqorqbyrg1nmm7s
254344,hguvgstqxu3gowfg
254341,gcmj7krrx5x6nf8r
    
por Rock 31.07.2016 / 06:41

5 respostas

6

awk é seu amigo

$ cat 299360
ipw1jc5ssyt6hx618,254343
ysezaratlycpuggl,254333
pht92h4adr3mrbz3,254343
hguvgstqxu3gowfg,254344
gqjp2rsjmk1a2v9c,254333
twdzyi2ddbnrfknd,254333
gcmj7krrx5x6nf8r,254341
tpqorqbyrg1nmm7s,254333
alnac47rt8d4ege3,254343
$ awk -v FS="," '/^$/{next} # for empty line go to next record
                {if(NR==1){ # checking for first record
                f2[$2]=$1;next} # Adding $1 to array f2 at index $2
                else{
                if($2 in f2){ # Check if $2 is already an index in f2
                f2[$2]=f2[$2]"-"$1;next #appending "-$1" to current value
                }
                else{
                f2[$2]=$1;next
                }
                }}
                END{ # This line will be processed at the end
                for(i in f2){  # for all the indexes i in f2
                printf "%s,%s\n",i,f2[i] #printing in the desired format
                }
                }
                ' 299360
254341,gcmj7krrx5x6nf8r
254333,ysezaratlycpuggl-gqjp2rsjmk1a2v9c-twdzyi2ddbnrfknd-tpqorqbyrg1nmm7s
254343,pw1jc5ssyt6hx618-pht92h4adr3mrbz3-alnac47rt8d4ege3
254344,hguvgstqxu3gowfg

Explicação

  1. FS="," - FS é a variável interna do awk que significa separador de campo. Definir o separador de campo como , definirá , como o delimitador.
  2. Você acessa os campos por $1 , $2 e assim por diante.
  3. O script awk é colocado entre aspas simples; ou seja, 'awk-script-goes-here'
  4. NR é uma variável interna do awk que representa o número do registro (o número do registro sendo processado atualmente). Por padrão, cada linha é um registro.
  5. Por f2[$2]=$1 , estamos configurando uma matriz associativa f2 com campo2 (ou seja, $2 ) como o índice.
  6. $2 in f2 verifica se o índice já está presente no array.
  7. Os if-else e printf são auto-explicativos.
  8. O bloco END no awk é executado apenas no final; ou seja, depois de todos os registros terem sido processados.
  9. for(i in f2) é uma construção de forloop usada para analisar as matrizes associativas no awk. É a outra maneira de dizer, for every index i in f2 do something
  10. Observe que o loop for acima pode não imprimir a matriz em um pedido. Você pode usar o comando sort bash para classificar a matriz.
  11. next vai para o próximo registro sem processar os comandos a seguir.
  12. O /pattern/ verifica um padrão no awk; o padrão ^$ verifica a linha vazia.

Referência

Se você deseja obter um especialista em awk, Programação eficaz do awk é um deve ler.

Um-liner feio

awk -v FS="," '/^$/{next}{if(NR==1){f2[$2]=$1;next}else{if($2 in f2){f2[$2]=f2[$2]"-"$1;next}else{f2[$2]=$1;next}}}END{for(i in f2){printf "%s,%s\n",i,f2[i]}}' 299360

Nota: Idealmente, não é uma boa ideia codificar novas linhas em scripts awk, como em printf "%s,%s\n",i,f2[i] . Você pode substituí-lo por printf "%s,%s\n",i,f2[i];print para portabilidade extra.

    
por 31.07.2016 / 07:09
6

Com o datamash do GNU:

datamash -t, -s -g 2 collapse 1 <data.txt | sed 's/,/-/2g'

Resultado:

254333,ysezaratlycpuggl-gqjp2rsjmk1a2v9c-twdzyi2ddbnrfknd-tpqorqbyrg1nmm7s
254341,gcmj7krrx5x6nf8r
254343,pw1jc5ssyt6hx618-pht92h4adr3mrbz3-alnac47rt8d4ege3
254344,hguvgstqxu3gowfg
    
por 31.07.2016 / 09:02
3

Em perl :

#! /usr/bin/perl

use strict;
my %mergecol = ();

while(<>) {
  s/#.*//;            # strip comments
  next if (m/^\s*$/); # skip empty lines
  chomp;
  my ($val,$key) = split ',';
  push @{ $mergecol{$key} }, $val;
};

foreach my $k (keys %mergecol) { 
  printf "%s,%s\n", $k, join('-', @{ $mergecol{$k} } );
}

O loop while lê a entrada e cria uma estrutura Hash-of-Arrays (HoA) - uma matriz associativa em que cada elemento é uma matriz (também conhecida como "lista"). As chaves para o hash são o segundo campo, enquanto os elementos de cada lista são os primeiros campos de linhas com o mesmo segundo campo.

No final do script, o HoA é impresso em uma linha por registro, com o nome da chave, uma vírgula e, em seguida, os elementos associados a um - .

executar como:

$ ./rock.pl rock.txt
254341,gcmj7krrx5x6nf8r
254333,ysezaratlycpuggl-gqjp2rsjmk1a2v9c-twdzyi2ddbnrfknd-tpqorqbyrg1nmm7s
254344,hguvgstqxu3gowfg
254343,pw1jc5ssyt6hx618-pht92h4adr3mrbz3-alnac47rt8d4ege3

Ou encurtado e incorporado em um comando ou script shell como "one-liner":

$ perl -n -e '
    s/#.*//;
    next if (m/^\s*$/);
    chomp; ($v,$k)=split ","; push @{ $mc{$k} }, $v;
    END {
     foreach $k (keys %mc) { printf "%s,%s\n", $k, join("-",@{$mc{$k}}) }
    }' rock.txt 

Ou

$ perl -e 'while(<>) {s/#.*//;next if (m/^\s*$/);chomp;($v,$k)=split ",";push @{$mc{$k}}, $v};
    foreach $k (keys %mc) {printf "%s,%s\n",$k,join("-",@{$mc{$k}})}' rock.txt

Observe que um hash ou matriz associativa é inerentemente desordenada , se você quiser uma saída classificada, canalize para sort ou use (sort keys %f) nas linhas foreach my $k acima.

    
por 31.07.2016 / 08:31
2

Com base na resposta do sjsam , mas mais simples:

awk -v FS="," '
    {
            f2[$2] = f2[$2] "-" $1   # append "-" and $1 to the current value
    }
    END {   # This code will be processed at the end
            for (i in f2) {  # for all the indexes i in f2
                             # (i.e., each unique value from column 2)
                             # print one line in the desired format
                printf "%s,%s\n", i, gensub("-", "", 1, f2[i])
            }
    }
'

Isso simplemente constrói strings que se parecem com isso:

   -       pw1jc5ssyt6hx618      -       pht92h4adr3mrbz3      -       alnac47rt8d4ege3
(hyphen)       (value)        (hyphen)       (value)        (hyphen)       (value)

(mas sem os espaços; eles são apenas para iluminação). Então, quando chega a hora de imprimir os dados, ele remove o primeiro - usando a função de substituição geral, gensub() . Infelizmente, isso não é suportado na especificação POSIX para awk ; requer o GNU Awk. Como alternativa, você pode alterar gensub("-", "", 1, f2[i]) para substr(f2[i], 2) e obter a substring de f2[i] do segundo personagem até o final (ou seja, todos, exceto o primeiro caractere, que é - ) de uma maneira compatível com POSIX.

Isto irá (tentar) processar cada linha na entrada. Se houver algumas linhas na entrada que devem ser ignoradas (por exemplo, linhas em branco), você pode alterar a primeira parte para

    /./ {
            f2[$2] = f2[$2] "-" $1   # …
    }

ou

    NF==2 {
            f2[$2] = f2[$2] "-" $1   # …
    }

Quando executo a resposta de sjsam ou a minha, obtenho

254333,ysezaratlycpuggl-gqjp2rsjmk1a2v9c-twdzyi2ddbnrfknd-tpqorqbyrg1nmm7s
254341,gcmj7krrx5x6nf8r
254343,pw1jc5ssyt6hx618-pht92h4adr3mrbz3-alnac47rt8d4ege3
254344,hguvgstqxu3gowfg

, isto é, classificado pelo valor da chave column2. Você mostra a saída desejada na ordem da primeira aparição da chave column2 na entrada. Se isso é importante para você, tente:

awk -v FS="," '
    {
            if (! ($2 in f2)) appearance[++x] = $2
            f2[$2] = f2[$2] "-" $1   # append "-" and $1 to the current value
    }
    END {   # This code will be processed at the end
            for (ix in appearance) {
                             # for all the indexes i in f2
                             # (i.e., each unique value from column 2)
                             # print one line in the desired format
                i = appearance[ix]
                printf "%s,%s\n", i, gensub("-", "", 1, f2[i])
            }
    }
'

que usa a matriz appearance para acompanhar a ordem de aparição.

    
por 31.07.2016 / 22:04
1

Com matrizes bidimensionais encontradas no% GNUawk

awk -F, '{
  a[$2][$1]
  }
  END{
    for (i in a) {
      c=0; printf "%s,", i; 
        for (j in a[i]) {
          ++c; printf "%s%s", j, length(a[i]) == c? "\n": "-"
          }
     }
   }' file
    
por 31.07.2016 / 15:01

Tags