Comment supprimer une colonne qui ne contient que des zéros dans Pandas?

87

J'ai actuellement un dataframe composé de colonnes avec 1 et 0 comme valeurs, je voudrais parcourir les colonnes et supprimer celles qui ne sont composées que de 0. Voici ce que j'ai essayé jusqu'à présent:

ones = []
zeros = []
for year in years:
    for i in range(0,599):
        if year[str(i)].values.any() == 1:
            ones.append(i)
        if year[str(i)].values.all() == 0:
            zeros.append(i)
    for j in ones:
        if j in zeros:
            zeros.remove(j)
    for q in zeros:
        del year[str(q)]

Dans quelles années est une liste de dataframes pour les différentes années que j'analyse, les uns se compose de colonnes avec un un en eux et les zéros est une liste de colonnes contenant tous les zéros. Existe-t-il un meilleur moyen de supprimer une colonne en fonction d'une condition? Pour une raison quelconque, je dois vérifier si les colonnes des unités sont également dans la liste des zéros et les supprimer de la liste des zéros pour obtenir une liste de toutes les colonnes zéro.

user2587593
la source
Double
Shihe Zhang

Réponses:

214
df.loc[:, (df != 0).any(axis=0)]

Voici un aperçu de son fonctionnement:

In [74]: import pandas as pd

In [75]: df = pd.DataFrame([[1,0,0,0], [0,0,1,0]])

In [76]: df
Out[76]: 
   0  1  2  3
0  1  0  0  0
1  0  0  1  0

[2 rows x 4 columns]

df != 0crée un DataFrame booléen qui est True où dfest différent de zéro:

In [77]: df != 0
Out[77]: 
       0      1      2      3
0   True  False  False  False
1  False  False   True  False

[2 rows x 4 columns]

(df != 0).any(axis=0)renvoie une série booléenne indiquant quelles colonnes ont des entrées différentes de zéro. (L' anyopération regroupe les valeurs le long de l'axe 0 - c'est-à-dire le long des lignes - en une seule valeur booléenne. Le résultat est donc une valeur booléenne pour chaque colonne.)

In [78]: (df != 0).any(axis=0)
Out[78]: 
0     True
1    False
2     True
3    False
dtype: bool

Et df.locpeut être utilisé pour sélectionner ces colonnes:

In [79]: df.loc[:, (df != 0).any(axis=0)]
Out[79]: 
   0  2
0  1  0
1  0  1

[2 rows x 2 columns]

Pour "supprimer" les colonnes zéro, réattribuez df:

df = df.loc[:, (df != 0).any(axis=0)]
unutbu
la source
J'essaie de supprimer une colonne si elle contient 0 ou 1 et qu'elle donne une erreur: df = df.loc [:, (df! = 0 & df! = 1) .any (axis = 0)]
morpheus
1
df.loc[:, (~df.isin([0,1])).any(axis=0)]fonctionnerait également.
unutbu
1
@IgorFobia: Beaucoup de choses sont fausses sans être 0. Par exemple, des chaînes vides ou None ou NaN. Pour démontrer la différence, if df = pd.DataFrame([[np.nan]*10]), df.loc[:, df.any(axis=0)]renvoie alors un DataFrame vide, tandis que df.loc[:, (df != 0).any(axis=0)]renvoie un DataFrame avec 10 colonnes.
unutbu
4
Je pense qu'il est plus facile de comprendre si nous vérifions qu'une condition est vraie, au lieu de vérifier si une condition non vraie n'est jamais satisfaite. Je pense que (df == 0).all(axis=0)c'est plus simple.
Ryszard Cetnarski
2
Merci pour la panne. Cela a rendu les choses très claires.
Regi Mathew
7

Voici une autre façon d'utiliser

df.replace(0,np.nan).dropna(axis=1,how="all")

Par rapport à la solution de unutbu, cette méthode est évidemment plus lente:

%timeit df.loc[:, (df != 0).any(axis=0)]
652 µs ± 5.7 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

%timeit df.replace(0,np.nan).dropna(axis=1,how="all")
1.75 ms ± 9.49 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
Jeremy Z
la source
0

Au cas où vous souhaiteriez une manière plus expressive d'obtenir les noms de colonne zéro afin que vous puissiez les imprimer / les enregistrer, et les déposer, sur place, par leurs noms :

zero_cols = [ col for col, is_zero in ((df == 0).sum() == df.shape[0]).items() if is_zero ]
df.drop(zero_cols, axis=1, inplace=True)

Certains se décomposent:

# a pandas Series with {col: is_zero} items
# is_zero is True when the number of zero items in that column == num_all_rows
(df == 0).sum() == df.shape[0])

# a list comprehension of zero_col_names is built from the_series
[ col for col, is_zero in the_series.items() if is_zero ]
mork
la source