Como combinar as informações de cada par de linhas em uma linha?

4

Eu tenho dados como este (os dados reais têm mais de 50.000 dígitos e 8000 linhas):

entrada:

1 11122
1 21121
2 22221
2 11122
3 21121
3 11122

Eu quero colocar o valor de cada segunda linha ao lado do valor da primeira linha com o mesmo nome. Além disso, deve haver dois espaços como deliminador entre cada par de valores e deve haver uma guia como delimitador entre diferentes pares de valores. A saída deve se parecer com:

saída:

1   1  2    1  1    1  1    2  2    2  1
2   2  1    2  1    2  1    2  2    1  2
3   2  1    1  1    1  1    2  2    1  2

alguma sugestão?

    
por zara 05.08.2016 / 19:34

2 respostas

5

Eu usaria o perl e o executaria como oneliner assim:

perl -wne 'sub parseline { ($id,$v) = split; return split //,$v };
    @a = parseline();
    print "$id\t";
    $_ = <>;
    @b = parseline();
    for ($i=0; $i<@a; $i++) {
      print "$a[$i]  $b[$i]\t"
    };
    print "\n"' < input  > output

Explicação:

  • perl -wne executa o resto do comando para cada linha de entrada
  • sub parseline { .... } analisará a entrada e definirá o primeiro número na linha como $id e retornará o restante como matriz de caracteres.
  • @a=parseline() armazenará os caracteres de primeira linha na matriz @a
  • em seguida, imprimimos $id , seguido por TAB ( \t )
  • $_=<>; @b=parseline(); lerá a linha seguinte (par) e colocará os dados no array @b
  • for ($i=0; $i<@a; $i++) { print "$a[$i] $b[$i]\t" } para cada elemento da matriz @a , imprimiremos esse elemento, dois espaços, elemento correspondente da matriz @b e, em seguida, a guia
  • print "\n" imprimirá nova linha no final
  • devido ao parâmetro -n para perl no início, todo o processo será reiniciado com a linha 3, depois com 5, depois com 7, etc.
  • < input > output indica a partir de qual arquivo lemos nossa entrada e em qual arquivo gravamos a saída.

Observação: o código imprimirá uma guia extra no final de cada linha. A remoção é deixada como um exercício para o leitor evitar tarefas de tarefas de crowdsourcing e manter o código pouco mais simples. Além disso, o código assume que as linhas a serem pareadas são sempre duas e uma após a outra (conforme fornecido no exemplo)

À medida que processa o arquivo de entrada linha por linha, ele pode ser facilmente dimensionado linearmente para muitos milhares de linhas ...

    
por 05.08.2016 / 20:41
2

Aqui está uma versão do script do Matija Nalis que usa a função pairwise do módulo List:MoreUtils para unir os dois arrays, e também não requer que as linhas com IDs correspondentes (primeiro campo) estejam em linhas consecutivas. ou seja, eles podem ser separados por qualquer número de linhas.

#! /usr/bin/perl

use strict;
use warnings;
use List::MoreUtils qw(pairwise);

sub parseline { my ($id,$v) = split; return $id, split //,$v };

my %ID=();

while (<>) {
    my ($id, @line) = parseline();

    if ( !defined($ID{$id}) ) {
      push @{ $ID{$id} }, @line ;
    } else {
      my @paired = pairwise { "$a  $b" } @{ $ID{$id} }, @line;
      print join("\t", $id, @paired), "\n";
      delete $ID{$id};
    };
};

Em vez de usar uma variável global para $id , a sub-rotina parseline do MN foi modificada para retornar o ID e uma matriz de elementos em cada linha.

$id é usado como chave para um hash %ID para armazenar cada linha analisada. A primeira vez que vemos um dado $id , apenas armazenamos o array de linhas analisadas ( @line ) no hash e passamos para a próxima linha. Na próxima vez que a virmos, juntaremos a matriz armazenada com a matriz @line atual, imprimiremos com separadores de campo TAB e, em seguida, excluiremos $id da %ID hash.

Veja man List::MoreUtils para detalhes sobre como funciona a função pairwise . BTW, List::Util e List::MoreUtils são dois excelentes módulos para fazer todos os tipos de manipulações de lista (aka array).

Saída:

$ ./zara.pl zara.txt  
1   1  2    1  1    1  1    2  2    2  1
2   2  1    2  1    2  1    2  2    1  2
3   2  1    1  1    1  1    2  2    1  2
    
por 06.08.2016 / 06:13