awk '{
if(NR!=1){a[$1]=$2";"a[$1]}
else print $0}
END{
n = asorti(a, b);
for (n in b) {
print b[n],a[b[n]]
}
}'
Eu ainda estou aprendendo programação e eu tentei muitas coisas, mas não consegui o formato correto. Eu tenho um arquivo delimitado por guias com 17 colunas e muitas (cerca de 50.000) linhas. O arquivo já está classificado pela primeira coluna. Eu quero mesclar linhas que têm a mesma primeira coluna (A), mas todas as outras 16 colunas são diferentes e eu quero manter todas as informações em uma linha, de preferência na mesma coluna com ponto e vírgula; como um delimitador entre eles. Eu quero manter a guia como um delimitador no arquivo de saída. Muito obrigado pelas respostas e se você também pudesse explicar a resposta onde eu errei, seria ainda melhor:).
Eu já tentei até agora:
awk -F'\t' 'NF>1{a[$1] = a[$1]";"$2}END{for(i in a){print i""a[i]}}' filename.txt
perl -F',' -anle 'next if /^$/;$h{$F[0]} = $h{$F[0]}.", ".$F[1];
END{print $_,$h{$_},"\n" for sort keys %h}' filename.txt
FORMATO DE ARQUIVO (outras 15 colunas têm o mesmo formato da coluna B)
A B C
123 fvv ggg
123 kjf ggg
123 ccd att
567 abc gst
567 abc hgt
879 ttt tyt
A saída que eu quero (eu preciso de todas as 17 colunas e para as colunas 2-16 eu preciso da mesma saída que na coluna B e C). Todos os casos de B devem estar em B e todos os casos de C devem estar em C e todos os casos de D devem estar em D etc. Assim, a saída tem 17 colunas exatamente como a entrada e, em vez de 50.000 linhas, deve ter cerca de 20.000 , porque há muitas repetições para a coluna 1 (para este arquivo específico):
A B C
123 fvv;kjf;ccd ggg;ggg;att
567 abc;abc gst;hgt
879 ttt lll
awk '{
if(NR!=1){a[$1]=$2";"a[$1]}
else print $0}
END{
n = asorti(a, b);
for (n in b) {
print b[n],a[b[n]]
}
}'
Uma solução perl:
$ perl -F"\t" -anle 'if($.==1){print; next} push @{$k{$F[0]}},@F[1..$#F];
END{print "$_\t" . join(";",@{$k{$_}}) for sort keys(%k)}' file
A B
123 fvv;kjf;ccd
567 abc;abc
879 ttt
Isso pode funcionar em um número arbitrário de campos. No entanto, é necessário carregar algumas coisas na memória e isso pode ser um problema se o arquivo for grande.
Quanto a onde você errou, não podemos dizer a menos que você explique o que realmente aconteceu, mas, acima de tudo, sua tentativa de perl falhará porque:
-F,
, que define o separador de campos para uma vírgula quando sua entrada possui guias. -l
e print "foo\n"
. O -l
já adiciona uma nova linha a cada chamada impressa, portanto você terá várias linhas em branco. $h{$F[0]}.", ".$F[1];
para anexar, então, a primeira vez que é executado e $h{$F[0]}
não está definido, você adicionará um ,
extra no início de seu valor armazenado. Da mesma forma, seu awk
falhará porque:
foo""bar
, que concatenará a saída sem espaço entre cada campo. Você quer print foo,bar
e também deseja OFS="\t"
para saída separada por tabulações. desculpas por este one-liner, mas aqui vai -
awk 'BEGIN{FS="\t"} {for(i=2; i<=NF; i++) { if (!a[$1]) a[$1]=$1FS$i ;else a[$1]=a[$1]";"$i};if ($1 != old) b[j++] = a[old];old=$1 } END{for (i=0; i<j; i++) print b[i] }' 1
123 fvv ;kjf;ccd
567 abc;abc
879 ttt
Perl pode fazer isso com facilidade, usando um hash:
#!/usr/bin/env perl
use strict;
use warnings;
my %stuff;
my @header = split ' ', <>;
#read in the data to "stuff"
while ( <> ) {
my ( $key, $value ) = split;
push ( @{$stuff{$key}}, $value );
}
print join ("\t", @header ),"\n";
foreach my $key ( sort keys %stuff ) {
print $key, "\t", join ";", @{$stuff{$key}},"\n";
}
Saída:
A B
123 fvv;kjf;ccd;
567 abc;abc;
879 ttt;
Onde você errou? Honestamente, eu sugeriria tentar compactar tudo em um único forro. Isso é - na minha opinião - uma prática muito ruim. Na melhor das hipóteses, promove códigos inescrutáveis que são difíceis de seguir.
O acima pode ser condensado, mas realmente vale a pena fazer isso em primeiro lugar.
Para suportar várias colunas, você começa a causar um pequeno incômodo com as larguras das colunas.
Isso funciona, mas produz resultados em que o recuo não está alinhado corretamente:
#!/usr/bin/env perl
use strict;
use warnings;
my %stuff;
my ( $id, @header ) = split ' ', <>;
while ( <> ) {
my ( $key, @values ) = split;
my %row;
@row{@header} = @values;
push ( @{$stuff{$key}{$_}}, $row{$_} ) for keys %row;
}
print join ( "\t", $id, @header),"\n";
foreach my $key ( sort keys %stuff ) {
print join ("\t", $key, map { join ";", @{$stuff{$key}{$_}}} @header), "\n";
}
Resultado de:
A B C
123 fvv;kjf;ccd ggg;ggg;att
567 abc;abc gst;hgt
879 ttt tyt
Se a separação de separadores não for adequada às suas necessidades, pode utilizar sprintf
para efetuar a formatação:
my $format = '%12s';
print map { sprintf($format, $_) } ( $id, @header),"\n";
foreach my $key ( sort keys %stuff ) {
print map { sprintf($format, $_) } ( $key, map { join ";", @{$stuff{$key}{$_}}} @header),"\n";
}
Fazemos algum uso de map
aqui, o que eu aprecio não é exatamente uma coisa óbvia.
O que ele faz é pegar uma lista e aplicar uma transformação a cada elemento. Então - no exemplo acima:
print join ("\y", map { join ";", @$_ } ([1,2,3],[4,5,6],[7,8,9]) )
gerará:
1;2;3 4;5;6 7;8;9
A operação map
está dizendo 'junte cada subarray'; e, em seguida, retornar isso como uma lista ... que podemos então juntar a uma aba. Isso é basicamente o que o acima está fazendo.
awk '
function p(n,A){
s = n
for(i=2;i<=NF;i++){
s = s "\t" A[i]
A[i] = $i
}
if(n)
print s
}
NR==1{
print
next
}
$1==n{
for(i=2;i<=NR;i++)
A[i] = A[i] ";" $i
next
}
{
p(n,A)
n = $1
}
END{
p(n,A)
}
' file
Eu quero mesclar linhas que têm a mesma primeira coluna (A), mas todas as outras 16 colunas são diferentes e eu quero manter todas as informações em uma linha
Você recebeu algumas respostas, mas quero salientar que o que você descreveu nessas 30 palavras é conhecido no SQL como join . Se você importar suas 50.000 linhas em duas tabelas no SQLite, você obtém o efeito desejado com
select * from R join S on r1 = s1
onde r1 e s1 são nomes de coluna que você determina.
Existem muitas vantagens em usar SQL para algo assim, especialmente quando os critérios de junção e seleção se tornam mais complexos. É uma das razões pelas quais o SQL foi inventado.
Tags text-processing perl awk