Contrôler les dimensions du graphe avec Matplotlib – Python

Pour certains tracés de cadrans solaires, j’ai eu besoin de générer des graphiques en pdf, pour lesquels 1 unité de graduation mesure exactement 1 cm. Cela revient à trouver comment imposer les dimensions du cadre du graphique. Voici la méthode à laquelle j’ai abouti avec Python et le module Matplotlib :

Dans la commande fig = plt.figure(), le mot-clé figsize=(largeur,hauteur) permet d’imposer les dimensions de la figure, mais pour le cadre du graphique il n’existe pas à ma connaissance de commande similaire.

J’utilise la commande plt.subplots_adjust() dans lesquelles on entre les valeurs des marges latérales, supérieure et inférieure. Il faut soi-même calculer les dimensions des marges, en créant les variables correspondantes.

import matplotlib.pyplot as plt

# limites des graduations :
xmin, xmax = -2, 10 # donc largeur 12 cm
ymin, ymax = 20, 38 # donc hauteur 18 cm

inch = 2.54 # conversion des cm en pouces
fig_width = 29.7 / inch 
fig_height = 21 / inch
marge_G = 2 / inch
largeur_axe = 12 / inch # 12 cm, en cohérence avec xmin et xmax
hauteur_axe = 18 / inch # 18 cm, en cohérence avec ymin et ymax
marge_D=fig_width-marge_G-largeur_axe
marge_B=marge_G
marge_H=fig_height-marge_B-hauteur_axe
fig = plt.figure(figsize=(fig_width, fig_height))

ax1 = plt.subplot(1, 1, 1)
ax1.set_xlim(xmin,xmax)
ax1.set_ylim(ymin,ymax)

# ajustement des dimensions de l'axe :
# les valeurs "left", "right", "bottom" et "top" sont des % entre 0 et 1
plt.subplots_adjust(
    left=marge_G / fig_width,
    right=1-marge_D/fig_width,
    bottom= marge_B / fig_height,
    top= 1 - marge_H / fig_height,
)

Pour contrôler le résultat final, j’ai trouvé dans le livre de Nicolas Rougier (Scientific Visualization: Python + Matplotlib) un script qui trace des règles verticale et horizontale en taille réelle :

# Cette partie trace un quadrillage réglé en cm, pour vérifier les dimensions
import matplotlib.ticker as ticker
import numpy as np

class Ruler:
    """ Ruler add a whole figure axis whose ticks indicate figure
        dimensions and adapt itself to figure resize event.
    """

    def __init__(self, fig=None):
        self.fig = fig or plt.gcf()
        self.ax = None
        self.show()

    def show(self):

        if self.ax is None:
            ax = fig.add_axes([0, 0, 1, 1], zorder=-10, facecolor="None")
            ax.spines["right"].set_visible(False)
            ax.spines["bottom"].set_visible(False)

            ax.xaxis.set_minor_locator(ticker.MultipleLocator(0.1))
            ax.tick_params(
                axis="x", which="both", labelsize="x-small", direction="in", pad=-15
            )
            ax.xaxis.tick_top()

            ax.yaxis.set_minor_locator(ticker.MultipleLocator(0.1))
            ax.yaxis.tick_left()
            ax.tick_params(
                axis="y", which="both", labelsize="x-small", direction="in", pad=-8
            )
            ax.yaxis.tick_left()
            for label in ax.yaxis.get_ticklabels():
                label.set_horizontalalignment("left")

            self.text = ax.text(
                0.5, 0.4, "cm", ha="center", va="center", size="x-small"
            )
            ax.grid(linestyle="--", linewidth=0.5)

            self.ax = ax

        self.update()
        plt.connect("resize_event", self.update)

    def update(self, *args):

        inch = 2.54
        width_cm = self.fig.get_figwidth() * inch
        height_cm = self.fig.get_figheight() * inch

        n = int(width_cm) + 1
        self.ax.set_xlim(0, width_cm)
        self.ax.set_xticks(np.arange(n))
        self.ax.set_xticklabels([""] + ["%d" % x for x in np.arange(1, n)])

        markersize = self.ax.xaxis.get_ticklines(True)[0].get_markersize()
        for line in self.ax.xaxis.get_ticklines(True)[2::9]:
            line.set_markersize(1.5 * markersize)

        n = int(height_cm) + 1
        self.ax.set_ylim(height_cm, 0)
        self.ax.set_yticks(np.arange(n))
        self.ax.set_yticklabels([""] + ["%d" % y for y in np.arange(1, n)])

        markersize = self.ax.yaxis.get_ticklines(True)[0].get_markersize()
        for line in self.ax.yaxis.get_ticklines(True)[1::9]:
            line.set_markersize(1.5 * markersize)

ruler=Ruler()

Un autre moyen de contrôler les dimensions du cadre graphique : ouvrir le pdf obtenu avec un logiciel de dessin vectoriel (comme par exemple Inkscape, gratuit et multiplateforme), en utilisant l’outil “mesure” (raccourci clavier “M”).

Code Python du script complet :

Soyez le premier à commenter

Laisser un commentaire