Einer der wichtigsten Aspekte einer Anwendung ist die Überprüfung ihrer Eingaben. Der grundlegendste Ansatz schlägt fehl, wenn die Eingabe die Anforderungen nicht erfüllt. In vielen Fällen reicht dies jedoch nicht aus. In vielen Systemen ist die Datenerfassung von der Datenanalyse getrennt. Dies kann eine Umfrage oder ein alter Datensatz sein.
In diesen Fällen müssen Sie vor der Analyse das gesamte Dataset durchgehen, ungültige oder fehlende Daten ermitteln, beheben, was behoben werden kann, und Daten kennzeichnen oder entfernen, die nicht gerettet werden können. Es ist auch nützlich, Statistiken über die Qualität der Daten und die Art der Fehler bereitzustellen.
In dieser zweiteiligen Serie erfahren Sie, wie Sie die Textfunktionen von Go verwenden, CSV-Dateien schneiden und würfeln und sicherstellen, dass Ihre Daten makellos sauber sind. Im ersten Teil konzentrieren wir uns auf die Grundlagen der Textverarbeitung in Go-Bytes, Runen und Strings sowie auf die Arbeit mit CSV-Dateien.
Bevor wir uns mit der Datenbereinigung beschäftigen, beginnen wir mit der Textgrundlage in Go. Die Bausteine bestehen aus Bytes, Runen und Strings. Mal sehen, was jeder repräsentiert und wie die Beziehungen zwischen ihnen sind.
Bytes sind 8-Bit-Zahlen. Jedes Byte kann einen von 256 möglichen Werten darstellen (2 bis 8). Jedes Zeichen im ASCII-Zeichensatz kann durch ein einzelnes Byte dargestellt werden. Aber bytes sind nicht Zeichen. Der Grund ist, dass Go als moderne Sprache Unicode unterstützt, wobei es weit mehr als 256 separate Zeichen gibt. Runen eingeben.
Eine Rune in Go ist ein anderer Name für den Typ int32. Dies bedeutet, dass jede Rune mehr als vier Milliarden separate Werte darstellen kann (2 bis 32), was für den gesamten Unicode-Zeichensatz ausreichend ist.
Im folgenden Code sehen Sie, dass die Rune '∆' (alt-J auf dem Mac) nur eine int32 ist. Um das Zeichen, das es darstellt, auf den Bildschirm zu drucken, muss ich es in eine Zeichenfolge konvertieren.
Paket main import ("fmt") func main () r: = '∆' fmt.Println (r) fmt.Println (int32 (r)) fmt.Println (string (r)) Ausgabe: 8710 8710 ∆
Unicode ist kompliziert. Eine Rune repräsentiert offiziell einen Unicode-Codepunkt. Unicode-Zeichen werden normalerweise durch einen einzelnen Unicode-Codepunkt dargestellt, manchmal jedoch auch mehr als ein.
Zeichenketten sind offiziell nur schreibgeschützte Byte-Abschnitte. Wenn Sie eine Zeichenfolge indizieren, erhalten Sie ein Byte zurück:
func main () s: = "abc" für i: = 0; ich < len(s); i++ fmt.Println(s[i]) Output: 97 98 99
String-Literale sind eine Folge von UTF-8-Zeichen in Anführungszeichen. Sie können Escape-Sequenzen enthalten, die ein Backslash sind, gefolgt von einem ASCII-Zeichen wie \ n
(Newline) oder \ t
(Tab). Sie haben besondere Bedeutungen. Hier ist die vollständige Liste:
\ a U + 0007 Alarm oder Glocke \ b U + 0008 Rücktaste \ f U + 000C-Formularvorschub \ n U + 000A-Zeilenvorschub oder Zeilenvorschub \ r U + 000D Wagenrücklauf \ t U + 0009 horizontaler Tab \ v U + 000b vertikal tab \\ U + 005c backslash \ 'U + 0027 einfaches Anführungszeichen (nur in Runen-Literalen gültig) \ "U + 0022-Anführungszeichen (nur in String-Literalen gültig)
Manchmal möchten Sie möglicherweise Literalbytes unabhängig von Escape-Sequenzen direkt in einer Zeichenfolge speichern. Sie könnten jedem Backslash entkommen, aber das ist langweilig. Ein viel besserer Ansatz ist die Verwendung von rohen Zeichenketten, die in Backticks eingeschlossen sind.
Hier ist ein Beispiel für einen String mit einem \ t
(tab) Escape-Sequenz, die einmal dargestellt wird, dann mit dem Escape-Backslash und dann als Roh-String:
func main () s1: = "1 \ t2" s2: = "1 \\ t2" s3: = '1 \ t2' fmt.Println (s1) fmt.Println (s2) fmt.Println (s3) Ausgabe : 1 2 1 \ t2 1 \ t2
Während Strings Byte-Abschnitte sind, erhalten Sie, wenn Sie mit einer For-Range-Anweisung einen String durchlaufen, in jeder Iteration eine Rune. Dies bedeutet, dass Sie ein oder mehrere Bytes erhalten können. Dies ist mit dem Index für die Bereichsreichweite leicht zu erkennen. Hier ist ein verrücktes Beispiel. Das hebräische Wort "שלום" bedeutet "Hallo" (und Frieden). Hebräisch wird auch von rechts nach links geschrieben. Ich konstruiere eine Zeichenfolge, die das hebräische Wort mit seiner englischen Übersetzung mischt.
Dann drucke ich es rune für rune aus, einschließlich des Byteindexes jeder rune in der Zeichenfolge. Wie Sie sehen, benötigt jede hebräische Rune zwei Bytes, während die englischen Zeichen ein Byte benötigen. Die Gesamtlänge dieser Zeichenfolge beträgt also 16 Bytes, obwohl sie vier hebräische Zeichen, drei Symbole und fünf englische Zeichen (12 Zeichen) enthält ). Die hebräischen Zeichen werden auch von rechts nach links angezeigt:
func main () hello: = "שלום = hallo" fmt.Println ("length:", len (hallo)) für i, r: = range (hallo) fmt.Println (i, string (r)) Ausgabe: Länge: 16 0 ש 2 4 4 6 6 ם 8 9 = 10 11 h 12 e 13 l 14 l 15 o
Alle diese Nuancen können äußerst wichtig sein, wenn Sie ein Dataset mit bizarren Anführungszeichen und einer Mischung aus Unicode-Zeichen und Symbolen aufräumen.
Beim Drucken von Strings und Byte-Slices gibt es mehrere Formatbezeichner, die für beide gleich sind. Das % s
format druckt die Bytes so wie sie sind, % x
gibt zwei hexadezimale Kleinbuchstaben pro Byte aus, % X
gibt zwei hexadezimale Großbuchstaben pro Byte aus und % q
druckt einen String mit doppelten Anführungszeichen und die Escape-Syntax.
Um das% -Zeichen in einem Formatstring-Bezeichner zu umgehen, verdoppeln Sie es einfach. Um die Bytes bei der Verwendung zu trennen % x
oder % X
, Sie können ein Leerzeichen wie in "% x" und "% X" hinzufügen. Hier ist die Demo:
func main () s: = "שלום" fmt.Printf ("%% s format:% s \ n", s) fmt.Printf ("%% x format:% x \ n", s) fmt.Printf ("%% X-Format:% X \ n", s) fmt.Printf ("%% x-Format:% x \ n", s) fmt.Printf ("%% X-Format:% X \ n", s ) fmt.Printf ("%% q-Format:% q \ n", s) Ausgabe:% s-Format: Format% x Format: d7a9d79cd795d79d% X-Format: D7A9D79CD795D79D% x Format: d7 a9 d7 9c d7 9d% X-Format: D7 A9 D7 9C D7 95 D7 9D% q Format: "שלום"
Daten können auf viele Arten und in verschiedenen Formaten ankommen. Eines der gebräuchlichsten Formate ist CSV (durch Kommas getrennte Werte). CSV-Daten sind sehr effizient. Die Dateien haben normalerweise eine Kopfzeile mit dem Namen der Felder oder Spalten und Datenzeilen, wobei jede Zeile einen durch Kommas getrennten Wert pro Feld enthält.
Hier ist ein kleiner Ausschnitt aus einem UFO-Sichtungsdatensatz (wirklich). Die erste Zeile (Kopfzeile) enthält die Spaltennamen und die anderen Zeilen enthalten die Daten. Sie sehen oft, dass die Spalte "Colors Reported" leer ist:
Stadt, berichtete Farben, berichtete Form, Zustand, Uhrzeit Ithaca ,, TRIANGLE, NY, 6/1/1930 22:00 Willingboro ,, ANDERE, NJ, 6/30/1930 20:00 Holyoke ,, OVAL, CO, 2 / 15/1931 14:00 Abilene ,, DISK, KS, 6/1/1931 13:00 Weltausstellung in New York, LIGHT, NY, 18.04.1933 19:00 Valley City ,, DISK, ND, 9/15 / 1934 15:30 Crater Lake ,, CIRCLE, CA, 6/15/1935 0:00 Alma, DISK, MI, 7/15/1936 0:00 Eklutna, CIGAR, AK, 10/15/1936 17: 00 Hubbard ,, CYLINDER, OR, 6/15/1937 0:00 Fontana ,, LIGHT, CA, 8/15/1937 21:00 Waterloo ,, FIREBALL, AL, 6/1/1939 20:00 Belton, ROT, SPHERE, SC, 30.6.1939 20:00 Uhr
Um diesen CSV-Datenblock in eine Datei zu schreiben, sind einige String-Vorgänge sowie das Arbeiten mit Dateien erforderlich. Bevor wir in die Hauptlogik eintauchen, sind hier die obligatorischen Teile: die Paketdefinition, die Importe und die Datenzeichenfolge (beachten Sie die Verwendung von const
).
Hauptimport des Pakets ("os" "Zeichenfolgen" "bufio") Daten: = 'Stadt, berichtete Farben, berichtete Form, Bundesstaat, Uhrzeit Ithaca ,, TRIANGLE, NY, 6/1/1930 22:00 Willingboro ,, ANDERE, NJ , 6/30/1930 20:00 Holyoke ,, OVAL, CO, 2/15/1931 14:00 Uhr Abilene ,, DISK, KS, 6/1/1931, 13:00 Uhr New York Worlds Fair, LIGHT, NY, 4 / 18/1933 19:00 Valley City ,, DISK, ND, 9/15/1934 15:30 Crater Lake, CIRCLE, CA, 6/15/1935 0:00 Alma ,, DISK, MI, 7/15 / 1936 0:00 Eklutna ,, ZIGARRE, AK, 10/15/1936 17:00 Hubbard, ZYLINDER ODER 6/15/1937 0:00 Fontana ,, LIGHT, CA, 8/15/1937 21:00 Waterloo ,, FIREBALL, AL, 6/1/1939 20:00 Belton, RED, SPHERE, SC, 6/30/1939 20:00 '
Das Main()
Die Funktion erstellt eine Datei mit dem Namen "ufo-sightings.csv", prüft, ob kein Fehler vorliegt, und erstellt dann einen gepufferten Writer w
. Das verschieben
Ein Aufruf in der nächsten Zeile, der den Inhalt des Puffers in die Datei löscht, wird am Ende der Funktion ausgeführt. Das ist der Sinn des Aufschubs. Dann verwendet es die Teilt()
Funktion des Strings-Pakets zum Aufteilen der Datenstrings in einzelne Zeilen.
Innerhalb der for-Schleife wird dann der führende und der nachfolgende Leerraum von jeder Zeile entfernt. Leere Zeilen werden übersprungen und nicht leere Zeilen werden in den Puffer geschrieben, gefolgt von einem Zeilenvorschubzeichen. Das ist es. Der Puffer wird am Ende in die Datei geschrieben.
func main () f, err: = os.Create ("ufo-sightings.csv") if err! = keine panic (e) w: = bufio.NewWriter (f) verschiebt w.Flush () Zeilen: = Zeichenfolgen.Split (Daten, "\ n") für _, Zeile: = Bereichszeilen Zeile: = Zeichenfolgen.Trim (Zeile, "") wenn Zeile == "" Weiter w.WriteString (Zeile) w. WriteString ("\ n")
Das Lesen aus der Datei ist ziemlich einfach:
Hauptimport des Pakets ("fmt" "io / ioutil") func main () data, err: = ioutil.ReadFile ("ufo-sightings.csv") wenn err! Zeichenfolge (Daten)
Go verfügt über solide Einrichtungen, um Texte aller Formen und Kodierungen zu verarbeiten. In diesem Teil der Serie haben wir uns mit den Grundlagen der Textdarstellung in Go, der Textverarbeitung mit dem String-Paket und dem Umgang mit CSV-Dateien befasst.
In Teil zwei werden wir das Gelernte in die Praxis umsetzen, um unordentliche Daten in Vorbereitung auf die Analyse zu bereinigen.