Pandas Cómo filtrar una serie

94

Tengo una serie como esta después de hacer groupby ('nombre') y usar la función mean () en otra columna

name
383      3.000000
663      1.000000
726      1.000000
737      9.000000
833      8.166667

¿Alguien podría mostrarme cómo filtrar las filas con valores medios de 1.000000? Gracias y agradezco mucho su ayuda.

Kiem Nguyen
fuente
Bueno, ¿cómo filtrarías una serie según una condición determinada?

Respuestas:

127
In [5]:

import pandas as pd

test = {
383:    3.000000,
663:    1.000000,
726:    1.000000,
737:    9.000000,
833:    8.166667
}

s = pd.Series(test)
s = s[s != 1]
s
Out[0]:
383    3.000000
737    9.000000
833    8.166667
dtype: float64
Andrés
fuente
10
Prefiero las respuestas a continuación porque se pueden encadenar (es decir, no es necesario definirlas sy luego usarlas dos veces en la expresión). Sin embargo, solo funciona desde pandas 0.18.
IanS
También vea las comparaciones de tiempo en la respuesta de piRSquared .
IanS
63

Desde la versión 0.18+ de pandas, el filtrado de una serie también se puede hacer de la siguiente manera

test = {
383:    3.000000,
663:    1.000000,
726:    1.000000,
737:    9.000000,
833:    8.166667
}

pd.Series(test).where(lambda x : x!=1).dropna()

Pago: http://pandas.pydata.org/pandas-docs/version/0.18.1/whatsnew.html#method-chaininng-improvements

DACW
fuente
3
Mucho mejor con el método de encadenamiento (y me recuerda a Spark.)
Dylan Hogg
Es cierto, pero Spark hace algo más intuitivo en este caso: simplemente se deshace de las filas que no coinciden con el predicado, eso significa no usar la parte ".dropna ()" que me pareció claramente superflua hasta que leí el documento. Me mordió eso: D
Florent Moiny
44

Como señaló DACW , hay mejoras de encadenamiento de métodos en pandas 0.18.1 que hacen muy bien lo que busca.

En lugar de usar .where, puede pasar su función al .locindexador o al indexador de la serie []y evitar la llamada a .dropna:

test = pd.Series({
383:    3.000000,
663:    1.000000,
726:    1.000000,
737:    9.000000,
833:    8.166667
})

test.loc[lambda x : x!=1]

test[lambda x: x!=1]

Se admite un comportamiento similar en las clases DataFrame y NDFrame.

Gordon Bean
fuente
2
Esta es mi respuesta favorita, y también parece ser la más rápida sin caer en un montón (ver comparaciones de tiempo).
IanS
21

Una forma rápida de hacer esto es reconstruir usando numpypara cortar las matrices subyacentes. Vea los tiempos a continuación.

mask = s.values != 1
pd.Series(s.values[mask], s.index[mask])

0
383    3.000000
737    9.000000
833    8.166667
dtype: float64

sincronización ingenua

ingrese la descripción de la imagen aquí

piRSquared
fuente
, Me gusta tu método, quiero saber qué pasa si tengo múltiples máscaras. Thx
Menglong Li
1
@MenglongLi depende, deberías hacer una pregunta. Lo más probable es que los combine con &. mask = mask1 & mask2
piRSquared
6

Otra forma es convertir primero a un DataFrame y usar el método de consulta (suponiendo que tenga instalado numexpr):

import pandas as pd

test = {
383:    3.000000,
663:    1.000000,
726:    1.000000,
737:    9.000000,
833:    8.166667
}

s = pd.Series(test)
s.to_frame(name='x').query("x != 1")
Kamil Sindi
fuente
No creo que sea una buena idea pasar una condición como una cadena
SzymonPajzert
1
Esto agrega toda la sobrecarga de un marco de datos y será muy lento.
fantástico
5

Si le gusta una operación encadenada, también puede usar la compressfunción:

test = pd.Series({
383:    3.000000,
663:    1.000000,
726:    1.000000,
737:    9.000000,
833:    8.166667
})

test.compress(lambda x: x != 1)

# 383    3.000000
# 737    9.000000
# 833    8.166667
# dtype: float64
Psidom
fuente
1

En mi caso tuve una serie panda donde los valores son tuplas de caracteres :

Out[67]
0    (H, H, H, H)
1    (H, H, H, T)
2    (H, H, T, H)
3    (H, H, T, T)
4    (H, T, H, H)

Por lo tanto, podría usar la indexación para filtrar la serie, pero para crear el índice que necesitaba apply. Mi condición es "encontrar todas las tuplas que tengan exactamente una 'H'".

series_of_tuples[series_of_tuples.apply(lambda x: x.count('H')==1)]

Admito que no es "encadenable" (es decir, observe que repito series_of_tuplesdos veces; debe almacenar cualquier serie temporal en una variable para que pueda llamar a aplicar (...) en ella).

También puede haber otros métodos (además .apply(...)) que pueden operar por elementos para producir un índice booleano.

Muchas otras respuestas (incluida la respuesta aceptada) usando las funciones encadenables como:

  • .compress()
  • .where()
  • .loc[]
  • []

¡Estos aceptan callables (lambdas) que se aplican a la Serie , no a los valores individuales en esas series!

Por lo tanto, mi Serie de tuplas se comportó de manera extraña cuando intenté usar mi condición anterior / invocable / lambda, con cualquiera de las funciones encadenables, como .loc[]:

series_of_tuples.loc[lambda x: x.count('H')==1]

Produce el error:

KeyError: 'El nivel H debe ser el mismo que el nombre (Ninguno)'

Estaba muy confundido, pero parece estar usando la función Series.countseries_of_tuples.count(...) , que no es lo que quería.

Admito que una estructura de datos alternativa puede ser mejor:

  • ¿Un tipo de datos de categoría?
  • Un marco de datos (cada elemento de la tupla se convierte en una columna)
  • Una serie de cadenas (simplemente concatenar las tuplas juntas):

Esto crea una serie de cadenas (es decir, concatenando la tupla; uniendo los caracteres de la tupla en una sola cadena)

series_of_tuples.apply(''.join)

Entonces puedo usar el encadenableSeries.str.count

series_of_tuples.apply(''.join).str.count('H')==1
El guisante rojo
fuente