Comparando entre linhas em Perl

1

Eu tenho um file :

Name v1 v2 
Type1 ABC 32
Type1 DEF 44
Type1 XXX 45
Type2 ABC 78 
Type2 XXX 23 
Type3 DEF 22 
Type3 XXX 12 
Type4 ABC 55 
Type4 DEF 78 
Type5 ABC 99 
Type6 DEF 00

Estou tentando imprimir apenas partes deste arquivo com certas condições:

  • Para um determinado nome, digamos Type1 , se houver XXX na coluna v1 , quero ignorar a impressão de todas as ocorrências de Type1 nesse arquivo.
  • Para um determinado nome, diga Type4 , se houver ABC e DEF na coluna v1 , desejo imprimir apenas essa linha que tenha um valor numérico menor para v2 .
  • Para um determinado nome, diga Type5 ou Type6 , que tem apenas ABC ou DEF , desejo imprimi-los.

Como faço para isso? Eu posso ler o arquivo em uma matriz, mas não sei como pesquisar em várias linhas para uma determinada coluna.

    
por Ganesh 20.10.2015 / 08:56

3 respostas

1

A ferramenta que você precisa para isso são os hashes - que são o modo de armazenar os pares de valores-chave do perl. Especificamente - precisamos pré-processar seus dados em um hash, para que possamos "procurar" lugares onde os valores mais baixos ou XXX estejam presentes.

Felizmente - sua terceira condição parece um subconjunto do seu segundo - se você está apenas imprimindo o valor mais baixo, o menor valor quando há apenas um, é o mesmo.

Então eu provavelmente faria algo assim:

#!/usr/bin/env perl
use strict;
use warnings;
use Data::Dumper;

#read header line, because we don't want to process it; 
#note - diamond operators are 'magic' file handles. 
#they read either piped input on STDIN, or 
#open/read files specified on command line. 
#this is almost exactly like how sed/grep work. 
my $header_line = <>;
#turn the rest of our intput into an array of arrays, split on whitespace/linefeeds. 
my @lines = map { [split] } <>;

#print for diag
print Dumper \@lines;

#this hash tracks if we've 'seen' an XXX
my %skip_type;
#this hash tracks the lowest V2 value. 
my %lowest_v2_for;
foreach my $record (@lines) {
    #we could work with $record ->[0], etc.
    #this is because I think it's more readable this way. 
    my ( $type, $v1, $v2 ) = @$record;

    #find all the lines with "XXX" - store in a hash.
    if ( $v1 eq "XXX" ) {
        $skip_type{$type}++;
    }

    #check if this v2 is the lowest for this particular type. 
    #make a note if it is. 
    if ( not defined $lowest_v2_for{$type}
        or $lowest_v2_for{$type} > $v2 )
    {
        $lowest_v2_for{$type} = $v2;
    }
}

#print for diag - things we are skipping. 
print Dumper \%skip_type;


print $header_line;

#run through our list again, testing the various conditions:
foreach my $record (@lines) {
    my ( $type, $v1, $v2 ) = @$record;

    #skip if it's got an XXX. 
    next if $skip_type{$type};
    #skip if it isn't the lowest value
    next if $lowest_v2_for{$type} < $v2;
    #print otherwise.
    print join( " ", @$record ), "\n";
}

Isto dá (menos alguma saída de diagnóstico de Dumper que pode ser descartada livremente se você não quiser):

Name v1 v2 
Type4 ABC 55
Type5 ABC 99
Type6 DEF 00
    
por 20.10.2015 / 11:08
0

Minha opinião:

perl -wE ' 
    # read the data 
    chomp( my $header = <> ); 
    my %data; 
    while (<>) { 
        chomp; 
        my @F = split; 
        $data{$F[0]}{$F[1]} = $F[2]; 
    } 

    # requirement 1 
    delete $data{Type1} if exists $data{Type1}{XXX}; 

    # requirement 2 
    if (exists $data{Type4}{ABC} and exists $data{Type4}{DEF}) { 
        if ($data{Type4}{ABC} <= $data{Type4}{DEF}) { 
            delete $data{Type4}{DEF}; 
        } 
        else { 
            delete $data{Type4}{ABC}; 
        } 
    } 

    # requirement 3 
    for my $name (qw/Type5 Type6/) { 
        delete $data{$name} unless ( 
            scalar keys %{$data{$name}} == 1 
            and (exists $data{$name}{ABC} or exists $data{$name}{DEF}) 
        ); 
    } 

    $, = " "; 
    say $header; 
    for my $name (sort keys %data) { 
        for my $v1 (sort keys %{$data{$name}}) { 
            say $name, $v1, $data{$name}{$v1}; 
        } 
    } 
' file 

saídas

Name v1 v2 
Type2 ABC 78
Type2 XXX 23
Type3 DEF 22
Type3 XXX 12
Type4 ABC 55
Type5 ABC 99
Type6 DEF 00

Não há requisitos sobre Type2 e Type3

    
por 21.10.2015 / 16:57
-1

Existem três tarefas diferentes. Tudo pode ser feito por awk :

  1. Ignorar impressão após XXX

    $1 == "Type1" {if($2 == "XXX")f=1;if(! f)print}

  2. Valor mínimo para Type4

    $1 == "Type4" {if(min > $3 || ! min)min = $3} END{print min}

  3. Linhas de escolha para impressão

    $1$2 ~ "^(Type5|Type6)(ABC|DEF)$"

por 20.10.2015 / 10:22