Pandas Como filtrar uma série

98

Eu tenho uma série como esta depois de fazer groupby ('nome') e usar a função mean () em outra coluna

name
383      3.000000
663      1.000000
726      1.000000
737      9.000000
833      8.166667

Alguém poderia me mostrar como filtrar as linhas com valores médios de 1.000000? Obrigado e agradeço muito sua ajuda.

Kiem Nguyen
fonte
Bem, como você filtraria uma série sob uma determinada condição?

Respostas:

133
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
Andrew
fonte
11
Prefiro as respostas abaixo porque podem ser encadeadas (ou seja, não há necessidade de definir se depois usar duas vezes na expressão). Porém, só funciona com pandas 0.18.
IanS
Veja também as comparações de tempo na resposta de piRSquared .
IanS
67

A partir do pandas versão 0.18+, a filtragem de uma série também pode ser feita como abaixo

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

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

DACW
fonte
3
Muito melhor com encadeamento de método (e me lembra do Spark.)
Dylan Hogg
É verdade, mas o Spark faz algo mais intuitivo neste caso: ele simplesmente se livra das linhas que não correspondem ao predicado, o que significa não usar a parte ".dropna ()" que parecia claramente supérflua para mim até eu ler o documento.
Foi
46

Como o DACW apontou , há melhorias no encadeamento de métodos no pandas 0.18.1 que fazem o que você está procurando muito bem.

Em vez de usar .where, você pode passar sua função para o .locindexador ou para o indexador Series []e evitar a chamada para .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]

Comportamento semelhante é suportado nas classes DataFrame e NDFrame.

Gordon Bean
fonte
2
Esta é a minha resposta favorita e também parece ser a mais rápida sem diminuir o número (veja as comparações de tempo).
IanS
22

Uma maneira rápida de fazer isso é reconstruir usando numpypara fatiar os arrays subjacentes. Veja os horários abaixo.

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

0
383    3.000000
737    9.000000
833    8.166667
dtype: float64

tempo ingênuo

insira a descrição da imagem aqui

piRSquared
fonte
, Eu gosto do seu método, quero saber e se eu tiver várias máscaras. Thx
Menglong Li
1
@MenglongLi depende, você deve fazer uma pergunta. Provavelmente, você os combinaria com &. mask = mask1 & mask2
piRSquared
6

Outra maneira é primeiro converter para um DataFrame e usar o método de consulta (supondo que você tenha numexpr instalado):

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
fonte
Não acho que seja uma boa ideia passar uma condição como uma string
SzymonPajzert
1
Isso adiciona toda a sobrecarga de um dataframe e será muito lento.
fantabolous de
5

Se você gosta de uma operação em cadeia, também pode usar a compressfunção:

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

No meu caso, tive uma série de panda onde os valores são 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)

Portanto, eu poderia usar a indexação para filtrar a série, mas para criar o índice de que precisava apply. Minha condição é "encontre todas as tuplas que têm exatamente um 'H'".

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

Eu admito que não é "encadeado" , (isto é, repito series_of_tuplesduas vezes; você deve armazenar qualquer série temporária em uma variável para que possa chamar apply (...) nela).

Também pode haver outros métodos (além .apply(...)) que podem operar elemento a elemento para produzir um índice booleano.

Muitas outras respostas (incluindo resposta aceita) usando as funções encadeadas como:

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

Eles aceitam chamáveis ​​(lambdas) que são aplicados à Série , não aos valores individuais dessas séries!

Portanto, minha série de tuplas se comportou de forma estranha quando tentei usar minha condição / callable / lambda acima, com qualquer uma das funções encadeadas, como .loc[]:

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

Produz o erro:

KeyError: 'Nível H deve ser igual ao nome (Nenhum)'

Fiquei muito confuso, mas parece que está usando a função Series.countseries_of_tuples.count(...) , que não é o que eu queria.

Admito que uma estrutura de dados alternativa pode ser melhor:

  • Um tipo de dados de categoria?
  • Um Dataframe (cada elemento da tupla se torna uma coluna)
  • Uma série de strings (apenas concatenar as tuplas):

Isso cria uma série de strings (ou seja, concatenando a tupla; juntando os caracteres na tupla em uma única string)

series_of_tuples.apply(''.join)

Então eu posso usar o encadeamentoSeries.str.count

series_of_tuples.apply(''.join).str.count('H')==1
The Red Pea
fonte