Come posso impostare le proporzioni in matplotlib?


108

Sto cercando di creare un grafico quadrato (usando imshow), cioè un rapporto di aspetto di 1: 1, ma non posso. Nessuno di questi funziona:

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

Sembra che le chiamate vengano semplicemente ignorate (un problema che spesso mi sembra di avere con matplotlib).


6
Hai provato ax.axis('equal'), per caso? Come tutti hanno detto, quello che hai fatto dovrebbe funzionare, ma ax.axispotrebbe essere un altro modo per provare una soluzione alternativa.
Joe Kington

Risposte:


77

Terza volta il fascino. La mia ipotesi è che si tratti di un bug e la risposta di Zhenya suggerisce che è stato risolto nell'ultima versione. Ho la versione 0.99.1.1 e ho creato la seguente soluzione:

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

Questo è "force.png": inserisci qui la descrizione dell'immagine

Di seguito sono riportati i miei tentativi infruttuosi, ma si spera informativi.

Seconda risposta:

La mia "risposta originale" di seguito è eccessiva, in quanto fa qualcosa di simile a axes.set_aspect(). Penso che tu voglia usare axes.set_aspect('auto'). Non capisco perché questo sia il caso, ma produce una trama dell'immagine quadrata per me, ad esempio questo 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')

Produce una trama dell'immagine con proporzioni "uguali": inserisci qui la descrizione dell'immagine e una con proporzioni "automatiche": inserisci qui la descrizione dell'immagine

Il codice fornito di seguito nella "risposta originale" fornisce un punto di partenza per un rapporto di aspetto controllato in modo esplicito, ma sembra essere ignorato una volta chiamato un imshow.

Risposta originale:

Ecco un esempio di una routine che regolerà i parametri della sottotrama in modo da ottenere le proporzioni desiderate:

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

Questo produce una figura come questa: inserisci qui la descrizione dell'immagine

Posso immaginare che se hai più sottotrame all'interno della figura, dovresti includere il numero di sottotrame y e x come parametri delle parole chiave (predefinito a 1 ciascuno) nella routine fornita. Quindi utilizzando quei numeri e le parole chiave hspacee wspace, puoi fare in modo che tutte le sottotrame abbiano le proporzioni corrette.


1
Per i casi in cui get_imagesè una lista vuota (come accadrebbe con ax.plot([0,1],[0,2]), puoi usare get_xlimeget_ylim
Joel

Mi sembra che questo non funzionerà se fatto con logscale. Ho aggiunto una risposta che lo verifica e lo gestisce. Sentiti libero di incorporarlo nella tua risposta e poi rimuoverò la mia.
Joel

1
Il motivo per cui l'aspetto sembra disuguale è perché aspetto uguale significa che la distanza visiva in x sarà la stessa di y. Se l'immagine è quadrata, ma la trama dx e dy sono diverse, non si tratta di un rapporto di aspetto 1: 1. Le proporzioni saranno dy / dx in quel caso.
bart cubrich

22

Qual è la matplotlibversione che stai utilizzando? Di recente ho dovuto passare a 1.1.0, e con esso, add_subplot(111,aspect='equal')funziona per me.


1
Funziona bene per me in matplotlibversione 2.0.2. jupyter notebookversione 5.0.0. Grazie.
Sathish

5

Dopo molti anni di successo con le risposte di cui sopra, ho scoperto che non funziona di nuovo, ma ho trovato una soluzione funzionante per le sottotrame su

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

Con tutto il merito ovviamente all'autore sopra (che forse può piuttosto postare qui), le righe rilevanti sono:

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

Il collegamento ha anche una spiegazione cristallina dei diversi sistemi di coordinate utilizzati da matplotlib.

Grazie per tutte le ottime risposte ricevute, in particolare @ Yann's che rimarrà il vincitore.


3

dovresti provare con figaspect. Per me funziona. Dai documenti:

Crea una figura con le proporzioni specificate. Se arg è un numero, usa quelle proporzioni. > Se arg è un array, figaspect determinerà la larghezza e l'altezza di una figura che si adatterebbe all'array preservando le proporzioni. Vengono restituite la larghezza della figura e l'altezza in pollici. Assicurati di creare un assi con uguale con e altezza, ad es

Utilizzo di esempio:

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

Modifica: non sono sicuro di quello che stai cercando. Il codice sopra cambia la tela (la dimensione della trama). Se vuoi cambiare la dimensione della finestra matplotlib, della figura, usa:

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

questo produce una finestra di 5x1 (lxh).


Grazie per questo - ha qualche effetto, nel cambiare le proporzioni della tela: Per essere più specifico, ho bisogno di cambiare le proporzioni della figura stessa, cosa che non fa (apols formattazione ..): fig = plt.figure (figsize = (plt.figaspect (2.0)))
jtlz2

3

Questa risposta è basata sulla risposta di Yann. Imposta le proporzioni per i grafici lineari o log-log. Ho utilizzato informazioni aggiuntive da https://stackoverflow.com/a/16290035/2966723 per verificare se gli assi sono in scala logaritmica.

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)

Ovviamente puoi usare qualsiasi versione logtu voglia, io ho usato scipy, ma numpyo mathdovrebbe andare bene.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.