Deux façons de désigner des données : par étiquette et par position
Un DataFrame s'adresse de deux façons, et les confondre est le bogue pandas le plus courant. .loc sélectionne par étiquette : étiquettes de lignes de l'index, noms de colonnes. .iloc sélectionne par position entière, comptée depuis 0, comme dans une liste ou un tableau NumPy. Les deux prennent [lignes, colonnes] : df.loc["AAPL", "price"] est une valeur, df.iloc[0, 0] est la valeur de la première ligne et de la première colonne. Omettez la seconde partie pour prendre des lignes entières : df.loc["AAPL"] est la ligne AAPL sous forme de Series.
import pandas as pd
df = pd.DataFrame(
{"price": [184.25, 410.2, 171.0, 251.0],
"volume": [200, 50, 300, 120],
"sector": ["tech", "tech", "tech", "auto"]},
index=["AAPL", "MSFT", "GOOG", "TSLA"],
)
print(df.loc["AAPL", "price"], df.iloc[0, 0]) # 184.25 184.25 same cell, two addresses
print(df.loc["TSLA"].to_dict()) # {'price': 251.0, 'volume': 120, 'sector': 'auto'}
print(df.iloc[-1].to_dict()) # the same row: position -1 is the last
print(df.loc[["TSLA", "AAPL"], "price"]) # a list of labels picks in that order
# TSLA 251.00
# AAPL 184.25
# Name: price, dtype: float64
try:
df.loc["XXX", "price"] # a label that does not exist
except KeyError as error:
print("KeyError", error) # KeyError 'XXX'Tranches : les tranches d'étiquettes incluent la fin
Les tranches se comportent différemment dans les deux systèmes. Une tranche de position df.iloc[1:3] suit la règle de Python et exclut la fin : les positions 1 et 2. Une tranche d'étiquettes df.loc["MSFT":"TSLA"] inclut les deux étiquettes extrêmes, car une étiquette n'a pas de « suivant » naturel. Les tranches d'étiquettes suivent l'ordre actuel de l'index, qui n'est pas forcément trié. Pour les colonnes, passez une liste (["price", "volume"]) pour les prendre dans l'ordre donné. Oublier dans quel système on est donne des résultats décalés d'un cran, sans aucune erreur.
import pandas as pd
df = pd.DataFrame(
{"price": [184.25, 410.2, 171.0, 251.0], "volume": [200, 50, 300, 120]},
index=["AAPL", "MSFT", "GOOG", "TSLA"],
)
print(df.iloc[1:3]) # positions 1 and 2: the end is excluded
# price volume
# MSFT 410.2 50
# GOOG 171.0 300
print(df.loc["MSFT":"TSLA", ["volume", "price"]]) # labels MSFT..TSLA: the end is included
# volume price
# MSFT 50 410.2
# GOOG 300 171.0
# TSLA 120 251.0
print(df.iloc[:, 0].tolist()) # the first column, by position
# [184.25, 410.2, 171.0, 251.0]Conditions et affectation avec loc
.loc accepte aussi un masque booléen pour les lignes : df.loc[df["volume"] > 100, "price"] est la colonne des prix des lignes qui réussissent le test, en une étape. Et .loc sert à affecter à une partie choisie : df.loc["MSFT", "price"] = 1.0 change une cellule et df.loc[df["volume"] < 100, "volume"] = 0 change toutes les lignes concernées. Évitez les formes chaînées comme df[df["volume"] < 100]["volume"] = 0 : elles agissent sur une copie temporaire. Dans pandas 3, un tableau dérivé ne modifie jamais l'original ; pour modifier un tableau dans une fonction, faites d'abord df.copy(), affectez avec .loc et renvoyez la copie.
import pandas as pd
df = pd.DataFrame(
{"price": [184.25, 410.2, 171.0, 251.0], "volume": [200, 50, 300, 120]},
index=["AAPL", "MSFT", "GOOG", "TSLA"],
)
print(df.loc[df["volume"] > 100, "price"]) # a Boolean mask for rows, a label for the column
# AAPL 184.25
# GOOG 171.00
# TSLA 251.00
# Name: price, dtype: float64
def with_price(frame, symbol, price):
out = frame.copy() # never edit the caller's table
out.loc[symbol, "price"] = price
return out
changed = with_price(df, "MSFT", 1.0)
print(changed.loc["MSFT", "price"], df.loc["MSFT", "price"]) # 1.0 410.2
fixed = df.copy()
fixed.loc[fixed["volume"] < 100, "volume"] = 0 # assign to all matching rows at once
print(fixed["volume"].tolist()) # [200, 0, 300, 120]import pandas as pd
# Integer labels are labels, not positions: a classic trap
d = pd.DataFrame({"x": [1.0, 2.0, 3.0]}, index=[10, 20, 30])
print(d.loc[20, "x"], d.iloc[0, 0]) # 2.0 1.0 label 20 is the second row
print(d.loc[10:20]["x"].tolist()) # [1.0, 2.0] labels 10 through 20, end included
print(d.iloc[0:2]["x"].tolist()) # [1.0, 2.0] positions 0 and 1, end excluded
try:
d.loc[0, "x"] # there is no label 0, even though position 0 exists
except KeyError as error:
print("KeyError", error) # KeyError 0.loc quand vous connaissez les noms (un symbole, une date, une colonne) et .iloc seulement quand la position est le sujet (« la dernière ligne », « les trois premières »). Le code fondé sur les étiquettes continue de fonctionner quand les lignes sont retriées ; le code fondé sur les positions se met silencieusement à choisir d'autres lignes. Le simple df[...] est un raccourci à plusieurs sens (colonne, liste de colonnes, masque de lignes) : dans les fonctions importantes, écrivez explicitement .loc ou .iloc. Le lab impose l'un ou l'autre à dessein.df.loc[étiquette_ligne, étiquette_colonne]par étiquette ;df.iloc[pos_ligne, pos_col]par position depuis 0.- Les tranches
ilocexcluent la fin ; les tranches d'étiquetteslocl'incluent ; une étiquette absente lèveKeyError. df.loc[masque, "col"]filtre et choisit ; affectez via.locsur une.copy()et renvoyez-la.
À suivre : groupby et agrégation, qui découpent un tableau en groupes et résument chacun.
Lab : ce qui est noté
- Chercher un prix par symbole avec
price_of25 pts - Prendre les lignes entre deux symboles avec
label_range25 pts - Prendre des lignes par position avec
position_range25 pts - Corriger un prix dans une copie avec
set_price25 pts