Como excluo uma coluna que contém apenas zeros no Pandas?

87

Atualmente, tenho um dataframe que consiste em colunas com valores de 1 e 0, gostaria de iterar pelas colunas e excluir aquelas que são compostas de apenas 0s. Aqui está o que tentei até agora:

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

Em que anos é uma lista de quadros de dados para os vários anos que estou analisando, uns consiste em colunas com um e zeros é uma lista de colunas contendo todos zeros. Existe uma maneira melhor de excluir uma coluna com base em uma condição? Por alguma razão, tenho que verificar se as colunas de uns estão na lista de zeros também e removê-las da lista de zeros para obter uma lista de todas as colunas de zero.

user2587593
fonte
Possível duplicata da linha
Shihe Zhang

Respostas:

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

Aqui está um resumo de como funciona:

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 != 0cria um DataFrame booleano que é True, onde dfé diferente de zero:

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)retorna uma série booleana indicando quais colunas têm entradas diferentes de zero. (A anyoperação agrega valores ao longo do eixo 0 - ou seja, ao longo das linhas - em um único valor booleano. Portanto, o resultado é um valor booleano para cada coluna.)

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

E df.locpode ser usado para selecionar essas colunas:

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

[2 rows x 2 columns]

Para "excluir" as colunas zero, reatribua df:

df = df.loc[:, (df != 0).any(axis=0)]
unutbu
fonte
Estou tentando remover uma coluna se ela tiver 0 ou 1 e der um erro: df = df.loc [:, (df! = 0 & df! = 1) .any (axis = 0)]
morpheus
1
df.loc[:, (~df.isin([0,1])).any(axis=0)]também funcionaria.
unutbu
1
@IgorFobia: Muitas coisas são falsas sem serem 0. Por exemplo, strings vazias ou None ou NaN. Para demonstrar a diferença, if df = pd.DataFrame([[np.nan]*10]), then df.loc[:, df.any(axis=0)]retorna um DataFrame vazio, enquanto df.loc[:, (df != 0).any(axis=0)]retorna um DataFrame com 10 colunas.
unutbu
4
Acredito que seja mais fácil de entender se verificarmos se uma condição é verdadeira, em vez de verificar se a condição não verdadeira nunca não é satisfeita. Eu acho que (df == 0).all(axis=0)é mais direto.
Ryszard Cetnarski
2
Obrigado pela repartição. Isso deixou as coisas muito claras.
Regi Mathew
7

Aqui está uma maneira alternativa de usar

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

Comparado com a solução do unutbu, este caminho é obviamente mais lento:

%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
fonte
0

Caso você queira uma maneira mais expressiva de obter os nomes de coluna zero, para poder imprimi-los / registrá-los e soltá-los no local pelos nomes :

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)

Alguns quebram:

# 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
fonte