J'ai un dataframe python pandas, dans lequel une colonne contient le nom du mois.
Comment puis-je effectuer un tri personnalisé à l'aide d'un dictionnaire, par exemple:
custom_dict = {'March':0, 'April':1, 'Dec':3}
J'ai un dataframe python pandas, dans lequel une colonne contient le nom du mois.
Comment puis-je effectuer un tri personnalisé à l'aide d'un dictionnaire, par exemple:
custom_dict = {'March':0, 'April':1, 'Dec':3}
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).
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 de df.sort("m")
), sinon vous obtiendrez un AttributeError: 'DataFrame' object has no attribute 'sort'
;)
Vous pourrez bientôt utiliser sort_values
avec key
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 .
Une méthode simple consiste à utiliser la sortie Series.map
et Series.argsort
à indexer en df
utilisant 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
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), ou pd.Categorical
, mais vous devez spécifier ordered=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.
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!
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