Der reguläre relationale Operator '

Der reguläre relationale Operator '

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, awkum 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.


  1. Das Erzeugen eines Bereichs durch wiederholtes Addieren 0.01ist fast ein Musterbeispiel dafür, was man mit Gleitkommazahlen NICHT tun sollte, da diese 0.01nicht exakt in der Basis 2 dargestellt werden können und sich der Fehler mit jeder Addition summiert.

  2. Das Scannen des gesamten Bereichs für jede Zeile ist ineffizient und sinnlos.

  3. Variablen in awk müssen nicht auf ""oder initialisiert werden 0.

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 * 30weil es in jedem Schritt eine Zwischenrundung gibt.)

Andere Antworten enthalten Lösungen. Bewerten Sie sie.

verwandte Informationen