cassionAnalyse de données

Retour à la leçonLeçon 3 sur 8Faire entrer l'export

CSV, Excel et largeur fixe, lus sans dommage

Le même diaporama que les téléchargements, rendu sous forme de page. Lancez le diaporama pour le présenter en plein écran — les flèches ou un clic avancent d'une diapositive, Échap quitte.

Diapositives · PDFDiapositives · PowerPoint

  1. Diapositive 1 / 29

    Ce que couvre cette leçon

    • Ce que cette leçon suppose
    • Le CSV n'est pas un format unique
    • Excel
    • Fichiers à largeur fixe
    • Vérifier la lecture avant d'en faire quoi que ce soit
    • Lire un gros fichier
    • Ce qui vient ensuite
    Notes du présentateur
    Encodages, séparateurs, classeurs multi-feuilles, lignes d'en-tête fusionnées et spécifications de colonnes — les pièges propres à chaque format, qui corrompent un fichier avant même que vous n'ayez regardé une seule valeur.
  2. Diapositive 2 / 29

    Ce que cette leçon suppose

    • Fondamentaux de l'analyse de données couvre le principe : déclarez vos types, déclarez vos sentinelles, et ne laissez jamais un lecteur en inférer l'un ou l'autre.
    Notes du présentateur
    Fondamentaux de l'analyse de données couvre le principe : déclarez vos types, déclarez vos sentinelles, et ne laissez jamais un lecteur en inférer l'un ou l'autre. Cette leçon le tient pour acquis et entre dans les formats eux-mêmes, car chacun échoue différemment et les défaillances ne se voient plus dans les données ensuite.
  3. Diapositive 3 / 29

    Le CSV n'est pas un format unique

    • Le séparateur
    Notes du présentateur
    read_csv compte une cinquantaine de paramètres. Quatre d'entre eux expliquent l'essentiel des dégâts. Un fichier produit sur une machine Windows française ou espagnole est très souvent séparé par des points-virgules, car la virgule y est le séparateur décimal.
  4. Diapositive 4 / 29

    Le CSV n'est pas un format unique — En Python

    import pandas as pd
    
    # Faux : tout atterrit dans une colonne unique nommee d'apres l'en-tete entier.
    wrong = pd.read_csv("export.csv")
    print(wrong.shape)              # (2736, 1)
    
    right = pd.read_csv("export.csv", sep=";", decimal=",")
    print(right.shape)              # (2736, 7)
  5. Diapositive 5 / 29

    Le CSV n'est pas un format unique

    • L'encodage
    Notes du présentateur
    Le symptôme est sans ambiguïté une fois connu : un DataFrame à exactement une colonne. Vérifiez .shape immédiatement après chaque lecture. Les caractères accentués dans les noms de communes — Gonaïves, L'Estère, Anse-Rouge — sont l'endroit où cela mord.
  6. Diapositive 6 / 29

    Le CSV n'est pas un format unique — En Python

    # UnicodeDecodeError, ou pire, silencieusement abime : "Gona\xefves"
    muac = pd.read_csv(path)
    
    muac = pd.read_csv(path, encoding="utf-8")       # ce que vous voulez
    muac = pd.read_csv(path, encoding="latin-1")     # ce qu'Excel produit souvent
  7. Diapositive 7 / 29

    Le CSV n'est pas un format unique

    • Les séparateurs de milliers transforment les nombres en texte
    Notes du présentateur
    latin-1 ne lève jamais d'erreur, car tout octet est un caractère latin-1 valide. Cela en fait un mauvais diagnostic et un repli correct : si un fichier lu en latin-1 affiche Gonaïves, il était en UTF-8 depuis le début et vous avez dit le contraire au lecteur.
  8. Diapositive 8 / 29

    Le CSV n'est pas un format unique — En Python

    # target_population arrive en "1,480" et pandas le lit comme une chaine.
    epi = pd.read_csv(path, thousands=",")
  9. Diapositive 9 / 29

    Le CSV n'est pas un format unique

    • Les zéros initiaux
    Notes du présentateur
    Sans thousands, cette colonne est de type objet et toute opération arithmétique dessus échoue ou concatène. Vérifiez .dtypes après lecture, à chaque fois. Le réglage par défaut le plus coûteux de ce secteur.
  10. Diapositive 10 / 29

    Le CSV n'est pas un format unique — En Python

    # facility_id "007" devient l'entier 7 ; la jointure a la liste des formations
    # echoue exactement pour celles dont le code portait un zero initial.
    epi = pd.read_csv(path)
    
    epi = pd.read_csv(path, dtype={"facility_id": "string"})
  11. Diapositive 11 / 29

    Le CSV n'est pas un format unique — En Python

    # Tout lire en texte d'abord, regarder, puis convertir deliberement.
    peek = pd.read_csv(path, dtype="string", nrows=200)
    print(peek.head())
    print(peek.columns.tolist())
    Notes du présentateur
    La règle se généralise : si vous n'allez jamais faire d'arithmétique dessus, ce n'est pas un nombre. Codes de formations sanitaires, numéros de téléphone, identifiants de grappes, numéros de ménages et codes administratifs sont du texte qui s'écrit avec des chiffres. Une habitude défensive pour un fichier dont vous ne connaissez pas encore les colonnes : Deux cents lignes suffisent à voir la forme et coûtent peu sur un gros fichier.
  12. Diapositive 12 / 29

    Excel

    • Il y a plus d'une feuille
  13. Diapositive 13 / 29

    Excel — En Python

    book = pd.ExcelFile(path)
    print(book.sheet_names)
    # ['Cover', 'Data', 'Codebook', 'Sheet3']
    
    data = pd.read_excel(path, sheet_name="Data")
  14. Diapositive 14 / 29

    Excel — En Python

    sheets = pd.read_excel(path, sheet_name=None)
    monthly = pd.concat(sheets, names=["sheet"]).reset_index(level="sheet")
    Notes du présentateur
    read_excel sans sheet_name renvoie la première feuille, qui dans un classeur de programme est le plus souvent une page de garde. Listez toujours les feuilles d'abord. sheet_name=None renvoie un dictionnaire de toutes les feuilles, ce qui permet de traiter un classeur à une feuille par mois :
  15. Diapositive 15 / 29

    Excel

    • L'en-tête n'est pas en ligne 1
    Notes du présentateur
    Les classeurs de programme portent couramment un titre, une ligne de logo et une ligne vide au-dessus du véritable en-tête.
  16. Diapositive 16 / 29

    Excel — En Python

    data = pd.read_excel(path, sheet_name="Data", skiprows=3)
  17. Diapositive 17 / 29

    Excel

    • Les cellules fusionnées produisent des valeurs manquantes
    Notes du présentateur
    Le symptôme, ce sont des noms de colonnes comme Unnamed: 0, Unnamed: 1. Si vous les voyez, la ligne d'en-tête est ailleurs. Une cellule fusionnée porte sa valeur en haut à gauche et rien dans le reste. Quand un nom de district est fusionné sur ses formations sanitaires, seule la première ligne de chaque district en porte un.
  18. Diapositive 18 / 29

    Excel — En Python

    data["district"] = data["district"].ffill()
  19. Diapositive 19 / 29

    Excel

    • Les dates Excel
    Notes du présentateur
    ffill est correct ici et dangereux en général : il n'est juste que parce que les blancs proviennent d'une fusion, non d'une non-réponse. Ne l'appliquez jamais à une colonne où un blanc pourrait signifier « non renseigné ». Excel stocke les dates comme un nombre de jours depuis le 30/12/1899. read_excel les convertit généralement, mais une colonne typée en texte dans le classeur arrive sous forme de numéro de série brut.
  20. Diapositive 20 / 29

    Excel — En Python

    # 45306 correspond au 15/01/2024
    data["screening_date"] = pd.to_datetime(
        data["screening_date"], unit="D", origin="1899-12-30"
    )
    Notes du présentateur
    Si une colonne de dates arrive en entiers à cinq chiffres, voilà pourquoi.
  21. Diapositive 21 / 29

    Fichiers à largeur fixe — Exemple

    CH00854Terre-Neuve    2024-01-15022m133false
    CH01207Gonaives       2024-01-15034f118false
    Notes du présentateur
    Encore courants depuis les anciens systèmes d'information sanitaire et depuis les extractions ministérielles. Il n'y a pas de séparateur ; chaque champ occupe une plage fixe de caractères.
  22. Diapositive 22 / 29

    Fichiers à largeur fixe — En Python

    COLSPECS = [(0, 7), (7, 22), (22, 32), (32, 35), (35, 36), (36, 39), (39, 44)]
    NAMES = ["child_id", "commune", "screening_date", "age_months", "sex",
             "muac_mm", "oedema"]
    
    muac = pd.read_fwf(
        path,
        colspecs=COLSPECS,
        names=NAMES,
        dtype={"child_id": "string", "commune": "string"},
    )
    
    muac["commune"] = muac["commune"].str.strip()
  23. Diapositive 23 / 29

    Fichiers à largeur fixe

    • Les plages sont semi-ouvertes, comme les tranches Python : (0, 7) couvre les caractères 0 à 6. Une erreur d'un…
    • Retirez le remplissage. Les champs à largeur fixe sont complétés par des espaces : "Terre-Neuve " ne sera pas…
    • Prenez la spécification dans la documentation, pas en comptant à l'écran. read_fwf sait inférer colspecs, et il…
    Notes du présentateur
    Trois remarques :
  24. Diapositive 24 / 29

    Vérifier la lecture avant d'en faire quoi que ce soit — En Python

    def check(df, expected_rows=None):
        print("forme     ", df.shape)
        print("types     ", df.dtypes.value_counts().to_dict())
        print("manquants ", df.isna().sum().to_dict())
        print("1re ligne ", df.iloc[0].to_dict())
        if expected_rows is not None:
            assert len(df) == expected_rows, f"attendu {expected_rows}, obtenu {len(df)}"
    
    muac = pd.read_csv(
        RAW / "muac-screening-artibonite-2024.v1.csv",
        dtype={"child_id": "string", "commune": "string", "sex": "string"},
        na_values={"muac_mm": ["-99"]},
    )
    check(muac, expected_rows=4218)
    Notes du présentateur
    Chaque lecture devrait être suivie des quatre mêmes contrôles. Ensemble ils prennent une minute et attrapent presque tout ce qui précède.
  25. Diapositive 25 / 29

    Vérifier la lecture avant d'en faire quoi que ce soit

    • la forme attrape le mauvais séparateur et un téléchargement tronqué.
    • les types attrapent les zéros initiaux perdus, les séparateurs de milliers et une colonne numérique lue comme du…
    • les manquants attrapent un mauvais encodage et une sentinelle non déclarée.
    • la première ligne attrape un décalage d'en-tête — si elle ressemble à un en-tête, c'en était un.
    Notes du présentateur
    L'assertion sur le nombre de lignes est celle qu'on saute. Un fichier qui arrive avec 4 190 lignes au lieu de 4 218 a perdu quelque chose entre le serveur et vous, et le script doit le dire plutôt que de rapporter discrètement une charge de cas plus faible.
  26. Diapositive 26 / 29

    Lire un gros fichier — En Python

    COLUMNS = ["student_id", "attendance_date", "present"]
    attendance = pd.read_csv(path, usecols=COLUMNS, dtype={"student_id": "string"})
    Notes du présentateur
    Si un fichier ne tient pas confortablement en mémoire, lisez les colonnes nécessaires plutôt que toutes :
  27. Diapositive 27 / 29

    Lire un gros fichier — En Python

    totals = []
    for chunk in pd.read_csv(path, chunksize=100_000, dtype={"student_id": "string"}):
        totals.append(chunk.groupby("student_id")["present"].sum())
    
    per_student = pd.concat(totals).groupby(level=0).sum()
    Notes du présentateur
    usecols économise beaucoup sur un export large. Au-delà, chunksize renvoie un itérateur de morceaux que vous agrégez au fil de l'eau : Le fichier de présence de 70 000 lignes n'en a pas besoin. Une extraction DHIS2 pluriannuelle, si.
  28. Diapositive 28 / 29

    Ce qui vient ensuite

    • Le fichier est en mémoire, forme et types intacts.
    Notes du présentateur
    Le fichier est en mémoire, forme et types intacts. La leçon suivante traite de ce qui est écrit à l'intérieur des colonnes : codes sentinelles, vocabulaires catégoriels, valeurs booléennes saisies de cinq façons différentes, et les étiquettes de valeurs qu'un fichier Stata porte et qu'un CSV jette.
  29. Diapositive 29 / 29

    La suite

    Lire la leçon complète, avec le code exécutable Retour à la leçon