Manipular formato científico sin la "e"

8

Estoy tratando de manipular un archivo que contiene números en notación científica, pero sin el esímbolo, 1.2e+3es decir, está escrito como 1.2+3.

Lo más fácil que pensé hacer awkfue reemplazar +con e+, usar la gsubfunción y hacer mi cálculo en el nuevo archivo. Lo mismo ocurre con el caso negativo. Entonces, se podría hacer una solución simple usando el siguiente comando

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

y hacer lo mismo en todas las columnas.

Sin embargo, el archivo también contiene números negativos, lo que hace las cosas un poco más complicadas. A continuación se puede ver un archivo de muestra

 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

¿Alguna idea sobre cómo manipular y calcular con un archivo así?

Thanos
fuente
2
¿Cómo desea hacer cálculos con un formato como ese 2.698100e-2-2.034300e-4?
ctac_
3
Parece que probablemente se debe analizar como datos de columna de ancho fijo . El espacio en blanco aparente entre columnas es solo un artefacto del formato de número que muestra valores positivos con un espacio inicial en lugar de un signo más.
Ilmari Karonen

Respuestas:

14

¿Es correcta esta salida?

 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

Explicación:

  • -lne cuidar las terminaciones de línea, procesar cada línea de entrada, ejecutar el código que sigue

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

    • sustituto ( s)
    • (.\d+)(\+|\-) encontrar dos grupos de (un punto y números) y (un más o menos)
    • \1e\2sustituirlos con el primer grupo y eluego con el segundo grupo
    • g globalmente: no se detenga en la primera sustitución en cada línea, sino que procese todos los golpes posibles
  • print imprime la línea

  • sample fichero de entrada

Este agrega espacio si falta. De hecho, pone espacio entre los números independientemente. Es decir. si hubiera dos espacios en algún caso, solo habría uno en la salida.

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

La mayor parte es similar a la anterior. Lo nuevo es el (\d+)grupo nr 3 y el (\s*)grupo nr 4. *aquí significa opcional. En la sustitución no \4se utiliza. Hay un espacio en su lugar.

La salida es 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 

fuente
¡Muchas gracias por la respuesta! Si parece correcto !! ¿Puede explicar lo que hizo, para referencia futura?
Thanos
¿También es posible separar la última columna ($ 5 $) de la anterior con un espacio?
Thanos
¡Eres perfecto! ¡Muchas gracias por su ayuda!
Thanos
@Thanos Ver la actualización. Y note que agregué una barra invertida antes .en el primer grupo. Esto es correcto. Sin esta barra invertida, el punto no significaría un punto literal.
2

También podría usar sed, por ejemplo:

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

Sin embargo, esto no tiene en cuenta que las columnas en la lista de OP a veces no están separadas. Aquí hay una solución alternativa con la precisión adecuada:

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

Salida:

1.056
20.4343
38.29842
41.68521
66.61784
72.78104
90.77287
92.4813
104.9935
121.6283
Thor
fuente
Esta resolución elimina de los números, y no estoy seguro de que funcionará cuando un valor negativo está al lado de otro como el ejemplo de la pregunta2.698100-2-2.034300-4
tubo de
@pipe: Tienes razón, me perdí ese detalle. He agregado una solución agregando espacio. Wrt. precisión, usé la OFMTvariable para establecer la precisión de awk a la misma que la entrada
Thor