Hash et somme

Signaler
Messages postés
4
Date d'inscription
mardi 12 août 2014
Statut
Membre
Dernière intervention
29 septembre 2020
-
 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

Messages postés
5550
Date d'inscription
mercredi 15 septembre 2004
Statut
Contributeur
Dernière intervention
12 mai 2021
933
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
Messages postés
4
Date d'inscription
mardi 12 août 2014
Statut
Membre
Dernière intervention
29 septembre 2020

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
Messages postés
5550
Date d'inscription
mercredi 15 septembre 2004
Statut
Contributeur
Dernière intervention
12 mai 2021
933
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
(...)
Messages postés
4
Date d'inscription
mardi 12 août 2014
Statut
Membre
Dernière intervention
29 septembre 2020

MERCI beaucoup!
Oui mon script est basé sur 3 colonnes pour que je puisse vérifier mon script rapidement...