Cómo eliminar filas de un DataFrame de pandas basado en una expresión condicional [duplicado]
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).
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:
|
foror
,&
forand
y~
fornot
. 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)
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 len
a cada elemento de la columna, utilice df['column name'].map(len)
. Así que intenta
df[df['column name'].map(len) < 2]
Puedes asignarlo DataFrame
a 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)
Ampliaré la solución genérica de @User para ofrecer una drop
alternativa 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 drop
es 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, Series
una NumPy
matriz, 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 :