web-dev-qa-db-fra.com

Tri de l'ordre des barres dans les graphiques à barres pandas / matplotlib

Quelle est la façon Pythonic/pandas de trier les "niveaux" dans une colonne dans pandas pour donner un ordre spécifique des barres dans le graphique à barres).

Par exemple, étant donné:

import pandas as pd
df = pd.DataFrame({
    'group': ['a', 'a', 'a', 'a', 'a', 'a', 'a', 
              'b', 'b', 'b', 'b', 'b', 'b', 'b'],
    'day': ['Mon', 'Tues', 'Fri', 'Thurs', 'Sat', 'Sun', 'Weds',
            'Fri', 'Sun', 'Thurs', 'Sat', 'Weds', 'Mon', 'Tues'],
    'amount': [1, 2, 4, 2, 1, 1, 2, 4, 5, 3, 4, 2, 1, 3]})
dfx = df.groupby(['group'])
dfx.plot(kind='bar', x='day')

Je peux générer la paire de parcelles suivante:

Disordered bar charts

L'ordre des barres suit l'ordre des lignes.

Quelle est la meilleure façon de réorganiser les données afin que les graphiques à barres aient des barres ordonnées du lundi au dimanche?

MISE À JOUR: cette solution de détritus fonctionne - mais elle est loin d'être élégante dans la façon dont elle utilise une colonne de tri supplémentaire:

df2 = pd.DataFrame({
    'day': ['Mon', 'Tues', 'Weds', 'Thurs', 'Fri', 'Sat', 'Sun'],
    'num': [0, 1, 2, 3, 4, 5, 6]})
df = pd.merge(df, df2, on='day')
df = df.sort_values('num')
dfx = df.groupby(['group'])
dfx.plot(kind='bar', x='day')

PLUS DE GÉNÉRALISATION:

Existe-t-il une solution qui fixe également l'ordre des barres dans un graphique à barres "esquivé":

df.pivot('day', 'group', 'amount').plot(kind='bar')

enter image description here

19
psychemedia

Vous devrez fournir un mappage pour spécifier comment classer les noms de jour. (S'ils étaient stockés en tant que dates appropriées, il y aurait d'autres façons de le faire.)

Mise à jour:

Construisez la clé. Vous pouvez écrire un dictionnaire explicitement ou utiliser quelque chose d'intelligent comme cette compréhension de dict.

weekdays = ['Mon', 'Tues', 'Weds', 'Thurs', 'Fri', 'Sat', 'Sun']
mapping = {day: i for i, day in enumerate(weekdays)}
key = df['day'].map(mapping)

Et le tri est simple:

df.iloc[key.argsort()]
16
Dan Allan

Je sais que cette réponse est en retard, mais une solution simpliste aux deux cas présentés, sans utilisation d'un dictionnaire/mappages serait quelque chose comme j'ai posté ci-dessous.

La définition de "jour" comme index vous permet d'utiliser .loc pour sélectionner les données dans un ordre spécifique

1) Pour les deux parcelles séparées

df=pd.DataFrame({'group':['a','a','a','a','a','a','a','b','b','b','b','b','b','b'],
     'day':['Mon','Tues','Fri','Thurs','Sat','Sun','Weds','Fri','Sun','Thurs','Sat','Weds','Mon','Tues'],
     'amount':[1,2,4,2,1,1,2,4,5,3,4,2,1,3]})

order = ['Mon', 'Tues', 'Weds','Thurs','Fri','Sat','Sun']`
df.set_index('day').loc[order].groupby('group').plot(kind='bar')

2) Pour l'exemple de pivot avec le tracé esquivé:

order = ['Mon', 'Tues', 'Weds','Thurs','Fri','Sat','Sun']
df.pivot('day','group','amount').loc[order].plot(kind='bar')

notez que pivot fait que day figure déjà dans l'index, vous pouvez donc réutiliser .loc ici.

Modifier: il est préférable d'utiliser .loc au lieu de .ix dans ces solutions, .ix sera obsolète et peut avoir des résultats étranges lorsque les noms de colonne et les index sont des nombres.

6
djakubosky

Je fournirai un code ci-dessous pour étendre la réponse de Dan afin de répondre à la section "GÉNÉRALISATION GÉNÉRALE" de la question du PO Tout d'abord, un exemple complet pour le cas simple (une seule variable) basé sur la solution de Dan:

import pandas as pd

# Create dataframe 
df=pd.DataFrame({
    'group':['a','a','a','a','a','a','a','b','b','b','b','b','b','b'],
    'day':['Mon','Tues','Fri','Thurs','Sat','Sun','Weds','Fri','Sun','Thurs','Sat','Weds','Mon','Tues'],
    'amount':[1,2,4,2,1,1,2,4,5,3,4,2,1,3]
})


# Calculate the total amount for each day
df_grouped = df.groupby(['day']).sum().amount.reset_index()

# Use Dan's trick to order days names in the table created by groupby
weekdays = ['Mon', 'Tues', 'Weds', 'Thurs', 'Fri', 'Sat', 'Sun']
mapping = {day: i for i, day in enumerate(weekdays)}
key = df_grouped['day'].map(mapping)    
df_grouped = df_grouped.iloc[key.argsort()]

# Draw the bar chart
df_grouped.plot(kind='bar', x='day')

Et maintenant, nous utilisons la même technique de classement pour classer les lignes du tableau croisé dynamique (au lieu des lignes créées par groupby).

import pandas as pd

# Create dataframe 
df=pd.DataFrame({
    'group':['a','a','a','a','a','a','a','b','b','b','b','b','b','b'],
    'day':['Mon','Tues','Fri','Thurs','Sat','Sun','Weds','Fri','Sun','Thurs','Sat','Weds','Mon','Tues'],
    'amount':[1,2,4,2,1,1,2,4,5,3,4,2,1,3]
})

# Get the amount for each day AND EACH GROUP
df_grouped = df.groupby(['group', 'day']).sum().amount.reset_index()

# Create pivot table to get the total amount for each day and each in the proper format to plot multiple series with pandas
df_pivot = df_grouped.pivot('day','group','amount').reset_index()

# Use Dan's trick to order days names in the table created by PIVOT (not the table created by groupby, in the previous example)
weekdays = ['Mon', 'Tues', 'Weds', 'Thurs', 'Fri', 'Sat', 'Sun']
mapping = {day: i for i, day in enumerate(weekdays)}
key = df_pivot['day'].map(mapping)    
df_pivot = df_pivot.iloc[key.argsort()]

# Draw the bar chart
df_pivot.plot(kind='bar', x='day')

Le résultat est indiqué ci-dessous:

enter image description here

4
Saul Berardo