J'ai un dataframe python pandas, dans lequel une colonne contient le nom du mois.
Comment puis-je faire un tri personnalisé en utilisant 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 faire un tri personnalisé en utilisant un dictionnaire, par exemple :
custom_dict = {'March':0, 'April':1, 'Dec':3}
Introduction de Pandas 0.15 Séries catégoriques qui permet de le faire de manière beaucoup plus claire :
Tout d'abord, faites de la colonne du mois une colonne catégorielle 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 trierez 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
sur ce point :
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 indiqué, dans les pandas les plus récents, Series possède un replace
pour faire cela de manière plus élégante :
s = df['m'].replace({'March':0, 'April':1, 'Dec':3})
La légère différence réside dans le fait qu'il n'y aura pas de message s'il existe une valeur en dehors du dictionnaire (la valeur restera la même).
Vous serez bientôt en mesure d'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
El key
prend en entrée une série et renvoie une série. Cette série est triée en interne et les indices triés sont utilisés pour réorganiser le DataFrame en entrée. S'il y a plusieurs colonnes à trier, la fonction clé sera appliquée à chacune d'elles à tour de rôle. Voir Triage avec des clés .
Une méthode simple consiste à utiliser la sortie Series.map
y Series.argsort
pour indexer dans df
en 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 avez besoin de trier dans ordre décroissant inverser la cartographie.
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 pour les éléments numériques. Dans le cas contraire, vous devrez contourner ce problème en utilisant la commande sort_values
et d'accéder à 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
(ceci est maintenant déprécié), ou pd.Categorical
mais vous devez spécifier ordered=True
pour qu'il 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
fera l'affaire :
df.sort_values('m')
a b m
0 1 2 March
2 3 4 April
1 5 6 Dec
L'ordre catégorique sera également respecté lorsque groupby
trie la sortie.
utiliser le réponse choisie ! il est plus récent que cet article et n'est pas seulement la façon officielle de maintenir des données ordonnées dans pandas, il est meilleur à tous les égards, y compris les fonctionnalités/performances, etc. N'utilisez pas la méthode bidon que je décris ci-dessous.
J'écris cette mise à jour uniquement parce que les gens n'arrêtent pas d'upvoter ma réponse, mais elle est définitivement pire que celle qui a été acceptée :)
Un peu tard, mais voici un moyen de créer une fonction qui trie les objets pandas Series, DataFrame et DataFrame multi-index en utilisant des fonctions arbitraires.
Je me sers de la df.iloc[index]
qui fait référence à une ligne d'une série ou d'un cadre de données par sa position (par rapport à la méthode de l'utilisateur). df.loc
qui fait référence par valeur). En utilisant cela, il suffit d'avoir une fonction qui renvoie 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 DataFrames et les objets Series multi-index :
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 il utilise fortement les opérations python plutôt que de s'appuyer sur des opérations pandas optimisées. Je n'ai pas fait de test de stress mais j'imagine que cela peut devenir lent sur des DataFrames de très grande taille. Je ne sais pas si les performances sont comparables à celles de l'ajout, du tri et de la suppression d'une colonne. Tout conseil pour accélérer le code serait apprécié !
J'avais la même tâche mais avec un ajout pour trier sur plusieurs colonnes.
Une des solutions consiste à faire en sorte que les deux colonnes pd.catégorique et passer l'ordre attendu comme argument "catégories".
Mais j'avais des exigences où je ne pouvais pas contraindre un inconnu. \unexpected et c'est malheureusement ce que fait pd.Categorical. De plus, None n'est pas soutenu comme une catégorie et contraint automatiquement.
Ma solution a donc consisté à utiliser une clé pour trier sur plusieurs colonnes avec un ordre de tri personnalisé :
import pandas as pd
df = pd.DataFrame([
[A2, 2],
[B1, 1],
[A1, 2],
[A2, 1],
[B1, 2],
[A1, 1]],
columns=['one','two'])
def custom_sorting(col: pd.Series) -> pd.Series:
"""Series is input and ordered series is expected as output"""
to_ret = col
# apply custom sorting only to column one:
if col.name == "one":
custom_dict = {}
# for example ensure that A2 is first, pass items in sorted order here:
def custom_sort(value):
return (value[0], int(value[1:]))
ordered_items = list(col.unique())
ordered_items.sort(key=custom_sort)
# apply custom order first:
for index, item in enumerate(ordered_items):
custom_dict[item] = index
to_ret = col.map(custom_dict)
# default text sorting is about to be applied
return to_ret
# pass two columns to be sorted
df.sort_values(
by=["two", "one"],
ascending=True,
inplace=True,
key=custom_sorting,
)
print(df)
Sortie :
5 A1 1
3 A2 1
1 B1 1
2 A1 2
0 A2 2
4 B1 2
Sachez que cette solution peut être lente.
Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.