Retour à la leçon·Leçon 5 sur 8·Travailler 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.
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à.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")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, avecloc. 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.- Par étiquette, avec
Trois façons de sélectionner, et quand chacune convient — En Python
muac.loc[muac["commune"] == "Gonaives", ["child_id", "muac_mm", "outcome"]]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, aveciloc. Lignes et colonnes par position entière.- Par position, avec
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 colonnesTrois 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
ilocsert à regarder, non à raisonner. La position dépend de l'ordre du fichier, et un script qui ditiloc[:, 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 :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 colonnesNotes 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.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 deTrue/Falsede même longueur que le tableau.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 » sansgroupby.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)Masques booléens
- Les valeurs manquantes ne sont pas False
Notes du présentateur
Écrireandici lèveValueError: 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.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 silencieusementMasques 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 avecNAvautNA, etNAn'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 :Masques booléens
- Le dénominateur est tout l'enjeu — Diviser par
len(muac)au lieu demeasured.sum()rapporte une prévalence plus… isin,between,query
Notes du présentateur
Le dénominateur est tout l'enjeu. Diviser parlen(muac)au lieu demeasured.sum()rapporte une prévalence plus faible pour une raison sans rapport avec la nutrition.- Le dénominateur est tout l'enjeu — Diviser par
Masques booléens — En Python
north = muac["commune"].isin(["Gonaives", "Terre-Neuve", "Anse-Rouge"]) plausible = muac["muac_mm"].between(80, 220)Masques booléens — En Python
impossible = muac["muac_mm"].notna() & ~muac["muac_mm"].between(80, 220)Notes du présentateur
betweenest inclusif aux deux bornes par défaut et — c'est l'essentiel — renvoieFalsepourNA. 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 :Masques booléens — En Python
muac.query("age_months < 60 and muac_mm < 125")Notes du présentateur
queryse 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.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 insteadNotes du présentateur
Les anciennes versions de pandas produisaient un avertissement que des générations d'analystes ont appris à masquer :L'avertissement devenu erreur — En Python
muac[muac["commune"] == "Gonaives"]["muac_mm"] = 0Notes du présentateur
Il signalait une affectation chaînée — sélectionner, puis affecter à la sélection :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.SettingWithCopyWarningn'existe plus ;pd.errors.SettingWithCopyWarninglèveAttributeError. L'affectation chaînée vous dit qu'elle n'a rien fait.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"] = 0L'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 ...L'avertissement devenu erreur — En Python
import warnings warnings.simplefilter("error", pd.errors.ChainedAssignmentError)Notes du présentateur
ChainedAssignmentErrordérive deWarning: par défaut la ligne s'affiche et l'exécution continue. Dans un script dont les chiffres comptent, promouvez-la :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.L'avertissement devenu erreur — En Python
# Un seul .loc, les deux axes, une seule operation. muac.loc[muac["commune"] == "Gonaives", "muac_mm"] = pd.NAL'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 :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"] < 115Notes 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é.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
assignrenvoie un nouveau tableau et se chaîne, ce qui garde une chaîne de traitement lisible :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
Lelambda d:compte : il désigne le tableau à ce point de la chaîne, si bien quesampeut se définir à partir de colonnes créées parassignune ligne plus tôt. Référencermuacdirectement dans la chaîne utiliserait le tableau d'avant-filtre et désalignerait tout. Pour des conditions à plus de deux branches,np.selectvaut mieux que deswhereimbriqués :Ajouter et modifier des colonnes — En Python
muac.loc[muac["muac_mm"].isna(), "band"] = pd.NANotes du présentateur
Les conditions sont évaluées dans l'ordre et la première qui correspond l'emporte :< 115doit donc venir en premier. Noter quedefaultattrape aussi les valeurs manquantes, ce qui n'est presque jamais souhaité — remettez-les explicitement :Trier n'est pas classer — En Python
muac.sort_values(["commune", "screening_date"], ascending=[True, False])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.nlargestest la forme lisible du « top N » et est plus rapide qu'un tri du tableau entier :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.