#1 Le 23/06/2017, à 11:04
- paul18
[clôt]Traitement de gros fichiers
Bonjour,
Le besoin venant, je m'auto-forme au shell pour traiter et mettre en forme des fichiers avec plusieurs millions de lignes (ils dépassent le Go en taille) - je ne suis pas un spécialiste loin s'en faut ; je m'intéresse à divers outils comme sed - awk par exemple.
A) Généralité :
Est-ce que awk charge la totalité en mémoire avant de travailler ? (je ne pense pas mais j'ai un doute)
B) optimisation
Le bout de code ci-dessous fonctionne sur un cas test, mais dès que j'attaque un gros fichier, "ça rame".
quelques remarques :
- le fichier original ne comporte que des blocs de données qui sont en ligne,
- je cherche à récupérer des sous-blocs et à les mettre en 4 colonnes (pour l'exemple joint),
- j'utilise ici 1 variable à chaque fois - j'ai testé avec un tableau et il n'y a pas de gain ou de perte sur mon cas test,
- je vais essayer de récupérer des blocs de 5 lignes avec awk et supprimer ensuite la colonne qui sera en trop (voir indices 2 4 5 6) - on reviens au tableau j'imagine ?
bref toute suggestion que ce soit pour mieux coder ou améliorer la vitesse et la bienvenue
Merci par avance
Paul
for ((j=1;j<$Nb+1;j++))
do
n1=$(awk -v i=$((position_N+5*($j-1)+2)) 'NR==i {print $0}' $fich_)
n2=$(awk -v i=$((position_N+5*($j-1)+4)) 'NR==i {print $0}' $fich_)
n3=$(awk -v i=$((position_N+5*($j-1)+5)) 'NR==i {print $0}' $fich_)
n4=$(awk -v i=$((position_N+5*($j-1)+6)) 'NR==i {print $0}' $fich_)
printf "%s %s %s %s \n" $n1 $n2 $n3 $n4 >> fich_noeuds
done
## mesure temps CPU
time
Dernière modification par paul18 (Le 23/06/2017, à 19:50)
Hors ligne
#2 Le 23/06/2017, à 11:25
- Compte supprimé
Re : [clôt]Traitement de gros fichiers
Bonjour,
Juste deux remarques :
* ($j-1)+2 se simplifie en $j+1
Sinon tu peux modifier for (j=0 ; j<=$Nb ; j++) avec $j + 2...
Pour tes autres questions, je ne sais pas mais tu peux observer la led du disque pour comprendre ce qui se passe...
Édit : '<=' ou '=<' je ne sais plus.
Dernière modification par Compte supprimé (Le 23/06/2017, à 11:31)
#3 Le 23/06/2017, à 11:39
- paul18
Re : [clôt]Traitement de gros fichiers
($j-1)+2 se simplifie en $j+1
oui s'il n'y avait pas un coefficient 5 devant
En fait j'ai l'impression qu'il balaie tout le fichier à chaque instruction
(je viens de vérifier sur un fichier de 105 millions de lignes)
Dernière modification par paul18 (Le 23/06/2017, à 11:46)
Hors ligne
#4 Le 23/06/2017, à 12:09
- pingouinux
Re : [clôt]Traitement de gros fichiers
Bonjour,
Effectivement, tu appelles awk 4 * $Nb fois. Montre un exemple précis de ce que tu veux.
Hors ligne
#5 Le 23/06/2017, à 12:24
- Compte supprimé
Re : [clôt]Traitement de gros fichiers
Ah oui ! Pardon, pas vu le 5* sur ma tablette 7 pouces...
(5*($j-1)+2) se simplifie en (5*$j - 3)
:-)
Dernière modification par Compte supprimé (Le 23/06/2017, à 12:25)
#6 Le 23/06/2017, à 13:10
- paul18
Re : [clôt]Traitement de gros fichiers
Merci pour ces retours ; je comprends qu'en premier lieu il me faut découper le fichier original.
les données sont du genre:
4
129
3
6.5
-1
0
128
3
7
-1.5
0
127
3
6.75
-1.25
5.E-1
126
3
6.25
-1.25
5.E-1
avec :
- le premier chiffre = 4 le nombre de blocs
- ensuite viennent les sous-blocs : 129 = numéro du sous-bloc / 3 le nombre de données qui le compose / les 3 ligne suivantes les données ... et ainsi de suite
Hors ligne
#7 Le 23/06/2017, à 13:24
- Compte supprimé
Re : [clôt]Traitement de gros fichiers
Un programme en C avec ouverture de fichier est-il envisageable ?
#8 Le 23/06/2017, à 13:43
- pingouinux
Re : [clôt]Traitement de gros fichiers
Un truc dans ce genre, mais comme tu n'as pas indiqué la sortie que tu voulais, j'ai fait ça au pif :
j=0
while read ligne
do
((j++))
((j%5==2)) && printf "%s " "$ligne"
((j%5==3)) && printf "%s " "$ligne"
((j%5==4)) && printf "%s " "$ligne"
((j%5==0)) && printf "\n"
done <fichier >fich_noeuds
Hors ligne
#9 Le 23/06/2017, à 13:44
- paul18
Re : [clôt]Traitement de gros fichiers
Un programme en C avec ouverture de fichier est-il envisageable ?
tout est envisageable à condition de savoir faire (je parle pour moi qui n'est qu'une connaissance basique du C) ; 2 axes que je compte explorer :
- revenir à la source et générer plusieurs fichiers au lieu d'un seul (ça je m'en occupe)
- éviter de boucler 4 fois comme dans mon premier post, mais prendre le bloc en une seule fois - je supprimerai ensuite la colonne en trop
je reste ouvert toutefois à toute suggestion, sur la façon de faire comme les outils à utiliser)
Paul
Hors ligne
#10 Le 23/06/2017, à 13:46
- paul18
Re : [clôt]Traitement de gros fichiers
@pingouin : je n'avais pas vu ton post, mais je te réponds
129 6.5 -1 0
128 7 -1.5 0
127 6.75 -1.25 5.E-1
126 6.25 -1.25 5.E-1
Hors ligne
#11 Le 23/06/2017, à 13:57
- Watael
Re : [clôt]Traitement de gros fichiers
salut,
il faut n'appeler awk qu'une seule fois :
¡ code non-testé, très certainement plein d'erreurs !
awk -v nb="$Nb" -v position_N="$position_N" 'j++ < nb {i f(NR == position_N+5*($j-1)+2){ if (++n == 4){printf("%s,%s,%s,%s\n",ar[1],ar[2],ar[3],ar[4]); delete ar}; ar[n]=$0}}' fich
juste pour donner une idée de piste à suivre...
Edit : le shell sera plus lent que awk.
Dernière modification par Watael (Le 23/06/2017, à 13:59)
Connected \o/
Welcome to sHell. · eval is evil.
En ligne
#12 Le 23/06/2017, à 14:02
- pingouinux
Re : [clôt]Traitement de gros fichiers
j=-1
while read ligne
do
((j++))
((j==0)) && continue
((j%5==1)) && printf "%s " "$ligne"
((j%5==3)) && printf "%s " "$ligne"
((j%5==4)) && printf "%s " "$ligne"
((j%5==0)) && printf "%s " "$ligne"
((j%5==0)) && printf "\n"
done <fichier >fich_noeuds
Hors ligne
#13 Le 23/06/2017, à 15:57
- paul18
Re : [clôt]Traitement de gros fichiers
awk -v nb="$Nb" -v position_N="$position_N" 'j++ < nb { if(NR == position_N+5*($j-1)+2){ if (++n == 5){printf("%s,%s,%s,%s,%s\n",ar[1],ar[2],ar[3],ar[4],ar[5]); delete ar}; ar[n]=$0}}' fich > titi
titi reste vide chez moi ; j'essaie de comprendre - est-ce que mon raisonnement est bon ?
- awk balaie sans rien faire le fichier depuis le début jusqu'à remplir la condition : if(NR == position_N+5*($j-1)+2)
- là il incrémente n tout en enregistrant les données dans un tableau ar[n]
- quand n ==5 il enregistre les ar[] et il sort de chacune des boucles
j'ai redirigé vers titi, mais même en copiant dans un terminal (et en donnant explicitement les valeurs de Nb et position_N) ça reste vide ; la solution paraît séduisante, mais il y a quelque chose qui m'échappe !
Entre-temps j'ai réussi à enregistrer des blocs de 5 lignes avec sed, mais la boucle du shell freine considérablement
Paul
Hors ligne
#14 Le 23/06/2017, à 16:18
- paul18
Re : [clôt]Traitement de gros fichiers
après quelques recherches, je me demande si je ne vais pas devoir passer sous Python
Hors ligne
#15 Le 23/06/2017, à 17:07
- pingouinux
Re : [clôt]Traitement de gros fichiers
Autre façon de faire :
(
read n_bloc
for ((bloc=0;bloc<n_bloc;bloc++))
do
read nu_bloc
printf "%s " "$nu_bloc"
read n_don
for ((don=0;don<n_don;don++))
do
read ligne
printf "%s " "$ligne"
done
printf "\n"
done
)<fichier >fich_noeuds
Hors ligne
#16 Le 23/06/2017, à 17:36
- Watael
Re : [clôt]Traitement de gros fichiers
il faudrait que tu nous donnes aussi Nb et position_N.
qu'on puisse tester ton script sur l'échantillon de données présenté,
et nous confirmer les données du fichier en entrée, et celles de la sortie correspondante.
j'ai un doute...
Connected \o/
Welcome to sHell. · eval is evil.
En ligne
#17 Le 23/06/2017, à 18:23
- paul18
Re : [clôt]Traitement de gros fichiers
il faudrait que tu nous donnes aussi Nb et position_N.
Dans les données jointes, il faut mettre position_N à 0 (zéro) et Nb à 4 (évidemment j'ai tronqué les données)
@pinguinlinux: j'avais trouvé ça avec un gain notable par rapport au premier code, mais la boucle ruine quand même la vitesse
declare -a n
for ((j=1;j<$Nb+1;j++))
do
n=$(sed -n "$((position_N+5*($j-1)+2)),$((position_N+5*($j-1)+6))p" $fich_)
printf "%s %s %s %s %s\n" ${n[*]} >> fich_n
done
on peut joindre un fichier type sur le forum ? sinon je peux déposer ça sur un site ftp genre free ...
Dernière modification par paul18 (Le 23/06/2017, à 18:56)
Hors ligne
#18 Le 23/06/2017, à 19:50
- paul18
Re : [clôt]Traitement de gros fichiers
premiers scripts développés sous Centos, et le novice que je suis vient de découvrir des incompatibilités avec ubuntu (sur mon portable)
ça me gave et autant mettre de l'énergie dans un langage qui lui sera portable
Merci pour le temps passé et les conseils, mais j'arrête là les frais
Paul
Hors ligne
#19 Le 23/06/2017, à 20:09
- Compte supprimé
Re : [clôt]Traitement de gros fichiers
???
Bash et Python sont portables...
Il manque peut-être l'entête du programme python, mais j'en viens à confondre le C et le Python.
#20 Le 23/06/2017, à 20:34
- Watael
Re : [clôt]Traitement de gros fichiers
ou le shebang du script bash, auquel cas le script est exécuté par /bin/sh qui n'est souvent pas un lien vers /bin/bash, et qui ne dispose pas de tableau.
les novices ne sont souvent pas compatibles avec Linux.
Edit:
pas compatible, pas compatible...
$ awk 'NR<=6 && NR>1 {tab[++n]=$0} n && NR==6 {print tab[1],tab[3],tab[4],tab[5]; NR=1; n=0}' "$fich_"
129 6.5 -1 0
128 7 -1.5 0
127 6.75 -1.25 5.E-1
126 6.25 -1.25 5.E-1
non, mais oh !
Dernière modification par Watael (Le 23/06/2017, à 20:52)
Connected \o/
Welcome to sHell. · eval is evil.
En ligne
#21 Le 23/06/2017, à 21:11
- pingouinux
Re : [clôt]Traitement de gros fichiers
Testés sur un fichier contenant 33554432 blocs de 5 lignes, mes scripts en #12 et #15 tournent respectivement en 6593 et 4082 secondes.
Le script en python3 qui suit traite le même fichier en 231 secondes.
$ cat ./script.py
#!/usr/bin/env python3
import time, sys
t0=time.time()
with open(sys.argv[1],'r') as f, open(sys.argv[2],'w') as g:
n_bloc=int(f.readline()[:-1])
for bloc in range(n_bloc):
nu_bloc=int(f.readline()[:-1])
g.write("%s "%nu_bloc)
n_don=int(f.readline()[:-1])
for don in range(n_don):
ligne=f.readline()[:-1]
g.write("%s "%ligne)
g.write("\n")
t1=time.time()
print(t1-t0)
À appeler ainsi
./script.py fichier fich_noeuds
Hors ligne
#22 Le 23/06/2017, à 21:14
- paul18
Re : [clôt]Traitement de gros fichiers
ou le shebang du script bash, auquel cas le script est exécuté par /bin/sh qui n'est souvent pas un lien vers /bin/bash, et qui ne dispose pas de tableau.
oui en copiant/collant un shell j'étais sous sh et non bash ; après les messages sous Ubuntu, j'ai corrigé et plusieurs lignes de codes conduisent à des erreurs (je suppose qu'elles ne respectent pas les règles)
quand je parle de gros fichiers, j'ai découpé le fichier principal et j'ai maintenant un fichier avec 754 000 blocs et plus de 3 millions de lignes (juste pour info)
Hors ligne
#23 Le 23/06/2017, à 21:21
- paul18
Re : [clôt]Traitement de gros fichiers
@puinguinux : merci pour cet exemple
A noter que dans des nombreux solveurs scientifiques, python est très utilisé pour le traitement de données, mais je n'en sentais pas le besoin ni l'envie de m'y mettre (ou la flemme aussi), mais là, plus le choix
Encore merci à tous pour ces posts
Paul
Hors ligne