Domanda

Ho dei dati che assomigliano a questo:

    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'.

Quello che voglio fare è

  1. Genera tutte le combinazioni di elementi in part1 attraverso partK
  2. Trova il prodotto degli elementi corrispondenti in @prob .

Quindi alla fine speriamo di ottenere questo risultato:

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

Il problema è che il mio codice seguente lo fa tramite hardcoding i loop. Dal momento che il numero di parti di @homopol può essere variato e ampio (ad es. ~ K = 50), abbiamo bisogno di un modo flessibile e compatto per ottenere lo stesso risultato. C'è qualche? Stavo pensando di usare Algorithm :: Loops , ma non so come raggiungerlo.

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";

        }
    }
}
È stato utile?

Soluzione

Consiglierei Set :: CrossProduct , che creerà un iteratore per produrre il prodotto incrociato di tutti i tuoi set. Poiché utilizza un iteratore, non è necessario generare anticipatamente ogni combinazione; piuttosto, produce ciascuno su richiesta.

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][

Consiglierei Set :: CrossProduct , che creerà un iteratore per produrre il prodotto incrociato di tutti i tuoi set. Poiché utilizza un iteratore, non è necessario generare anticipatamente ogni combinazione; piuttosto, produce ciascuno su richiesta.

<*>], $prob[$i][

Consiglierei Set :: CrossProduct , che creerà un iteratore per produrre il prodotto incrociato di tutti i tuoi set. Poiché utilizza un iteratore, non è necessario generare anticipatamente ogni combinazione; piuttosto, produce ciascuno su richiesta.

<*>]] for 0 .. @{$homopol[$i]} - 1; }; my $iterator = Set::CrossProduct->new([ @combined ]); while( my $tuple = $iterator->get ){ my @h = map {

Consiglierei Set :: CrossProduct , che creerà un iteratore per produrre il prodotto incrociato di tutti i tuoi set. Poiché utilizza un iteratore, non è necessario generare anticipatamente ogni combinazione; piuttosto, produce ciascuno su richiesta.

<*>->[0] } @$tuple; my @p = map {

Consiglierei Set :: CrossProduct , che creerà un iteratore per produrre il prodotto incrociato di tutti i tuoi set. Poiché utilizza un iteratore, non è necessario generare anticipatamente ogni combinazione; piuttosto, produce ciascuno su richiesta.

<*>->[1] } @$tuple; my $product = 1; $product *=

Consiglierei Set :: CrossProduct , che creerà un iteratore per produrre il prodotto incrociato di tutti i tuoi set. Poiché utilizza un iteratore, non è necessario generare anticipatamente ogni combinazione; piuttosto, produce ciascuno su richiesta.

<*> for @p; print join('-', @h), ' ', join(' x ', @p), ' = ', $product, "\n"; }

Altri suggerimenti

Una soluzione che utilizza Algorithm :: Loops sembrerebbe simile a :

use Algorithm::Loops;

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

Una soluzione che utilizza Algorithm :: Loops sembrerebbe simile a :

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

Ma penso che potresti effettivamente rendere il codice più chiaro cambiando la struttura in uno più simile

<*>

perché senza le strutture di dati parallele puoi semplicemente iterare sulle sequenze di base disponibili, invece di iterare su indici e quindi cercare quegli indici in due luoghi diversi.

] } @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; });

Ma penso che potresti effettivamente rendere il codice più chiaro cambiando la struttura in uno più simile

<*>

perché senza le strutture di dati parallele puoi semplicemente iterare sulle sequenze di base disponibili, invece di iterare su indici e quindi cercare quegli indici in due luoghi diversi.

Perché non usi la ricorsione? Passa la profondità come parametro e lascia che la funzione si chiami con profondità + 1 all'interno del loop.

puoi farlo creando un array di indicazioni della stessa lunghezza dell'array @homopol (N dire), per tenere traccia di quale combinazione stai guardando. In effetti questo array è proprio come un numero nella base N, con gli elementi che sono le cifre. Iterate nello stesso modo in cui scrivereste i numeri progressivi nella base N, ad es. (0 0 0 ... 0), (0 0 0 ... 1), ..., (0 0 0 ... N- 1), (0 0 0 ... 1 0), ....

Approccio 1: calcolo dagli indici

Calcola il prodotto delle lunghezze in omopol (lunghezza1 * lunghezza2 * ... * lunghezzaN). Quindi, scorrere i da zero al prodotto. Ora, gli indici desiderati sono i% lunghezza1, (i / lunghezza1)% lunghezza2, (i / lunghezza1 / lunghezza2)% lunghezza3, ...

Approccio 2: ricorsione

Sono stato picchiato, vedi la risposta di Nikie. : -)

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top