web-dev-qa-db-fra.com

Comment définir le rapport de format dans matplotlib?

J'essaie de faire un tracé carré (en utilisant imshow), c'est-à-dire un rapport de format de 1: 1, mais je ne peux pas. Aucun de ces travaux:

import matplotlib.pyplot as plt

ax = fig.add_subplot(111,aspect='equal')
ax = fig.add_subplot(111,aspect=1.0)
ax.set_aspect('equal')
plt.axes().set_aspect('equal')

Il semble que les appels soient simplement ignorés (un problème que je semble souvent avoir avec matplotlib).

95
jtlz2

Troisième fois le charme. Mon hypothèse est qu'il s'agit d'un bogue et réponse de Zhenya suggère qu'il soit corrigé dans la dernière version. J'ai la version 0.99.1.1 et j'ai créé la solution suivante:

import matplotlib.pyplot as plt
import numpy as np

def forceAspect(ax,aspect=1):
    im = ax.get_images()
    extent =  im[0].get_extent()
    ax.set_aspect(abs((extent[1]-extent[0])/(extent[3]-extent[2]))/aspect)

data = np.random.Rand(10,20)

fig = plt.figure()
ax = fig.add_subplot(111)
ax.imshow(data)
ax.set_xlabel('xlabel')
ax.set_aspect(2)
fig.savefig('equal.png')
ax.set_aspect('auto')
fig.savefig('auto.png')
forceAspect(ax,aspect=1)
fig.savefig('force.png')

C'est 'force.png': enter image description here

Vous trouverez ci-dessous mes tentatives infructueuses, mais heureusement informatives.

Deuxième réponse:

Ma "réponse originale" ci-dessous est excessive, car elle a un effet similaire à axes.set_aspect(). Je pense que vous voulez utiliser axes.set_aspect('auto'). Je ne comprends pas pourquoi c'est le cas, mais cela produit un tracé d'image carrée pour moi, par exemple ce script:

import matplotlib.pyplot as plt
import numpy as np

data = np.random.Rand(10,20)

fig = plt.figure()
ax = fig.add_subplot(111)
ax.imshow(data)
ax.set_aspect('equal')
fig.savefig('equal.png')
ax.set_aspect('auto')
fig.savefig('auto.png')

Produit un tracé d’image avec un rapport de format 'égal': enter image description here et un avec le rapport d'aspect 'auto': enter image description here

Le code fourni ci-dessous dans la "réponse originale" fournit un point de départ pour un rapport de format explicitement contrôlé, mais il semble être ignoré une fois qu'un imshow est appelé.

Réponse originale:

Voici un exemple de routine qui ajustera les paramètres de la sous-parcelle afin d'obtenir le rapport d'aspect souhaité:

import matplotlib.pyplot as plt

def adjustFigAspect(fig,aspect=1):
    '''
    Adjust the subplot parameters so that the figure has the correct
    aspect ratio.
    '''
    xsize,ysize = fig.get_size_inches()
    minsize = min(xsize,ysize)
    xlim = .4*minsize/xsize
    ylim = .4*minsize/ysize
    if aspect < 1:
        xlim *= aspect
    else:
        ylim /= aspect
    fig.subplots_adjust(left=.5-xlim,
                        right=.5+xlim,
                        bottom=.5-ylim,
                        top=.5+ylim)

fig = plt.figure()
adjustFigAspect(fig,aspect=.5)
ax = fig.add_subplot(111)
ax.plot(range(10),range(10))

fig.savefig('axAspect.png')

Cela produit une figure comme celle-ci: enter image description here

Je peux imaginer que si vous avez plusieurs sous-parcelles dans la figure, vous voudriez inclure le nombre de sous-parcelles y et x en tant que paramètres de mot-clé (par défaut, 1 chacun) à la routine fournie. Ensuite, en utilisant ces nombres et les mots-clés hspace et wspace, vous pouvez faire en sorte que toutes les sous-parcelles aient le rapport hauteur/largeur correct.

66
Yann

Quelle est la version matplotlib que vous utilisez? J'ai récemment dû passer à 1.1.0, et avec lui, add_subplot(111,aspect='equal') fonctionne pour moi.

19
ev-br

vous devriez essayer avec figaspect. Ça marche pour moi. De la docs:

Créez une figure avec un rapport d'aspect spécifié. Si arg est un nombre, utilisez ce format. > Si arg est un tableau, figaspect déterminera la largeur et la hauteur d'une figure qui conviendrait au tableau en préservant les proportions. La largeur de la figure, la hauteur en pouces sont renvoyées. Assurez-vous de créer un axe avec la même hauteur et la même hauteur, par exemple

Exemple d'utilisation:

  # make a figure twice as tall as it is wide
  w, h = figaspect(2.)
  fig = Figure(figsize=(w,h))
  ax = fig.add_axes([0.1, 0.1, 0.8, 0.8])
  ax.imshow(A, **kwargs)

  # make a figure with the proper aspect for an array
  A = Rand(5,3)
  w, h = figaspect(A)
  fig = Figure(figsize=(w,h))
  ax = fig.add_axes([0.1, 0.1, 0.8, 0.8])
  ax.imshow(A, **kwargs)

Edit: Je ne suis pas sûr de ce que vous recherchez. Le code ci-dessus modifie le canevas (la taille du tracé). Si vous voulez changer la taille de la fenêtre matplotlib, de la figure, utilisez:

In [68]: f = figure(figsize=(5,1))

cela produit une fenêtre de 5x1 (lxh).

3
joaquin

Cette réponse est basée sur la réponse de Yann. Il définira les proportions des tracés linéaires ou log-log. J'ai utilisé des informations supplémentaires provenant de https://stackoverflow.com/a/16290035/296672 pour vérifier si les axes sont à l'échelle du journal.

def forceAspect(ax,aspect=1):
    #aspect is width/height
    scale_str = ax.get_yaxis().get_scale()
    xmin,xmax = ax.get_xlim()
    ymin,ymax = ax.get_ylim()
    if scale_str=='linear':
        asp = abs((xmax-xmin)/(ymax-ymin))/aspect
    Elif scale_str=='log':
        asp = abs((scipy.log(xmax)-scipy.log(xmin))/(scipy.log(ymax)-scipy.log(ymin)))/aspect
    ax.set_aspect(asp)

Évidemment, vous pouvez utiliser n’importe quelle version de log, j’ai utilisé scipy, mais numpy ou math devrait aller.

2
Joel

Après plusieurs années de succès avec les réponses ci-dessus, j’ai trouvé que cela ne fonctionnait plus - mais j’ai trouvé une solution de travail pour les sous-parcelles sur

https://jdhao.github.io/2017/06/03/change-aspect-ratio-in-mpl

Avec tout le mérite de l'auteur ci-dessus (qui peut peut-être plutôt poster ici), les lignes pertinentes sont:

ratio = 1.0
xleft, xright = ax.get_xlim()
ybottom, ytop = ax.get_ylim()
ax.set_aspect(abs((xright-xleft)/(ybottom-ytop))*ratio)

Le lien contient également une explication claire des différents systèmes de coordonnées utilisés par matplotlib.

Merci pour toutes les bonnes réponses reçues - en particulier @ Yann qui restera le gagnant.

0
jtlz2