2012-06-05 17 views
5

He vuelto con otra pregunta. Tengo una lista de datos:perl comparar elementos de matrices y agrupamiento

1 L DIELTQSPE H EVQLQESDAELVKPGASVKISCKASGYTFTDHE 
2 L DIVLTQSPRVT H EVQLQQSGAELVKPGASIKDTY 
3 A ALQLTQSPSSLSAS B RITLKESGPPLVKPTCS C ELDKWAN 
4 A ALQLTQSPSSLSAS B RITLKESGPPLVKPTCS C ELDKWAG 
5 A ALQLTQSPSSLSAS B RITLKESGPPLVKPTCS C LELDKWASL 
6 L DIQMTQIPSSLSASLSIC H EVQLQQSGVEVKMSCKASGYTFTS 
7 L SYELTQPPSVSVSPGSIT H QVQLVQSAKGSGYSFS P YNKRKAFYTTKNIIG 
8 L SYELTQPPSVSVSPGRIT H EVQLVQSGAASGYSFS P NNTRKAFYATGDIIG 
9 A MPIMGSSVAVLAIL B DIVMTQSPTVTI C EVQLQQSGRGP 
10 A MPIMGSSVVLAIL B DIVMTQSPTVTI C EVQLQQSGRGP 
11 L DVVMTQTPLQ H EVKLDESVTVTSSTWPSQSITCNVAHPASSTKVDKKIE 
12 A DIVMTQSPDAQYYSTPYSFGQGTKLEIKR 

Y me gustaría comparar la 3ª elementos & & elementos 5 de cada fila, a continuación, agruparlos si tienen las mismas 3ª & & elementos 5º. Por ejemplo, con los datos anteriores, los resultados serán:

3: 3 A ALQLTQSPSSLSAS B RITLKESGPPLVKPTCS C ELDKWAN 
    4 A ALQLTQSPSSLSAS B RITLKESGPPLVKPTCS C ELDKWAG 
    5 A ALQLTQSPSSLSAS B RITLKESGPPLVKPTCS C LELDKWASL 
9: 9 A MPIMGSSVAVLAIL B DIVMTQSPTVTI C EVQLQQSGRGP 
    10 A MPIMGSSVVLAIL B DIVMTQSPTVTI C EVQLQQSGRGP 

Fyi, en los datos reales, los días 3, 5, 7 elementos son muy largos. Los hice cortar para ver el todo.

Esto es lo que he hecho, sé que es muy torpe, pero como principiante, estoy haciendo todo lo posible. Y el problema es que muestra solo el primer conjunto del "mismo" grupo. ¿Podría mostrarme dónde salió mal y/u otros métodos bonitos para resolver esto, por favor?

my $file = <>; 
open(IN, $file)|| die "no $file: $!\n"; 
my @arr; 
while (my $line=<IN>){ 
     push @arr, [split (/\s+/, $line)] ; 
} 
close IN; 

my (@temp1, @temp2,%hash1); 
for (my $i=0;$i<=$#arr ;$i++) { 
    push @temp1, [$arr[$i][2], $arr[$i][4]]; 
    for (my $j=$i+1;$j<=$#arr ;$j++) { 
     push @temp2, [$arr[$j][2], $arr[$j][4]]; 
     if (($temp1[$i][0] eq $temp2[$j][0])&& ($temp1[$i][1] eq $temp2[$j][1])) { 
      push @{$hash1{$arr[$i][0]}}, $arr[$i], $arr[$j]; 
     } 
    } 
} 
print Dumper \%hash1; 
+0

Gracias a todos. Todos tus comentarios y códigos son realmente útiles para mí. Gracias incluso por corregir mis datos 'simulados' y por considerar otros pasos. :-) – Krista

Respuesta

2

Parece que tiene demasiado complicado esto un poco más de lo que debe ser, pero eso es común para los principiantes. Piense más acerca de cómo lo haría manualmente:

  • Mire cada línea.
  • Vea si los campos tercero y quinto son los mismos que la línea anterior.
  • Si es así, imprímalas.

El bucle y todo lo que es completamente innecesario:

#!/usr/bin/env perl 

use strict; 
use warnings; 

my ($previous_row, $third, $fifth) = ('') x 3; 

while (<DATA>) { 
    my @fields = split; 
    if ($fields[2] eq $third && $fields[4] eq $fifth) { 
    print $previous_row if $previous_row; 
    print "\t$_"; 
    $previous_row = ''; 
    } else { 
    $previous_row = $fields[0] . "\t" . $_; 
    $third = $fields[2]; 
    $fifth = $fields[4]; 
    } 
} 

__DATA__ 
1 L DIELTQSPE H EVQLQESDAELVKPGASVKISCKASGYTFTDHE 
2 L DIVLTQSPRVT H EVQLQQSGAELVKPGASIKDTY 
3 A ALQLTQSPSSLSAS B RITLKESGPPLVKPTCS C ELDKWAN 
4 A ALQLTQSPSSLSAS B RITLKESGPPLVKPTCS C ELDKWAG 
5 A ALQLTQSPSSLSAS B RITLKESGPPLVKPTCS C LELDKWASL 
6 L DIQMTQIPSSLSASLSIC H EVQLQQSGVEVKMSCKASGYTFTS 
7 L SYELTQPPSVSVSPGSIT H QVQLVQSAKGSGYSFS P YNKRKAFYTTKNIIG 
8 L SYELTQPPSVSVSPGRIT H EVQLVQSGAASGYSFS P NNTRKAFYATGDIIG 
9 A MPIMGSSVAVLAIL B DIVMTQSPTVTI C EVQLQQSGRGP 
10 A MPIMGSSVAVLAIL B DIVMTQSPTVTI C EVQLQQSGRGP 
11 L DVVMTQTPLQ H EVKLDESVTVTSSTWPSQSITCNVAHPASSTKVDKKIE 
12 A DIVMTQSPDAQYYSTPYSFGQGTKLEIKR 

(Tenga en cuenta que he cambiado la línea 10 un poco para que su tercer campo coincidirá con la línea 9 con el fin de obtener los mismos grupos en la salida como especificado.)

Editar: Una línea de código se ha duplicado mediante un error de copiar/pegar.

Edición 2: En respuesta a los comentarios, he aquí una segunda versión que no asumen que las líneas que deben ser agrupadas son contiguos:

#!/usr/bin/env perl 

use strict; 
use warnings; 

my @lines; 
while (<DATA>) { 
    push @lines, [ $_, split ]; 
} 

# Sort @lines based on third and fifth fields (alphabetically), then on 
# first field/line number (numerically) when third and fifth fields match 
@lines = sort { 
    $a->[3] cmp $b->[3] || $a->[5] cmp $b->[5] || $a->[1] <=> $b->[1] 
} @lines; 

my ($previous_row, $third, $fifth) = ('') x 3; 
for (@lines) { 
    if ($_->[3] eq $third && $_->[5] eq $fifth) { 
    print $previous_row if $previous_row; 
    print "\t$_->[0]"; 
    $previous_row = ''; 
    } else { 
    $previous_row = $_->[1] . "\t" . $_->[0]; 
    $third = $_->[3]; 
    $fifth = $_->[5]; 
    } 
} 

__DATA__ 
1 L DIELTQSPE H EVQLQESDAELVKPGASVKISCKASGYTFTDHE 
3 A ALQLTQSPSSLSAS B RITLKESGPPLVKPTCS C ELDKWAN 
2 L DIVLTQSPRVT H EVQLQQSGAELVKPGASIKDTY 
5 A ALQLTQSPSSLSAS B RITLKESGPPLVKPTCS C LELDKWASL 
7 L SYELTQPPSVSVSPGSIT H QVQLVQSAKGSGYSFS P YNKRKAFYTTKNIIG 
6 L DIQMTQIPSSLSASLSIC H EVQLQQSGVEVKMSCKASGYTFTS 
9 A MPIMGSSVAVLAIL B DIVMTQSPTVTI C EVQLQQSGRGP 
8 L SYELTQPPSVSVSPGRIT H EVQLVQSGAASGYSFS P NNTRKAFYATGDIIG 
11 L DVVMTQTPLQ H EVKLDESVTVTSSTWPSQSITCNVAHPASSTKVDKKIE 
10 A MPIMGSSVAVLAIL B DIVMTQSPTVTI C EVQLQQSGRGP 
12 A DIVMTQSPDAQYYSTPYSFGQGTKLEIKR 
4 A ALQLTQSPSSLSAS B RITLKESGPPLVKPTCS C ELDKWAG 
+0

+1. Buena, simple respuesta. Pregunta, sin embargo: ¿su código no supone que las líneas a agrupar deben aparecer en secuencia? Si es así, entonces esta puede ser una buena suposición, pero la pregunta parece valer la pena. – thb

+1

Suponiendo que las líneas siempre se agrupan en la entrada como se desea, esta es una buena manera de hacerlo. – Qtax

+0

@thb: Sí, hace esa suposición. Si el OP responde que no son contiguos en la entrada, corregiré el código para incluir su clasificación. (En realidad, primero pensé que se trataba de una cuestión de clasificación hasta que miré más de cerca la salida de la muestra.) –

0

Su enfoque muestra una comprensión bastante sólida del lenguaje Perl y tiene mérito, pero todavía no es así como lo haría.

creo que va a tener un tiempo más fácil con esto si usted estructura sus datos de forma ligeramente diferente: Vamos %hash1 ser algo así como

(
    'ALQLTQSPSSLSAS' => { 
     'RITLKESGPPLVKPTCS' => [3, 4, 5], 
     'ABCXYZ' => [93, 95, 96], 
    }, 
    'MPIMGSSVAVLAIL' => { 
     'DIVMTQSPTVTI' => [9, 10], 
    }, 
) 

donde he añadido un dato ABCXYZ que no está en su ejemplo para mostrar la estructura de datos en su plenitud.

1

Ejemplo:

use strict; 
use warnings; 

{ ... } 

open my $fh, '<', $file or die "can't open $file: $!"; 

my %hash; 

# read and save it 
while(my $line = <$fh>){ 
    my @line = split /\s+/, $line; 
    my $key = $line[2] . ' ' . $line[4]; 

    $hash{$key} ||= []; 
    push @{$hash{$key}}, $line; 
} 

# remove single elements 
for my $key (keys %hash){ 
    delete $hash{$key} if @{$hash{$key}} < 2; 
} 

print Dumper \%hash; 
+0

+1. No es tan clásico como mi respuesta, pero ¿desde cuándo se hace Perl clásicamente? Esto debería funcionar. Me gusta. – thb

1

enfoque ligeramente diferente:

#!/usr/bin/perl 

use strict; 
use warnings; 

my %lines; # hash with 3rd and 5th elements as key 
my %first_line_per_group; # stores in which line a group appeared first 

while(my $line = <>) { 
    # remove line break 
    chomp $line; 

    # retrieve elements form line 
    my @elements = split /\s+/, $line; 

    # ignore invalid lines 
    next if @elements < 5; 

    # build key from elements 3 and 5 (array 0-based!) 
    my $key = $elements[2] . " " . $elements[4]; 

    if(! $lines{key}) { 
     $first_line_per_group{$key} = $elements[0]; 
    } 

    push @{ $lines{$key} }, $line; 
} 


# output 
for my $key (keys %lines) { 
    print $first_line_per_group{$key} . ":\n"; 

    print " $_\n" for @{ $lines{$key} }; 
} 
+0

+1. Ver sin embargo mi comentario a @Qtax. – thb

0

Debe utilizar la forma de 3 argumentos de open() y puede simplificar la lectura en los datos:

open my $fh, '<', $file 
    or die "Cannot open '$file': $!\n"; 

chomp(my @rows = <$fh>); 
@rows = map {[split]} @rows; 

close $fh; 

Para agrupar las filas, puede usar un hash con los campos 3 y 5 concatenados como claves. Editar: debe agregar un carácter de separación para eliminar los resultados no válidos "si las diferentes líneas producen la misma concatenación" (Qtax). Los datos adicionales, por ejemplo, el número de las filas de datos individuales, se pueden almacenar como el valor hash. Aquí, los campos de la fila se almacenan:

my %groups; 
for (@rows) { 
    push @{ $groups{$_->[2] . ' ' . $_->[4]} }, $_ 
     if @$_ >= 4; 
} 

elementos individuales Clasificar:

@{ $groups{$_} } < 2 && delete $groups{$_} 
    for keys %groups; 

saluda, Matthias

+0

Tenga en cuenta que con solo '$ _-> [2]. $ _-> [4] 'como la clave puede dar resultados inválidos si las diferentes líneas producen la misma concatenación de esos valores. – Qtax

+0

¡Ah! No pensé en eso. Entonces debes insertar un caracter de separación (como en la respuesta de halo). – Matthias

Cuestiones relacionadas