Hash et somme

debousole Messages postés 4 Date d'inscription   Statut Membre Dernière intervention   -  
 Pat -
Bonjour,

J'ai un tableau ne contenant que des 1 et 0. Il est possible d'avoir 2 fois le même éléments. Je souhaiterai avoir chaque élément qu'une seule fois et faire la somme de chaque colonne et remplacer quand la somme de la colonne est égale à 2 indiquer 1.

tableau initial :
element1 1 0 1
element2 0 1 1
element3 1 1 0
element1 0 0 1

tableau final:
element1 1 0 1
element2 0 1 1
element3 1 1 0


Si quelqu'un peux m'aider s'il vous plaît, je "patauge" depuis un moment merci
my $fichier = 'exemple';

open my $fh, '<', $fichier or die "Impossible de lire le fichier $fichier\n";

my %data;
my $i=0;
my $j=0;


while(my $ligne = <$fh>){
 $i++;
 chomp $ligne;
 if($ligne =~ /gene.*/){
  $j++;
  my ($gene,$champ1,$champ2,$champ3) = split "\t", $ligne;
  my $element =$champ1."\t".$champ2."\t".$champ3;
  push @{$data{$gene}}, [$element];
 }
}

for my $id (keys %data) {
 my @array = @{$data{$id}};
 $data{$id} = \@array;
}
#print Dumper \%data; #ok bonne vérif


for my $id (keys %data) {
 print "--$id\n";
 my @tableau=();
 my @array = @{$data{$id}};


 for my $pair (@array) { 
  #print ("\t$pair->[0]\n");#ok bonne vérif

  #faire la somme de chaque colonne 
  my @hit = split("\t",$pair->[0]);

  for (my $m = 0; $m < @hit ; $m++) {
   print "colonne $m - valeur $hit[$m]\n";
  }
 }
}
close($fh);

Configuration: Linux / Firefox 68.0

3 réponses

[Dal] Messages postés 6205 Date d'inscription   Statut Contributeur Dernière intervention   1 105
 
Salut,

Il ne peut y avoir qu'une répétition d'élément, selon ce que je comprends, et il y a toujours 3 colonnes de données pour chaque élément ?

Si oui, j'aurai fait plus simple avec une regexp et un hash de tableaux :

#!/usr/bin/perl
  
use strict;
use warnings;

my %data;

while (<DATA>) {
    if (/(element\d+)\s+(\d)\s+(\d)\s+(\d)/) {
        # la ligne matche et nous avons capturé les 4 données
        if (!$data{$1}) {
            # si l'élément n'est pas déjà stocké dans le hash, on crée l'entrée
            $data{$1} = [$2, $3, $4];
        } else {
            # si l'élément est déjà stocké dans le hash, on retraite les données
            $data{$1} = [ 
                $2 + $data{$1}[0] == 2 ? 1 : $2 + $data{$1}[0],
                $3 + $data{$1}[1] == 2 ? 1 : $3 + $data{$1}[1],
                $4 + $data{$1}[2] == 2 ? 1 : $4 + $data{$1}[2]
            ];
        }
    }
}

# affichage ordonné par clef
foreach my $elem (sort keys %data) {
    print "$elem\t$data{$elem}[0]\t$data{$elem}[1]\t$data{$elem}[2]\n";
}

__END__
element1    1   0   1
element2    0   1   1
element3    1   1   0
element1    0   0   1

donne sur ton jeu de données :

$ perl 36867025.pl
element1 1 0 1
element2 0 1 1
element3 1 1 0



Dal
0
Pat
 
Bien joué BRAVO
0
debousole Messages postés 4 Date d'inscription   Statut Membre Dernière intervention  
 
Merci bien.
Oui Il ne peut y avoir qu'une répétition d'élément,
J'ai 20 colonnes, en fait il y a toujours un nombre fixe de colonnes de donnée pour chaque élément

Ah oui nettement plus simple...

J'ai avancé et trouvé une solution (sauf pour le remplacement de 2 par 1, un sed après traitement au pire)

open my $fh, '<', $fichier or die "Impossible de lire le fichier $fichier\n";

my %data;
my $i=0;
my $j=0;

while(my $ligne = <$fh>){
$i++;
chomp $ligne;
if($ligne =~ /gene.*/){
$j++;
my ($gene,$champ1,$champ2,$champ3) = split "\t", $ligne;
my $element =$champ1."\t".$champ2."\t".$champ3;
push @{$data{$gene}}, [$element];
}
}
for my $id (keys %data) {
my @array = @{$data{$id}};
$data{$id} = \@array;
}
#print Dumper \%data;

for my $id (keys %data) {
print "--$id\n";
my @tableau=();
my @array = @{$data{$id}};

my @sums;
for my $pair (@array) {
print ("\t$pair->[0]\n");

#faire la somme de chaque colonne quand la somme est égale à 2 remplacer par 1
my @nums = split("\t",$pair->[0]);
my $o = 0;
foreach my $num (@nums){
$sums[$o] += $num;
$o += 1;
}
}
print"=>\t";
foreach my $sum (@sums){
print("$sum\t");
}
print"\n";
}




Encore MERCI
0
[Dal] Messages postés 6205 Date d'inscription   Statut Contributeur Dernière intervention   1 105
 
De rien :-)

1.

Dans ton code, il suffit d'insérer la ligne suivante dans la boucle
foreach
entre
$sums[$o] += $num;
et
$o += 1; 
:

$sums[$o] = 1 if $sums[$o] == 2;


Pour rectifier les données mises à 2 en 1.

2.

Ton code semble travailler sur 3 colonnes de données, et non 20.

Tu peux, bien sûr extrapoler ton code (ou le mien) pour traiter 20 colonnes de données, mais cela fait beaucoup de copier-collers de lignes et de variables ou indices à traquer correctement.

Avec une regexp et le modificateur
g
tu peux répéter les matches et obtenir le résultat dans un tableau. Que tu aies 3 ou 20 colonnes de données ne changera rien au code.

Si tu n'aimes pas les regexp,
split
(que tu utilises), fonctionne en contexte de liste, et te permet d'obtenir directement un tableau des éléments séparés par les tabulations. Tu peux alors supprimer le premier élément du tableau avec
shift
pour l'utiliser ultérieurement comme clef de ton hash, et du disposeras du tableau restant sans le premier élément pour en faire ce que tu veux.

C'est à dire :
    if ($ligne =~ /gene.*/) {
        # obtention de toutes les colonnes dans un tableau
        my @arr = split "\t", $ligne;
        # on retire la donnée en première colonne, qui est notre clef
        my $key = shift @arr;
        # le tableau @arr contient désormais les données des colonnes
        # sauf la première qui a été retirée
(...)
0
debousole Messages postés 4 Date d'inscription   Statut Membre Dernière intervention  
 
MERCI beaucoup!
Oui mon script est basé sur 3 colonnes pour que je puisse vérifier mon script rapidement...
0