2012-06-05 10 views
5

Wróciłem z innym pytaniem. Mam listę danych:perl porównaj elementy tablic i grupowania

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 

I chciałbym porównać 3rd elementy & & 5th elementy każdego wiersza, a następnie pogrupować je, jeśli mają te same 3rd & & 5th elementy. Na przykład, z powyższych danych, wyniki będą:

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, w rzeczywistych danych, 3., 5., 7. Elementy są bardzo długie. Zmusiłam je, by zobaczyły całość.

Oto, co zrobiłem, wiem, że jest bardzo niezgrabny, ale jako początkujący robię, co w mojej mocy. Problem polega na tym, że pokazuje tylko pierwszy zestaw "tej samej" grupy. Czy możesz mi pokazać, gdzie poszło nie tak i/lub inne ładne metody rozwiązania tego problemu?

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

Dziękuję wszystkim. Wszystkie komentarze i kody są dla mnie bardzo pomocne. Dziękuję za nawet poprawienie moich "pozorowanych" danych i rozważenie dalszych kroków. :-) – Krista

Odpowiedz

2

Wygląda na to jest zbyt skomplikowana to nieco więcej niż musi być, ale to typowe dla początkujących. Zastanów się, jak to zrobić ręcznie:

  • Spójrz na każdą linię.
  • Sprawdź, czy trzecie i piąte pole są takie same jak poprzednia linia.
  • Jeśli tak, wydrukuj je.

zapętlenie i wszystko, co jest zupełnie niepotrzebne:

#!/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 

(Zauważ, że zmieniła linię 10 nieznacznie, tak że jego Trzecie pole będzie pasował do linii 9 w celu uzyskania tych samych grup w wyjściu jako określony.)

Edytuj: Jedna linia kodu została zduplikowana przez błąd kopiowania/wklejania.

Edit 2: W odpowiedzi na komentarze, oto druga wersja, która nie zakłada, że ​​linie, które powinny być zgrupowane są przyległe:

#!/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. Dobra, prosta odpowiedź. Pytanie: czy twój kod nie zakłada, że ​​linie, które mają być grupowane, muszą pojawiać się kolejno? Jeśli tak, to może to być dobre założenie, ale pytanie wydaje się warte pytania. – thb

+1

Zakładając, że linie są zawsze pogrupowane w dane wejściowe zgodnie z potrzebami, jest to dobry sposób na zrobienie tego. – Qtax

+0

@thb: Tak, robi to założenie. Jeśli PO odpowie, że nie sąsiaduje z danymi wejściowymi, zmienię kod, aby uwzględnić jego sortowanie. (Właściwie to najpierw pomyślałem, że to było pytanie do sortowania, dopóki nie przyjrzałem się dokładniej przykładowemu wynikowi.) –

0

Twoje podejście pokazuje całkiem solidny chwyt języka Perla i ma wartość, ale nadal nie jest tak, jakbym to zrobił.

myślę, że trzeba będzie łatwiej z tego, jeśli struktura danych nieco inaczej: Niech %hash1 być coś

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

gdzie Dodałem punkt odniesienia ABCXYZ, która nie jest w swoim przykładzie pokazać struktura danych w jej pełni.

1

Przykład:

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. Nie jest to tak klasycznie zrobione jak moja odpowiedź, ale od kiedy klasycznie robi się Perl? To powinno działać. Lubię to. – thb

1

Nieco inne podejście:

#!/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. Zobacz jednak mój komentarz do @ Qtax. – thb

0

Powinieneś używać 3-argumentowej formy open() i możesz uprościć czytanie w danych:

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

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

close $fh; 

Aby pogrupować wiersze, można użyć skrótu z polami 3 i 5 połączonymi jako klucze. Edytuj: Musisz dodać znak separacji, aby wyeliminować nieprawidłowe wyniki "jeśli różne linie wytwarzają to samo połączenie" (Qtax). Dodatkowe dane, na przykład liczba poszczególnych wierszy danych, mogą być przechowywane jako wartość mieszania. Tutaj, pola rzędu są przechowywane:

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

Sortuj się pojedyncze elementy:

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

wita, Matthias

+0

Zauważ, że używając tylko '$ _-> [2]. $ _-> [4] 'ponieważ klucz może dawać nieprawidłowe wyniki, jeśli różne linie dają takie samo połączenie tych wartości. – Qtax

+0

Ah! Nie myślałem o tym. Musisz więc wstawić znak separacji (jak w odpowiedzi halo). – Matthias