OVH Cloud OVH Cloud

selectionner les lignes d'un fichier contenant des maximums

33 réponses
Avatar
geo cherchetout
Bonjour,
J'ai un fichier texte dont chaque ligne contient, après un premier
nombre et une espace, un second nombre compris entre 0 et 1. En voici un
extrait :

0.0023129252 0.68157959
0.0023356009 0.68902588
0.0023582766 0.71615601
0.0023809524 0.72515869 <--
0.0024036281 0.71878052
0.0024263039 0.72677612 <==
0.0024489796 0.71615601
0.0024716553 0.70773315
0.0024943311 0.68917847

Ce second nombre représentant lui-même des échantillons d'une onde
sonore de composition spectrale très pauvre, passe toutes les 50 à 200
lignes par un vrai maximum comme celui de la ligne 6. Deux maximums
successifs sont séparés par un minimum dont la valeur est au moins 10
fois plus faible, ce qui permet de les distinguer de quelques faux
maximums comme celui de la ligne 4.
Je voudrais écrire un script dont l'exécution réduise ce fichier aux
seules lignes contenant ces vrais maximums. Quelles commandes ou quel
langage de programmation, et donc quelles pages de manuel me conseillez
vous, sachant que mes connaissances sont des plus modestes ?
Merci d'avance.

10 réponses

1 2 3 4
Avatar
Stephane Chazelas
2004-11-24, 18:09(+01), geo cherchetout:
[...]
0.0023129252 0.68157959
0.0023356009 0.68902588
0.0023582766 0.71615601
0.0023809524 0.72515869 <--
0.0024036281 0.71878052
0.0024263039 0.72677612 <= > 0.0024489796 0.71615601
0.0024716553 0.70773315
0.0024943311 0.68917847

Ce second nombre représentant lui-même des échantillons d'une onde
sonore de composition spectrale très pauvre, passe toutes les 50 à 200
lignes par un vrai maximum comme celui de la ligne 6. Deux maximums
successifs sont séparés par un minimum dont la valeur est au moins 10
fois plus faible, ce qui permet de les distinguer de quelques faux
maximums comme celui de la ligne 4.
Je voudrais écrire un script dont l'exécution réduise ce fichier aux
seules lignes contenant ces vrais maximums. Quelles commandes ou quel
langage de programmation, et donc quelles pages de manuel me conseillez
vous, sachant que mes connaissances sont des plus modestes ?
[...]


awk est pile-poil ce qu'il te faut:

#! /usr/bin/awk -f
# Usage: script.awk < fichier.in > fichier.out
$2 > max {
max = $2
ligne = $0
next
}
10 * $2 < max {
print ligne
max = 0
}
END {
if (max) print ligne
}

--
Stephane

Avatar
Pascal Bourguignon
Stephane Chazelas writes:

2004-11-24, 18:09(+01), geo cherchetout:
[...]
0.0023129252 0.68157959
0.0023356009 0.68902588
0.0023582766 0.71615601
0.0023809524 0.72515869 <--
0.0024036281 0.71878052
0.0024263039 0.72677612 <= > > 0.0024489796 0.71615601
0.0024716553 0.70773315
0.0024943311 0.68917847

Ce second nombre représentant lui-même des échantillons d'une onde
sonore de composition spectrale très pauvre, passe toutes les 50 à 200
lignes par un vrai maximum comme celui de la ligne 6. Deux maximums
successifs sont séparés par un minimum dont la valeur est au moins 10
fois plus faible, ce qui permet de les distinguer de quelques faux
maximums comme celui de la ligne 4.
Je voudrais écrire un script dont l'exécution réduise ce fichier aux
seules lignes contenant ces vrais maximums. Quelles commandes ou quel
langage de programmation, et donc quelles pages de manuel me conseillez
vous, sachant que mes connaissances sont des plus modestes ?
[...]


awk est pile-poil ce qu'il te faut:

#! /usr/bin/awk -f
# Usage: script.awk < fichier.in > fichier.out
$2 > max {
max = $2
ligne = $0
next
}
10 * $2 < max {
print ligne
max = 0
}
END {
if (max) print ligne
}


Il me semble que ça ne fonctionne pas:

[ pascal]$ cat data data data > data2
[ pascal]$ script.awk < data2
0.0024263039 0.72677612 <=


[ pascal]$ cat script.awk
#! /usr/bin/awk -f
# Usage: script.awk < fichier.in > fichier.out
BEGIN {
max=-1;
max_nr=-1;
}
{
if(max<$2){
line=$0;
max=$2;
max_nr=NR;
}else{
if(NR==max_nr+2){
print line;
max=-1;
max_nr=-1;
}
}
}
END{
if(max>=0){
print line;
}
}

#### script.awk -- -- ####
[ pascal]$ cat data2
0.0023129252 0.68157959
0.0023356009 0.68902588
0.0023582766 0.71615601
0.0023809524 0.72515869 <--
0.0024036281 0.71878052
0.0024263039 0.72677612 <= 0.0024489796 0.71615601
0.0024716553 0.70773315
0.0024943311 0.68917847
0.0023129252 0.68157959
0.0023356009 0.68902588
0.0023582766 0.71615601
0.0023809524 0.72515869 <--
0.0024036281 0.71878052
0.0024263039 0.72677612 <= 0.0024489796 0.71615601
0.0024716553 0.70773315
0.0024943311 0.68917847
0.0023129252 0.68157959
0.0023356009 0.68902588
0.0023582766 0.71615601
0.0023809524 0.72515869 <--
0.0024036281 0.71878052
0.0024263039 0.72677612 <= 0.0024489796 0.71615601
0.0024716553 0.70773315
0.0024943311 0.68917847
[ pascal]$ script.awk < data2
0.0024263039 0.72677612 <= 0.0024943311 0.68917847
0.0024263039 0.72677612 <= 0.0024943311 0.68917847
0.0024263039 0.72677612 <= 0.0024943311 0.68917847
[ pascal]$


--
__Pascal Bourguignon__ http://www.informatimago.com/
The world will now reboot; don't bother saving your artefacts.


Avatar
Jérémy JUST
On Wed, 24 Nov 2004 18:09:01 +0100
geo cherchetout wrote:

Quelles commandes ou quel langage de programmation


Perl est parfaitement adapté.

Voilà une idée de départ (si j'ai bien compris ton problème):

<<<<<
#!/usr/bin/perl -w

use strict ;

my $max = 0 ;
my $max_line ;

while(<>)
{if (/^S+s+(S+)/)
{my $curr = $1 ;

if ($curr > $max)
{$max = $curr ;
$max_line = $_ ;
}
elsif ($curr < $max /10)
{print $max_line ;
$max = 0 ;
$max_line = '' ;
}
}
}

print $max_line ;

exit 0 ;







--
Jérémy JUST





Avatar
geo cherchetout
Le 24.11.2004 19:28, *Pascal Bourguignon* a écrit fort à propos :

Il me semble que ça ne fonctionne pas:

[ pascal]$ cat data data data > data2
[ pascal]$ script.awk < data2
0.0024263039 0.72677612 <=
Merci déjà à vous deux.

Je n'ai pas encore tout compris, et il me faudra un certain nombre
d'heures pour cela, mais le fichier que tu construis pour ton test ne
comporte pas les minimums que comporte mon vrai fichier.
Testé sur ce vrai fichier, le seul défaut du script de Stéphane est
qu'il ne conserve que les maximums égaux à 1 ou presque. On est tout
près du but, j'ai l'impression.

[ pascal]$ cat script.awk
#! /usr/bin/awk -f
# Usage: script.awk < fichier.in > fichier.out
BEGIN {
max=-1;
max_nr=-1;
}
{
if(max<$2){
line=$0;
max=$2;
max_nr=NR;
}else{
if(NR==max_nr+2){
print line;
max=-1;
max_nr=-1;
}
}
}
END{
if(max>=0){
print line;
}
}


J'ai essayé aussi celui-ci, en l'appliquant jusqu'à 5 fois
successivement, mais les lignes conservées ne sont pas celles contenant
les maximums...

Si le jeu vous intéresse encore, je viens de placer ici le début de mon
fichier réel :
http://cjoint.com/?lyx0aSrYhm
À bientôt.

Avatar
Stephane Chazelas
2004-11-25, 00:28(+01), geo cherchetout:
Le 24.11.2004 19:28, *Pascal Bourguignon* a écrit fort à propos :

Il me semble que ça ne fonctionne pas:

[ pascal]$ cat data data data > data2
[ pascal]$ script.awk < data2
0.0024263039 0.72677612 <= >
Merci déjà à vous deux.

Je n'ai pas encore tout compris, et il me faudra un certain nombre
d'heures pour cela, mais le fichier que tu construis pour ton test ne
comporte pas les minimums que comporte mon vrai fichier.
Testé sur ce vrai fichier, le seul défaut du script de Stéphane est
qu'il ne conserve que les maximums égaux à 1 ou presque. On est tout
près du but, j'ai l'impression.
[...]


Mon script est censé trouver les maxima separés par une valeur
10 fois plus petite que le precedent maximum (c'est ce que j'ai
cru comprendre de ta question.

awk lit un "record" a la fois (un "record" == une ligne par
defaut) et applique le jeu de <condition> { <action> } pour
chaque

#! /usr/bin/awk -f
# Usage: script.awk < fichier.in > fichier.out
$2 > max { # <condition> est si le deuxieme champs du /record/
# courant est superieur
# a la valeur courante de la variable /max/
max = $2 # alors (<action>) on m.a.j max
ligne = $0 # et on stocke la ligne courante
next # on passe au record suivant sans evaluer les autres
# <condition> { <action> }
}
10 * $2 < max { # <condition>: si le $2 du record courant est 10
# fois plus petit que la valeur de <max>, alors
# on a trouvé un separateur de maxima
print ligne # donc on affiche le maximum qu'on a recuperé
# precedemment
max = 0 # et on remet le max a 0
}
END { # condition speciale qui est evaluee apres que tous les
# records aient ete lus.
if (max) print ligne # affiche le dernier maximum
}

L'ennui, c'est que pour:

0 0.1
0 0.2
0 0.1
0 0.4
0 0.039
0 0.032
0 0.003
0 0.002

il va trouver 0.4 comme maximum (parce que suivi d'un 0.039 qui
est plus de 10 fois plus petit) et 0.032 (parce que suivi de
0.003 plus de 10 fois plus petit) et 0.002 (parce que c'est le
dernier maximum).

Ca serait peut-etre mieux comme ca:

#! /usr/bin/awk -f
# Usage: script.awk < fichier.in > fichier.out
$2 > max && $2 > 10 * min {
max = $2
ligne = $0
next
}
10 * $2 < max {
print ligne
max = 0
min = $2
}
END {
if (max) print ligne
}

C'est a dire qu'un maximum doit etre precedé *et suivi* d'un
minimum au moins dix fois plus petit.

--
Stephane


Avatar
geo cherchetout
Le 24.11.2004 23:27, *Jérémy JUST* a écrit fort à propos :

Voilà une idée de départ (si j'ai bien compris ton problème):


Pour une idée de départ, elle s'avère déjà presque utilisable telle
quelle. Toutefois je ne parviens pas à trouver un rapport maxi/mini,
(initialement fixé à 10) qui conserve toutes les lignes voulues et
seulement elles. La faute à mon cahier des charges que je vais essayer
d'améliorer.
Quoi qu'il en soit, je sais à présent où sont les bonnes caisses à
outils, ce qui simplifie beaucoup le problème. Raisonnablement, je peux
désormais espérer savoir le résoudre avant la fin 2005. ;-)
Merci à tous.

Avatar
Stephane Chazelas
2004-11-25, 11:44(+01), geo cherchetout:
Le 24.11.2004 23:27, *Jérémy JUST* a écrit fort à propos :

Voilà une idée de départ (si j'ai bien compris ton problème):


Pour une idée de départ, elle s'avère déjà presque utilisable telle
quelle.


Note que c'est l'exacte traduction en perl de la premiere
solution en awk que je donnais. Il existe a2p pour convertir un
programme awk en perl. Donc, ma deuxieme solution en perl
donnerait:

#!/usr/bin/perl
$[ = 1; # set array base to 1
$ = "n"; # set output record separator

line: while (<>) {
chomp; # strip record separator
@Fld = split(' ', $_, 9999);
if ($Fld[2] > $max && $Fld[2] > 10 * $min) {
$max = $Fld[2];
$ligne = $_;
next line;
}
if (10 * $Fld[2] < $max) {
print $ligne;
$max = 0;
$min = $Fld[2];
}
}

if ($max) {
print $ligne;
}

--
Stephane


Avatar
geo cherchetout
Le 25.11.2004 09:48, *Stephane Chazelas* a écrit fort à propos :

Ca serait peut-etre mieux comme ca:


Bonjour Stéphane,

Et merci pour ton aide très appréciée, et particulièrement tes
explications détaillées.
Ne t'étonne pas de mes réactions à retardement : Je suis très lent
et j'ai aussi des obligations qui me retiennent loin du chantier
durant de longues heures.
Je viens d'essayer ton amélioration de 9h48 qui ne pourrait que diminuer
le nombre des lignes sauvegardées, et le résultat est identique à celui
de ton premier script : Seuls 2 "super-maximums" sont retenus au lieu de
la centaine attendus sur mon fichier de 5000 lignes :

$ cat magnetic_sc.lite
0.070362812 1
0.096462585 1

$ cat magnetic_sc2.lite
0.070362812 1
0.096462585 1

Il doit donc y avoir une autre erreur de principe.

J'ai aussi pris connaissance de ta traduction en Perl.
Dès que j'aurai mis au point ce script, ce qui sera forcément long
puisque je ne maîtrise ni awk ni perl ni aucun autre langage de
programmation, j'indiquerai ici comment j'ai fait.
Optimisme ou inconscience ? Un peu des deux probablement.
À bientôt donc, j'espère.
Bien cordialement.

Avatar
Stephane Chazelas
2004-11-25, 16:22(+01), geo cherchetout:
[...]
Je viens d'essayer ton amélioration de 9h48 qui ne pourrait que diminuer
le nombre des lignes sauvegardées, et le résultat est identique à celui
de ton premier script : Seuls 2 "super-maximums" sont retenus au lieu de
la centaine attendus sur mon fichier de 5000 lignes :

$ cat magnetic_sc.lite
0.070362812 1
0.096462585 1
[...]


Peux-tu donner un exemple d'un maximum (avec son contexte) qui
aurait du etre affiché ? Es-tu sur que ce maximum est suivi et
precedé d'un minimum 10 fois plus petit que lui ? Auquel cas, il
pourrait s'agir d'un bug de ton awk. Tu es sur quel systeme ?
Essaie peut-etre gawk ou nawk a la place de awk.

Ici, pour un fichier comme ca:
-min
0 0.01
0 0.2 -max
0 0.001 -min
0 0.02
0 0.1 -max
0 0.0001 -min
0 0.002 -max
0 0.00001 -min

J'obtiens:

0 0.2 -max
0 0.1 -max
0 0.002 -max

Ce qui est bien ce que j'attends. Maintenant, peut-etre que je
n'ai pas bien compris la question.

--
Stephane

Avatar
geo cherchetout
Le 25.11.2004 17:07, *Stephane Chazelas* a écrit fort à propos :

Peux-tu donner un exemple d'un maximum (avec son contexte) qui
aurait du etre affiché ?


J'ai déposé un fichier test de 5000 lignes ici :
http://cjoint.com/?lyx0aSrYhm

Et voici un exemple de ligne qui aurait du être conservée, avec un
certain nombre avant et après pour montrer qu'il s'agit bien d'un
maximum précédé et suivi de minimums conformes :

0.0018367347 0.038726807
0.0018594104 0.0036621094 (mini)
0.0018820862 0.020843506
0.0019047619 0.065124512
0.0019274376 0.10272217
0.0019501134 0.12268066
0.0019727891 0.17111206
0.0019954649 0.19995117
0.0020181406 0.23526001
0.0020408163 0.27081299
0.0020634921 0.29904175
0.0020861678 0.34286499
0.0021088435 0.37606812
0.0021315193 0.42178345
0.002154195 0.4510498
0.0021768707 0.47857666
0.0021995465 0.5272522
0.0022222222 0.5541687
0.002244898 0.59091187
0.0022675737 0.62094116
0.0022902494 0.6499939
0.0023129252 0.68157959
0.0023356009 0.68902588
0.0023582766 0.71615601
0.0023809524 0.72515869 (faux maxi)
0.0024036281 0.71878052
0.0024263039 0.72677612 (maxi)
0.0024489796 0.71615601
0.0024716553 0.70773315
0.0024943311 0.68917847
0.0025170068 0.66540527
0.0025396825 0.65777588
0.0025623583 0.64834595
0.002585034 0.64517212
0.0026077098 0.63427734
0.0026303855 0.61038208
0.0026530612 0.59265137
0.002675737 0.57641602
0.0026984127 0.54556274
0.0027210884 0.50982666
0.0027437642 0.47860718
0.0027664399 0.43197632
0.0027891156 0.38720703
0.0028117914 0.35092163
0.0028344671 0.30703735
0.0028571429 0.2684021
0.0028798186 0.23449707
0.0029024943 0.20385742
0.0029251701 0.17630005
0.0029478458 0.15722656
0.0029705215 0.14297485
0.0029931973 0.12008667
0.003015873 0.10848999
0.0030385488 0.099853516
0.0030612245 0.074615479
0.0030839002 0.052581787
0.003106576 0.03503418
0.0031292517 0.0047912598 (mini)


Es-tu sur que ce maximum est suivi et
precedé d'un minimum 10 fois plus petit que lui ? Auquel cas, il
pourrait s'agir d'un bug de ton awk. Tu es sur quel systeme ?


Je barbote sous Linux (Mandrake 10.0) avec un awk qui s'appelle en
réalité gawk 3.1.3-3mdk. Pour le moment je n'en ai pas d'autre, mais
j'essaierai awk ou nawk si je peux les installer librement.

J'obtiens:

0 0.2 -max
0 0.1 -max
0 0.002 -max

Ce qui est bien ce que j'attends. Maintenant, peut-etre que je
n'ai pas bien compris la question.


Est-ce que la proximité des minis et des maxis pourrait favoriser le
fonctionnement de ton script ? Il donne bien le résultat attendu.
D'un autre côté, je n'ai sûrement pas posé le problème de façon parfaite.

Pour mieux le cerner, il s'agit de traîter le signal analogique issu de
la tête de lecture d'un lecteur de cartes magnétiques, en vue d'en lire
le contenu. (Ce qui est facile mais un peu fastidieux à la main.)
- J'ai enregistré ce signal au format ogg avec Audacity et l'ai amplifié
jusqu'à la limite de la saturation.
- Je l'ai transformé en fichier texte avec Sox.
- Je l'ai redressé en supprimant le signe - des échantillons négatifs,
pensant ainsi simplifier le problème.
Le but du traitement est d'attribuer la valeur logique 0 à tout
intervalle long entre deux crêtes successives, et la valeur 1 à toute
paire d'intervalles courts. Après quoi je n'aurai plus qu'à décoder les
paquets de 5 bits.

1 2 3 4