Je rencontre des problèmes pour placer Seaborn Jointplot
à l'intérieur d'une colonne multicolonne subplot
.
import pandas as pd
import seaborn as sns
df = pd.DataFrame({'C1': {'a': 1,'b': 15,'c': 9,'d': 7,'e': 2,'f': 2,'g': 6,'h': 5,'k': 5,'l': 8},
'C2': {'a': 6,'b': 18,'c': 13,'d': 8,'e': 6,'f': 6,'g': 8,'h': 9,'k': 13,'l': 15}})
fig = plt.figure();
ax1 = fig.add_subplot(121);
ax2 = fig.add_subplot(122);
sns.jointplot("C1", "C2", data=df, kind='reg', ax=ax1)
sns.jointplot("C1", "C2", data=df, kind='kde', ax=ax2)
Remarquez comment seule une partie du jointplot
est placée à l'intérieur du sous-tracé et le reste à l'intérieur de deux autres cadres de tracé. Ce que je voudrais, c'est que le distributions
soit également inséré à l'intérieur du subplots
.
Quelqu'un peut-il m'aider?
Le déplacement des axes dans matplotlib n'est plus aussi simple que dans les versions précédentes. Ce qui suit fonctionne avec la version actuelle de matplotlib.
Comme cela a été souligné à plusieurs endroits ( cette question , également ce problème ) plusieurs des commandes de seaborn créent automatiquement leur propre figure. Ceci est codé en dur dans le code maritime, il n'y a donc actuellement aucun moyen de produire de telles parcelles dans les chiffres existants. Ce sont PairGrid
, FacetGrid
, JointGrid
, pairplot
, jointplot
et lmplot
.
Il y a un fourchette née de la mer disponible qui permettrait de fournir une grille de sous-intrigue aux classes respectives de telle sorte que l'intrigue soit créée dans une figure préexistante. Pour l'utiliser, vous devez copier le axisgrid.py
de la fourchette au dossier seaborn. Notez qu'il est actuellement limité à être utilisé avec matplotlib 2.1 (éventuellement 2.0 également).
Une alternative pourrait être de créer une figure marine et de copier les axes sur une autre figure. Le principe de ceci est montré dans cette réponse et pourrait être étendu aux parcelles Searborn. L'implémentation est un peu plus compliquée que je m'y attendais au départ. Ce qui suit est une classe SeabornFig2Grid
qui peut être appelé avec une instance de grille seaborn (le retour de l'une des commandes ci-dessus), une figure matplotlib et un subplot_spec
, qui est une position d'une grille gridspec
.
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
import seaborn as sns
import numpy as np
class SeabornFig2Grid():
def __init__(self, seaborngrid, fig, subplot_spec):
self.fig = fig
self.sg = seaborngrid
self.subplot = subplot_spec
if isinstance(self.sg, sns.axisgrid.FacetGrid) or \
isinstance(self.sg, sns.axisgrid.PairGrid):
self._movegrid()
Elif isinstance(self.sg, sns.axisgrid.JointGrid):
self._movejointgrid()
self._finalize()
def _movegrid(self):
""" Move PairGrid or Facetgrid """
self._resize()
n = self.sg.axes.shape[0]
m = self.sg.axes.shape[1]
self.subgrid = gridspec.GridSpecFromSubplotSpec(n,m, subplot_spec=self.subplot)
for i in range(n):
for j in range(m):
self._moveaxes(self.sg.axes[i,j], self.subgrid[i,j])
def _movejointgrid(self):
""" Move Jointgrid """
h= self.sg.ax_joint.get_position().height
h2= self.sg.ax_marg_x.get_position().height
r = int(np.round(h/h2))
self._resize()
self.subgrid = gridspec.GridSpecFromSubplotSpec(r+1,r+1, subplot_spec=self.subplot)
self._moveaxes(self.sg.ax_joint, self.subgrid[1:, :-1])
self._moveaxes(self.sg.ax_marg_x, self.subgrid[0, :-1])
self._moveaxes(self.sg.ax_marg_y, self.subgrid[1:, -1])
def _moveaxes(self, ax, gs):
#https://stackoverflow.com/a/46906599/4124317
ax.remove()
ax.figure=self.fig
self.fig.axes.append(ax)
self.fig.add_axes(ax)
ax._subplotspec = gs
ax.set_position(gs.get_position(self.fig))
ax.set_subplotspec(gs)
def _finalize(self):
plt.close(self.sg.fig)
self.fig.canvas.mpl_connect("resize_event", self._resize)
self.fig.canvas.draw()
def _resize(self, evt=None):
self.sg.fig.set_size_inches(self.fig.get_size_inches())
L'utilisation de cette classe ressemblerait à ceci:
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
import seaborn as sns; sns.set()
import SeabornFig2Grid as sfg
iris = sns.load_dataset("iris")
tips = sns.load_dataset("tips")
# An lmplot
g0 = sns.lmplot(x="total_bill", y="tip", hue="smoker", data=tips,
palette=dict(Yes="g", No="m"))
# A PairGrid
g1 = sns.PairGrid(iris, hue="species")
g1.map(plt.scatter, s=5)
# A FacetGrid
g2 = sns.FacetGrid(tips, col="time", hue="smoker")
g2.map(plt.scatter, "total_bill", "tip", edgecolor="w")
# A JointGrid
g3 = sns.jointplot("sepal_width", "petal_length", data=iris,
kind="kde", space=0, color="g")
fig = plt.figure(figsize=(13,8))
gs = gridspec.GridSpec(2, 2)
mg0 = sfg.SeabornFig2Grid(g0, fig, gs[0])
mg1 = sfg.SeabornFig2Grid(g1, fig, gs[1])
mg2 = sfg.SeabornFig2Grid(g2, fig, gs[3])
mg3 = sfg.SeabornFig2Grid(g3, fig, gs[2])
gs.tight_layout(fig)
#gs.update(top=0.7)
plt.show()
Notez qu'il peut y avoir plusieurs inconvénients à copier des axes et ce qui précède n'est pas (encore) testé à fond.
Cela ne peut pas être fait facilement sans piratage. jointplot
appelle la méthode JointGrid
, qui à son tour crée un nouvel objet figure
à chaque appel.
Par conséquent, le hack consiste à créer deux tracés communs (JG1
JG2
), puis créez une nouvelle figure, puis migrez les objets axes depuis JG1
JG2
à la nouvelle figure créée.
Enfin, nous ajustons les tailles et les positions des sous-parcelles dans la nouvelle figure que nous venons de créer.
JG1 = sns.jointplot("C1", "C2", data=df, kind='reg')
JG2 = sns.jointplot("C1", "C2", data=df, kind='kde')
#subplots migration
f = plt.figure()
for J in [JG1, JG2]:
for A in J.fig.axes:
f._axstack.add(f._make_key(A), A)
#subplots size adjustment
f.axes[0].set_position([0.05, 0.05, 0.4, 0.4])
f.axes[1].set_position([0.05, 0.45, 0.4, 0.05])
f.axes[2].set_position([0.45, 0.05, 0.05, 0.4])
f.axes[3].set_position([0.55, 0.05, 0.4, 0.4])
f.axes[4].set_position([0.55, 0.45, 0.4, 0.05])
f.axes[5].set_position([0.95, 0.05, 0.05, 0.4])
C'est un hack car nous utilisons maintenant _axstack
et _add_key
méthodes privées, qui pourraient et pourraient ne pas rester les mêmes que dans les versions futures de matplotlib
.