Jak mogę ustawić współczynnik proporcji w matplotlib?

108

Próbuję zrobić kwadratowy wykres (używając imshow), czyli proporcje 1: 1, ale nie mogę. Żadna z tych czynności:

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

Wygląda na to, że połączenia są po prostu ignorowane (problem, który często mam z matplotlib).

jtlz2
źródło
6
Czy próbowałeś ax.axis('equal')przez przypadek? Jak wszyscy powiedzieli, to, co zrobiłeś, powinno działać, ale ax.axismoże być inną drogą do wypróbowania obejścia.
Joe Kington

Odpowiedzi:

77

Trzeci razy urok. Domyślam się, że jest to błąd, a odpowiedź Zhenya sugeruje, że został naprawiony w najnowszej wersji. Mam wersję 0.99.1.1 i stworzyłem następujące rozwiązanie:

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

To jest „force.png”: wprowadź opis obrazu tutaj

Poniżej znajdują się moje nieudane, ale miejmy nadzieję, próby informacyjne.

Druga odpowiedź:

Moja „oryginalna odpowiedź” poniżej jest przesada, ponieważ robi coś podobnego do axes.set_aspect(). Myślę, że chcesz użyć axes.set_aspect('auto'). Nie rozumiem, dlaczego tak jest, ale generuje dla mnie kwadratowy wykres obrazu, na przykład ten skrypt:

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

Tworzy wykres obrazu o „równych” proporcjach: wprowadź opis obrazu tutaj i jeden z „automatycznym” współczynnikiem proporcji: wprowadź opis obrazu tutaj

Kod podany poniżej w „oryginalnej odpowiedzi” stanowi punkt wyjścia dla wyraźnie kontrolowanego współczynnika proporcji, ale wydaje się być ignorowany po wywołaniu imshow.

Oryginalna odpowiedź:

Oto przykład procedury, która dostosuje parametry subplotu, aby uzyskać pożądany współczynnik proporcji:

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

Daje to następującą liczbę: wprowadź opis obrazu tutaj

Mogę sobie wyobrazić, że jeśli masz wiele wykresów podrzędnych na rysunku, chciałbyś uwzględnić liczbę wykresów podrzędnych y i x jako parametry słów kluczowych (domyślnie 1 każdy) do podanej procedury. Następnie używając tych liczb oraz słów kluczowych hspacei wspace, możesz sprawić, że wszystkie działki podrzędne będą miały prawidłowe proporcje.

Yann
źródło
1
W przypadkach, w których get_imagesjest pusta lista (tak jak by się stało ax.plot([0,1],[0,2]), możesz użyć get_xlimiget_ylim
Joel
Wydaje mi się, że to nie zadziała, jeśli zostanie wykonane z logscale. Dodałem odpowiedź, która testuje to i obsługuje. Zapraszam do włączenia tego do swojej odpowiedzi, a ja usunę moją.
Joel
1
Powodem, dla którego aspekt wygląda na nierówny, jest to, że równy aspekt oznacza, że ​​odległość wizualna w x będzie taka sama jak w y. Jeśli obraz jest kwadratowy, ale wykresy dx i dy są różne, to nie jest to współczynnik proporcji 1: 1. W takim przypadku współczynnik proporcji będzie wynosił dy / dx.
Bart Cubrich
22

Jaka jest matplotlibużywana wersja? Niedawno musiałem dokonać aktualizacji 1.1.0i wraz z nią add_subplot(111,aspect='equal')działa dla mnie.

ev-br
źródło
1
U mnie dobrze działa w matplotlibwersji 2.0.2. jupyter notebookwersja 5.0.0. Dzięki.
Sathish
5

Po wielu latach sukcesów z odpowiedziami powyżej stwierdziłem, że to nie działa ponownie - ale znalazłem działające rozwiązanie dla wątków podrzędnych w

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

Oczywiście z pełnym uznaniem autora powyżej (który może raczej zamieścić tutaj), odpowiednie wiersze to:

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

Łącze zawiera również krystalicznie jasne wyjaśnienie różnych układów współrzędnych używanych przez matplotlib.

Dziękuję za wszystkie otrzymane świetne odpowiedzi - zwłaszcza @ Yann, które pozostanie zwycięzcą.

jtlz2
źródło
3

powinieneś spróbować z figaspect. Mi to pasuje. Z dokumentów:

Utwórz figurę o określonym współczynniku proporcji. Jeśli argument jest liczbą, użyj tego współczynnika proporcji. > Jeśli arg jest tablicą, figaspect określi szerokość i wysokość figury, która pasowałaby do tablicy zachowując proporcje. Zwracana jest szerokość i wysokość figury w calach. Pamiętaj, aby utworzyć osie o równej wysokości i równej wysokości, np

Przykładowe użycie:

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

Edycja: nie jestem pewien, czego szukasz. Powyższy kod zmienia płótno (rozmiar wydruku). Jeśli chcesz zmienić rozmiar okna matplotlib rysunku, użyj:

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

to daje okno 5x1 (szer. x wys.).

joaquin
źródło
Dzięki za to - ma to jakiś wpływ na zmianę proporcji płótna: Mówiąc dokładniej, muszę zmienić proporcje samej figury, co nie robi tego (formatowanie apoli ..): rys = plt.figure (figsize = (plt.figaspect (2.0)))
jtlz2
3

Ta odpowiedź jest oparta na odpowiedzi Yanna. Ustawi współczynnik kształtu dla wykresów liniowych lub log-log. Użyłem dodatkowych informacji z https://stackoverflow.com/a/16290035/2966723, aby sprawdzić, czy osie mają skalę logarytmiczną.

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)

Oczywiście możesz użyć dowolnej wersji log, której użyłem scipy, ale numpylub mathpowinno być w porządku.

Joel
źródło