Operador relacional regular '

Operador relacional regular '

Eu tenho um arquivo 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

E eu gostaria de contar o número de valores incluídos em um intervalo, por exemplo:

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 você pode ver, uso um intervalo de 0,01. estou a usarawk para conseguir isso, mas estou tendo um comportamento estranho:

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

Alguém poderia me ajudar? Eu acho que <não está funcionando como esperado porque satisfaz quando $1 == j+0.01. Certamente, não estou considerando nada. Obrigado!

Responder1

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

O s(início) e(fim) ed (delta/etapa) podem ser ajustadas conforme necessário.


  1. gerando um intervalo adicionando repetidamente0.01 é quase um exemplo clássico do que NÃO fazer com números de ponto flutuante, porque 0.01não pode ser representado exatamente na base 2 e o erro se acumulará a cada adição.

  2. digitalizar todo o intervalo para cada linha é ineficiente e inútil.

  3. variáveis ​​​​no awk não precisam ser inicializadas como ""ou 0.

Responder2

Tente 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]}'

ou isto, que considero menos compreensível:

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 saber o motivo pelo qual o original não funcionou, veja a observação de Quasimodo acima.

Responder3

Não tenho certeza se devo postar isso como resposta ou comentário, mas aqui está uma breve demonstração da imprecisão e dos números resultantes neste caso, na minha 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

O primeiro número é como 0,01 realmente é armazenado; não é preciso, pois 1/100 inclui um fator 1/5 e isso não pode ser representado em binário.

O segundo é como 0,3 é armazenado e o terceiro é 0,01 adicionado a si mesmo 30 vezes. (Isso nem é o mesmo que0.01 * 30 porque há arredondamento intermediário em cada etapa, suponho.)

Outras respostas têm soluções, vote nelas.

informação relacionada