Добавление цветовой шкалы на ось matplotlib с make_axes_locatable и фиксированным соотношением сторон imshow - PullRequest
1 голос
/ 24 апреля 2020

Я пытаюсь построить некоторые данные массива и добавить цветную полосу справа от оси, соответствующую высоте и с установленной шириной.

Начиная с генерации некоторых данных.

import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1 import make_axes_locatable

data = np.random.rand(700, 400)

У меня есть следующая функция.

def plot_data(data, aspect, pad):
    fig, ax = plt.subplots()
    img = ax.imshow(data, aspect=aspect)
    last_axes = plt.gca()
    divider = make_axes_locatable(ax)
    cax = divider.append_axes('right', size='5%', pad=pad)
    cbar = fig.colorbar(im, cax=cax)
    plt.sca(last_axes)

Запуск plot_data(data, None, 0.05) дает то, что я ожидал - изображение с цветовой шкалой, занимающей 5% ширины, сопоставленной с той же высотой и дополнены правильно.

Однако при выполнении plot_data(data, 2.5, 0) получается фигура с изображением, имеющим правильное соотношение сторон, но с цветной полосой, которая дополняется слишком сильно. Я могу исправить это делает отрицательный отступ, находя хорошее значение методом проб и ошибок. Однако мне нужно, чтобы это было generic c и работало без мониторинга пользователя.

Я нашел эту ветку , но, похоже, ответ не решает этот конкретный случай.

Любые предложения приветствуются!

Ответы [ 2 ]

2 голосов
/ 24 апреля 2020

Я играл с этим, и похоже, что цветовая полоса всегда основывается на исходном местоположении края графика данных. Это означает, что для положительного аспекта rat ios высота графика остается фиксированной, а ширина графика уменьшается. Затем изображение центрируется, поэтому необходимо использовать отступ, чтобы соответственно отрегулировать цветовую полосу (внутрь) по ширине - высоте / аспекту

width=last_axes.get_position().width
height=last_axes.get_position().height
cax = divider.append_axes('right', size='5%', pad=-((width/0.7)-(height/(0.7*aspect)) + pad))

. Странно, что я столкнулся с тем, что это не точно центрирует данные, но скорее центр данных и меток осей, поэтому мы должны соответственно уменьшить масштаб корректировки, следовательно, 1 / 0,7 в формуле. Я понимаю, что это не идеально, так как отметки не уменьшаются аспектом, поэтому линейное смещение было бы более подходящим, но я сделал это сейчас!

обратите внимание, это НЕ РАБОТАЕТ для аспектных крыс ios МЕНЬШЕ 1, потому что в этот момент ширина фиксируется, а высота изменяется при применении аспекта. Я буду продолжать возиться с этим и посмотреть, смогу ли я обобщить для пейзажа

edit:

Хорошо, у меня это есть. Функция оси добавления заставляет вертикальную цветную полосу быть по какой-то причине исходной высотой графика. отлично подходит для портретных графиков, но не подходит для ландшафта, где данные сжимаются по вертикали, но график не такой, поэтому мне пришлось поместить переключатель в следующий код:

def plot_data(data, aspect, pad):
    fig, ax = plt.subplots()
    img = ax.imshow(data, aspect=aspect)
    last_axes = plt.gca()
    divider = make_axes_locatable(ax)
    if(aspect<1): 
        hscale=aspect
        cbar = fig.colorbar(img,shrink=hscale,pad=(-0.43+pad))
    else:
        hscale=1
        width=last_axes.get_position().width
        height=last_axes.get_position().height
        padfix = -((width/0.7)-(height/(0.7*aspect))) 
        cax = divider.append_axes('right',size='5%', pad=padfix+ pad)
        cbar = fig.colorbar(img,cax=cax)

Опять происходит какая-то странность фиксированное смещение (на этот раз ~ 0,43), оно было найдено методом проб и ошибок, и, возможно, его необходимо скорректировать, если вычерчиваете действительно длинные тонкие графики.

0 голосов
/ 24 апреля 2020

Решение проблемы

«Нелинейная регрессия» с несколькими приблизительными значениями в интервале (1,2,5) дала мне:

y=a*x^2+b*x+c
a=0.25
b=-1.29
c=1.09

Попробуйте использовать:

cax = divider.append_axes('right', size='5%', pad=0.25*aspect**2-1.29*aspect+1.09)

Пока вы не можете использовать None в качестве аспекта, когда aspect=1, и вам не нужно передавать pad. Кроме того, эта формула работает для aspect>=1, вам может потребоваться получить другую формулу для значений меньше 1, потому что поведение действительно отличается. Для значений выше 2,5 вам нужно вычислить новые коэффициенты (объяснение ниже).

def plot_data(data, aspect, pad=None):
    if aspect == None:
        aspect = 1
    fig, ax = plt.subplots()
    img = ax.imshow(data, aspect=aspect)
    last_axes = plt.gca()
    divider = make_axes_locatable(ax)
    cax = divider.append_axes('right', size='5%', pad=0.25*aspect**2-1.29*aspect+1.09)
    cbar = fig.colorbar(img, cax=cax)
    plt.sca(last_axes)

О «нелинейной регрессии»

Чтобы получить коэффициенты, первое, что мне нужно было сделать это получить несколько пар (аспект, отступы). Как получить их? Метод проб и ошибок: первая пара - оригинал (1,05), остальные были визуально хороши: (1,5, -0,305), (2, -0,48), (2,5, -0,58). Если вы видите, что в данных нет никакой линейности, но давайте в любом случае построим график

import matplotlib.pyplot as plt
data = [(1,0.05),(1.5,-0.305),(2,-0.48),(2.5,-0.58)]
plt.plot(*zip(*data))
plt.plot(*zip(*data),'or')
plt.xlabel("aspect")
plt.ylabel("padding")
plt.show()

aspect/padding

Теперь давайте получим коэффициенты, делающие кривую примерка:

fit = np.polynomial.polynomial.Polynomial.fit(*zip(*pairs),2)
c,b,a = fit.convert().coef

(Да, я только подгонял кривую с полиномом второй степени, извините!) Различия между значениями, которые я изначально написал, и значением, полученным python, возникают из-за того, что я использовал другое программное обеспечение и я сделал округление до 2 десятичных знаков в большинстве случаев.

Почему я использовал полином второй степени (fit (x, y, 2))? Я пытался сделать модель максимально простой.

pairs = [(1,0.05),(1.5,-0.305),(2,-0.48),(2.5,-0.58)]
x = np.linspace(1,2.5,100)
yp = [a*x**2+b*x+c for x in x]
plt.plot(*zip(*pairs),'or', label='pairs')
plt.plot(x,yp,'g', label='Curve fitting')
plt.xlabel("aspect")
plt.ylabel("padding")
plt.legend(loc="upper right")
plt.show()

Plot of the fitted curve

Работает ли это для значений за пределами [1,2.5]? На самом деле, нет. Для этого вы должны включить больше точек в аппроксимацию кривой, возможно, измените полином порядка 2, скажем, на логарифмический c один.

enter image description here

...