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?


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 

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

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
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.