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.