Supprimer les lignes avec tous les zéros dans le cadre de données pandas

104

Je peux utiliser la pandas dropna()fonctionnalité pour supprimer des lignes avec certaines ou toutes les colonnes définies comme NAs. Existe-t-il une fonction équivalente pour supprimer des lignes avec toutes les colonnes ayant la valeur 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

Dans cet exemple, nous aimerions supprimer les 4 premières lignes du bloc de données.

Merci!

user308827
la source
Juste pour clarifier, ce sont deux questions. Un, pour supprimer les colonnes avec toutes les valeurs comme 0. Mais aussi, pour une fonction équivalente à dropna () qui supprimerait les colonnes avec n'importe quelle valeur comme 0.
alchimie

Réponses:

112

Il s'avère que cela peut être bien exprimé de manière vectorisée:

> 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
la source
6
Bien, mais je pense que vous pouvez éviter la négation avecdf = df[(df.T != 0).any()]
Akavall
1
@Akavall Beaucoup mieux!
U2EF1
1
Juste une note: OP voulait abandonner rows with all columns having value 0, mais on peut en déduire la allméthode.
paulochf
1
Toutes ces réponses expliquent comment supprimer des lignes avec tous les zéros.Cependant, je voulais supprimer des lignes, avec 0 dans la première colonne. Avec l'aide de toutes les discussions et réponses dans ce post, je l'ai fait en faisant df.loc [df.iloc [:, 0]! = 0]. Je voulais juste partager car ce problème est lié à cette question !!
hemanta
2
La transposition n'est pas nécessaire, any () peut prendre un axe comme paramètre. Donc, cela fonctionne: df = df [df.any (axis = 1)]
Rahul Jha
130

Bon mot. Aucune transposition nécessaire:

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

Et pour ceux qui aiment la symétrie, cela fonctionne aussi ...

df.loc[(df!=0).any(axis=1)]
8one6
la source
1
Par souci de concision (et, à mon avis, la clarté de l' objectif) et combiner ce commentaire de Akavall: df.loc[(df != 0).any(1)]. Travail en équipe!
Dan Allan
1
+1, 30% plus rapide que la transposition - 491 à 614 microsec, et j'aime le axis=1pour être explicite; plus pythonique à mon avis
gt6989b
Il convient de mentionner la différence entre l'utilisation de .all et .any puisque la question originale mentionnait l'équivalence de dropna. Si vous souhaitez supprimer toutes les lignes avec une colonne contenant un zéro, vous devez inverser les réponses .all et .any ci-dessus. Il m'a fallu un certain temps pour réaliser cela alors que je cherchais cette fonctionnalité.
Zak Keirn
Cela ne fonctionne pas pour moi, mais me renvoie exactement la même chosedf
Robvh
Existe-t-il une version «en place» de ceci? Je vois que pour supprimer des lignes dans un df comme l'OP l'a demandé, cela devrait être df = df.loc[(df!=0).all(axis=1)]et df = df.loc[(df!=0).any(axis=1)]supprimer des lignes avec des zéros comme ce serait l'équivalent réel de dropna ().
alchimie le
20

Je recherche cette question environ une fois par mois et je dois toujours trouver la meilleure réponse dans les commentaires:

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

Merci Dan Allan!

Le chat impertinent
la source
2
Aucun creusage requis. @ 8one6 l'a inclus dans sa réponse en 2014 même, la partie qui dit: "Et pour ceux qui aiment la symétrie ...".
Rahul Murmuria
14

Remplacez les zéros par nan, puis supprimez les lignes avec toutes les entrées comme nan. Après cela, remplacez nanpar des zéros.

import numpy as np
df = df.replace(0, np.nan)
df = df.dropna(how='all', axis=0)
df = df.replace(np.nan, 0)
empilé
la source
4
Cela échouera si vous avez des NaN-s préexistants dans les données.
OmerB
11

Je pense que cette solution est la plus courte:

df= df[df['ColName'] != 0]
ikbel benab
la source
1
Et c'est en place aussi!
Max Kleiner
@MaxKleiner inplace en vertu de la réaffectation de la variable
lukas
7

Quelques solutions que j'ai trouvées utiles lors de la recherche, en particulier pour les ensembles de données plus volumineux:

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

Poursuivant avec l'exemple 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

Sur un ensemble de données plus grand:

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
horloge
la source
Est-ce que de mauvaises choses se produisent si votre ligne contient un -1 et un 1?
Rhys Ulerich
Bien sûr, la somme ne fonctionnerait pas si vous aviez des lignes égales totalisant 0. Voici une solution rapide pour ce qui n'est que légèrement plus lent: df[~(df.values.prod(axis=1) == 0) | ~(df.values.sum(axis=1)==0)]
clocker
La fonction prod () ne résout rien. Si vous avez un 0 dans la ligne qui renverra 0. Si vous devez gérer une ligne comme celle-ci: [-1, -0,5, 0, 0,5, 1], aucune de vos solutions ne fonctionnera.
Rahul Murmuria
Voici une version correcte qui fonctionne 3x plus vite que la réponse acceptée: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)

Résultat:

>>> df
   a  b
2  1 -1
Akavall
la source
Cela n'a pas fonctionné pour moi avec un dataframe à 1 colonne. GotValueError: labels [True ... ] not contained in matrix
The Unfun Cat
au lieu d' df = df.drop(temp)utilisationdf = df.drop(df[temp].index)
Douglas Ferreira
3

Vous pouvez utiliser une lambdafonction rapide pour vérifier si toutes les valeurs d'une ligne donnée le sont 0. Ensuite, vous pouvez utiliser le résultat de l'application de cela lambdacomme un moyen de choisir uniquement les lignes qui correspondent ou ne correspondent pas à cette condition:

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

Rendements:

              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
la source
1

Une autre alternative:

# 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
la source
0

Pour moi ce code: df.loc[(df!=0).any(axis=0)] n'a pas fonctionné. Il a renvoyé l'ensemble de données exact.

Au lieu de cela, j'ai utilisé df.loc[:, (df!=0).any(axis=0)] et supprimé toutes les colonnes avec 0 valeurs dans l'ensemble de données

La fonction a .all()supprimé toutes les colonnes dans lesquelles se trouvent des valeurs nulles dans mon ensemble de données.

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

Essayez cette commande, elle fonctionne parfaitement.

Kumar Prasanna
la source
-2

Pour supprimer toutes les colonnes avec les valeurs 0 dans n'importe quelle ligne:

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