2010-04-06 17 views
5

Estoy tratando de realizar algunos filtros basados ​​en la composición en una gran colección de cadenas (secuencias de proteínas).
Escribí un grupo de tres subrutinas para encargarme de ello, pero estoy teniendo problemas de dos maneras: una menor, una mayor. El problema menor es que cuando uso List::MoreUtils 'pairwise' recibo advertencias sobre el uso de $a y $b solo una vez y no se inicializan. Pero creo que estoy llamando a este método correctamente (basado en la entrada de CPAN y algunos ejemplos de la web).
El principal problema es un error "Can't use string ("17/32") as HASH ref while "strict refs" in use..."¿Por qué esta sentencia se trata como una cadena en lugar de su resultado?

Parece que esto sólo puede ocurrir si el bucle foreach en &comp está dando los valores de hash como una cadena en lugar de evaluar la operación de división. Estoy seguro de haber cometido un error de novato, pero no puedo encontrar la respuesta en la web. La primera vez que miré el código perl fue el miércoles pasado ...

use List::Util; 
use List::MoreUtils; 
my @alphabet = (
'A', 'R', 'N', 'D', 'C', 'Q', 'E', 'G', 'H', 'I', 
'L', 'K', 'M', 'F', 'P', 'S', 'T', 'W', 'Y', 'V' 
); 
my $gapchr = '-'; 
# Takes a sequence and returns letter => occurrence count pairs as hash. 
sub getcounts { 
my %counts =(); 
foreach my $chr (@alphabet) { 
    $counts{$chr} = ($_[0] =~ tr/$chr/$chr/); 
} 
$counts{'gap'} = ($_[0] =~ tr/$gapchr/$gapchr/); 
return %counts; 
} 

# Takes a sequence and returns letter => fractional composition pairs as a hash. 
sub comp { 
my %comp = getcounts($_[0]); 
foreach my $chr (@alphabet) { 
    $comp{$chr} = $comp{$chr}/(length($_[0]) - $comp{'gap'}); 
} 
return %comp; 
} 

# Takes two sequences and returns a measure of the composition difference between them, as a scalar. 
# Originally all on one line but it was unreadable. 

sub dcomp { 
my @dcomp = pairwise { $a - $b } @{ values(%{ comp($_[0]) }) }, @{ values(%{ comp($_[1]) }) }; 
@dcomp = apply { $_ ** 2 } @dcomp; 
my $dcomp = sqrt(sum(0, @dcomp))/20; 
return $dcomp; 
} 

¡Muchísimas gracias por cualquier respuesta o consejo!

+0

Vea también http://stackoverflow.com/questions/2261871/whats-the-point-of-use-vars-in-this-perl-subroutine/2261957#2261957 para obtener más explicaciones de por qué/cómo estas cosas sucede. +1 – DVK

+0

¿En qué línea está ese error de ref hash? – DVK

+0

Para el problema menor, vea http://stackoverflow.com/questions/1490505/how-do-i-prevent-listmoreutils-from-warning-about-using-a-and-b-only-once. – FMc

Respuesta

2

%{ $foo } tratará $foo como referencia de hash y lo desreferenciará; del mismo modo, @{} desreferencia referencias de matriz. Como comp devuelve un hash como una lista (los hashes se convierten en listas cuando se pasan a las funciones y desde ellas) y no una referencia de hash, el %{} es incorrecto. Podría dejar el %{}, pero values es un formulario especial y necesita un hash, no un hash aprobado como una lista. Para pasar el resultado de comp a values, comp necesita devolver una referencia hash que luego se desreferencia.

Hay otro problema con su dcomp, a saber, que el orden de values (como el documentation dice) "se devuelven en un orden aparentemente aleatoria", por lo que los valores pasados ​​al bloque pairwise no son necesariamente para el mismo carácter. En lugar de values, puede usar rebanadas hash. Volvemos al comp devolviendo un hash (como una lista).

sub dcomp { 
    my %ahisto = comp($_[0]); 
    my %bhisto = comp($_[1]); 
    my @keys = uniq keys %ahisto, keys %bhisto; 
    my @dcomp = pairwise { $a - $b } , @ahisto{@keys}, @bhisto{@keys}; 
    @dcomp = apply { $_ ** 2 } @dcomp; 
    my $dcomp = sqrt(sum(0, @dcomp))/20; 
    return $dcomp; 
} 

Esto no se refiere a lo que sucede si un personaje aparece en sólo una de $_[0] y $_[1].

uniq dejó como un ejercicio para el lector.

+0

Creo que las rebanadas de hash deben estar encerradas por @ {[]}? Ahora sé sobre el peligro de los valores(), gracias. –

1

Re: Problema menor

Esto está muy bien y un problema común con (algunos) de los módulos List::Util y List::MoreUtils.

Una forma de eliminar las advertencias es simplemente declarar los special variables con antelación de esta manera:

our ($a, $b); 

Otra sería preceder a la pairwise con:

no warnings 'once'; 

Ver perlvar para obtener más información sobre $ a y $ b

/I3az/

+0

¿Haría esa declaración en el subbloque o en el, uh, espacio de nombres global (no soy tan bueno con los términos correctos)? Me parece como el primero si uso el método una vez y el último si lo uso en muchos lugares? –

+0

Poner 'our ($ a, $ b)' en la parte superior del programa está bien en esta instancia. $ a & $ b son especiales y, por lo tanto, * no deben * usarse para nada más ordenar y módulos bar como List :: Util. El problema es que 'advertencias una vez 'no está tomando $ a & $ b variables especiales en cuenta a excepción de' sort' – draegtun

4

Hay algunos errores en su código. En primer lugar, tenga en cuenta de perldoc perlop:

Debido a que la tabla de transliteración se construye en tiempo de compilación, ni el SEARCHLIST ni el REPLACEMENTLIST están sometidos a la interpolación comillas dobles.

Así que su método de recuento es incorrecto. También creo que estás haciendo un uso indebido de pairwise. Es difícil evaluar qué constituye el uso correcto porque no da ejemplos de qué salida debe obtener con algunas entradas simples.

En cualquier caso, me gustaría volver a escribir este guión como (hay algunas instrucciones de depuración espolvoreado en):

#!/usr/bin/perl 

use List::AllUtils qw(sum); 
use YAML; 

our ($a, $b); 
my @alphabet = ('A' .. 'Z'); 
my $gap = '-'; 

my $seq1 = 'ABCD-EFGH--MNOP'; 
my $seq2 = 'EFGH-ZZZH-KLMN'; 

print composition_difference($seq1, $seq2); 

sub getcounts { 
    my ($seq) = @_; 
    my %counts; 
    my $pattern = join '|', @alphabet, $gap; 
    $counts{$1} ++ while $seq =~ /($pattern)/g; 
    warn Dump \%counts; 
    return \%counts; 
} 

sub fractional_composition_pairs { 
    my ($seq) = @_; 
    my $comp = getcounts($seq); 
    my $denom = length $seq - $comp->{$gap}; 
    $comp->{$_} /= $denom for @alphabet; 
    warn Dump $comp; 
    return $comp; 
} 

sub composition_difference { 
    # I think your use of pairwise in the original script 
    # is very buggy unless every sequence always contains 
    # all the letters in the alphabet and the gap character. 
    # Is the gap character supposed to factor in the computations here? 

    my ($comp1, $comp2) = map { fractional_composition_pairs($_) } @_; 
    my %union; 
    ++ $union{$_} for (keys %$comp1, keys %$comp2); 

    my $dcomp; 
    { 
     no warnings 'uninitialized'; 
     $dcomp = sum map { 
      ($comp1->{$_} - $comp2->{$_}) ** 2 
     } keys %union; 
    } 

    return sqrt($dcomp)/20; # where did 20 come from? 
} 
+0

Sin entrar en la biología (¡no parece el lugar!), Las brechas de secuencia no constituyen diferencias en la "composición" de las secuencias, porque no son una letra en el alfabeto (de los 20 aminoácidos) El 20 es para (arbitrariamente) normalizar $ dcomp de 0 a 1. Obtengo el mapa() ahora ... ¡gracias! –

2

acaba de pasar por el código que ha proporcionado, así es como yo lo hubiera escrito . Sin embargo, no sé si esto funcionará de la manera que usted quería que funcionara.

use strict; 
use warnings; 
our($a, $b); 

use List::Util; 
use List::MoreUtils; 

my @alphabet = split '', 'ARNDCQEGHILKMFPSTWYV'; 
my $gapchr = '-'; 
# Takes a sequence and returns letter => occurrence count pairs as hash. 
sub getcounts { 
    my($sequence) = @_; 
    my %counts; 
    for my $chr (@alphabet) { 
    $counts{$chr} =() = $sequence =~ /($chr)/g; 
    #() = forces list context 
    } 
    $counts{'gap'} =() = $sequence =~ /($gapchr)/g; 
    return %counts if wantarray; # list context 
    return \%counts; # scalar context 
    # which is what happens inside of %{ } 
} 

# Takes a sequence and returns letter => fractional composition pairs as a hash 
sub comp { 
    my($sequence) = @_; 
    my %counts = getcounts($sequence); 
    my %comp; 
    for my $chr (@alphabet) { 
    $comp{$chr} = $comp{$chr}/(length($sequence) - $counts{'gap'}); 
    } 
    return %comp if wantarray; # list context 
    return \%comp; # scalar context 
} 

# Takes two sequences and returns a measure of the composition difference 
# between them, as a scalar. 
sub dcomp { 
    my($seq1, $seq2) = @_; 
    my @dcomp = pairwise { $a - $b } 
    @{[ values(%{ comp($seq1) }) ]}, 
    @{[ values(%{ comp($seq2) }) ]}; 
    # @{[ ]} makes a list into an array reference, then dereferences it. 
    # values always returns a list 
    # a list, or array in scalar context, returns the number of elements 
    # ${ } @{ } and %{ } forces their contents into scalar context 

    @dcomp = apply { $_ ** 2 } @dcomp; 
    my $dcomp = sqrt(sum(0, @dcomp))/20; 
    return $dcomp; 
} 

Una de las cosas más importantes que necesita saber son las diferencias entre escalar, lista y contextos vacío. Esto es porque todo se comporta diferente, en los diferentes contextos.

+0

Ah, sí veo sobre los contextos. 'si wantarray' es un buen truco. –

Cuestiones relacionadas