web-dev-qa-db-fra.com

Barplot Seaborn avec deux axes Y

en considérant les pandas suivants DataFrame:

          labels  values_a  values_b  values_x  values_y
  0       date1      1         3        150       170
  1       date2      2         6        200       180

Il est facile de tracer cela avec Seaborn (voir l'exemple de code ci-dessous). Cependant, en raison de la grande différence entre values_a/values_b et values_x/values_y, les barres de valeurs_a et values_b ne sont pas facilement visibles (en réalité, le jeu de données donné ci-dessus est juste un échantillon et dans mon jeu de données réel, la différence est encore plus grande). Par conséquent, je voudrais utiliser deux axes y, c’est-à-dire un axe y pour values_a/values_b et un pour valeurs_x/values_y. J'ai essayé d'utiliser plt.twinx () pour obtenir un deuxième axe, mais malheureusement, le graphique ne montre que deux barres pour values_x et values_y, même s'il existe au moins deux axes y avec la bonne mise à l'échelle. :) Avez-vous une idée de la façon de résoudre ce problème et d'obtenir quatre barres pour chaque étiquette alors que les barres values_a/values_b se rapportent à l'axe des ordonnées gauche et que les barres values_x/values_y se rapportent à l'axe des ordonnées de droite?

Merci d'avance!

import matplotlib.pyplot as plt
import pandas as pd
import seaborn as sns

columns = ["labels", "values_a", "values_b", "values_x", "values_y"]
test_data = pd.DataFrame.from_records([("date1", 1, 3, 150, 170),\
                                       ("date2", 2, 6, 200, 180)],\
                                       columns=columns)

# working example but with unreadable values_a and values_b
test_data_melted = pd.melt(test_data, id_vars=columns[0],\
                           var_name="source", value_name="value_numbers")
g = sns.barplot(x=columns[0], y="value_numbers", hue="source",\
                data=test_data_melted)
plt.show()

# values_a and values_b are not displayed
values1_melted = pd.melt(test_data, id_vars=columns[0],\
                         value_vars=["values_a", "values_b"],\
                         var_name="source1", value_name="value_numbers1")
values2_melted = pd.melt(test_data, id_vars=columns[0],\
                         value_vars=["values_x", "values_y"],\
                         var_name="source2", value_name="value_numbers2")
g1 = sns.barplot(x=columns[0], y="value_numbers1", hue="source1",\
                 data=values1_melted)
ax2 = plt.twinx()
g2 = sns.barplot(x=columns[0], y="value_numbers2", hue="source2",\
                 data=values2_melted, ax=ax2)
plt.show()

 Working example  values_a/values_b missing

6
Flo1895

Cela convient probablement mieux à plusieurs sous-tracés, mais si vous êtes vraiment défini sur un seul tracé, vous pouvez redimensionner les données avant le traçage, créer un autre axe, puis modifier les valeurs de graduation.

import matplotlib.pyplot as plt
import pandas as pd
import seaborn as sns
import numpy as np

columns = ["labels", "values_a", "values_b", "values_x", "values_y"]
test_data = pd.DataFrame.from_records([("date1", 1, 3, 150, 170),\
                                       ("date2", 2, 6, 200, 180)],\
                                       columns=columns)

test_data_melted = pd.melt(test_data, id_vars=columns[0],\
                           var_name="source", value_name="value_numbers")

# Scale the data, just a simple example of how you might determine the scaling
mask = test_data_melted.source.isin(['values_a', 'values_b'])
scale = int(test_data_melted[~mask].value_numbers.mean()
            /test_data_melted[mask].value_numbers.mean())
test_data_melted.loc[mask, 'value_numbers'] = test_data_melted.loc[mask, 'value_numbers']*scale

fig, ax1 = plt.subplots()
g = sns.barplot(x=columns[0], y="value_numbers", hue="source",\
                data=test_data_melted, ax=ax1)

# Create a second y-axis with the scaled ticks
ax1.set_ylabel('X and Y')
ax2 = ax1.twinx()

# Ensure ticks occur at the same positions, then modify labels
ax2.set_ylim(ax1.get_ylim())
ax2.set_yticklabels(np.round(ax1.get_yticks()/scale,1))
ax2.set_ylabel('A and B')

plt.show()

 enter image description here

2
ALollz