Wie kann ich das Seitenverhältnis in matplotlib einstellen?

108

Ich versuche ein quadratisches Diagramm (mit imshow) zu erstellen, dh ein Seitenverhältnis von 1: 1, aber ich kann nicht. Keine dieser Arbeiten:

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

Es scheint, als würden die Anrufe einfach ignoriert (ein Problem, das ich bei matplotlib oft zu haben scheine).

jtlz2
quelle
6
Hast du es ax.axis('equal')zufällig versucht ? Wie alle gesagt haben, sollte das, was Sie getan haben, funktionieren, ax.axiskönnte aber ein anderer Weg sein, um eine Problemumgehung zu versuchen.
Joe Kington

Antworten:

77

Das dritte Mal ist der Charme. Ich vermute, dass dies ein Fehler ist und Zhenyas Antwort legt nahe, dass er in der neuesten Version behoben ist. Ich habe Version 0.99.1.1 und habe die folgende Lösung erstellt:

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

Dies ist 'force.png': Geben Sie hier die Bildbeschreibung ein

Nachfolgend sind meine erfolglosen, aber hoffentlich informativen Versuche aufgeführt.

Zweite Antwort:

Meine 'ursprüngliche Antwort' unten ist übertrieben, da sie etwas Ähnliches tut axes.set_aspect(). Ich denke du willst es benutzen axes.set_aspect('auto'). Ich verstehe nicht, warum dies der Fall ist, aber es erzeugt für mich ein quadratisches Bilddiagramm, zum Beispiel dieses Skript:

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

Erzeugt ein Bilddiagramm mit dem gleichen Seitenverhältnis: Geben Sie hier die Bildbeschreibung ein und eines mit dem automatischen Seitenverhältnis: Geben Sie hier die Bildbeschreibung ein

Der unten in der 'ursprünglichen Antwort' angegebene Code bietet einen Ausgangspunkt für ein explizit gesteuertes Seitenverhältnis, scheint jedoch ignoriert zu werden, sobald eine Imshow aufgerufen wird.

Ursprüngliche Antwort:

Hier ist ein Beispiel für eine Routine, die die Subplot-Parameter so anpasst, dass Sie das gewünschte Seitenverhältnis erhalten:

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

Dies ergibt eine Figur wie folgt: Geben Sie hier die Bildbeschreibung ein

Ich kann mir vorstellen, dass Sie, wenn Sie mehrere Unterzeichnungen in der Abbildung haben, die Anzahl der Unterzeichnungen y und x als Schlüsselwortparameter (standardmäßig jeweils 1) in die bereitgestellte Routine aufnehmen möchten. Wenn Sie dann diese Zahlen und die Schlüsselwörter hspaceund verwenden wspace, können Sie sicherstellen, dass alle Unterzeichnungen das richtige Seitenverhältnis haben.

Yann
quelle
1
Für Fälle, in denen get_imageseine leere Liste ist (wie es passieren würde, ax.plot([0,1],[0,2])können Sie get_xlimundget_ylim
Joel
Es sieht für mich so aus, als würde dies nicht funktionieren, wenn es mit logscale gemacht wird. Ich habe eine Antwort hinzugefügt, die das testet und damit umgeht. Fühlen Sie sich frei, das in Ihre Antwort aufzunehmen, und dann werde ich meine entfernen.
Joel
1
Der Grund dafür, dass der Aspekt ungleich aussieht, ist, dass der gleiche Aspekt bedeutet, dass der visuelle Abstand in x der gleiche ist wie in y. Wenn das Bild quadratisch ist, aber die Darstellung dx und dy unterschiedlich sind, ist dies kein Seitenverhältnis von 1: 1. Das Seitenverhältnis ist in diesem Fall dy / dx.
Bart Cubrich
22

Welche matplotlibVersion verwenden Sie? Ich musste kürzlich ein Upgrade durchführen 1.1.0und add_subplot(111,aspect='equal')funktioniert damit für mich.

ev-br
quelle
1
Funktioniert gut für mich in der matplotlibVersion 2.0.2. jupyter notebookVersion 5.0.0. Vielen Dank.
Sathish
5

Nach vielen Jahren des Erfolgs mit den obigen Antworten habe ich festgestellt, dass dies nicht wieder funktioniert - aber ich habe eine funktionierende Lösung für Nebenhandlungen bei gefunden

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

Mit voller Anerkennung des oben genannten Autors (der vielleicht lieber hier posten kann) sind die relevanten Zeilen:

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

Die Verknüpfung enthält auch eine kristallklare Erklärung der verschiedenen Koordinatensysteme, die von matplotlib verwendet werden.

Vielen Dank für alle tollen Antworten - insbesondere für @ Yann's, die der Gewinner bleiben werden.

jtlz2
quelle
3

Sie sollten es mit figaspect versuchen. Für mich geht das. Aus den Dokumenten:

Erstellen Sie eine Figur mit dem angegebenen Seitenverhältnis. Wenn arg eine Zahl ist, verwenden Sie dieses Seitenverhältnis. > Wenn arg ein Array ist, bestimmt figaspect die Breite und Höhe einer Figur, die zum Seitenverhältnis unter Beibehaltung des Arrays passt. Die Figurenbreite und -höhe in Zoll werden zurückgegeben. Stellen Sie sicher, dass Sie Achsen mit gleicher Höhe und Höhe erstellen, z

Anwendungsbeispiel:

  # 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: Ich bin mir nicht sicher, wonach du suchst. Der obige Code ändert die Zeichenfläche (die Plotgröße). Wenn Sie die Größe des matplotlib-Fensters der Abbildung ändern möchten, verwenden Sie:

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

Dies ergibt ein Fenster von 5x1 (Bxh).

Joaquin
quelle
Vielen Dank dafür - es hat einen gewissen Effekt beim Ändern des Seitenverhältnisses der Leinwand: Um genauer zu sein, muss ich das Seitenverhältnis der Figur selbst ändern, was im Folgenden nicht der Fall ist (Apols-Formatierung ..): Abb = plt.figure (figsize = (plt.figaspect (2.0)))
jtlz2
3

Diese Antwort basiert auf Yanns Antwort. Hiermit wird das Seitenverhältnis für lineare oder Log-Log-Diagramme festgelegt. Ich habe zusätzliche Informationen von https://stackoverflow.com/a/16290035/2966723 verwendet , um zu testen, ob die Achsen logarithmisch skaliert sind.

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)

Natürlich können Sie jede Version von logIhnen verwenden, die ich verwendet habe scipy, aber numpyoder mathsollte in Ordnung sein.

Joel
quelle