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.
gerando um intervalo adicionando repetidamente
0.01
é quase um exemplo clássico do que NÃO fazer com números de ponto flutuante, porque0.01
não pode ser representado exatamente na base 2 e o erro se acumulará a cada adição.digitalizar todo o intervalo para cada linha é ineficiente e inútil.
variáveis no awk não precisam ser inicializadas como
""
ou0
.
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.