Nettoyer vos données avec Go Partie 1

Vue d'ensemble

L'un des aspects les plus importants de toute application est la validation de son entrée. L'approche la plus fondamentale échoue si l'entrée ne satisfait pas aux exigences. Cependant, dans de nombreux cas, cela ne suffit pas. Dans de nombreux systèmes, la collecte de données est distincte de l'analyse de données. Il pourrait s'agir d'une enquête ou d'un ancien ensemble de données. 

Dans ces cas, il est nécessaire d'examiner l'intégralité du jeu de données avant l'analyse, de détecter les données non valides ou manquantes, de corriger ce qui peut être corrigé et de signaler ou de supprimer les données impossibles à récupérer. Il est également utile de fournir des statistiques sur la qualité des données et les types d’erreurs rencontrées.. 

Dans cette série en deux parties, vous apprendrez à utiliser les fonctionnalités de texte, les fichiers CSV Slice and Dice de Go et à vous assurer que vos données sont parfaitement propres. Dans la première partie, nous allons nous concentrer sur les bases du traitement de texte dans les octets, les runes et les chaînes, ainsi que sur l'utilisation de fichiers CSV..

Texte en Go

Avant de plonger dans le nettoyage des données, commençons par la base du texte dans Go. Les blocs de construction sont des octets, des runes et des chaînes. Voyons ce que chacun représente et quelles sont les relations entre eux. 

Octets

Les octets sont des nombres de 8 bits. Chaque octet peut représenter l'une des 256 valeurs possibles (2 à la puissance de 8). Chaque caractère du jeu de caractères ASCII peut être représenté par un seul octet. Mais les octets sont ne pas personnages. La raison en est que Go, en tant que langage moderne, prend en charge le format Unicode, qui contient plus de 256 caractères distincts. Entrez les runes. 

Les runes

Une rune dans Go est un autre nom pour le type int32. Cela signifie que chaque rune peut représenter plus de quatre milliards de valeurs distinctes (2 à la puissance de 32), ce qui est suffisant pour couvrir l'ensemble du jeu de caractères Unicode.. 

Dans le code suivant, vous pouvez voir que la rune '∆' (alt-J sur Mac) est juste un int32. Pour imprimer le caractère qu'il représente à l'écran, je dois le convertir en chaîne..

package main import ("fmt") func main () r: = '' '' fmt.Println (r) fmt.Println (int32 (r)) fmt.Println (string (r)) Sortie: 8710 8710 ∆ 

Unicode est compliqué. Une rune représente officiellement un point de code Unicode. Les caractères Unicode sont généralement représentés par un seul point de code Unicode, mais parfois plusieurs.

Les cordes

Les chaînes ne sont officiellement que des tranches d'octets en lecture seule. Si vous indexez une chaîne, vous récupérez un octet:

func main () s: = "abc" pour i: = 0; je < len(s); i++  fmt.Println(s[i])   Output: 97 98 99 

Les littéraux de chaîne sont une séquence de caractères UTF-8 placés entre guillemets. Ils peuvent contenir des séquences d'échappement, qui sont une barre oblique inverse suivie d'un caractère ASCII tel que \ n (nouvelle ligne) ou \ t (languette). Ils ont des significations spéciales. Voici la liste complète:

\ a Alerte ou sonnette U + 0007 \ b U + 0008 retour arrière \ f Saut de page U + 000C \ n Retour à la ligne U + 000A ou nouvelle ligne \ r Retour chariot U + 000D \ t U + 0009 onglet horizontal \ v U + 000b vertical tab \\ U + 005c backslash \ 'U + 0027 guillemet simple (valide uniquement dans les littéraux runiques) \ "U + 0022 guillemet double (valide uniquement dans les littéraux de chaîne) 

Parfois, vous souhaiterez peut-être stocker des octets littéraux directement dans une chaîne, quelles que soient les séquences d'échappement. Vous pouvez échapper à chaque barre oblique inverse, mais c'est fastidieux. Une approche bien meilleure consiste à utiliser des chaînes brutes enfermées dans des guillemets. 

Voici un exemple de chaîne avec un \ t (tab) séquence d'échappement, qui est représentée une fois telle quelle, puis avec la barre oblique inverse d'échappement, puis sous forme de chaîne brute:

func main () s1: = "1 \ t2" s2: = "1 \\ t2" s3: = '1 \ t2' fmt.Println (s1) fmt.Println (s2) fmt.Println (s3) Sortie : 1 2 1 \ t2 1 \ t2 

Alors que les chaînes sont des tranches d'octets, lorsque vous parcourez une chaîne avec une instruction for-range, vous obtenez une rune à chaque itération. Cela signifie que vous pouvez obtenir un ou plusieurs octets. C'est facile à voir avec l'index for-range. Voici un exemple fou. Le mot hébreu "ום" signifie "Bonjour" (et paix). L'hébreu est également écrit de droite à gauche. Je vais construire une chaîne qui mélange le mot hébreu avec sa traduction anglaise. 

Ensuite, je l’imprimerai rune par rune, y compris l’indice d’octets de chaque rune dans la chaîne. Comme vous le verrez, chaque rune hébraïque prend deux octets, tandis que les caractères anglais prennent un octet. La longueur totale de cette chaîne est donc de 16 octets, même si elle comporte quatre caractères hébreux, trois symboles et cinq caractères anglais (12 caractères). ). De plus, les caractères hébreux seront affichés de droite à gauche:

func main () hello: = "ום = hello" fmt.Println ("length:", len (hello)) pour i, r: = plage (hello) fmt.Println (i, chaîne (r))  Sortie: longueur: 16 0 2 4 sur 6 ם 8 9 = 10 11 h 12 et 13 l 14 l 15 o 

Toutes ces nuances peuvent être extrêmement importantes lorsque vous avez un jeu de données à nettoyer avec des guillemets étranges et un mélange de caractères Unicode et de symboles.

Lors de l'impression de chaînes et de tranches d'octets, plusieurs spécificateurs de format fonctionnent de la même manière. le % s format imprime les octets tels quels, %X affiche deux caractères hexadécimaux minuscules par octet, %X affiche deux caractères hexadécimaux majuscules par octet, et % q affiche une chaîne entre guillemets échappée avec la syntaxe go. 

Pour échapper au signe% à l'intérieur d'un spécificateur de chaîne de format, il suffit de le doubler. Pour séparer les octets lors de l'utilisation %X ou %X, vous pouvez ajouter un espace, comme dans "% x" et "% X". Voici la démo:

func main () s: = "ום" fmt.Printf ("%% s format:% s \ n", s) fmt.Printf ("%% x format:% x \ n", s) fmt.Printf ("%% format X:% X \ n", s) fmt.Printf ("%% x format:% x \ n", s) fmt.Printf ("%% X format:% X \ n", s ) fmt.Printf ("%% q format:% q \ n", s) Sortie:% s format: םום% x format: d7a9d79cd795d79d% X format: D7A9D79CD795D79D% x format: d7 a9 d7 9c d7 9d% Format X: D7 A9 D7 9C D7 95 D7 9D% q ​​format: "ום"

Lecture et écriture de fichiers CSV

Les données peuvent arriver de plusieurs manières et formats. CSV (valeurs séparées par des virgules) est l’un des formats les plus courants. Les données CSV sont très efficaces. Les fichiers ont généralement une ligne d’en-tête avec le nom des champs ou des colonnes et des lignes de données, chaque ligne contenant une valeur par champ, séparés par des virgules.. 

Voici un petit extrait d'un jeu de données d'observations d'OVNI (vraiment). La première ligne (en-tête) contient les noms des colonnes et les autres lignes contiennent les données. Vous pouvez voir que souvent la colonne "Couleurs signalées" est vide:

Ville, Couleurs rapportées, Forme signalée, État, Temps Ithaca ,, TRIANGLE, NY, 6/1/1930 22:00 Willingboro ,, AUTRE, NJ, 6/30/1930 20:00 Holyoke ,, OVAL, CO, 2 / 15/1931 14:00 Abilene ,, DISK, KS, 6/1/1931 13:00 Foire des mondes de New York ,, LIGHT, NY, 4/18/1933 19:00 Valley City, DISK, ND, 9/15 / 1934 15h30 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, RED, SPHERE, SC, 6/30/1939 20:00 

L'écriture de cette partie de données CSV dans un fichier implique certaines opérations sur les chaînes, ainsi que l'utilisation de fichiers. Avant de plonger dans la logique principale, voici les éléments obligatoires: la définition du package, les importations et la chaîne de données (notez l'utilisation de const).

paquet principal import ("os" "chaînes" "bufio") données: = 'Ville, Couleurs signalées, Forme signalée, État, Heure Ithaca ,, TRIANGLE, NY, 6/1/1930 22:00 Willingboro ,, AUTRE, NJ , 6/30/1930 20:00 Holyoke ,, OVAL, CO, 2/15/1931 14:00 Abilene ,, DISK, KS, 6/1/1931 13:00 Foire des mondes de New York ,, 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 ,, 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, ROUGE, SPHERE, SC, 6/30/1939 20:00 ' 

le principale() function crée un fichier appelé "ufo-sightings.csv", vérifie qu'il n'y a pas d'erreur, puis crée un écrivain en mémoire tampon w. le reporter call dans la ligne suivante, qui vide le contenu du tampon dans le fichier, est exécuté à la fin de la fonction. C'est le sens de différer. Ensuite, il utilise le Divisé() fonction du paquetage de chaînes pour décomposer les chaînes de données en lignes individuelles. 

Ensuite, dans la boucle for, les espaces de début et de fin sont supprimés de chaque ligne. Les lignes vides sont ignorées et les lignes non vides sont écrites dans la mémoire tampon, suivies d'un caractère de nouvelle ligne. C'est tout. Le tampon sera vidé dans le fichier à la fin.

func main () f, err: = os.Create ("ufo-sightings.csv") si err! = nil panique (e) w: = bufio.NewWriter (f) diffère les lignes w.Flush (): = strings.Split (data, "\ n") pour _, line: = rangée de lignes line: = strings.Trim (line, "") si line == "" continue w.WriteString (line) w. WriteString ("\ n")

La lecture du fichier est assez simple:

package main import ("fmt" "io / ioutil") func main () données, err: = ioutil.ReadFile ("ufo-sightings.csv") si err! = nil panique (err) fmt.Println ( string (data))

Conclusion

Go dispose d'installations performantes pour gérer le texte de toutes les formes et de tous les encodages. Dans cette partie de la série, nous avons examiné les bases de la représentation de texte dans Go, le traitement de texte à l'aide du paquet strings et le traitement des fichiers CSV.. 

Dans la deuxième partie, nous allons mettre en pratique ce que nous avons appris pour nettoyer les données confuses en vue de l'analyse..