Contenu | Rechercher | Menus

Annonce

Si vous avez des soucis pour rester connecté, déconnectez-vous puis reconnectez-vous depuis ce lien en cochant la case
Me connecter automatiquement lors de mes prochaines visites.

À propos de l'équipe du forum.

#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 tongue ; 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 smile

En fait j'ai l'impression qu'il balaie tout le fichier à chaque instruction hmm
(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.

Hors 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 hmm

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.

Hors 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) mad

ç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. big_smile

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.

Hors 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) wink

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