Манипулировать научным форматом без «е»

8

Я пытаюсь манипулировать файлом, который содержит цифры в научной нотации, но без eсимвола, т.е. 1.2e+3записывается как 1.2+3.

Самой простой вещью, с которой я думал, awkбыло заменить ее +с e+помощью gsubфункции и выполнить мои вычисления в новом файле. То же самое касается минус дела. Таким образом, простое исправление может быть сделано с помощью следующей команды

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

и сделать то же самое во всех столбцах.

Однако файл также содержит отрицательные числа, что немного усложняет ситуацию. Пример файла можно увидеть ниже

 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

Любая идея о том, как манипулировать и расчеты с таким файлом?

Танос
источник
2
Как вы хотите производить расчеты в таком формате, как 2.698100e-2-2.034300e-4?
ctac_
3
Похоже, что он, вероятно, должен быть проанализирован как данные столбца фиксированной ширины . Кажущийся пробел между столбцами - это просто артефакт числового формата, в котором положительные значения отображаются с начальным пробелом вместо знака плюс.
Ильмари

Ответы:

14

Этот вывод правильный?

 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

Код:

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

Объяснение:

  • -lne позаботиться об окончаниях строк, обработать каждую строку ввода, выполнить следующий код

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

    • заменить ( s)
    • (.\d+)(\+|\-) найти две группы (точка и цифры) и (плюс или минус)
    • \1e\2замените их первой группой, затем eвторой группой
    • g глобально - не останавливайтесь на первой замене в каждой строке, но обрабатывайте все возможные попадания
  • print напечатать строку

  • sample входной файл

Этот добавляет пространство, если оно отсутствует. На самом деле это ставит пробел между числами независимо. То есть. если бы в некоторых случаях было два пробела, в выходных данных был бы только один.

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

Большая часть похожа на предыдущую. Новым является (\d+)группа № 3 и (\s*)группа № 4. *Здесь это означает, что необязательно. В подстановке нет \4используется. Вместо этого есть пробел.

Вывод такой:

 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 

источник
Большое спасибо за ответ! Да, это кажется правильным! Можете ли вы объяснить, что вы сделали, для дальнейшего использования?
Танос
Можно ли отделить последний столбец ($ 5 $) от предыдущего пробелом?
Танос
Вы идеальны! Спасибо большое за помощь!
Танос
@Thanos Смотрите обновление. И обратите внимание, я добавил обратную косую черту раньше .в первой группе. Это правильно. Без этой обратной косой черты точка не означала бы буквальную точку.
2

Вы также можете использовать sed, например:

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

Однако это не учитывает, что столбцы в списке OP иногда не разделяются. Вот обходной путь с соответствующей точностью:

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

Вывод:

1.056
20.4343
38.29842
41.68521
66.61784
72.78104
90.77287
92.4813
104.9935
121.6283
Тор
источник
Это убирает разрешение из чисел, и я не уверен, что оно будет работать, когда отрицательное значение будет рядом с другим, как в примере в вопросе2.698100-2-2.034300-4
труба
@pipe: Вы правы, я пропустил эту деталь. Я добавил обходной путь, добавив пробел. Wrt. точность, я использовал OFMTпеременную, чтобы установить точность awk,
Thor