Manipola il formato scientifico senza la "e"


8

Sto cercando di manipolare un file che contiene numeri in notazione scientifica, ma senza il esimbolo, cioè 1.2e+3è scritto come 1.2+3.

La cosa più facile ho pensato di fare con awkera quello di sostituire +con e+, utilizzando la gsubfunzione di e fare il mio calcolo nel nuovo file. Lo stesso vale per il caso negativo. Quindi una semplice correzione potrebbe essere fatta usando il seguente comando

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

e fare lo stesso in tutte le colonne.

Tuttavia, il file contiene anche numeri negativi che rendono le cose un po 'più complicate. Un file di esempio può essere visualizzato sotto

 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

Qualche idea su come manipolare e calcoli con un tale file?


2
Come vuoi fare calcoli con un formato come quello 2.698100e-2-2.034300e-4?
ctac_

3
Sembra che probabilmente debba essere analizzato come dati di colonne a larghezza fissa . Lo spazio bianco apparente tra le colonne è solo un artefatto del formato numerico che mostra valori positivi con uno spazio iniziale anziché un segno più.
Ilmari Karonen,

Risposte:


14

Questo output è corretto?

 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

Codice:

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

Spiegazione:

  • -lne prendersi cura delle terminazioni di riga, elaborare ciascuna riga di input, eseguire il codice che segue

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

    • sostituto ( s)
    • (.\d+)(\+|\-) trova due gruppi di (un punto e numeri) e (un segno più o meno)
    • \1e\2sostituirli con il primo gruppo equindi con il secondo gruppo
    • g a livello globale - non fermarti alla prima sostituzione in ogni riga, ma elabora tutti i possibili colpi
  • print stampa la linea

  • sample file di input

Questo aggiunge spazio se manca. In effetti mette lo spazio tra i numeri a prescindere. Vale a dire. se in qualche caso esistessero due spazi, ne verrebbe solo uno nell'output.

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

La maggior parte è simile alla precedente. La novità è il (\d+)gruppo n. 3 e il (\s*)gruppo n. 4. *qui significa facoltativo. Nella sostituzione non \4viene utilizzato. C'è invece uno spazio.

L'output è questo:

 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 

Grazie mille per la risposta! Sì sembra corretto !! Puoi spiegare cosa hai fatto, per riferimento futuro?
Thanos,

È anche possibile separare l'ultima colonna ($ 5 $) dalla precedente con uno spazio?
Thanos,

Tu sei perfetto! Grazie mille per il tuo aiuto!
Thanos,

@Grazie Vedi l'aggiornamento. E nota che ho aggiunto una barra rovesciata prima .nel primo gruppo. Questo è corretto. Senza questa barra rovesciata il punto non significherebbe un punto letterale.

2

Puoi anche usare sed, ad esempio:

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

Tuttavia, ciò non tiene conto del fatto che le colonne nell'elenco dell'OP a volte non sono separate. Ecco una soluzione alternativa con la precisione appropriata:

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

Produzione:

1.056
20.4343
38.29842
41.68521
66.61784
72.78104
90.77287
92.4813
104.9935
121.6283

Questo rimuove la risoluzione dai numeri e non sono sicuro che funzionerà quando un valore negativo è accanto a un altro come nell'esempio nella domanda2.698100-2-2.034300-4
pipe

@pipe: hai ragione, ho perso quel dettaglio. Ho aggiunto una soluzione alternativa aggiungendo spazio. WRT. precisione, ho usato la OFMTvariabile per impostare la precisione di awk sullo stesso di quella dell'input
Thor
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.