Tenho dataframe python pandas, em que uma coluna contém o nome do mês.
Como posso fazer uma classificação personalizada usando um dicionário, por exemplo:
custom_dict = {'March':0, 'April':1, 'Dec':3}
Tenho dataframe python pandas, em que uma coluna contém o nome do mês.
Como posso fazer uma classificação personalizada usando um dicionário, por exemplo:
custom_dict = {'March':0, 'April':1, 'Dec':3}
pd.Categoricalnão interpreta as categorias como ordenadas por padrão. Veja esta resposta .
Respostas:
O Pandas 0.15 introduziu a série categórica , que permite uma maneira muito mais clara de fazer isso:
Primeiro, torne a coluna do mês categórica e especifique a ordem a ser usada.
In [21]: df['m'] = pd.Categorical(df['m'], ["March", "April", "Dec"])
In [22]: df # looks the same!
Out[22]:
a b m
0 1 2 March
1 5 6 Dec
2 3 4 April
Agora, quando você classificar a coluna do mês, ela classificará em relação a essa lista:
In [23]: df.sort_values("m")
Out[23]:
a b m
0 1 2 March
2 3 4 April
1 5 6 Dec
Nota: se um valor não estiver na lista, ele será convertido para NaN.
Uma resposta mais antiga para os interessados ...
Você poderia criar uma série intermediária, e set_indexsobre isso:
df = pd.DataFrame([[1, 2, 'March'],[5, 6, 'Dec'],[3, 4, 'April']], columns=['a','b','m'])
s = df['m'].apply(lambda x: {'March':0, 'April':1, 'Dec':3}[x])
s.sort_values()
In [4]: df.set_index(s.index).sort()
Out[4]:
a b m
0 1 2 March
1 3 4 April
2 5 6 Dec
Conforme comentado, em pandas mais recentes, Series tem um replacemétodo para fazer isso de forma mais elegante:
s = df['m'].replace({'March':0, 'April':1, 'Dec':3})
A pequena diferença é que ele não aumentará se houver um valor fora do dicionário (ele apenas permanecerá o mesmo).
s = df['m'].replace({'March':0, 'April':1, 'Dec':3})funciona para a linha 2 também - apenas para o bem de qualquer pessoa que esteja aprendendo pandas como eu
.apply({'March':0, 'April':1, 'Dec':3}.get):) No 0.15 teremos séries / colunas categóricas, então a melhor maneira será usar isso e então a classificação funcionará.
df.sort_values("m")em pandas mais novos (em vez de df.sort("m")), caso contrário, você terá um AttributeError: 'DataFrame' object has no attribute 'sort';)
Em breve você poderá usar sort_valuescom o keyargumento:
pd.__version__
# '1.1.0.dev0+2004.g8d10bfb6f'
custom_dict = {'March': 0, 'April': 1, 'Dec': 3}
df
a b m
0 1 2 March
1 5 6 Dec
2 3 4 April
df.sort_values(by=['m'], key=lambda x: x.map(custom_dict))
a b m
0 1 2 March
2 3 4 April
1 5 6 Dec
O keyargumento recebe como entrada uma série e retorna uma série. Esta série é classificada internamente e os índices classificados são usados para reordenar o DataFrame de entrada. Se houver várias colunas para classificar, a função principal será aplicada a cada uma delas. Consulte Classificando com chaves .
Um método simples é usar a saída Series.mape Series.argsortindexar em dfusing DataFrame.iloc(uma vez que argsort produz posições inteiras classificadas); já que você tem um dicionário; isso se torna fácil.
df.iloc[df['m'].map(custom_dict).argsort()]
a b m
0 1 2 March
2 3 4 April
1 5 6 Dec
Se você precisar classificar em ordem decrescente , inverta o mapeamento.
df.iloc[(-df['m'].map(custom_dict)).argsort()]
a b m
1 5 6 Dec
2 3 4 April
0 1 2 March
Observe que isso só funciona com itens numéricos. Caso contrário, você precisará contornar isso usando sort_valuese acessando o índice:
df.loc[df['m'].map(custom_dict).sort_values(ascending=False).index]
a b m
1 5 6 Dec
2 3 4 April
0 1 2 March
Mais opções estão disponíveis com astype(está obsoleto agora), ou pd.Categorical, mas você precisa especificar ordered=Truepara que funcione corretamente .
# Older version,
# df['m'].astype('category',
# categories=sorted(custom_dict, key=custom_dict.get),
# ordered=True)
df['m'] = pd.Categorical(df['m'],
categories=sorted(custom_dict, key=custom_dict.get),
ordered=True)
Agora, uma simples sort_valueschamada resolverá o problema:
df.sort_values('m')
a b m
0 1 2 March
2 3 4 April
1 5 6 Dec
A ordem categórica também será respeitada ao groupbyclassificar a saída.
ordered=Nonepor padrão. Se não for definido, a ordem estará errada ou falhará no V23. A função Max em particular fornece um TypeError (Categórico não é solicitado para a operação max).
use a resposta selecionada ! é mais recente do que este post e não é apenas a forma oficial de manter dados ordenados nos pandas, é melhor em todos os aspectos, incluindo recursos / desempenho, etc. Não use meu método hacky que descrevo abaixo.
Só estou escrevendo esta atualização porque as pessoas continuam votando em minha resposta, mas é definitivamente pior do que a aceita :)
Um pouco tarde para o jogo, mas aqui está uma maneira de criar uma função que classifica objetos da série pandas, DataFrame e DataFrame multiindex usando funções arbitrárias.
Eu uso o df.iloc[index]método, que faz referência a uma linha em um Series / DataFrame por posição (em comparação com df.loc, que faz referência por valor). Usando isso, só precisamos ter uma função que retorne uma série de argumentos posicionais:
def sort_pd(key=None,reverse=False,cmp=None):
def sorter(series):
series_list = list(series)
return [series_list.index(i)
for i in sorted(series_list,key=key,reverse=reverse,cmp=cmp)]
return sorter
Você pode usar isso para criar funções de classificação personalizadas. Isso funciona no dataframe usado na resposta de Andy Hayden:
df = pd.DataFrame([
[1, 2, 'March'],
[5, 6, 'Dec'],
[3, 4, 'April']],
columns=['a','b','m'])
custom_dict = {'March':0, 'April':1, 'Dec':3}
sort_by_custom_dict = sort_pd(key=custom_dict.get)
In [6]: df.iloc[sort_by_custom_dict(df['m'])]
Out[6]:
a b m
0 1 2 March
2 3 4 April
1 5 6 Dec
Isso também funciona em DataFrames multi-índice e objetos Series:
months = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec']
df = pd.DataFrame([
['New York','Mar',12714],
['New York','Apr',89238],
['Atlanta','Jan',8161],
['Atlanta','Sep',5885],
],columns=['location','month','sales']).set_index(['location','month'])
sort_by_month = sort_pd(key=months.index)
In [10]: df.iloc[sort_by_month(df.index.get_level_values('month'))]
Out[10]:
sales
location month
Atlanta Jan 8161
New York Mar 12714
Apr 89238
Atlanta Sep 5885
sort_by_last_digit = sort_pd(key=lambda x: x%10)
In [12]: pd.Series(list(df['sales'])).iloc[sort_by_last_digit(df['sales'])]
Out[12]:
2 8161
0 12714
3 5885
1 89238
Para mim, isso parece limpo, mas usa fortemente as operações do python em vez de depender de operações otimizadas do pandas. Não fiz nenhum teste de estresse, mas imagino que isso poderia ficar lento em DataFrames muito grandes. Não tenho certeza de como o desempenho se compara à adição, classificação e exclusão de uma coluna. Quaisquer dicas sobre como acelerar o código serão apreciadas!
df.sort_index()para classificar todos os níveis de índice.
import pandas as pd
custom_dict = {'March':0,'April':1,'Dec':3}
df = pd.DataFrame(...) # with columns April, March, Dec (probably alphabetically)
df = pd.DataFrame(df, columns=sorted(custom_dict, key=custom_dict.get))
retorna um DataFrame com colunas março, abril, dezembro