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!


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

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

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!


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)

3
Isso falhará se você tiver algum NaN-s pré-existente nos dados.
— OmerB


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

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

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]

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

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.


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

Tente este comando está funcionando perfeitamente.


-2

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

new_df = df[df.loc[:]!=0].dropna()
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.