Suelta filas con todos ceros en el marco de datos de pandas

104

Puedo usar la pandas dropna()funcionalidad para eliminar filas con algunas o todas las columnas configuradas como NA's. ¿Existe una función equivalente para eliminar filas con todas las columnas con 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

En este ejemplo, nos gustaría eliminar las primeras 4 filas del marco de datos.

¡Gracias!

user308827
fuente
Solo para aclarar, estas son dos preguntas. Uno, eliminar columnas con todos los valores como 0. Pero también, para una función equivalente a dropna () que eliminaría columnas con cualquier valor como 0.
alquimia

Respuestas:

114

Resulta que esto se puede expresar muy bien de manera vectorizada:

> 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
fuente
6
Bien, pero creo que puedes evitar la negación condf = df[(df.T != 0).any()]
Akavall
1
@Akavall ¡Mucho mejor!
U2EF1
1
Solo una nota: OP quería eliminar rows with all columns having value 0, pero se puede inferir el allmétodo.
paulochf
1
Todas estas respuestas explican cómo podemos eliminar filas con ceros. Sin embargo, quería eliminar filas, con 0 en la primera columna. Con la ayuda de todas las discusiones y respuestas en esta publicación, hice esto haciendo df.loc [df.iloc [:, 0]! = 0]. ¡Solo quería compartir porque este problema está relacionado con esta pregunta!
hemanta
2
La transposición no es necesaria, any () puede tomar un eje como parámetro. Entonces esto funciona: df = df [df.any (axis = 1)]
Rahul Jha
130

Un trazador de líneas. No se necesita transposición:

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

Y para aquellos a los que les gusta la simetría, esto también funciona ...

df.loc[(df!=0).any(axis=1)]
8one6
fuente
1
Por razones de brevedad (y, en mi opinión, la claridad de propósito) combinar esto y comentario de Akavall: df.loc[(df != 0).any(1)]. ¡Trabajo en equipo!
Dan Allan
1
+1, 30% más rápido que la transposición - 491 a 614 microsegundos, y me gusta el axis=1por ser explícito; más pitónico en mi opinión
gt6989b
Debe hacerse alguna mención a la diferencia entre usar .all y .any ya que la pregunta original mencionaba la equivalencia de dropna. Si desea eliminar todas las filas con cualquier columna que contenga un cero, debe invertir el .all y .any en la respuesta anterior. Me tomó un tiempo darme cuenta de esto ya que estaba buscando esa funcionalidad.
Zak Keirn
Esto no me funciona, pero me devuelve exactamente lo mismodf
Robvh
¿Existe una versión "in situ" de esto? Veo que para eliminar filas en un df como lo solicitó el OP, esto debería ser df = df.loc[(df!=0).all(axis=1)]y df = df.loc[(df!=0).any(axis=1)]eliminar filas con ceros, ya que sería el equivalente real a dropna ().
alquimia
20

Busco esta pregunta una vez al mes y siempre tengo que buscar la mejor respuesta de los comentarios:

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

¡Gracias Dan Allan!

El gato unfun
fuente
2
No es necesario excavar. @ 8one6 ha incluido esto en su respuesta en el mismo 2014, la parte que dice: "Y para los que les gusta la simetría ...".
Rahul Murmuria
15

Reemplace los ceros con nany luego suelte las filas con todas las entradas como nan. Después de eso, reemplácelo nancon ceros.

import numpy as np
df = df.replace(0, np.nan)
df = df.dropna(how='all', axis=0)
df = df.replace(np.nan, 0)
apilado
fuente
4
Esto fallará si tiene NaN-s preexistentes en los datos.
OmerB
11

Creo que esta solución es la más corta:

df= df[df['ColName'] != 0]
ikbel benab
fuente
1
¡Y también está en su lugar!
Max Kleiner
@MaxKleiner en su lugar en virtud de reasignar la variable
lukas
7

Un par de soluciones que encontré útiles al buscar esto, especialmente para conjuntos de datos más grandes:

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

Continuando con el ejemplo 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

En un conjunto de datos más grande:

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
relojero
fuente
¿Suceden cosas malas si su fila contiene un -1 y un 1?
Rhys Ulerich
Por supuesto, la suma no funcionaría si tuviera filas iguales que sumen 0. Aquí hay una solución rápida para eso que es solo un poco más lento: df[~(df.values.prod(axis=1) == 0) | ~(df.values.sum(axis=1)==0)]
clocker
La función prod () no resuelve nada. Si tiene cualquier 0 en la fila, devolverá 0. Si tiene que manejar una fila como esta: [-1, -0.5, 0, 0.5, 1], ninguna de sus soluciones funcionará.
Rahul Murmuria
Aquí hay una versión correcta que funciona 3 veces más rápido que la respuesta aceptada: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
fuente
No funcionó para mí con un marco de datos de 1 columna. GotValueError: labels [True ... ] not contained in matrix
The Unfun Cat
en lugar de df = df.drop(temp)usardf = df.drop(df[temp].index)
Douglas Ferreira
3

Puede utilizar una lambdafunción rápida para comprobar si todos los valores de una fila determinada lo son 0. Luego, puede usar el resultado de aplicar eso lambdacomo una forma de elegir solo las filas que coinciden o no coinciden con esa condición:

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

Rendimientos:

              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
fuente
1

Otra 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
fuente
0

Para mí este código: df.loc[(df!=0).any(axis=0)] no funcionó. Devolvió el conjunto de datos exacto.

En cambio, usé df.loc[:, (df!=0).any(axis=0)]y eliminé todas las columnas con valores 0 en el conjunto de datos

La función .all()eliminó todas las columnas en las que hay valores cero en mi conjunto de datos.

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

Prueba este comando, funciona perfectamente.

Kumar Prasanna
fuente
-2

Para eliminar todas las columnas con valores 0 en cualquier fila:

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