Como agrupar um monte de linhas com base nas informações da primeira linha?

0

Eu tenho um arquivo de dados parecido com:

1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 3 3 3 3 3  . . .
1 0 4 4 3 1 2 0 0 0 3 1 1 2 1 1 1 1 1 1 0 1 1 3  . . .
0 0 0 0 0 0 0 3 3 1 1 2 3 2 1 2 2 3 1 2 3 1 2 2  . . .          
.
.
.

Primeiro quero inserir espaço entre cada 5 valores idênticos, mantendo cada 5 números idênticos juntos em uma coluna, olhando para a primeira linha e, em seguida, não quero espaço anz entre esses caracteres de grupo:  primeiro passo:

1 1 1 1 1  1 1 1 1 1  1  2 2 2 2 2  2 2 2  3 3 3 3 3  . . .
1 0 4 4 3  1 2 0 0 0  3  1 1 2 1 1  1 1 1  1 0 1 1 3  . . .
0 0 0 0 0  0 0 3 3 1  1  2 3 2 1 2  2 3 1  2 3 1 2 2  . . .          
.
.
.

secouns step (output):

11111  11111  1  22222  222  33333  . . .
10443  12000  3  11211  111  10113  . . .
00000  00331  1  23212  231  23122  . . .          
.
.
.

Enquanto isso, em meus dados reais, que são enormes, posso querer experimentar diferentes tamanhos de grupo. Então eu preciso do script para ser flexível .. alguma sugestão, por favor?

    
por zara 30.01.2016 / 17:14

3 respostas

2

Outra variante com awk

awk '
    NR==1{
        for(i=2;i<=NF;i++){
            count++
            if($(i-1)!=$i || count>4){
                D[i]=1
                count=0
            }
        }
     }
     {
        for(i in D)
            $i=" "$i
            print
     }
     ' OFS="" data.file >new.file

E sed

sed -re '
    s/ +//g;s/^/\n/
    ' -f <(
        sed -r '
            s/(. )*/s_\n(&)_\n/g
            s/\S /./g
            s/\n\s*/\1 \n_\n/g
            s/\n[^\n]*\n$/ \n__/
            1q
        ' data.file
        ) -e '
    s/\S{5}/& /g
    ' data.file >new.file
    
por 30.01.2016 / 19:44
3

Aqui está um script awk. Você acabou de alterar o número 5 para ter outros agrupamentos.

awk '
NR==1{
 previous = $1
 for(i = 1;i<=NF+1;i++)
  if($i!=previous){
    col[++numcol] = i
    previous = $i
  }
}
{ j = 1; start = 1
  for(i = 1;i<NF;i++){
   printf "%s",$i
   if(i==col[j]-1){printf "  "; start = col[j++]}
   else if((i-start+1)%5==0)printf "  "
  }
  printf "%s\n",$NF
}'

A primeira parte apenas manipula a linha 1 e coleta na matriz col da coluna inicial para cada conjunto de mesmos números. A segunda parte imprime cada campo sem separação, a menos que na coluna 5 da coluna inicial ou no final de uma sequência.

    
por 30.01.2016 / 18:35
2

Uma maneira possível de fazer isso seria usar a função descompactar do perl, com um modelo construído a partir de a primeira linha do arquivo.

Depois de remover o espaço em branco, ele usa uma expressão regular com uma referência de repetição repetitiva para localizar as sequências contíguas mais longas de caracteres idênticos que não excedem o dado maxwidth e armazena suas posições em uma matriz. As larguras de campo são extraídas subtraindo elementos adjacentes da matriz e mapeadas em uma string de modelo adequada da forma A5A5A1A5A3A5A3 para passar para a função unpack .

#!/usr/bin/perl -l

use strict;
use warnings;

my $filename = shift or die "Usage: $0 FILENAME MAXWIDTH\n";
my $maxwidth = shift or die "Usage: $0 FILENAME MAXWIDTH\n";
open my $infile, $filename or die "Could not open $filename: $!";

my $n = $maxwidth-1;
my $template;

while( my $line = <$infile> )  { 
    $line =~ s/\s+//g;

    if ($. == 1) {
        my @ends = (0);
        while ($line =~ /(.)\g1{0,$n}/g) {
            push(@ends, pos $line);
        }
        my @fieldwidths = map $ends[$_] - $ends[$_-1], 1 .. $#ends;
        # http://stackoverflow.com/a/29821158/4440445
        $template =  join "", map { 'A' . $_ } @fieldwidths;
        # http://stackoverflow.com/a/2725663/4440445
    }

    my @fields = unpack($template, $line);
    print join " ", @fields;
}

close $infile;
    
por 31.01.2016 / 01:06

Tags