Hash et somme

Fermé
debousole Messages postés 4 Date d'inscription mardi 12 août 2014 Statut Membre Dernière intervention 29 septembre 2020 - Modifié le 28 sept. 2020 à 18:18
 Pat - 30 janv. 2021 à 09:44
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 6174 Date d'inscription mercredi 15 septembre 2004 Statut Contributeur Dernière intervention 2 février 2024 1 083
Modifié le 28 sept. 2020 à 19:22
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
Bien joué BRAVO
0
debousole Messages postés 4 Date d'inscription mardi 12 août 2014 Statut Membre Dernière intervention 29 septembre 2020
29 sept. 2020 à 14:30
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 6174 Date d'inscription mercredi 15 septembre 2004 Statut Contributeur Dernière intervention 2 février 2024 1 083
Modifié le 29 sept. 2020 à 18:08
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 mardi 12 août 2014 Statut Membre Dernière intervention 29 septembre 2020
29 sept. 2020 à 18:37
MERCI beaucoup!
Oui mon script est basé sur 3 colonnes pour que je puisse vérifier mon script rapidement...
0