Cómo eliminar filas de un DataFrame de pandas basado en una expresión condicional [duplicado]

Resuelto sjs asked hace 12 años • 6 respuestas

Tengo un DataFrame de pandas y quiero eliminar filas donde la longitud de la cadena en una columna en particular es mayor que 2.

Espero poder hacer esto (según esta respuesta ):

df[(len(df['column name']) < 2)]

pero me sale el error:

KeyError: u'no item named False'

¿Qué estoy haciendo mal?

(Nota: sé que puedo usar df.dropna()para deshacerme de filas que contienen any NaN, pero no vi cómo eliminar filas en función de una expresión condicional).

sjs avatar Dec 13 '12 08:12 sjs
Aceptado

Para responder directamente al título original de esta pregunta "Cómo eliminar filas de un DataFrame de pandas basado en una expresión condicional" (que entiendo que no es necesariamente el problema del OP, pero podría ayudar a otros usuarios a encontrar esta pregunta), una forma de hacerlo es usar el método de caída :

df = df.drop(some labels)
df = df.drop(df[<some boolean condition>].index)

Ejemplo

Para eliminar todas las filas donde la columna 'puntuación' es <50:

df = df.drop(df[df.score < 50].index)

Versión vigente (como se señala en los comentarios)

df.drop(df[df.score < 50].index, inplace=True)

Múltiples condiciones

(ver Indexación booleana )

Los operadores son: |for or, &for andy ~for not. Estos deben agruparse mediante paréntesis.

Para eliminar todas las filas donde la columna 'puntuación' es <50 y >20

df = df.drop(df[(df.score < 50) & (df.score > 20)].index)
User avatar Dec 08 '2014 14:12 User

Cuando lo hace, len(df['column name'])solo obtiene un número, es decir, el número de filas en el DataFrame (es decir, la longitud de la columna misma). Si desea aplicar lena cada elemento de la columna, utilice df['column name'].map(len). Así que intenta

df[df['column name'].map(len) < 2]
BrenBarn avatar Dec 13 '2012 01:12 BrenBarn

Puedes asignarlo DataFramea una versión filtrada de sí mismo:

df = df[df.score > 50]

Esto es más rápido que drop:

%%timeit
test = pd.DataFrame({'x': np.random.randn(int(1e6))})
test = test[test.x < 0]
# 54.5 ms ± 2.02 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

%%timeit
test = pd.DataFrame({'x': np.random.randn(int(1e6))})
test.drop(test[test.x > 0].index, inplace=True)
# 201 ms ± 17.9 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

%%timeit
test = pd.DataFrame({'x': np.random.randn(int(1e6))})
test = test.drop(test[test.x > 0].index)
# 194 ms ± 7.03 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
somesingsomsing avatar Oct 18 '2016 14:10 somesingsomsing

Ampliaré la solución genérica de @User para ofrecer una dropalternativa gratuita. Esto es para las personas dirigidas aquí según el título de la pregunta (no el problema de OP)

Supongamos que desea eliminar todas las filas con valores negativos. Una solución de revestimiento es: -

df = df[(df > 0).all(axis=1)]

Explicación paso a paso:--

Generemos un marco de datos de distribución normal aleatoria de 5x5

np.random.seed(0)
df = pd.DataFrame(np.random.randn(5,5), columns=list('ABCDE'))
      A         B         C         D         E
0  1.764052  0.400157  0.978738  2.240893  1.867558
1 -0.977278  0.950088 -0.151357 -0.103219  0.410599
2  0.144044  1.454274  0.761038  0.121675  0.443863
3  0.333674  1.494079 -0.205158  0.313068 -0.854096
4 -2.552990  0.653619  0.864436 -0.742165  2.269755

Dejemos que la condición sea eliminar los negativos. Un df booleano que satisface la condición: -

df > 0
      A     B      C      D      E
0   True  True   True   True   True
1  False  True  False  False   True
2   True  True   True   True   True
3   True  True  False   True  False
4  False  True   True  False   True

Una serie booleana para todas las filas que cumplen la condición. Nota: si algún elemento de la fila no cumple la condición, la fila se marca como falsa

(df > 0).all(axis=1)
0     True
1    False
2     True
3    False
4    False
dtype: bool

Finalmente filtre las filas del marco de datos según la condición

df[(df > 0).all(axis=1)]
      A         B         C         D         E
0  1.764052  0.400157  0.978738  2.240893  1.867558
2  0.144044  1.454274  0.761038  0.121675  0.443863

Puede asignarlo nuevamente a df para eliminarlo en lugar del filtrado realizado anteriormente.
df = df[(df > 0).all(axis=1)]

Esto se puede ampliar fácilmente para filtrar filas que contienen NaN (entradas no numéricas): -
df = df[(~df.isnull()).all(axis=1)]

Esto también se puede simplificar para casos como: Eliminar todas las filas donde la columna E es negativa

df = df[(df.E>0)]

Me gustaría terminar con algunas estadísticas de perfiles sobre por qué la solución de @Usuario dropes más lenta que la filtración basada en columnas sin formato: -

%timeit df_new = df[(df.E>0)]
345 µs ± 10.5 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
%timeit dft.drop(dft[dft.E < 0].index, inplace=True)
890 µs ± 94.9 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

Una columna es básicamente, es decir, Seriesuna NumPymatriz, que se puede indexar sin ningún costo. Para las personas interesadas en cómo influye la organización de la memoria subyacente en la velocidad de ejecución, aquí hay un excelente enlace sobre Acelerar Pandas :

Zakir avatar Feb 16 '2019 03:02 Zakir