Как установить соотношение сторон в matplotlib?

Я пытаюсь сделать квадратный график (используя imshow), т.е. соотношение сторон 1:1, но я не могу. Ни одна из этих работ:

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

Кажется, что вызовы просто игнорируются (проблема, с которой я часто сталкиваюсь с matplotlib).

Ответ 1

Третий раз очарование. Я предполагаю, что это ошибка, и Ответ Женя предполагает, что она исправлена ​​в последней версии. У меня есть версия 0.99.1.1, и я создал следующее решение:

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

Это "force.png": enter image description here

Ниже приведены мои неудачные, но, надеюсь, информативные попытки.

Второй ответ:

Мой "исходный ответ" ниже, это излишний, поскольку он делает что-то похожее на axes.set_aspect(). Я думаю, вы хотите использовать axes.set_aspect('auto'). Я не понимаю, почему это так, но для меня получается квадратный график изображения, например, этот 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')

Производит графику изображения с равным соотношением сторон: enter image description here и один с форматом "auto": enter image description here

Код, приведенный ниже в "исходном ответе", обеспечивает отправную точку для явно контролируемого соотношения сторон, но, похоже, он игнорируется после вызова imshow.

Исходный ответ:

Вот пример процедуры, которая будет корректировать параметры подзадачи, чтобы получить желаемое соотношение сторон:

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

В результате получается такая цифра: enter image description here

Я могу предположить, что если у вас есть несколько подзаговоров в фигуре, вы бы хотели включить количество подзаговоров y и x в качестве параметров ключевого слова (по умолчанию по умолчанию 1) в предоставленную процедуру. Затем, используя эти числа и ключевые слова hspace и wspace, вы можете сделать все подзаголовки имеющими правильное соотношение сторон.

Ответ 2

Какова версия matplotlib, которую вы используете? Мне недавно пришлось обновить до 1.1.0, а вместе с ним add_subplot(111,aspect='equal') работает для меня.

Ответ 3

вы должны попробовать с figaspect. Меня устраивает. Из документов:

Создайте фигуру с указанным соотношением сторон. Если arg - число, используйте это соотношение сторон. > Если arg - массив, то фигас будет определить ширину и высоту фигуры, которая будет соответствовать массиву сохраняя соотношение сторон. Ширина рисунка, высота в дюймах вернулся. Не забудьте создать оси с равными и высотой, например

Пример использования:

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

Изменить: я не уверен, что вы ищете. Вышеупомянутый код изменяет холст (размер графика). Если вы хотите изменить размер окна matplotlib, на рисунке, используйте:

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

это создает окно размером 5x1 (wxh).

Ответ 4

Этот ответ основан на ответе Янна. Он установит соотношение сторон для линейных или логарифмических графиков. Я использовал дополнительную информацию из fooobar.com/questions/76268/..., чтобы проверить, являются ли оси масштабными.

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)

Очевидно, вы можете использовать любую версию log, которую хотите, я использовал scipy, но numpy или math должен быть в порядке.

Ответ 5

После многих лет успеха с ответами выше, я обнаружил, что это больше не работает - но я нашел рабочее решение для участков на

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

Конечно, с полным уважением к автору выше (который может публиковать здесь), соответствующие строки:

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

Ссылка также содержит кристально четкое объяснение различных систем координат, используемых matplotlib.

Спасибо за все полученные великолепные ответы - особенно @Yann, которая останется победителем.