Modifica di un sottoinsieme di righe in un frame di dati Panda


143

Supponiamo di avere un DataFrame panda con due colonne, A e B. Mi piacerebbe modificare questo DataFrame (o creare una copia) in modo che B sia sempre NaN ogni volta che A è 0. Come lo raggiungerei?

Ho provato quanto segue

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

e

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

senza successo.


Se stai cercando una soluzione molto veloce usa NumPy wherecome mostrato in questa soluzione di seguito
Ted Petrou,

Risposte:


243

Utilizzare .locper l'indicizzazione basata su etichette:

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

L' df.A==0espressione crea una serie booleana che indicizza le righe, 'B'seleziona la colonna. Puoi anche usarlo per trasformare un sottoinsieme di una colonna, ad esempio:

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

Non conosco abbastanza i panda interni per sapere esattamente perché funzioni, ma il problema di base è che a volte l'indicizzazione in un DataFrame restituisce una copia del risultato e talvolta restituisce una vista sull'oggetto originale. Secondo la documentazione qui , questo comportamento dipende dal comportamento intorpidito sottostante. Ho scoperto che l'accesso a tutto in un'unica operazione (piuttosto che [uno] [due]) ha maggiori probabilità di funzionare per l'impostazione.


La seconda parte di questa è una bella risposta a una domanda che non è stata nemmeno posta ;-) Mi chiedo se questa è ancora la risposta dei panda canonici, in particolare b / c è un'ovvia violazione DRY, anche se presumo che sia in fatto necessario per violare il DRY dato i vincoli dei panda interni? (Potrei pubblicare esattamente questo tipo di domanda, in modo più dettagliato, ma volevo vedere se avevi una risposta rapida prima che lo facessi)
JohnE

Come sottoinsieme un Dataframe che non ha nomi di colonna, come sottoinsieme df solo per indice? df.loc [df [0] == 0] non funziona ... Qual è l'alternativa? Grazie
amipro

89

Ecco dai documenti di Panda sull'indicizzazione avanzata:

La sezione spiegherà esattamente di cosa hai bisogno! Si scopre df.loc(poiché .ix è stato deprecato - come molti hanno sottolineato di seguito) può essere utilizzato per tagliare / tagliare a dadini un frame di dati. E. Può anche essere usato per impostare le cose.

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

Quindi la risposta di Bren sta dicendo "trovami tutti i posti in cui df.A == 0, seleziona la colonna Be impostala su np.nan"


2
Mi hai reso felice. Spiegazione chiara.
TwinPenguins,

1
Sì, in qualche modo si loc[selection criteria, columns I want]adatta perfettamente alla tua mente ...
EmE

29

A partire da Panda 0.20 ix è deprecato . Il modo giusto è usare df.loc

ecco un esempio funzionante

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

Spiegazione:

Come spiegato nel documento qui , .loc è principalmente basato sull'etichetta, ma può anche essere usato con una matrice booleana .

Quindi, quello che stiamo facendo sopra sta applicando df.loc[row_index, column_index]:

  • Sfruttando il fatto che locpuò prendere un array booleano come maschera che dice ai panda in quale sottoinsieme di righe vogliamo cambiarerow_index
  • Lo sfruttamento del fatto locè anche basato sull'etichetta per selezionare la colonna usando l'etichetta 'B'incolumn_index

Possiamo usare logico, condizione o qualsiasi operazione che ritorni una serie di booleani per costruire l'array di booleani. Nell'esempio sopra, vogliamo rowsche contenga un 0, per quello che possiamo usare df.A == 0, come puoi vedere nell'esempio sotto, questo restituisce una serie di booleani.

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

Quindi, utilizziamo l'array sopra di booleani per selezionare e modificare le righe necessarie:

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

Per ulteriori informazioni, consultare la documentazione di indicizzazione avanzata qui .


11

Per un notevole aumento della velocità, usa la funzione Where di NumPy.

Impostare

Crea un DataFrame a due colonne con 100.000 righe con alcuni zeri.

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

Soluzione rapida con numpy.where

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

Tempi

%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 whereè circa 4x più veloce


Ero curioso di questo, quindi l'ho testato da solo e la differenza era ancora maggiore usando altri parametri. Numpy è stato quasi 10 volte più veloce nel sostituire 0 con un numero intero anziché np.nan. Mi chiedo cosa ci vuole del tempo extra.
Alexander,

È necessario utilizzare .valuesin np.where(df.a.values == 0, np.nan, df.b.values)? Sembra che funzioni np.where(df.a == 0, np.nan, df.b)anche?
hsl

4

Per sostituire le colonne multiple converti in matrice numpy usando .values:

df.loc[df.A==0, ['B', 'C']] = df.loc[df.A==0, ['B', 'C']].values / 2
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.