Manipulez le format scientifique sans le «e»

8

J'essaie de manipuler un fichier qui contient des nombres en notation scientifique, mais sans le esymbole, c'est 1.2e+3-à- dire écrit comme 1.2+3.

La chose la plus simple à laquelle j'ai pensé awkétait de remplacer +par e+, en utilisant la gsubfonction et de faire mon calcul dans le nouveau fichier. Il en va de même pour le cas négatif. Donc, un simple correctif pourrait être fait en utilisant la commande suivante

awk '{gsub("+", "e+", $1); print $1, $2, $3, $4, $5}' file_in

et faites de même dans toutes les colonnes.

Cependant, le fichier contient également des nombres négatifs, ce qui complique un peu les choses. Un exemple de fichier peut être vu ci-dessous

 1.056000+0 5.000000-1 2.454400-3 2.914800-2 8.141500-6
 2.043430+1 5.000000-1 2.750500-3 2.698100-2-2.034300-4
 3.829842+1 5.000000-1 1.969923-2 2.211364-2 9.499900-6
 4.168521+1 5.000000-1 1.601262-2 3.030919-2-3.372000-6
 6.661784+1 5.000000-1 5.250575-2 3.443669-2 2.585500-5
 7.278104+1 5.000000-1 2.137055-2 2.601701-2 8.999800-5
 9.077287+1 5.000000-1 1.320498-2 2.961020-2-1.011600-5
 9.248130+1 5.000000-1 3.069610-3 2.786329-2-6.317000-5
 1.049935+2 5.000000-1 4.218794-2 3.321955-2-5.097000-6
 1.216283+2 5.000000-1 1.432105-2 3.077165-2 4.300300-5

Une idée sur la façon de manipuler et de calculer avec un tel fichier?

Thanos
la source
2
Comment voulez-vous faire des calculs avec un format comme celui 2.698100e-2-2.034300e-4?
ctac_
3
Il semble que cela soit probablement destiné à être analysé en tant que données de colonne à largeur fixe . L'espace blanc apparent entre les colonnes n'est qu'un artefact du format numérique affichant des valeurs positives avec un espace de tête au lieu d'un signe plus.
Ilmari Karonen

Réponses:

14

Cette sortie est-elle correcte?

 1.056000e+0 5.000000e-1 2.454400e-3 2.914800e-2 8.141500e-6
 2.043430e+1 5.000000e-1 2.750500e-3 2.698100e-2-2.034300e-4
 3.829842e+1 5.000000e-1 1.969923e-2 2.211364e-2 9.499900e-6
 4.168521e+1 5.000000e-1 1.601262e-2 3.030919e-2-3.372000e-6
 6.661784e+1 5.000000e-1 5.250575e-2 3.443669e-2 2.585500e-5
 7.278104e+1 5.000000e-1 2.137055e-2 2.601701e-2 8.999800e-5
 9.077287e+1 5.000000e-1 1.320498e-2 2.961020e-2-1.011600e-5
 9.248130e+1 5.000000e-1 3.069610e-3 2.786329e-2-6.317000e-5
 1.049935e+2 5.000000e-1 4.218794e-2 3.321955e-2-5.097000e-6
 1.216283e+2 5.000000e-1 1.432105e-2 3.077165e-2 4.300300e-5

Code:

perl -lne 's/(\.\d+)(\+|\-)/\1e\2/g; print' sample

Explication:

  • -lne prendre soin des fins de ligne, traiter chaque ligne d'entrée, exécuter le code qui suit

  • s/(\.\d+)(\+|\-)/\1e\2/g:

    • substitut ( s)
    • (.\d+)(\+|\-) trouver deux groupes de (un point et des nombres) et (un plus ou un moins)
    • \1e\2les remplacer par le premier groupe epuis le deuxième groupe
    • g globalement - ne vous arrêtez pas à la première substitution de chaque ligne, mais traitez tous les hits possibles
  • print imprimer la ligne

  • sample fichier d'entrée

Celui-ci ajoute de l'espace s'il manque. En fait, cela met de l'espace entre les nombres malgré tout. C'est à dire. s'il y avait deux espaces dans certains cas, il n'y en aurait qu'un dans la sortie.

perl -lne 's/(\.\d+)(\+|\-)(\d+)(\s*)/\1e\2\3 /g; print' sample

La majeure partie est similaire à la précédente. La nouveauté est le (\d+)groupe n ° 3 et le (\s*)groupe n ° 4. *signifie ici facultatif. Dans la substitution, aucun \4n'est utilisé. Il y a un espace à la place.

La sortie est la suivante:

 1.056000e+0 5.000000e-1 2.454400e-3 2.914800e-2 8.141500e-6 
 2.043430e+1 5.000000e-1 2.750500e-3 2.698100e-2 -2.034300e-4 
 3.829842e+1 5.000000e-1 1.969923e-2 2.211364e-2 9.499900e-6 
 4.168521e+1 5.000000e-1 1.601262e-2 3.030919e-2 -3.372000e-6 
 6.661784e+1 5.000000e-1 5.250575e-2 3.443669e-2 2.585500e-5 
 7.278104e+1 5.000000e-1 2.137055e-2 2.601701e-2 8.999800e-5 
 9.077287e+1 5.000000e-1 1.320498e-2 2.961020e-2 -1.011600e-5 
 9.248130e+1 5.000000e-1 3.069610e-3 2.786329e-2 -6.317000e-5 
 1.049935e+2 5.000000e-1 4.218794e-2 3.321955e-2 -5.097000e-6 
 1.216283e+2 5.000000e-1 1.432105e-2 3.077165e-2 4.300300e-5 

la source
Merci beaucoup pour la réponse! Oui ça semble correct !! Pouvez-vous expliquer ce que vous avez fait, pour référence future?
Thanos
Est-il également possible de séparer la dernière colonne (5 $) de la précédente par un espace?
Thanos
Tu est parfait! Merci beaucoup pour votre aide!
Thanos
@Thanos Voir la mise à jour. Et remarquez que j'ai ajouté une barre oblique inverse .dans le premier groupe. C'est correct. Sans cette barre oblique inverse, le point ne signifierait pas un point littéral.
2

Vous pouvez également utiliser sed, par exemple:

<infile sed -E 's/([0-9])([+-])([0-9])/\1e\2\3/g' | awk '{ print $1 + 0 }'

Cependant, cela ne tient pas compte du fait que les colonnes de la liste OP ne sont parfois pas séparées. Voici une solution de contournement avec une précision appropriée:

<infile sed -E 's/.{11}/& /g'       |
sed -E 's/([0-9])([+-])/\1e\2/g'    |
gawk '{ print $1 + 0 }' OFMT='%.7g'

Production:

1.056
20.4343
38.29842
41.68521
66.61784
72.78104
90.77287
92.4813
104.9935
121.6283
Thor
la source
Cela supprime la résolution des nombres, et je ne suis pas sûr que cela fonctionnera quand une valeur négative est à côté d'une autre comme l'exemple dans la question2.698100-2-2.034300-4
pipe
@pipe: Vous avez raison, j'ai raté ce détail. J'ai ajouté une solution de contournement en ajoutant de l'espace. Wrt. précision, j'ai utilisé la OFMTvariable pour définir la précision de awk sur la même valeur que celle de l'entrée
Thor