Memanipulasi format ilmiah tanpa "e"

8

Saya mencoba memanipulasi file yang berisi angka dalam notasi ilmiah, tetapi tanpa esimbol, yaitu 1.2e+3ditulis sebagai 1.2+3.

Hal termudah yang saya pikirkan awkadalah mengganti +dengan e+, menggunakan gsubfungsi dan melakukan perhitungan saya di file baru. Hal yang sama berlaku untuk case minus. Jadi perbaikan sederhana dapat dilakukan dengan menggunakan perintah berikut

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

dan lakukan hal yang sama di semua kolom.

Namun file tersebut juga berisi angka negatif yang membuat segalanya sedikit lebih rumit. File sampel dapat dilihat di bawah

 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

Adakah ide tentang cara memanipulasi dan perhitungan dengan file seperti itu?

Terima kasih
sumber
2
Bagaimana Anda ingin membuat perhitungan dengan format seperti itu 2.698100e-2-2.034300e-4?
ctac_
3
Sepertinya ini mungkin diurai sebagai data kolom dengan lebar tetap . Ruang kosong yang tampak di antara kolom hanyalah artifak dari format angka yang menampilkan nilai positif dengan spasi terdepan alih-alih tanda tambah.
Ilmari Karonen

Jawaban:

14

Apakah output ini benar?

 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

Kode:

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

Penjelasan:

  • -lne urus ujung jalur, proses setiap jalur input, jalankan kode yang mengikuti

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

    • pengganti ( s)
    • (.\d+)(\+|\-) temukan dua kelompok (titik dan angka) dan (plus atau minus)
    • \1e\2gantikan mereka dengan kelompok pertama kemudian ekelompok kedua
    • g secara global - jangan berhenti pada subtitusi pertama di setiap baris, tetapi proses semua hit yang mungkin
  • print cetak garis

  • sample masukan file

Yang ini menambah ruang jika tidak ada. Bahkan itu membuat ruang di antara angka-angka terlepas. Yaitu. jika ada dua ruang dalam beberapa kasus, hanya akan ada satu di output.

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

Sebagian besar mirip dengan yang sebelumnya. Yang baru adalah (\d+)grup nr 3 dan (\s*)grup nr 4. di *sini berarti opsional. Dalam substitusi tidak \4digunakan. Ada ruang sebagai gantinya.

Outputnya adalah ini:

 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 

sumber
Terima kasih banyak atas jawabannya! Ya sepertinya benar !! Bisakah Anda menjelaskan apa yang Anda lakukan, untuk referensi di masa mendatang?
Thanos
Apakah mungkin untuk memisahkan kolom terakhir ($ 5) dari kolom sebelumnya dengan spasi?
Thanos
Kamu sempurna! Terima kasih banyak atas bantuan Anda!
Thanos
@Hanos Lihat pembaruan. Dan perhatikan saya menambahkan backslash sebelumnya .di grup pertama. Ini benar. Tanpa garis miring terbalik ini, titik tidak akan berarti titik literal.
2

Anda juga dapat menggunakan sed, misalnya:

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

Namun, ini tidak memperhitungkan bahwa kolom dalam daftar OP terkadang tidak dipisahkan. Berikut ini solusinya dengan presisi yang sesuai:

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

Keluaran:

1.056
20.4343
38.29842
41.68521
66.61784
72.78104
90.77287
92.4813
104.9935
121.6283
Thor
sumber
Ini menghilangkan resolusi dari angka-angka, dan saya tidak yakin itu akan berfungsi ketika nilai negatif di sebelah yang lain seperti contoh dalam pertanyaan2.698100-2-2.034300-4
pipa
@pipe: Anda benar, saya melewatkan detail itu. Saya telah menambahkan solusi dengan menambah ruang. Wrt. presisi, saya menggunakan OFMTvariabel untuk mengatur presisi awk sama dengan input
Thor