Est-ce qu'une colonne contient le nom du mois signifie qu'il y a une colonne qui contient des noms de mois (comme ma réponse), ou de nombreuses colonnes avec des noms de colonne comme noms de mois (comme eumiro)?
Andy Hayden
1
La réponse acceptée est obsolète et est également techniquement incorrecte, car elle pd.Categoricaln'interprète pas les catégories comme ordonnées par défaut. Voyez cette réponse .
cs95
Réponses:
141
Pandas 0.15 a introduit la série catégorielle , qui permet une manière beaucoup plus claire de le faire:
Commencez par définir la colonne du mois comme catégorique et spécifiez l'ordre à utiliser.
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
Maintenant, lorsque vous triez la colonne du mois, elle sera triée par rapport à cette liste:
In [23]: df.sort_values("m")
Out[23]:
a b m
012 March
234 April
156 Dec
Remarque: si une valeur ne figure pas dans la liste, elle sera convertie en NaN.
Une réponse plus ancienne pour ceux qui sont intéressés ...
Vous pourriez créer une série intermédiaire, et set_indexà ce sujet:
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
Comme commenté, dans les pandas plus récents, Series a une replaceméthode pour le faire plus élégamment:
s = df['m'].replace({'March':0, 'April':1, 'Dec':3})
La légère différence est que cela n'augmentera pas s'il y a une valeur en dehors du dictionnaire (elle restera simplement la même).
s = df['m'].replace({'March':0, 'April':1, 'Dec':3})fonctionne également pour la ligne 2 - juste pour le bien de quiconque apprend des pandas comme moi
kdauria
@kdauria bon endroit! (Cela fait un moment que j'ai écrit ceci!) remplace définitivement la meilleure option, une autre consiste à utiliser .apply({'March':0, 'April':1, 'Dec':3}.get):) En 0.15, nous aurons des séries / colonnes catégoriques, donc la meilleure façon sera de l'utiliser et ensuite le tri fonctionnera.
Andy Hayden
@AndyHayden J'ai pris la liberté de remplacer la deuxième ligne par la méthode «replace». J'espère que c'est bon.
Faheem Mitha le
@AndyHayden edit rejeté, mais je pense toujours que c'est un changement raisonnable.
Faheem Mitha
7
Assurez-vous simplement de l'utiliser df.sort_values("m")dans les nouveaux pandas (au lieu de df.sort("m")), sinon vous obtiendrez un AttributeError: 'DataFrame' object has no attribute 'sort';)
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
L' keyargument prend comme entrée une série et renvoie une série. Cette série est triée en interne et les index triés sont utilisés pour réorganiser le DataFrame d'entrée. S'il y a plusieurs colonnes sur lesquelles trier, la fonction clé sera appliquée à chacune à son tour. Voir Tri avec des clés .
pandas <= 1.0.X
Une méthode simple consiste à utiliser la sortie Series.mapet Series.argsortà indexer en dfutilisant DataFrame.iloc(puisque argsort produit des positions entières triées); puisque vous avez un dictionnaire; cela devient facile.
df.iloc[df['m'].map(custom_dict).argsort()]
a b m
012 March
234 April
156 Dec
Si vous devez trier par ordre décroissant , inversez le mappage.
df.iloc[(-df['m'].map(custom_dict)).argsort()]
a b m
156 Dec
234 April
012 March
Notez que cela ne fonctionne que sur les éléments numériques. Sinon, vous devrez contourner ce problème en utilisant sort_valueset en accédant à l'index:
df.loc[df['m'].map(custom_dict).sort_values(ascending=False).index]
a b m
156 Dec
234 April
012 March
Plus d'options sont disponibles avec astype(c'est désormais obsolète), ou pd.Categorical, mais vous devez spécifier ordered=Truepour que cela fonctionne correctement .
Vous l'avez déjà souligné, mais je voudrais le répéter au cas où quelqu'un d'autre le passerait à côté et le manquerait: Pandas Sets catégoriques ordered=Nonepar défaut. S'il n'est pas défini, la commande sera erronée ou sera interrompue sur V23. La fonction Max en particulier donne un TypeError (le catégoriel n'est pas ordonné pour l'opération max).
Dave Liu
16
Un peu tard dans le jeu, mais voici un moyen de créer une fonction qui trie les objets pandas Series, DataFrame et multiindex DataFrame à l'aide de fonctions arbitraires.
J'utilise la df.iloc[index]méthode, qui référence une ligne dans un Series / DataFrame par position (par rapport à df.loc, quelles références par valeur). En utilisant cela, nous devons juste avoir une fonction qui retourne une série d'arguments positionnels:
defsort_pd(key=None,reverse=False,cmp=None):defsorter(series):
series_list = list(series)
return [series_list.index(i)
for i in sorted(series_list,key=key,reverse=reverse,cmp=cmp)]
return sorter
Vous pouvez l'utiliser pour créer des fonctions de tri personnalisées. Cela fonctionne sur le dataframe utilisé dans la réponse d'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
Cela fonctionne également sur les objets DataFrames et Series multiindex:
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
Pour moi, cela semble propre, mais cela utilise beaucoup les opérations python plutôt que de s'appuyer sur des opérations pandas optimisées. Je n'ai fait aucun test de résistance, mais j'imagine que cela pourrait ralentir sur de très grands DataFrames. Je ne sais pas comment les performances se comparent à l'ajout, au tri, puis à la suppression d'une colonne. Tous les conseils pour accélérer le code seraient appréciés!
Cela fonctionnerait-il pour trier plusieurs colonnes / index?
ConanG
oui, mais la réponse choisie est une bien meilleure façon de le faire. Si vous avez plusieurs index, organisez-les simplement selon l'ordre de tri que vous préférez, puis utilisez df.sort_index()pour trier tous les niveaux d'index.
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))
renvoie un DataFrame avec des colonnes mars, avril, décembre
pd.Categorical
n'interprète pas les catégories comme ordonnées par défaut. Voyez cette réponse .Réponses:
Pandas 0.15 a introduit la série catégorielle , qui permet une manière beaucoup plus claire de le faire:
Commencez par définir la colonne du mois comme catégorique et spécifiez l'ordre à utiliser.
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
Maintenant, lorsque vous triez la colonne du mois, elle sera triée par rapport à cette liste:
In [23]: df.sort_values("m") Out[23]: a b m 0 1 2 March 2 3 4 April 1 5 6 Dec
Remarque: si une valeur ne figure pas dans la liste, elle sera convertie en NaN.
Une réponse plus ancienne pour ceux qui sont intéressés ...
Vous pourriez créer une série intermédiaire, et
set_index
à ce sujet: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
Comme commenté, dans les pandas plus récents, Series a une
replace
méthode pour le faire plus élégamment:s = df['m'].replace({'March':0, 'April':1, 'Dec':3})
La légère différence est que cela n'augmentera pas s'il y a une valeur en dehors du dictionnaire (elle restera simplement la même).
la source
s = df['m'].replace({'March':0, 'April':1, 'Dec':3})
fonctionne également pour la ligne 2 - juste pour le bien de quiconque apprend des pandas comme moi.apply({'March':0, 'April':1, 'Dec':3}.get)
:) En 0.15, nous aurons des séries / colonnes catégoriques, donc la meilleure façon sera de l'utiliser et ensuite le tri fonctionnera.df.sort_values("m")
dans les nouveaux pandas (au lieu dedf.sort("m")
), sinon vous obtiendrez unAttributeError: 'DataFrame' object has no attribute 'sort'
;)pandas> = 1,1
Vous pourrez bientôt utiliser
sort_values
aveckey
argument: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
L'
key
argument prend comme entrée une série et renvoie une série. Cette série est triée en interne et les index triés sont utilisés pour réorganiser le DataFrame d'entrée. S'il y a plusieurs colonnes sur lesquelles trier, la fonction clé sera appliquée à chacune à son tour. Voir Tri avec des clés .pandas <= 1.0.X
Une méthode simple consiste à utiliser la sortie
Series.map
etSeries.argsort
à indexer endf
utilisantDataFrame.iloc
(puisque argsort produit des positions entières triées); puisque vous avez un dictionnaire; cela devient facile.df.iloc[df['m'].map(custom_dict).argsort()] a b m 0 1 2 March 2 3 4 April 1 5 6 Dec
Si vous devez trier par ordre décroissant , inversez le mappage.
df.iloc[(-df['m'].map(custom_dict)).argsort()] a b m 1 5 6 Dec 2 3 4 April 0 1 2 March
Notez que cela ne fonctionne que sur les éléments numériques. Sinon, vous devrez contourner ce problème en utilisant
sort_values
et en accédant à l'index: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
Plus d'options sont disponibles avec
astype
(c'est désormais obsolète), oupd.Categorical
, mais vous devez spécifierordered=True
pour que cela fonctionne correctement .# 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)
Maintenant, un simple
sort_values
appel fera l'affaire:df.sort_values('m') a b m 0 1 2 March 2 3 4 April 1 5 6 Dec
L'ordre des catégories sera également respecté lors du
groupby
tri de la sortie.la source
ordered=None
par défaut. S'il n'est pas défini, la commande sera erronée ou sera interrompue sur V23. La fonction Max en particulier donne un TypeError (le catégoriel n'est pas ordonné pour l'opération max).Un peu tard dans le jeu, mais voici un moyen de créer une fonction qui trie les objets pandas Series, DataFrame et multiindex DataFrame à l'aide de fonctions arbitraires.
J'utilise la
df.iloc[index]
méthode, qui référence une ligne dans un Series / DataFrame par position (par rapport àdf.loc
, quelles références par valeur). En utilisant cela, nous devons juste avoir une fonction qui retourne une série d'arguments positionnels: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
Vous pouvez l'utiliser pour créer des fonctions de tri personnalisées. Cela fonctionne sur le dataframe utilisé dans la réponse d'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
Cela fonctionne également sur les objets DataFrames et Series multiindex:
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
Pour moi, cela semble propre, mais cela utilise beaucoup les opérations python plutôt que de s'appuyer sur des opérations pandas optimisées. Je n'ai fait aucun test de résistance, mais j'imagine que cela pourrait ralentir sur de très grands DataFrames. Je ne sais pas comment les performances se comparent à l'ajout, au tri, puis à la suppression d'une colonne. Tous les conseils pour accélérer le code seraient appréciés!
la source
df.sort_index()
pour trier tous les niveaux d'index.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))
renvoie un DataFrame avec des colonnes mars, avril, décembre
la source