Rilascia le righe con tutti zeri nel frame di dati dei panda


103

Posso utilizzare la pandas dropna()funzionalità per rimuovere le righe con alcune o tutte le colonne impostate come NA. Esiste una funzione equivalente per eliminare righe con tutte le colonne aventi valore 0?

P   kt  b   tt  mky depth
1   0   0   0   0   0
2   0   0   0   0   0
3   0   0   0   0   0
4   0   0   0   0   0
5   1.1 3   4.5 2.3 9.0

In questo esempio, vorremmo eliminare le prime 4 righe dal frame di dati.

Grazie!


Giusto per chiarire, queste sono due domande. Uno, eliminare le colonne con tutti i valori come 0. Ma anche, per una funzione equivalente a dropna () che eliminerebbe le colonne con qualsiasi valore come 0.
alchemy

Risposte:


111

Si scopre che questo può essere ben espresso in modo vettorializzato:

> df = pd.DataFrame({'a':[0,0,1,1], 'b':[0,1,0,1]})
> df = df[(df.T != 0).any()]
> df
   a  b
1  0  1
2  1  0
3  1  1

6
Bello, ma penso che tu possa evitare la negazione condf = df[(df.T != 0).any()]
Akavall

1
@ Akavall Molto meglio!
U2EF1

1
Solo una nota: OP voleva eliminare rows with all columns having value 0, ma si può dedurre il allmetodo.
paulochf

1
Tutte queste risposte spiegano come possiamo rilasciare righe con tutti zeri, tuttavia, volevo eliminare righe, con 0 nella prima colonna. Con l'aiuto di tutte le discussioni e risposte in questo post, l'ho fatto facendo df.loc [df.iloc [:, 0]! = 0]. Volevo solo condividere perché questo problema è legato a questa domanda !!
hemanta

2
La trasposizione non è necessaria, any () può prendere un asse come parametro. Quindi funziona: df = df [df.any (axis = 1)]
Rahul Jha

129

One-liner. Nessuna trasposizione necessaria:

df.loc[~(df==0).all(axis=1)]

E per chi ama la simmetria, funziona anche ...

df.loc[(df!=0).any(axis=1)]

1
Per brevità (e, a mio parere, chiarezza di intenti) combinano questo e commentare di Akavall: df.loc[(df != 0).any(1)]. Lavoro di squadra!
Dan Allan

1
+1, 30% più veloce che trasporre - da 491 a 614 microsec, e mi piace axis=1per essere esplicito; più pitonico secondo me
gt6989b

Si dovrebbe fare qualche accenno alla differenza tra l'utilizzo di .all e .any poiché la domanda originale menzionava l'equivalenza di dropna. Se vuoi eliminare tutte le righe con qualsiasi colonna contenente uno zero, devi invertire .all e .any nella risposta precedente. Mi ci è voluto un po 'per rendermene conto mentre stavo cercando quella funzionalità.
Zak Keirn

Questo non funziona per me, ma mi restituisce esattamente lo stessodf
Robvh

Esiste una versione "inplace" di questo? Vedo che per rilasciare righe in un df come richiesto dall'OP, questo dovrebbe essere df = df.loc[(df!=0).all(axis=1)]e df = df.loc[(df!=0).any(axis=1)]per eliminare righe con zeri come sarebbe l'equivalente effettivo di dropna ().
alchimia

19

Cerco questa domanda circa una volta al mese e devo sempre trovare la risposta migliore dai commenti:

df.loc[(df!=0).any(1)]

Grazie Dan Allan!


2
Non è necessario scavare. @ 8one6 ha incluso questo nella sua risposta nel 2014 stesso, la parte che dice: "E per chi ama la simmetria ...".
Rahul Murmuria

14

Sostituisci gli zeri con nane quindi rilascia le righe con tutte le voci come nan. Dopodiché sostituisci nancon zeri.

import numpy as np
df = df.replace(0, np.nan)
df = df.dropna(how='all', axis=0)
df = df.replace(np.nan, 0)

3
Questo fallirà se hai NaN-s preesistenti nei dati.
OmerB


7

Un paio di soluzioni che ho trovato utili durante la ricerca, soprattutto per set di dati più grandi:

df[(df.sum(axis=1) != 0)]       # 30% faster 
df[df.values.sum(axis=1) != 0]  # 3X faster 

Continuando con l'esempio di @ U2EF1:

In [88]: df = pd.DataFrame({'a':[0,0,1,1], 'b':[0,1,0,1]})

In [91]: %timeit df[(df.T != 0).any()]
1000 loops, best of 3: 686 µs per loop

In [92]: df[(df.sum(axis=1) != 0)]
Out[92]: 
   a  b
1  0  1
2  1  0
3  1  1

In [95]: %timeit df[(df.sum(axis=1) != 0)]
1000 loops, best of 3: 495 µs per loop

In [96]: %timeit df[df.values.sum(axis=1) != 0]
1000 loops, best of 3: 217 µs per loop

Su un set di dati più ampio:

In [119]: bdf = pd.DataFrame(np.random.randint(0,2,size=(10000,4)))

In [120]: %timeit bdf[(bdf.T != 0).any()]
1000 loops, best of 3: 1.63 ms per loop

In [121]: %timeit bdf[(bdf.sum(axis=1) != 0)]
1000 loops, best of 3: 1.09 ms per loop

In [122]: %timeit bdf[bdf.values.sum(axis=1) != 0]
1000 loops, best of 3: 517 µs per loop

Succedono cose brutte se la tua riga contiene -1 e 1?
Rhys Ulerich

Ovviamente, la somma non funzionerebbe se avessi righe uguali che sommano fino a 0. Ecco una soluzione rapida per quella che è solo leggermente più lenta: df[~(df.values.prod(axis=1) == 0) | ~(df.values.sum(axis=1)==0)]
clocker

La funzione prod () non risolve nulla. Se hai uno 0 nella riga che restituirà 0. Se devi gestire una riga come questa: [-1, -0.5, 0, 0.5, 1], nessuna delle tue soluzioni funzionerà.
Rahul Murmuria

Ecco una versione corretta che funziona 3 volte più velocemente della risposta accettata:bdf[np.square(bdf.values).sum(axis=1) != 0]
Rahul Murmuria

5
import pandas as pd

df = pd.DataFrame({'a' : [0,0,1], 'b' : [0,0,-1]})

temp = df.abs().sum(axis=1) == 0      
df = df.drop(temp)

Risultato:

>>> df
   a  b
2  1 -1

Non ha funzionato per me con un dataframe a 1 colonna. GotValueError: labels [True ... ] not contained in matrix
The Unfun Cat

invece di df = df.drop(temp)usaredf = df.drop(df[temp].index)
Douglas Ferreira

3

È possibile utilizzare una lambdafunzione rapida per verificare se tutti i valori in una determinata riga lo sono 0. Quindi puoi utilizzare il risultato dell'applicazione lambdacome un modo per scegliere solo le righe che corrispondono o non corrispondono a quella condizione:

import pandas as pd
import numpy as np

np.random.seed(0)

df = pd.DataFrame(np.random.randn(5,3), 
                  index=['one', 'two', 'three', 'four', 'five'],
                  columns=list('abc'))

df.loc[['one', 'three']] = 0

print df
print df.loc[~df.apply(lambda row: (row==0).all(), axis=1)]

Rendimenti:

              a         b         c
one    0.000000  0.000000  0.000000
two    2.240893  1.867558 -0.977278
three  0.000000  0.000000  0.000000
four   0.410599  0.144044  1.454274
five   0.761038  0.121675  0.443863

[5 rows x 3 columns]
             a         b         c
two   2.240893  1.867558 -0.977278
four  0.410599  0.144044  1.454274
five  0.761038  0.121675  0.443863

[3 rows x 3 columns]

1

Un'altra alternativa:

# Is there anything in this row non-zero?
# df != 0 --> which entries are non-zero? T/F
# (df != 0).any(axis=1) --> are there 'any' entries non-zero row-wise? T/F of rows that return true to this statement.
# df.loc[all_zero_mask,:] --> mask your rows to only show the rows which contained a non-zero entry.
# df.shape to confirm a subset.

all_zero_mask=(df != 0).any(axis=1) # Is there anything in this row non-zero?
df.loc[all_zero_mask,:].shape

0

Per me questo codice: df.loc[(df!=0).any(axis=0)] non ha funzionato. Ha restituito il set di dati esatto.

Invece, ho usato df.loc[:, (df!=0).any(axis=0)]e rilasciato tutte le colonne con valori 0 nel set di dati

La funzione ha .all()eliminato tutte le colonne in cui sono presenti valori zero nel mio set di dati.


-1
df = df [~( df [ ['kt'  'b'   'tt'  'mky' 'depth', ] ] == 0).all(axis=1) ]

Prova questo comando è perfettamente funzionante.


-2

Per eliminare tutte le colonne con valori 0 in qualsiasi riga:

new_df = df[df.loc[:]!=0].dropna()
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.