Uma coluna contém o nome do mês significa que há uma coluna que contém os nomes dos meses (como minha resposta), ou muitas colunas com nomes de colunas como nomes de meses (como eumiro)?
Andy Hayden
1
A resposta aceita está desatualizada e também é tecnicamente incorreta, pois pd.Categoricalnão interpreta as categorias como ordenadas por padrão. Veja esta resposta .
cs95 de
Respostas:
149
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
012 March
156 Dec
234 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
012 March
234 April
156 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
012 March
134 April
256 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
kdauria
@kdauria good spot! (já faz um tempo que eu escrevi isso!) substitua definitivamente a melhor opção, outra é usar .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á.
Andy Hayden,
@AndyHayden Tomei a liberdade de substituir a segunda linha pelo método 'replace'. Espero que esteja tudo bem.
Faheem Mitha 01 de
@AndyHayden edição rejeitada, mas ainda acho que é uma mudança razoável.
Faheem Mitha 01 de
7
Apenas certifique-se de usar 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';)
brainstorm de
22
pandas> = 1,1
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
012 March
156 Dec
234 April
df.sort_values(by=['m'], key=lambda x: x.map(custom_dict))
a b m
012 March
234 April
156 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 .
pandas <= 1.0.X
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
012 March
234 April
156 Dec
Se você precisar classificar em ordem decrescente , inverta o mapeamento.
df.iloc[(-df['m'].map(custom_dict)).argsort()]
a b m
156 Dec
234 April
012 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
156 Dec
234 April
012 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 .
Você já enfatizou isso, mas eu gostaria de reiterar no caso de outra pessoa passar despercebida e perder: Conjuntos categóricos do Pandas 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).
Dave Liu
17
Atualizar
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 :)
Postagem original
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:
defsort_pd(key=None,reverse=False,cmp=None):defsorter(series):
series_list = list(series)
return [series_list.index(i)
for i insorted(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
012 March
234 April
156 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]:
2816101271435885189238
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!
Isso funcionaria para classificar várias colunas / índices?
ConanG
sim, mas a resposta selecionada é uma maneira muito melhor de fazer isso. Se você tiver vários índices, organize-os de acordo com a ordem de classificação de sua preferência e use df.sort_index()para classificar todos os níveis de índice.
Michael Delgado
9
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
pd.Categorical
nã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_index
sobre 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
replace
mé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).
fonte
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 dedf.sort("m")
), caso contrário, você terá umAttributeError: 'DataFrame' object has no attribute 'sort'
;)pandas> = 1,1
Em breve você poderá usar
sort_values
com okey
argumento: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
key
argumento 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 .pandas <= 1.0.X
Um método simples é usar a saída
Series.map
eSeries.argsort
indexar emdf
usingDataFrame.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_values
e 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), oupd.Categorical
, mas você precisa especificarordered=True
para 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_values
chamada 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
groupby
classificar a saída.fonte
ordered=None
por 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).Atualizar
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 :)
Postagem original
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 comdf.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!
fonte
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
fonte