Modifier un sous-ensemble de lignes dans un dataframe pandas

143

Supposons que j'ai un pandas DataFrame avec deux colonnes, A et B. Je voudrais modifier ce DataFrame (ou créer une copie) afin que B soit toujours NaN chaque fois que A est 0. Comment pourrais-je y parvenir?

J'ai essayé ce qui suit

df['A'==0]['B'] = np.nan

et

df['A'==0]['B'].values.fill(np.nan)

sans succès.

Arthur B.
la source
Si vous recherchez une solution très rapide, utilisez NumPy's wherecomme indiqué dans cette solution ci
Ted Petrou

Réponses:

243

Utilisation .locpour l'indexation basée sur les étiquettes:

df.loc[df.A==0, 'B'] = np.nan

L' df.A==0expression crée une série booléenne qui indexe les lignes, 'B'sélectionne la colonne. Vous pouvez également l'utiliser pour transformer un sous-ensemble d'une colonne, par exemple:

df.loc[df.A==0, 'B'] = df.loc[df.A==0, 'B'] / 2

Je n'en sais pas assez sur les composants internes de pandas pour savoir exactement pourquoi cela fonctionne, mais le problème de base est que parfois l'indexation dans un DataFrame renvoie une copie du résultat, et parfois il renvoie une vue sur l'objet d'origine. Selon la documentation ici , ce comportement dépend du comportement numpy sous-jacent. J'ai trouvé que tout accéder en une seule opération (plutôt que [un] [deux]) est plus susceptible de fonctionner pour le réglage.

BrenBarn
la source
La deuxième partie de ceci est une bonne réponse à une question qui n'a même pas été posée ;-) Je me demande si c'est toujours la réponse canonique des pandas, en particulier parce que c'est une violation évidente de DRY, même si je suppose que c'est en fait nécessaire pour violer DRY compte tenu des contraintes internes des pandas? (Je peux poster exactement ce genre de question, plus en détail, mais je voulais voir si vous aviez une réponse rapide avant moi)
JohnE
Comment sous-définir un Dataframe qui n'a pas de nom de colonne, comment sous-définir df juste par index? df.loc [df [0] == 0] ne fonctionne pas ... Quelle est l'alternative? Merci
amipro
89

Voici des documents pandas sur l'indexation avancée:

La section vous expliquera exactement ce dont vous avez besoin! Il s'avère que df.loc(comme .ix est obsolète - comme beaucoup l'ont souligné ci-dessous) peut être utilisé pour le découpage / découpage en dés d'une dataframe. Et. Il peut également être utilisé pour définir les choses.

df.loc[selection criteria, columns I want] = value

La réponse de Bren est donc: `` trouvez-moi tous les endroits où df.A == 0, sélectionnez la colonne Bet réglez-la sur np.nan''

badgley
la source
2
Tu as fait ma journée. Explication claire.
TwinPenguins
1
Ouais, loc[selection criteria, columns I want]
ça
29

À partir de pandas 0.20 ix est obsolète . La bonne façon est d'utiliser df.loc

voici un exemple de travail

>>> import pandas as pd 
>>> import numpy as np 
>>> df = pd.DataFrame({"A":[0,1,0], "B":[2,0,5]}, columns=list('AB'))
>>> df.loc[df.A == 0, 'B'] = np.nan
>>> df
   A   B
0  0 NaN
1  1   0
2  0 NaN
>>> 

Explication:

Comme expliqué dans le document ici , .loc est principalement basé sur les étiquettes, mais peut également être utilisé avec un tableau booléen .

Donc, ce que nous faisons ci-dessus est d'appliquer df.loc[row_index, column_index]en:

  • Exploiter le fait que locpeut prendre un tableau booléen comme masque qui indique aux pandas dans quel sous-ensemble de lignes nous voulons changerrow_index
  • L'exploitation du fait locest également basée sur l'étiquette pour sélectionner la colonne en utilisant l'étiquette 'B'dans lecolumn_index

Nous pouvons utiliser la logique, la condition ou toute opération qui retourne une série de booléens pour construire le tableau de booléens. Dans l'exemple ci-dessus, nous voulons tout ce rowsqui contient un 0, pour cela, nous pouvons utiliser df.A == 0, comme vous pouvez le voir dans l'exemple ci-dessous, cela renvoie une série de booléens.

>>> df = pd.DataFrame({"A":[0,1,0], "B":[2,0,5]}, columns=list('AB'))
>>> df 
   A  B
0  0  2
1  1  0
2  0  5
>>> df.A == 0 
0     True
1    False
2     True
Name: A, dtype: bool
>>> 

Ensuite, nous utilisons le tableau de booléens ci-dessus pour sélectionner et modifier les lignes nécessaires:

>>> df.loc[df.A == 0, 'B'] = np.nan
>>> df
   A   B
0  0 NaN
1  1   0
2  0 NaN

Pour plus d'informations, consultez la documentation sur l'indexation avancée ici .

Mohamed Ali JAMAOUI
la source
11

Pour une augmentation massive de la vitesse, utilisez la fonction where de NumPy.

Installer

Créez un DataFrame à deux colonnes avec 100 000 lignes avec quelques zéros.

df = pd.DataFrame(np.random.randint(0,3, (100000,2)), columns=list('ab'))

Solution rapide avec numpy.where

df['b'] = np.where(df.a.values == 0, np.nan, df.b.values)

Timings

%timeit df['b'] = np.where(df.a.values == 0, np.nan, df.b.values)
685 µs ± 6.4 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

%timeit df.loc[df['a'] == 0, 'b'] = np.nan
3.11 ms ± 17.2 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

Numpy whereest environ 4x plus rapide

Ted Petrou
la source
J'étais curieux à ce sujet, alors je l'ai testé moi-même et la différence était encore plus grande en utilisant d'autres paramètres. Numpy était presque 10 fois plus rapide pour remplacer les 0 par un entier au lieu de np.nan. Je me demande ce qui prend du temps supplémentaire.
Alexander
Faut - il utiliser .valuesdans np.where(df.a.values == 0, np.nan, df.b.values)? On dirait que ça np.where(df.a == 0, np.nan, df.b)marche aussi?
hsl le
4

Pour remplacer plusieurs colonnes, convertissez-vous en tableau numpy en utilisant .values:

df.loc[df.A==0, ['B', 'C']] = df.loc[df.A==0, ['B', 'C']].values / 2
Adrien Renaud
la source