Manipule o formato científico sem o "e"

8

Estou tentando manipular um arquivo que contém números em notação científica, mas sem o esímbolo, ou seja, 1.2e+3está escrito como 1.2+3.

A coisa mais fácil que pensei em fazer awkfoi substituir +por e+, usando a gsubfunção e fazer meu cálculo no novo arquivo. O mesmo vale para o caso negativo. Portanto, uma correção simples pode ser feita usando o seguinte comando

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

e faça o mesmo em todas as colunas.

No entanto, o arquivo também contém números negativos, o que torna as coisas um pouco mais complicadas. Um arquivo de exemplo pode ser visto abaixo

 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

Alguma idéia de como manipular e cálculos com esse arquivo?

Thanos
fonte
2
Como você deseja fazer cálculos com um formato como o 2.698100e-2-2.034300e-4?
Ctac_
3
Parece que provavelmente foi feito para ser analisado como dados de coluna de largura fixa . O espaço em branco aparente entre as colunas é apenas um artefato do formato numérico que exibe valores positivos com um espaço à esquerda em vez de um sinal de adição.
Ilmari Karonen 5/03/19

Respostas:

14

Esta saída está correta?

 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

Código:

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

Explicação:

  • -lne cuide das terminações da linha, processe cada linha de entrada, execute o código a seguir

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

    • substituto ( s)
    • (.\d+)(\+|\-) encontre dois grupos de (um ponto e números) e (um mais ou menos)
    • \1e\2substitua-os pelo primeiro grupo e edepois pelo segundo grupo
    • g globalmente - não pare na primeira substituição em cada linha, mas processe todos os hits possíveis
  • print imprima a linha

  • sample Arquivo de entrada

Este adiciona espaço se estiver faltando. De fato, coloca espaço entre os números, independentemente. Ou seja. se houvesse dois espaços em algum caso, haveria apenas um na saída.

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

A maior parte é semelhante à anterior. A novidade é o (\d+)grupo nº 3 e o (\s*)grupo nº 4. *aqui significa opcional. Na substituição, não \4é usado. Há um espaço em seu lugar.

A saída é esta:

 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 

fonte
Muito obrigado pela resposta! Sim, parece correto !! Você pode explicar o que fez, para referência futura?
Thanos
Também é possível separar a última coluna ($ 5 $) da anterior com um espaço?
Thanos
Você é perfeito! Muito obrigado pela sua ajuda!
Thanos
@ Thanos Veja a atualização. E observe que eu adicionei uma barra invertida antes .no primeiro grupo. Isto está correto. Sem essa barra invertida, o ponto não significaria um ponto literal.
2

Você também pode usar sed, por exemplo:

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

No entanto, isso não leva em consideração que as colunas na listagem do OP às vezes não são separadas. Aqui está uma solução alternativa com precisão apropriada:

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

Resultado:

1.056
20.4343
38.29842
41.68521
66.61784
72.78104
90.77287
92.4813
104.9935
121.6283
Thor
fonte
Isso remove a resolução dos números e não tenho certeza se funcionará quando um valor negativo estiver próximo a outro como o exemplo da pergunta2.698100-2-2.034300-4
canal
@ pipe: Você está certo, eu perdi esse detalhe. Eu adicionei uma solução alternativa adicionando espaço. Wrt. precisão, eu usei a OFMTvariável a precisão conjunto de awk para o mesmo que o de entrada
Thor