
Ich habe eine Datei wie diese:
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
Und ich möchte die Anzahl der Werte zählen, die in einem Bereich enthalten sind, zB:
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
Wie Sie sehen, verwende ich ein Intervall von 0,01. Ich verwende, awk
um dies zu erreichen, aber es kommt zu einem seltsamen Verhalten:
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
Ergebnis:
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
Kann mir jemand helfen? Ich schätze, <
es funktioniert nicht wie erwartet, weil es erfüllt, wenn $1 == j+0.01
. Ich denke sicher nicht an etwas. Danke!
Antwort1
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
Die Variablen s
(Start), e
(Ende) und d
(Delta/Schritt) können nach Bedarf angepasst werden.
Das Erzeugen eines Bereichs durch wiederholtes Addieren
0.01
ist fast ein Musterbeispiel dafür, was man mit Gleitkommazahlen NICHT tun sollte, da diese0.01
nicht exakt in der Basis 2 dargestellt werden können und sich der Fehler mit jeder Addition summiert.Das Scannen des gesamten Bereichs für jede Zeile ist ineffizient und sinnlos.
Variablen in awk müssen nicht auf
""
oder initialisiert werden0
.
Antwort2
Probier diese:
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]}'
oder dies, was ich weniger verständlich finde:
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]}'
Informationen dazu, warum das Original nicht funktionierte, finden Sie in Quasimodos Anmerkung oben.
Antwort3
Ich bin nicht sicher, ob ich dies als Antwort oder als Kommentar posten soll, aber hier ist eine kurze Demonstration der Ungenauigkeit und der daraus resultierenden Zahlen in diesem Fall auf meinem Computer:
$ 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 wird tatsächlich als die erste Zahl gespeichert. Diese Zahl ist nicht genau, da 1/100 einen Faktor von 1/5 enthält und dieser nicht binär dargestellt werden kann.
Als zweites wird 0,3 gespeichert und als drittes wird 0,01 30 Mal zu sich selbst addiert. (Das ist wohl nicht einmal dasselbe, 0.01 * 30
weil es in jedem Schritt eine Zwischenrundung gibt.)
Andere Antworten enthalten Lösungen. Bewerten Sie sie.