Solte as linhas com zeros no quadro de dados do pandas

103

Posso usar a pandas dropna()funcionalidade para remover linhas com algumas ou todas as colunas definidas como NA's. Existe uma função equivalente para descartar linhas com todas as colunas com valor 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

Neste exemplo, gostaríamos de eliminar as primeiras 4 linhas do quadro de dados.

obrigado!

user308827
fonte
Só para esclarecer, são duas perguntas. Um, para eliminar colunas com todos os valores como 0. Mas também, para uma função equivalente a dropna () que eliminaria colunas com qualquer valor como 0.
alquimia

Respostas:

111

Acontece que isso pode ser bem expresso de uma forma vetorizada:

> 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
U2EF1
fonte
6
Legal, mas acho que você pode evitar a negação comdf = df[(df.T != 0).any()]
Akavall
1
@Akavall Muito melhor!
U2EF1
1
Apenas uma observação: OP queria abandonar rows with all columns having value 0, mas pode-se inferir o allmétodo.
paulochf
1
Todas essas respostas explicam como podemos descartar linhas com zeros. No entanto, eu queria descartar linhas com 0 na primeira coluna. Com a ajuda de todas as discussões e respostas neste post, fiz isso fazendo df.loc [df.iloc [:, 0]! = 0]. Só queria compartilhar porque esse problema está relacionado a essa questão !!
hemanta
2
A transposição não é necessária, any () pode tomar um eixo como parâmetro. Portanto, isso funciona: df = df [df.any (axis = 1)]
Rahul Jha
129

One-liner. Sem necessidade de transposição:

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

E pra quem gosta de simetria, isso também funciona ...

df.loc[(df!=0).any(axis=1)]
8one6
fonte
1
Para abreviar (e, na minha opinião, clareza de propósito) combinar isso e comentário de Akavall: df.loc[(df != 0).any(1)]. Trabalho em equipe!
Dan Allan
1
+1, 30% mais rápido que transpõe - 491 a 614 microseg, e gosto do axis=1por ser explícito; mais pitônico na minha opinião
gt6989b
Alguma menção deve ser feita sobre a diferença entre usar .all e .any, uma vez que a questão original mencionava equivalência de dropna. Se você quiser descartar todas as linhas com qualquer coluna que contenha um zero, deverá inverter .all e .any na resposta acima. Levei um tempo para perceber isso, pois estava procurando por essa funcionalidade.
Zak Keirn
Isso não funciona para mim, mas me retorna exatamente o mesmodf
Robvh
Existe uma versão 'local' disso? Vejo que para descartar linhas em um df como o OP solicitou, isso precisaria ser df = df.loc[(df!=0).all(axis=1)]e df = df.loc[(df!=0).any(axis=1)]descartar linhas com quaisquer zeros, pois seria o equivalente real a dropna ().
alquimia
19

Eu procuro esta questão uma vez por mês e sempre tenho que descobrir a melhor resposta dos comentários:

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

Obrigado Dan Allan!

The Unfun Cat
fonte
2
Nenhuma escavação necessária. @ 8one6 incluiu isso em sua resposta em 2014, a parte que diz: "E para quem gosta de simetria ...".
Rahul Murmuria
14

Substitua os zeros por nane, em seguida, elimine as linhas com todas as entradas como nan. Depois disso, substitua nanpor zeros.

import numpy as np
df = df.replace(0, np.nan)
df = df.dropna(how='all', axis=0)
df = df.replace(np.nan, 0)
empilhado
fonte
3
Isso falhará se você tiver algum NaN-s pré-existente nos dados.
OmerB
9

Acho que essa solução é a mais curta:

df= df[df['ColName'] != 0]
ikbel benab
fonte
1
E também está no lugar!
Max Kleiner
7

Algumas soluções que descobri serem úteis ao pesquisar isso, especialmente para conjuntos de dados maiores:

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

Continuando com o exemplo de @ 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

Em um conjunto de dados maior:

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
relojoeiro
fonte
Coisas ruins acontecem se sua linha contém -1 e 1?
Rhys Ulerich
Claro, a soma não funcionaria se você tivesse linhas iguais somando 0. Aqui está uma solução rápida para isso, que é apenas um pouco mais lento: df[~(df.values.prod(axis=1) == 0) | ~(df.values.sum(axis=1)==0)]
cronômetro de
A função prod () não resolve nada. Se você tiver qualquer 0 na linha que retornará 0. Se você tiver que lidar com uma linha como esta: [-1, -0,5, 0, 0,5, 1], nenhuma de suas soluções funcionará.
Rahul Murmuria
Aqui está uma versão correta que funciona 3x mais rápido do que a resposta aceita: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)

Resultado:

>>> df
   a  b
2  1 -1
Akavall
fonte
Não funcionou para mim com um dataframe de 1 coluna. GotValueError: labels [True ... ] not contained in matrix
The Unfun Cat
em vez de df = df.drop(temp)usardf = df.drop(df[temp].index)
Douglas Ferreira
3

Você pode usar uma lambdafunção rápida para verificar se todos os valores em uma determinada linha são 0. Em seguida, você pode usar o resultado da aplicação disso lambdacomo uma forma de escolher apenas as linhas que correspondem ou não a essa condição:

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

Rendimentos:

              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]
8one6
fonte
1

Outra 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
bmc
fonte
0

Para mim, este código: df.loc[(df!=0).any(axis=0)] não funcionou. Ele retornou o conjunto de dados exato.

Em vez disso, usei df.loc[:, (df!=0).any(axis=0)] e eliminei todas as colunas com valor 0 no conjunto de dados

A função .all()eliminou todas as colunas nas quais há qualquer valor zero em meu conjunto de dados.

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

Tente este comando está funcionando perfeitamente.

Kumar Prasanna
fonte
-2

Para eliminar todas as colunas com valores 0 em qualquer linha:

new_df = df[df.loc[:]!=0].dropna()
Yapi
fonte