cassionAnalyse de données

Retour à la leçonLeçon 5 sur 8Travailler le tableau

Sélectionner et filtrer, et l'avertissement devenu erreur

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 / 35

    Ce que couvre cette leçon

    • Trois façons de sélectionner, et quand chacune convient
    • Masques booléens
    • L'avertissement devenu erreur
    • Ajouter et modifier des colonnes
    • Trier n'est pas classer
    • Ce qui vient ensuite
    Notes du présentateur
    loc, iloc et masques booléens utilisés délibérément — plus ce que signalait le SettingWithCopyWarning, pourquoi pandas 3 l'a remplacé par ChainedAssignmentError, et ce que cela change pour le code que vous avez déjà.
  2. Diapositive 2 / 35

    Trois façons de sélectionner, et quand chacune convient — En Python

    import pandas as pd
    
    muac = read_register(RAW / "muac-screening-artibonite-2024.v1.csv")
  3. Diapositive 3 / 35

    Trois façons de sélectionner, et quand chacune convient

    • Par étiquette, avec loc — Les lignes par valeur d'index, les colonnes par nom
    Notes du présentateur
    Par étiquette, avec loc. Les lignes par valeur d'index, les colonnes par nom. C'est celle vers laquelle aller par défaut, car elle dit ce qu'elle fait.
  4. Diapositive 4 / 35

    Trois façons de sélectionner, et quand chacune convient — En Python

    muac.loc[muac["commune"] == "Gonaives", ["child_id", "muac_mm", "outcome"]]
  5. Diapositive 5 / 35

    Trois façons de sélectionner, et quand chacune convient

    • Par position, avec iloc — Lignes et colonnes par position entière
    Notes du présentateur
    Par position, avec iloc. Lignes et colonnes par position entière.
  6. Diapositive 6 / 35

    Trois façons de sélectionner, et quand chacune convient — En Python

    muac.iloc[0]           # premiere ligne
    muac.iloc[:5, :3]      # cinq premieres lignes, trois premieres colonnes
  7. Diapositive 7 / 35

    Trois façons de sélectionner, et quand chacune convient

    • Une colonne seule — est une Series ; une liste de colonnes est un DataFrame :
    Notes du présentateur
    iloc sert à regarder, non à raisonner. La position dépend de l'ordre du fichier, et un script qui dit iloc[:, 3] casse silencieusement le jour où l'export gagne une colonne. Réservez-le à l'inspection et au travail réellement positionnel. Une colonne seule est une Series ; une liste de colonnes est un DataFrame :
  8. Diapositive 8 / 35

    Trois façons de sélectionner, et quand chacune convient — En Python

    muac["muac_mm"]              # Series
    muac[["muac_mm"]]            # DataFrame a une colonne
    muac[["commune", "muac_mm"]] # DataFrame a deux colonnes
    Notes du présentateur
    Le double crochet n'est pas un choix esthétique. Certaines fonctions exigent un DataFrame, et une Series échouera loin de l'endroit où l'erreur a été commise.
  9. Diapositive 9 / 35

    Masques booléens — En Python

    severe = muac["muac_mm"] < 115
    print(severe.sum())              # combien
    print(severe.mean())             # quelle part
    muac.loc[severe]
    Notes du présentateur
    Un masque est une Series de True/False de même longueur que le tableau.
  10. Diapositive 10 / 35

    Masques booléens

    • Combiner des conditions
    Notes du présentateur
    .sum() et .mean() sur un masque booléen donnent l'effectif et la proportion. C'est la façon la plus rapide de répondre à « combien » et « quelle part » sans groupby.
  11. Diapositive 11 / 35

    Masques booléens — En Python

    # Les parentheses sont obligatoires : & lie plus fort que <
    under_five = (muac["age_months"] < 60) & (muac["muac_mm"] < 125)
    
    # Utilisez & | ~ — non and / or / not, qui ne savent pas operer element par element
    either = (muac["muac_mm"] < 115) | (muac["oedema"] == True)
  12. Diapositive 12 / 35

    Masques booléens

    • Les valeurs manquantes ne sont pas False
    Notes du présentateur
    Écrire and ici lève ValueError: The truth value of a Series is ambiguous. Le message est opaque la première fois et signifie toujours la même chose : vous avez utilisé un opérateur scalaire sur un vecteur. C'est le piège qui change une charge de cas.
  13. Diapositive 13 / 35

    Masques booléens — En Python

    print(len(muac))                                   # 4218
    print((muac["muac_mm"] < 125).sum())               # ne compte que les enfants mesures
    print(muac["muac_mm"].isna().sum())                # ceux exclus silencieusement
  14. Diapositive 14 / 35

    Masques booléens — En Python

    measured = muac["muac_mm"].notna()
    gam = measured & (muac["muac_mm"] < 125)
    
    print(f"evalues : {measured.sum()}, cas de MAG : {gam.sum()}")
    print(f"taux de MAG : {gam.sum() / measured.sum():.3f}")
    Notes du présentateur
    Une comparaison avec NA vaut NA, et NA n'est pas sélectionné. Un filtre écarte donc discrètement tout enfant non mesuré — ce qui est juste si vous vouliez dire « enfants mesurés en dessous de 125 mm » et faux si vous vouliez dire « enfants dont on ne sait pas qu'ils sont au-dessus de 125 mm ». Soyez explicite sur ce que vous vouliez dire :
  15. Diapositive 15 / 35

    Masques booléens

    • Le dénominateur est tout l'enjeu — Diviser par len(muac) au lieu de measured.sum() rapporte une prévalence plus…
    • isin, between, query
    Notes du présentateur
    Le dénominateur est tout l'enjeu. Diviser par len(muac) au lieu de measured.sum() rapporte une prévalence plus faible pour une raison sans rapport avec la nutrition.
  16. Diapositive 16 / 35

    Masques booléens — En Python

    north = muac["commune"].isin(["Gonaives", "Terre-Neuve", "Anse-Rouge"])
    plausible = muac["muac_mm"].between(80, 220)
  17. Diapositive 17 / 35

    Masques booléens — En Python

    impossible = muac["muac_mm"].notna() & ~muac["muac_mm"].between(80, 220)
    Notes du présentateur
    between est inclusif aux deux bornes par défaut et — c'est l'essentiel — renvoie False pour NA. Donc ~muac["muac_mm"].between(80, 220) compte une mesure manquante comme une mesure impossible. Ce sont deux défauts différents et un seul est une erreur de saisie :
  18. Diapositive 18 / 35

    Masques booléens — En Python

    muac.query("age_months < 60 and muac_mm < 125")
    Notes du présentateur
    query se lit bien pour les conditions longues et mérite d'être connu : Il est plus lent et masque les fautes de frappe dans une chaîne : préférez les masques dans un script qui doit échouer bruyamment.
  19. Diapositive 19 / 35

    L'avertissement devenu erreur — Exemple

    SettingWithCopyWarning:
    A value is trying to be set on a copy of a slice from a DataFrame.
    Try using .loc[row_indexer, col_indexer] = value instead
    Notes du présentateur
    Les anciennes versions de pandas produisaient un avertissement que des générations d'analystes ont appris à masquer :
  20. Diapositive 20 / 35

    L'avertissement devenu erreur — En Python

    muac[muac["commune"] == "Gonaives"]["muac_mm"] = 0
    Notes du présentateur
    Il signalait une affectation chaînée — sélectionner, puis affecter à la sélection :
  21. Diapositive 21 / 35

    L'avertissement devenu erreur

    • Ce que fait pandas 3 à la place
    • Le comportement est désormais défini — Une sélection n'écrit jamais dans son parent — non pas parfois, jamais
    • L'affectation chaînée vous dit qu'elle n'a rien fait
    Notes du présentateur
    Le premier crochet produit un nouvel objet. Que l'affectation atteigne ou non le tableau d'origine dépendait de l'agencement mémoire, ce qui faisait que la même ligne pouvait fonctionner lundi et pas mardi. L'avertissement existait parce que pandas ne pouvait pas vous dire lequel des deux s'était produit. Le Copy-on-Write est toujours actif et ne peut plus être désactivé. Deux conséquences. Le comportement est désormais défini. Une sélection n'écrit jamais dans son parent — non pas parfois, jamais. SettingWithCopyWarning n'existe plus ; pd.errors.SettingWithCopyWarning lève AttributeError. L'affectation chaînée vous dit qu'elle n'a rien fait.
  22. Diapositive 22 / 35

    L'avertissement devenu erreur — En Python

    import pandas as pd
    
    df = pd.DataFrame({"a": [1, 2, 3], "b": [10, 20, 30]})
    df[df["a"] > 1]["b"] = 0
  23. Diapositive 23 / 35

    L'avertissement devenu erreur — Exemple

    ChainedAssignmentError: A value is being set on a copy of a
    DataFrame or Series through chained assignment. Such chained
    assignment never works ...
  24. Diapositive 24 / 35

    L'avertissement devenu erreur — En Python

    import warnings
    
    warnings.simplefilter("error", pd.errors.ChainedAssignmentError)
    Notes du présentateur
    ChainedAssignmentError dérive de Warning : par défaut la ligne s'affiche et l'exécution continue. Dans un script dont les chiffres comptent, promouvez-la :
  25. Diapositive 25 / 35

    L'avertissement devenu erreur

    • La forme correcte, et ce qui change pour le code existant
    Notes du présentateur
    Le script s'arrête alors au lieu de rapporter un chiffre calculé à partir d'une modification qui n'a jamais eu lieu.
  26. Diapositive 26 / 35

    L'avertissement devenu erreur — En Python

    # Un seul .loc, les deux axes, une seule operation.
    muac.loc[muac["commune"] == "Gonaives", "muac_mm"] = pd.NA
  27. Diapositive 27 / 35

    L'avertissement devenu erreur

    • .copy() et les cas où vous le voulez encore
    Notes du présentateur
    Si vous maintenez du code écrit pour pandas 1 ou 2, le risque de migration va dans un seul sens : une affectation chaînée qui modifiait le tableau ne le modifie plus. Le chiffre change et rien ne plante. Cherchez ][" et les appels à .copy() ajoutés pour faire taire l'ancien avertissement — ces copies sont désormais inutiles, et chacune retirée est une économie réelle sur un gros tableau. Sous Copy-on-Write, df[mask] est déjà indépendant : un .copy() défensif n'apporte rien. Il reste utile quand il documente une intention :
  28. Diapositive 28 / 35

    L'avertissement devenu erreur — En Python

    # Ce sous-ensemble va etre modifie et n'est la vue de rien.
    gonaives = muac.loc[muac["commune"] == "Gonaives"].copy()
    gonaives["flag"] = gonaives["muac_mm"] < 115
    Notes du présentateur
    Ajouter une colonne à une sélection non copiée fonctionne et c'est le cas courant ; le .copy() ci-dessus est une note au lecteur, non une nécessité.
  29. Diapositive 29 / 35

    Ajouter et modifier des colonnes — En Python

    summary = (
        muac
        .loc[muac["muac_mm"].notna()]
        .assign(
            gam=lambda d: d["muac_mm"] < 125,
            sam=lambda d: d["muac_mm"] < 115,
        )
    )
    Notes du présentateur
    assign renvoie un nouveau tableau et se chaîne, ce qui garde une chaîne de traitement lisible :
  30. Diapositive 30 / 35

    Ajouter et modifier des colonnes — En Python

    import numpy as np
    
    muac["band"] = np.select(
        [muac["muac_mm"] < 115, muac["muac_mm"] < 125],
        ["severe", "moderate"],
        default="normal",
    )
    Notes du présentateur
    Le lambda d: compte : il désigne le tableau à ce point de la chaîne, si bien que sam peut se définir à partir de colonnes créées par assign une ligne plus tôt. Référencer muac directement dans la chaîne utiliserait le tableau d'avant-filtre et désalignerait tout. Pour des conditions à plus de deux branches, np.select vaut mieux que des where imbriqués :
  31. Diapositive 31 / 35

    Ajouter et modifier des colonnes — En Python

    muac.loc[muac["muac_mm"].isna(), "band"] = pd.NA
    Notes du présentateur
    Les conditions sont évaluées dans l'ordre et la première qui correspond l'emporte : < 115 doit donc venir en premier. Noter que default attrape aussi les valeurs manquantes, ce qui n'est presque jamais souhaité — remettez-les explicitement :
  32. Diapositive 32 / 35

    Trier n'est pas classer — En Python

    muac.sort_values(["commune", "screening_date"], ascending=[True, False])
  33. Diapositive 33 / 35

    Trier n'est pas classer — En Python

    muac.groupby("commune")["muac_mm"].mean().nsmallest(5)
    Notes du présentateur
    Trier range des lignes. Cela ne dit rien de la réalité des écarts entre lignes voisines — un point que développe le projet Tableau de bord de programme nutritionnel, où neuf communes sur douze ont des intervalles de confiance qui se recouvrent et où l'ordre de tri n'est pas une information. nlargest est la forme lisible du « top N » et est plus rapide qu'un tri du tableau entier :
  34. Diapositive 34 / 35

    Ce qui vient ensuite

    • Vous savez sélectionner un sous-ensemble et le modifier sans vous demander si la modification a eu lieu.
    Notes du présentateur
    Vous savez sélectionner un sous-ensemble et le modifier sans vous demander si la modification a eu lieu. La leçon suivante agrège : groupby, agrégation nommée, et l'obtention d'un numérateur et d'un dénominateur issus de la même opération, pour qu'ils ne puissent pas diverger.
  35. Diapositive 35 / 35

    La suite

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