El operador relacional normal '

El operador relacional normal '

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 awkpara 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.


  1. Generar un rango sumando repetidamente 0.01es casi un ejemplo de libro de texto de lo que NO se debe hacer con números de coma flotante, porque 0.01no se pueden representar exactamente en base 2 y el error se acumulará con cada suma.

  2. escanear todo el rango para cada línea es ineficiente y inútil.

  3. Las variables en awk no tienen que inicializarse en ""o 0.

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 * 30porque hay un redondeo intermedio en cada paso).

Otras respuestas tienen soluciones, votalas.

información relacionada