Tengo un archivo como este:
0.2
0.2
0.2
0.2
0.2
0.2
0.2024
0.2025
0.2027
0.2027
0.2029
0.2059
0.2059
0.2059
0.2059
0.2099
0.2099
0.2099
0.2105
0.2113
0.2113
0.2195
0.2198
0.2206
0.2206
0.2206
0.2989
0.2989
0.2989
0.3
0.3
Y me gustaría contar la cantidad de valores que se incluyen en un rango, por ejemplo:
0.2 18
0.21 5
0.22 3
0.23 0
0.24 0
0.25 0
0.26 0
0.27 0
0.28 0
0.29 3
0.3 2
Como puede ver, utilizo un intervalo de 0,01. Lo estoy usando awk
para lograrlo pero obtengo un comportamiento extraño:
awk 'BEGIN {for (i=0;i<1.01;i+=0.01) a[i]=0} {
for (j=0;j<=1;j+=0.01)
if($1>=j && $1<j+0.01) {
a[j]+=1
}
}
END {for (k=0;k<1.01;k+=0.01) print k,a[k]}' test_OH.txt
Resultado:
0.19 6
0.2 12
0.21 5
0.22 3
0.23 0
0.24 0
0.25 0
0.26 0
0.27 0
0.28 0
0.29 5
0.3 0
¿Alguien podría ayudarme? Supongo <
que no funciona como se esperaba porque satisface cuando $1 == j+0.01
. Seguramente no estoy considerando nada. ¡Gracias!
Respuesta1
awk -v s=0.2 -v e=0.3 -v d=0.01 '
BEGIN { m = 1/d }
{ a[int($1*m)]++ }
END{ e *= m; for(s = int(s*m); s <= e; s++) print s*d, a[s]+0 }
' test_OH.txt
0.2 18
0.21 5
0.22 3
0.23 0
0.24 0
0.25 0
0.26 0
0.27 0
0.28 0
0.29 3
0.3 2
Las variables s
(inicio) e
(fin) y d
(delta/paso) se pueden ajustar según sea necesario.
Generar un rango sumando repetidamente
0.01
es casi un ejemplo de libro de texto de lo que NO se debe hacer con números de coma flotante, porque0.01
no se pueden representar exactamente en base 2 y el error se acumulará con cada suma.escanear todo el rango para cada línea es ineficiente y inútil.
Las variables en awk no tienen que inicializarse en
""
o0
.
Respuesta2
Prueba este:
awk '
BEGIN {for (i=0;i<1.01;i+=0.01) a[i]=0}
{
n = int($1 * 100) / 100
a[n] += 1
}
END {for (k=0;k<1.01;k+=0.01) print k,a[k]}'
o esto, que me parece menos comprensible:
awk 'BEGIN {for (i=0;i<1.01;i+=0.01) a[i]=0} {
for (j=0;j<=1;j+=0.01)
if(("X" $1 >= "X" j) && ("X" $1 < "X" j+0.01)) {
a[j]+=1
}
}
END {for (k=0;k<1.01;k+=0.01) print k,a[k]}'
Para conocer la razón por la que el original no funcionó, consulte el comentario de Quasimodo arriba.
Respuesta3
No estoy seguro de si debo publicar esto como respuesta o comentario, pero aquí hay una breve demostración de la inexactitud y los números resultantes en este caso, en mi máquina:
$ awk 'BEGIN { d = 0.01; printf "%.20f\n", d; for (i = 0; i < 30; i++) a += d; printf "%.20f\n%.20f\n", 0.3, a }'
0.01000000000000000021
0.29999999999999998890
0.30000000000000009992
El primer número es como realmente se almacena 0.01, no es exacto ya que 1/100 incluye un factor 1/5 y eso no se puede representar en binario.
El segundo es como se almacena 0.3, y el tercero es 0.01 agregado a sí mismo 30 veces. (Supongo que eso ni siquiera es lo mismo 0.01 * 30
porque hay un redondeo intermedio en cada paso).
Otras respuestas tienen soluciones, votalas.