matplotlib 如何优化zoomed_inset_axes中的缩放参数?

k2fxgqgv  于 2022-12-23  发布在  其他
关注(0)|答案(1)|浏览(221)

我正在创建包含缩放插入的绘图。数据是多种多样的,我不可能在程序启动之前知道数据的样子。我想使缩放插入尽可能地放大,而不与绘图的任何其他元素重叠。下面是一个示例,我使用的缩放为2。理想情况下,我想自动确定该数字应该是多少:

import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1.inset_locator import zoomed_inset_axes
from mpl_toolkits.axes_grid1.inset_locator import mark_inset

fig, ax = plt.subplots()

xin = np.linspace(0, np.random.uniform(.5, 4), 1000)    
x_samples = np.random.uniform(0.9, 1.1, (1, 1000)) * np.sqrt(xin[:, np.newaxis])

ax.fill_between(xin, x_samples.min(1), x_samples.max(1))

axins = zoomed_inset_axes(ax, zoom=2, loc='upper left')
axins.fill_between(xin, x_samples.min(1), x_samples.max(1))
axins.set_xlim(.05, .1)
idx = np.logical_and(xin > 0.05, xin < 0.1)
axins.set_ylim(x_samples.min(1)[idx].min(), x_samples.max(1)[idx].max())
axins.set_xticks([])
axins.set_yticks([])

mark_inset(ax, axins, loc1=4, loc2=3, fc="none", ec="0.5")

plt.savefig('hey')
plt.clf()

如您所见,zoom=2的值太小。我可以手动将缩放参数设置为正确的值。这是一个繁琐的过程。是否有一种方法可以自动找到缩放参数,使插入尺寸最大化,同时避免与绘图的其他部分重叠?

hzbexzde

hzbexzde1#

我们可以用迭代的方式来面对这个问题:
1.从最大可能的缩放开始(使插图占据图的整个高度)。因此,插图的一部分将与图重叠。
1.检查重叠开始点之前存在多少垂直间隙。
1.根据插图的“当前”高度,将其缩小以避免重叠。
1.在重新缩放之后,插图的宽度也会减小,因此我们可以使用留下的自由垂直间隙再次将其放大。
1.返回到2.,直到达到收敛/最大迭代次数。
在实践中,收敛是快速的,并且对于给定的数据在少于10次迭代中达到。
目视检查:

左上角 * 位置处的插图的代码**。

import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1.inset_locator import zoomed_inset_axes, mark_inset

def get_inset_max_zoom(x, y, xlim_inset, ylim_inset, max_iters=10):
    """ Zoom that maximizes inset size without overlapping the artists """
    # width and height of the inset in non-scaled coordinates.
    inset_w = xlim_inset[1] - xlim_inset[0]
    inset_h = ylim_inset[1] - ylim_inset[0]

    # max y-coordinate of the whole plot.
    y_max_plot = y.max()

    # start with maximum zoom.
    y_gap = y_max_plot - y.min()
    zoom = y_gap / inset_h

    for i in range(max_iters):
        y_affected_max = y[x < zoom * inset_w].max()

        # recalculate zoom by adjusting the gap.
        y_gap = y_max_plot - y_affected_max
        zoom = y_gap / inset_h

    return zoom

if __name__ == "__main__":
    # Change the seed to show produce different values.
    rng = np.random.RandomState(seed=0)

    # main plot.
    fig, ax = plt.subplots()
    xin = np.linspace(0, rng.uniform(.5, 4), 1000)
    x_samples = rng.uniform(
        0.9, 1.1, (1, 1000)) * np.sqrt(xin[:, np.newaxis])
    ax.fill_between(xin, x_samples.min(1), x_samples.max(1))

    # get xy pairs.
    y = x_samples.ravel()
    x = np.repeat(xin, x_samples.shape[1])

    # define the limits and location of the zoom inset.
    xlim_inset = (.05, .1)
    idx = np.logical_and(xin > xlim_inset[0], xin < xlim_inset[1])
    ylim_inset = (x_samples.min(1)[idx].min(), x_samples.max(1)[idx].max())
    loc = 'upper left'

    # get max zoom.
    zoom = get_inset_max_zoom(x, y, xlim_inset, ylim_inset, max_iters=5)

    # create the inset.
    axins = zoomed_inset_axes(ax, zoom=zoom, loc=loc, borderpad=0.5)
    axins.set(
        xlim=xlim_inset,
        ylim=ylim_inset,
        xticks=[], yticks=[])

    # connect the bboxes.
    mark_inset(ax, axins, loc1=4, loc2=3, fc="none", ec="0.5")
    # plot within the inset.
    axins.fill_between(xin, x_samples.min(1), x_samples.max(1))

泛化到4个角位置{* 左上角 右上角 右下角 左下角 *}。例如,对于loc = 'lower right'

import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1.inset_locator import zoomed_inset_axes, mark_inset

def get_inset_max_zoom_given_loc(
        x, y, xlim_inset, ylim_inset, loc='upper left', max_iters=10):
    """ Zoom that maximizes inset size without overlapping the artists """
    # width and height of the inset in non-scaled coordinates.
    inset_w = xlim_inset[1] - xlim_inset[0]
    inset_h = ylim_inset[1] - ylim_inset[0]

    # handy variables.
    is_left = 'left' in loc
    is_upper = 'upper' in loc

    y_min_plot, y_max_plot = y.min(), y.max()
    y_xtm_plot = y_max_plot if is_upper else y_min_plot
    x_max_plot = x.max()

    # start with maximum zoom.
    y_gap = y_max_plot - y_min_plot
    zoom = y_gap / inset_h

    for i in range(max_iters):
        # get affected x-coordinate range.
        if is_left:
            x_affected = x < zoom * inset_w
        else:
            x_affected = x > x_max_plot - zoom * inset_w

        # get affected y-coordinate extremum.
        y_affected = y[x_affected]
        y_affected_xtm = y_affected.max() if is_upper else y_affected.min()

        # recalculate zoom by adjusting the gap.
        y_gap = abs(y_xtm_plot - y_affected_xtm)
        zoom = y_gap / inset_h

    return zoom

if __name__ == "__main__":
    # Change the seed to show produce different values.
    rng = np.random.RandomState(seed=0)

    # main plot.
    fig, ax = plt.subplots()
    xin = np.linspace(0, rng.uniform(.5, 4), 1000)
    x_samples = rng.uniform(
        0.9, 1.1, (1, 1000)) * np.sqrt(xin[:, np.newaxis])
    ax.fill_between(xin, x_samples.min(1), x_samples.max(1))

    # get xy pairs.
    y = x_samples.ravel()
    x = np.repeat(xin, x_samples.shape[1])

    # define the limits and location of the zoom inset.
    xlim_inset = (.05, .1)
    idx = np.logical_and(xin > xlim_inset[0], xin < xlim_inset[1])
    ylim_inset = (x_samples.min(1)[idx].min(), x_samples.max(1)[idx].max())
    loc = 'lower right'

    # get max zoom.
    zoom = get_inset_max_zoom_given_loc(
        x, y, xlim_inset, ylim_inset, loc=loc, max_iters=10)

    # create the inset.
    axins = zoomed_inset_axes(ax, zoom=zoom, loc=loc, borderpad=0.5)
    axins.set(
        xlim=xlim_inset,
        ylim=ylim_inset,
        xticks=[], yticks=[])

    # connect the bboxes.
    mark_inset(ax, axins, loc1=4, loc2=3, fc="none", ec="0.5")
    # plot within the inset.
    axins.fill_between(xin, x_samples.min(1), x_samples.max(1))

相关问题