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

108

Я пытаюсь сделать квадратный сюжет (используя 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).

jtlz2
источник
6
Вы ax.axis('equal')случайно не пробовали ? Как все говорили, то, что вы сделали, должно сработать, но, ax.axisвозможно, это еще один способ найти обходной путь.
Джо Кингтон

Ответы:

77

Третий раз очарование. Я предполагаю, что это ошибка, и ответ Жени предполагает, что она исправлена ​​в последней версии. У меня версия 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: введите описание изображения здесь

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

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

Мой «оригинальный ответ» ниже - излишний, так как он делает что-то похожее на axes.set_aspect(). Я думаю, вы хотите использовать axes.set_aspect('auto'). Я не понимаю, почему это так, но он создает для меня график квадратного изображения, например этот скрипт:

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

Создает график изображения с «равным» соотношением сторон: введите описание изображения здесь и один с «автоматическим» соотношением сторон: введите описание изображения здесь

Код, приведенный ниже в «исходном ответе», обеспечивает отправную точку для явно контролируемого соотношения сторон, но кажется, что он игнорируется после вызова 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')

Это дает такую ​​фигуру: введите описание изображения здесь

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

Янн
источник
1
В случаях, когда get_imagesэто пустой список (как в случае с ax.plot([0,1],[0,2]), вы можете использовать get_xlimиget_ylim
Джоэл
Мне кажется, что это не сработает, если будет выполнено с помощью logscale. Я добавил ответ, который проверяет это и обрабатывает. Не стесняйтесь включить это в свой ответ, а затем я удалю свой.
Joel
1
Причина, по которой аспект выглядит неравным, заключается в том, что равный аспект означает, что визуальное расстояние по x будет таким же, как и по y. Если изображение квадратное, но графики dx и dy различаются, то соотношение сторон не 1: 1. Соотношение сторон в этом случае будет dy / dx.
Барт Кубрич
22

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

ev-br
источник
1
У меня хорошо работает в matplotlibверсии 2.0.2. jupyter notebookверсия 5.0.0. Спасибо.
Sathish
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, который останется победителем.

jtlz2
источник
3

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

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

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

  # 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 (шxв).

Хоакин
источник
Спасибо за это - это действительно влияет на изменение соотношения сторон холста: Чтобы быть более конкретным, мне нужно изменить соотношение сторон самого рисунка, чего не делают следующие действия (форматирование apols ..): fig = plt.figure (figsize = (plt.figaspect (2.0)))
jtlz2 01
3

Этот ответ основан на ответе Янна. Он установит соотношение сторон для линейных или логарифмических графиков. Я использовал дополнительную информацию из https://stackoverflow.com/a/16290035/2966723, чтобы проверить, являются ли оси логарифмическими.

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должно быть в порядке.

Джоэл
источник