web-dev-qa-db-fra.com

Comment tracer plusieurs Seaborn Jointplot dans Subplot

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?

32
Afloz

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()

enter image description here

Notez qu'il peut y avoir plusieurs inconvénients à copier des axes et ce qui précède n'est pas (encore) testé à fond.

30

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 (JG1JG2), puis créez une nouvelle figure, puis migrez les objets axes depuis JG1JG2 à 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.

enter image description here

26
CT Zhu