Pergunta

Eu tenho dados que se parece com isso:

    my @homopol = (
                   ["T","C","CC","G"],  # part1
                   ["T","TT","C","G","A"], #part2
                   ["C","CCC","G"], #part3 ...upto part K=~50
                  );


    my @prob = ([1.00,0.63,0.002,1.00,0.83],
                [0.72,0.03,1.00, 0.85,1.00],
                [1.00,0.97,0.02]);


   # Note also that the dimension of @homopol is always exactly the same with @prob.
   # Although number of elements can differ from 'part' to 'part'.

O que eu quero fazer é

  1. Gerar todas as combinações de elementos em part1 através de fora partK
  2. Encontre o produto dos elementos correspondentes em @prob.

Assim, no final esperamos obter esta saída:

T-T-C  1 x 0.72 x 1 = 0.720
T-T-CCC     1 x 0.72 x 0.97 = 0.698
T-T-G  1 x 0.72 x 0.02 = 0.014
...
G-G-G  1 x 0.85 x 0.02 = 0.017
G-A-C  1 x 1 x 1 = 1.000
G-A-CCC     1 x 1 x 0.97 = 0.970
G-A-G  1 x 1 x 0.02 = 0.020

O problema é que o seguinte código de mina faz que hardcoding as laçadas. Como o número de peças de @homopol é pode ser variado e grande (Por exemplo, ~ K = 50), que precisa de uma maneira flexível e compacta para obter o mesmo resultado. Existe alguma? Eu estava pensando em usar Algorithm :: Loops , mas não tenho certeza como conseguir isso.

use strict;
use Data::Dumper;
use Carp;


my @homopol = (["T","C","CC","G"],
               ["T","TT","C","G","A"],
               ["C","CCC","G"]);


my @prob = ([1.00,0.63,0.002,1.00,0.83],
            [0.72,0.03,1.00, 0.85,1.00],
            [1.00,0.97,0.02]);



my $i_of_part1 = -1;
foreach my $base_part1 ( @{ $homopol[0] } ) {
    $i_of_part1++;
    my $probpart1 = $prob[0]->[$i_of_part1];

    my $i_of_part2 =-1;
    foreach my $base_part2 ( @{ $homopol[1] } ) {
        $i_of_part2++;
        my $probpart2 = $prob[1]->[$i_of_part2];

        my $i_of_part3 = -1;
        foreach my $base_part3 ( @{ $homopol[2] } ) {
            $i_of_part3++;
            my $probpart3 = $prob[2]->[$i_of_part3];

            my $nstr = $base_part1."".$base_part2."".$base_part3;
            my $prob_prod = sprintf("%.3f",$probpart1 * $probpart2 *$probpart3);

            print "$base_part1-$base_part2-$base_part3 \t";
            print "$probpart1 x $probpart2 x $probpart3 = $prob_prod\n";

        }
    }
}
Foi útil?

Solução

Eu recomendaria Set::CrossProduct , que irá criar um iterador para produzir a cruz produto de todos os seus sets. Porque ele usa um iterador, ele não precisa de gerar todas as combinações de antecedência; em vez disso, ele produz cada um sob demanda.

use strict;
use warnings;
use Set::CrossProduct;

my @homopol = (
    [qw(T C CC G)],
    [qw(T TT C G A)],
    [qw(C CCC G)], 
);

my @prob = (
    [1.00,0.63,0.002,1.00],
    [0.72,0.03,1.00, 0.85,1.00],
    [1.00,0.97,0.02],
);

# Prepare by storing the data in a list of lists of pairs.
my @combined;
for my $i (0 .. $#homopol){
    push @combined, [];
    push @{$combined[-1]}, [$homopol[$i][$_], $prob[$i][$_]]
        for 0 .. @{$homopol[$i]} - 1;
};

my $iterator = Set::CrossProduct->new([ @combined ]);
while( my $tuple = $iterator->get ){
    my @h = map { $_->[0] } @$tuple;
    my @p = map { $_->[1] } @$tuple;
    my $product = 1;
    $product *= $_ for @p;
    print join('-', @h), ' ', join(' x ', @p), ' = ', $product, "\n";
}

Outras dicas

A solução usando Algorithm :: Loops sem alterar os dados de entrada seria algo parecido :

use Algorithm::Loops;

# Turns ([a, b, c], [d, e], ...) into ([0, 1, 2], [0, 1], ...)
my @lists_of_indices = map { [ 0 .. @$_ ] } @homopol;

NestedLoops( [ @lists_of_indices ], sub {
  my @indices = @_;
  my $prob_prod = 1; # Multiplicative identity
  my @base_string;
  my @prob_string;
  for my $n (0 .. $#indices) {
    push @base_string, $hompol[$n][ $indices[$n] ];
    push @prob_string, sprintf("%.3f", $prob[$n][ $indices[$n] ]);
    $prob_prod *= $prob[$n][ $indices[$n] ];
  }
  print join "-", @base_string; print "\t";
  print join "x", @prob_string; print " = ";
  printf "%.3f\n", $prob_prod;
});

Mas eu acho que você realmente pode tornar o código mais claro, alterando a estrutura para um mais como

[ 
  { T => 1.00, C => 0.63, CC => 0.002, G => 0.83 },
  { T => 0.72, TT => 0.03, ... },
  ...
]

porque sem as estruturas de dados paralelos você pode simplesmente iterar sobre as seqüências de bases disponíveis, em vez de iteração sobre índices e, em seguida, olhando para cima esses índices em dois lugares diferentes.

Por que você não usar recursão? Passe a profundidade como um parâmetro e deixar a própria chamada de função com profundidade + 1 dentro do loop.

Você poderia fazê-lo através da criação de uma série de indicies o mesmo comprimento que a matriz @homopol (digamos N), para manter o controle de qual combinação que você está olhando. Na verdade, esta matriz é apenas como um número na base de N, com os elementos que são os dígitos. Iterate da mesma forma como você iria escrever para baixo os números consectutive na base de N, por exemplo (0 0 0 ... 0), (0 0 0 ... 1), ..., (0 0 0 ... N- 1), (0 0 0 ... 1 0), ....

Abordagem 1: Cálculo de índices

Compute o produto dos comprimentos em homopol (comprimento1 * length2 * ... * lengthN). Então, iteração i de zero para o produto. Agora, os índices desejados são i% comprimento1, (i / comprimento1)% length2, (i / comprimento1 / length2)% Comprimento3, ...

Abordagem 2: recursão

Eu fui agredido a ele, ver a resposta de Nikie. : -)

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top