Matplotlib: получение подзаголовков для заполнения фигуры

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

import numpy as np
import matplotlib.pyplot as plt

data=np.random.rand(10,4)

#creating a wide figure with 2 subplots in 1 row
fig,ax=plt.subplots(1,2, figsize=(9,3))  
ax=ax.reshape(1,len(ax))

for i in [0,1]:
    plt.sca(ax[0,i])
    plt.imshow(data,interpolation='nearest')
    plt.colorbar()

Я хотел бы, чтобы подсюжеты были растянуты по горизонтали, чтобы они заполняли пространство фигуры. Я сделаю много подобных графиков с разным количеством значений по каждой оси, и расстояние между графиками, по-видимому, зависит от отношения значений x к значениям y, поэтому я хотел бы знать, есть ли хороший общий способ установить ширина подзаголовка, чтобы заполнить пространство. Можно ли как-то указать физический размер подзаголовков? Я искал решения в течение нескольких часов, поэтому заранее спасибо за любую помощь, которую вы можете оказать.


person PeterW    schedule 02.07.2014    source источник


Ответы (3)


Во-первых, вы используете вызовы plt, когда в вашем распоряжении есть Axes объектов. Эта дорога ведет к боли. Во-вторых, imshow устанавливает соотношение сторон масштабов осей равным 1. Вот почему оси такие узкие. Зная все это, ваш пример становится:

import numpy as np
import matplotlib.pyplot as plt

data = np.random.rand(10,4)

#creating a wide figure with 2 subplots in 1 row
fig, axes = plt.subplots(1, 2, figsize=(9,3))  

for ax in axes.flatten():  # flatten in case you have a second row at some point
    img = ax.imshow(data, interpolation='nearest')
    ax.set_aspect('auto')

plt.colorbar(img)

В моей системе это выглядит так: введите здесь описание изображения

person Paul H    schedule 03.07.2014

Вы можете настроить область оси, используя метод ax.set_position. Он работает с относительными координатами, поэтому если вы хотите сделать изображение формата А4, то:

import numpy as np
import matplotlib.pyplot as plt

# figsize keyword talks some obscure units which need a conversion from standard units
plt.figure(figsize=np.array([210,297]) / 25.4)

x = np.linspace(0,2*np.pi, 100)
plt.plot(x, np.sin(x))

plt.gca().set_position([0, 0, 1, 1])

plt.show()

Теперь область оси (область графика) заполняет всю страницу.

Координаты, заданные для set_position, являются относительными координатами [слева, ниже, ширина, высота], где каждое направление масштабируется по размеру страницы.

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

  • если imshow вызывается без аргументов ключевого слова extent=[...] или aspect='auto', он делает то, что указано в локальных значениях по умолчанию, обычно пытается сохранить пиксели квадратными
  • если это происходит (или установлено aspect='equal'), оси действуют так, как если бы было вызвано plt.axis('scaled'), т. е. сохраняют координаты X и Y равной длины (в пикселях на единицу) и изменяют размер оси в соответствии с экстентами.
  • это можно переопределить, установив plt.axis('tight') (что делает пределы x и y точно подходящими для изображения)

Старый трюк заключается в использовании axis('auto') или axis('normal'), но в настоящее время они устарели (используйте scaled, equal или tight).

Да, это немного беспорядок.

person DrV    schedule 02.07.2014
comment
Большое спасибо за ответ. Кажется, это решение работает при использовании plt.plot(), но не при использовании plt.imshow(), что я и использую. Я использую это для построения двумерных гистограмм, где каждый квадрат на графике соответствует ячейке, а его цвет соответствует количеству содержащихся в нем значений. Знаете ли вы способ изменить размер объектов imshow() или лучший способ представить 2D-гистограммы, где ваш метод будет работать? - person PeterW; 03.07.2014
comment
@user3798292 user3798292, вы можете попробовать выполнить set_position, как указано здесь, но тогда вместо использования команды pyplot используйте интерфейс прямого объекта (ax.imshow(...), а не plt.imshow(...)) - person Ajean; 03.07.2014
comment
@ user3798292: Вам указали несколько решений, но я добавил несколько комментариев по этому поводу. imshow немного странный в этом смысле. - person DrV; 03.07.2014
comment
plt.gca().set_position([0, 0, 1, 1]) было именно то, что мне было нужно, чтобы получить графики спутниковых изображений, используя .imshow() с Cartopy, чтобы заполнить всю фигуру. В моем случае plt.axis('tight') показал всю проекцию земного шара, а это не то, что мне нужно. Спасибо за очень полезный ответ! - person kevinmicke; 15.02.2018
comment
Мне нравилось использовать непонятные единицы для обозначения дюймов. Тоже совершенно верно. - person Archimaredes; 22.08.2019

Друг придумал решение, которое, кажется, работает достаточно хорошо, и я подумал, что опубликую его для всех, у кого есть похожая проблема - настройка аспекта = 'auto' в imshow, кажется, помогает для любого выбора nx, figsize, nplots_hori и nplots_vert ниже.

import numpy as np
import matplotlib.pyplot as plt

nx=5
data=np.random.rand(10,nx)

figsize=[10,8]
nplots_hori=2
nplots_vert=2

fig,ax=plt.subplots(nplots_vert, nplots_hori, figsize=figsize)
if nplots_vert==1: ax=ax.reshape(1,len(ax))

plt.tight_layout()
for i in range(nplots_hori):
    for j in range(nplots_vert):
        plt.sca(ax[j,i])
        plt.imshow(data, aspect='auto')

plt.tight_layout()
plt.show()
person PeterW    schedule 03.07.2014