Il s’agit de la deuxième partie d’une série sur le nettoyage des données à l’aide de Go. Dans la première partie, nous avons abordé les fonctionnalités de texte de base de Go et l'utilisation de fichiers CSV. Dans ce tutoriel, nous allons plonger dans le nettoyage des données réelles.
Nous commencerons par comprendre le problème des données en désordre et d'élaborer une stratégie, puis nous examinerons la vérification des champs individuels, la correction des données si possible, et la décision à prendre concernant les valeurs manquantes..
Une stratégie de nettoyage des données devrait dicter quoi faire lorsque des données non valides, en désordre, partielles ou manquantes sont rencontrées. Il devrait également déterminer quel niveau de rapport est nécessaire sur le processus de nettoyage..
Les données sur lesquelles nous nous concentrons ici sont des données tabulaires, dans lesquelles chaque ligne est indépendante. Il n'y a pas de hiérarchies ou de connexions imbriquées entre différentes lignes de données. Beaucoup de jeux de données du monde réel ont cette belle propriété.
L'approche la plus simple pour traiter les données non valides consiste à les supprimer. Si un champ est manquant ou contient des données non valides, supprimez simplement la ligne entière. C'est très facile, et parfois c'est la bonne chose à faire. Si le champ problématique est critique et que vous n’avez aucun moyen de le récupérer, vous ne pouvez que supprimer l’enregistrement complet..
La meilleure solution consiste à réparer le mauvais champ. Dans certains cas, il est facile de détecter le problème et de le résoudre. Dans le jeu de données d'observations d'OVNIS, le champ d'état peut être l'un des 52 états des États-Unis..
Si la valeur doit être entièrement en majuscule et que certaines lignes contiennent des lettres minuscules, vous pouvez simplement les mettre en majuscule..
La génération de rapports sur les lignes non valides, supprimées ou réparées, est importante. L'organisation peut décider de laisser les gens essayer de réparer les données supprimées. Il peut être nécessaire d’exécuter des données fixes par le contrôle de la qualité pour s’assurer que les correctifs automatiques n’introduisent pas de données non valides..
La collecte de statistiques sur le processus de nettoyage est nécessaire pour évaluer la qualité des données source et parfois pour déterminer si les données nettoyées valent même la peine d'être traitées. Les statistiques peuvent inclure le nombre de lignes supprimées et fixes ainsi que le nombre de champs incorrects et manquants pour chaque colonne..
Jusqu'à présent, j'ai décrit une approche de prétraitement pour le nettoyage des données. Cependant, il est possible d'effectuer un nettoyage pendant le traitement. Chaque ligne est vérifiée juste avant son traitement. Ceci est parfois utile, si le prétraitement ne sert à rien car personne ne peut réparer les mauvaises données à l’avance pour une analyse ultérieure ou si le traitement est sensible au temps..
Dans ce scénario, l'objectif principal du nettoyage est de s'assurer que les lignes de données incorrectes ne détruisent pas tout le pipeline de traitement et peuvent être ignorées ou corrigées si nécessaire..
Comment allez-vous vérifier les champs? Vous devez savoir exactement quel type de données est censé être présent et parfois quelles valeurs. Voici quelques exemples.
Les champs numériques sont très courants dans les ensembles de données. Au-delà du type de nombre (entier, réel, complexe), certains champs sont plus spécialisés. Par exemple, un champ de prix peut nécessiter exactement deux décimales et être positif. Voici une fonction qui vérifie si une chaîne représente un prix:
func validate_price (chaîne de caractères) bool parts: = strings.Split (s, ".") si len (parts)! = 2 return false dollars, erreur: = strconv.Atoi (pièces [0]) si erreur! = nil return false si dollars < 0 return false cents, err := strconv.Atoi(parts[1]) if err != nil return false if cents < 0 || cents > 99 return false return true
Parfois, vous devez aller au-delà des attentes. Si vous avez besoin de vérifier qu'une URL est valide, il existe deux approches:
Si vous ne vous souciez que de l'URL bien formée, la première approche fonctionne. Mais si vous voulez vous assurer que l'URL pointe réellement sur une destination réelle, vous devez utiliser la deuxième approche. Puisque la deuxième approche est un sur-ensemble de la première, utilisons-la simplement:
func validate_url (chaîne d'URL) bool _, err: = http.Head (url) renvoie err == nil
Si les valeurs doivent respecter un format personnalisé, vous pouvez généralement le faire correspondre à l'aide de chaînes simples telles que Divisé()
ou dans des cas plus complexes, utilisez des expressions régulières. Par exemple, si votre ensemble de données contient des numéros de sécurité sociale (j'espère que non) au format XXX-XX-XXXX
alors vous pouvez diviser par "-" et vous assurer qu'il y a trois jetons où le premier est composé de trois chiffres, le second de deux chiffres et le troisième de quatre chiffres. Mais il est plus concis d’utiliser un regex comme ^ \ d 3 -? \ d 2 -? \ d 4 $
.
Corriger des valeurs non valides n’est pas une mince affaire. Si votre méthode de réparation est incorrecte, vous pouvez vous retrouver avec des données corrompues. Vous devez examiner attentivement l’importance du champ, la gamme de valeurs valides possibles et votre confiance dans le fait que vous pouvez réellement corriger automatiquement toute valeur non valide..
Ceci est une solution assez sûre. Si un champ de texte est censé être tout en majuscule, vous pouvez le réparer sans trop de risques, car les caractères qui étaient à l'origine en minuscules ne constituent pas un élément d'information important. Il n’est pas nécessaire d’écrire un code spécial car le paquet de chaînes a une ToUpper ()
une fonction. Il y a aussi Baisser()
et même Titre ()
et ToTitleSpecific ()
fonctions pour capitaliser le texte correctement.
Une autre solution simple consiste à supprimer les espaces de début et de fin. Vous serez surpris du nombre de personnes qui ajoutent des espaces ou de nouvelles lignes lors de la saisie de données. Le paquet de chaînes a une sélection de TrimXXX ()
fonctions pouvant prendre en charge la plupart des situations:
Dans certains cas, vous pouvez supprimer les caractères non valides. Je recommande de ne le faire que pour les champs non critiques et facultatifs. Par exemple, vous pouvez avoir un champ de description ou de notes contenant du texte libre et vous voulez vous assurer qu'il ne contient pas certains symboles tels que des guillemets ou des guillemets. Voici comment faire:
func remove_quotes (s string) chaîne var b octets.Buffer pour _, r: = plage (s) if r! = '' '&& r! =' \ "b.WriteRune (r) return b. String () func main () original: = "quotes" et "doubles quotes". ' clean: = remove_quotes (original) fmt.Println (original) fmt.Println (clean) Sortie: 'quotes' et "doubles quotes". quotes et doubles guillemets.
Les valeurs numériques sont souvent faciles à corriger. Si vous avez besoin d'une précision de deux chiffres décimaux, vous pouvez tronquer ou arrondir des chiffres supplémentaires. De la même manière, il est facile de convertir des nombres entiers en nombres à virgule flottante.
Parfois, il existe une plage de valeurs valides et vous pouvez apporter des nombres trop grands ou trop petits pour s’adapter à la plage. La fonction suivante prend une chaîne et une plage d'entiers et renvoie une chaîne représentant un entier compris dans la plage. Trop grandes valeurs deviennent la valeur maximale, et trop petites deviennent la valeur minimale.
func fit_into_range (chaîne s, min int, max int) chaîne n, _: = strconv.Atoi (s) si n < min n = min else if n > max n = max sinon return s return strconv.Itoa (n) func main () fmt.Println (fit_into_range ("15", 10, 20)) fmt.Println (fit_into_range ("- 15", 10, 20)) fmt.Println (fit_into_range ("55", 10, 20)) Sortie: 15 10 20
Les URL peuvent souvent être corrigées en essayant différents modèles ("http" ou "https") ou en ajoutant ou supprimant des sous-domaines "www". En combinant les options avec la recherche des candidats, vous pouvez être sûr que le correctif est correct..
Les valeurs manquantes sont très courantes lors de l'acquisition de données réelles. Si la valeur manquante est requise, il existe deux méthodes principales pour la gérer (sans rejeter complètement la ligne): utilisez les valeurs par défaut ou récupérez la valeur à partir d'une source alternative..
Les valeurs par défaut sont utiles car le code de traitement n'a pas à vérifier si une valeur est présente ou non. Le code de nettoyage des données garantit qu'il y a toujours une valeur en place. Dans de nombreux cas, la valeur par défaut est si courante qu’il s’agit également d’une aide à la saisie des données pour laquelle il n’est pas nécessaire de saisir encore et encore la même valeur par défaut..
Cette approche est un peu plus compliquée. L'idée est de consulter une autre source de données contenant les informations demandées. Par exemple, si vous avez un courrier électronique d'utilisateur, mais que le nom et le prénom sont manquants, vous pouvez consulter votre base de données d'utilisateurs et extraire le nom de l'utilisateur. Cela évite au code de traitement d’accéder à la base de données ou même d’être conscient de cette dépendance..
Nettoyons un petit ensemble de données de produits. Les champs sont:
Nom de colonne | Description de la colonne |
---|---|
Id | PRD-XXXX-XXXX (où X est un chiffre) |
prénom | jusqu'à 40 caractères |
Prix | champ numérique de précision fixe (deux décimales) |
La description | jusqu'à 500 caractères (facultatif) |
Voici le jeu de données sous une forme lisible (les espaces seront coupés lors du nettoyage):
const data = 'Id, Nom, Prix, Description PRD-1234-0000, Airzooka, 9,99, Tire de l'air chez des personnes PRD-1234-0017, Pink Onesie, 34,55, PRD-1234-666, Oh, 18,18, ID de produit non valide PRD-1234-7777, Oh oh 2,, Prix manquant prd-1234-8888, PostIt !, 13.13, corrigible: id minuscule '
Les deux premiers produits sont valables. Le troisième produit, "PRD-1234-666", manque un chiffre dans son identifiant. Le prochain produit, "PRD-1234-7777", manque un prix. Le dernier produit, "prd-1234-8888", a un identifiant de produit non valide, mais il peut être corrigé en toute sécurité (mettez-le en majuscule).
Le code suivant nettoie les données, corrige ce qui peut être corrigé, supprime les lignes qui ne peuvent pas être corrigées et génère un ensemble de données vierge ainsi qu'un rapport pouvant être utilisé pour corriger manuellement les données non valides..
Pour vérifier l'id du produit et le prix, je vais utiliser des expressions régulières. Voici les deux fonctions d'assistance:
func verifyProductId (s string) bool apparié, _: = regexp.MatchString ('^ PRD- \ d 4 - \ d 4 $', s) retourne apparié func verifyProductPrice (s chaîne) bool apparié, _: = regexp.MatchString ('^ \ d + \. \ d \ d $', s) a été comparé
Une fois que les données ont été nettoyées et que toutes les lignes de données non valides ont été supprimées, la fonction suivante enregistre les données épurées dans un nouveau fichier CSV appelé "clean.csv" et les affiche à l'écran..
func writeCleanData (cleanData [] chaîne) f, _: = os.Create ("clean.csv") w: = bufio.NewWriter (f) fmt.Println ("Nettoyer les données:") diffère de w.Flush () pour _, line: = range cleanData fmt.Println (ligne) w.WriteString (ligne) w.WriteString ("\ n")
le principale()
la fonction fait le plus gros du travail. Il effectue une itération sur le jeu de données d'origine, élimine les espaces redondants, corrige ce qu'il peut, garde la trace des lignes de données supprimées, écrit les données épurées dans un fichier et finalement affiche les rapports sur les lignes supprimées.
func main () cleanData: = [] chaîne "Id, Nom, Prix, Description" supprimée: = [] chaîne // Nettoyer les données all_lines: = strings.Split (données, "\ n") pour _, line: = range all_lines fields: = strings.Split (line, ",") if len (fields)! = 4 continue // Supprime tous les espaces de début et de fin de chaque champ pour i, f: = range fields fields [i] = strings.TrimSpace (f) // Correction automatique (inutile de vérifier) id: = strings.ToUpper (fields [0]) if! verifyProductId (id) supprimé = append (supprimé, ligne ) continue nom: = champs [1] // Les noms de produits ne peuvent pas être vides si nom == "" abandonné = ajouter (supprimé, ligne) continuer // nom tronqué à 40 caractères (runes) si len ([ ] rune (nom))> 40 nom = chaîne ([] rune (nom) [: 40]) prix: = champs [2] if! verifyProductPrice (prix) supprimé = ajouter (supprimé, ligne) continuer description : = champs [3] // tronque la description à 500 caractères (runes) si len ([] rune (nom))> 500 nom = chaîne ([] rune (nom) [: 500]) cleanLine: = chaînes. Joindre ([] string id, nom, prix, description, ",") cleanData = append (cleanData, cleanLine) writeCleanData (cleanData) // Rapport fmt.Println ("Lignes supprimées:") pour _, s: = plage supprimée fmt.Println (s)
Go propose des packages bien conçus pour le traitement de texte. Contrairement à la plupart des langues, l’objet chaîne n’est en réalité qu’une tranche d’octets. Toute la logique de traitement des chaînes se trouve dans des packages distincts tels que "chaînes" et "strconv".
Dans la deuxième partie du didacticiel, nous avons utilisé un grand nombre de concepts pour réaliser une tâche commune du monde réel consistant à nettoyer un jeu de données au format CSV avant analyse..