У меня есть такой файл:
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
И я хотел бы подсчитать количество значений, которые включены в диапазон, например:
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
Как видите, я использую интервал 0,01. Я использую awk
для достижения этого, но получаю странное поведение:
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
Результат:
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
Кто-нибудь может мне помочь? Я думаю, <
что это не работает так, как ожидалось, потому что это удовлетворяет, когда $1 == j+0.01
. Конечно, я что-то не учитываю. Спасибо!
решение1
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
Переменные s
(начало), e
(конец) и d
(дельта/шаг) можно корректировать по мере необходимости.
Создание диапазона путем многократного сложения
0.01
— это практически хрестоматийный пример того, чего НЕ следует делать с числами с плавающей точкой, поскольку их0.01
невозможно точно представить в двоичной системе счисления, и ошибка будет накапливаться с каждым сложением.Сканирование всего диапазона для каждой строки неэффективно и бессмысленно.
Переменные в awk не обязательно должны быть инициализированы как
""
или0
.
решение2
Попробуй это:
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]}'
или вот это, что я нахожу менее понятным:
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]}'
О причине, по которой оригинал не сработал, см. замечание Квазимодо выше.
решение3
Не уверен, следует ли мне публиковать это как ответ или комментарий, но вот короткая демонстрация неточности и полученных в этом случае чисел на моем компьютере:
$ 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
Первое число — это то, как на самом деле хранится 0,01. Оно неточно, поскольку 1/100 включает в себя множитель 1/5, а его невозможно представить в двоичном виде.
Во-вторых, это то, как хранится 0,3, а в-третьих, это 0,01, добавленное само к себе 30 раз. (Это даже не то же самое, 0.01 * 30
потому что на каждом шаге есть промежуточное округление, я полагаю.)
У других ответов есть решения, проголосуйте за них.