如何向树图中的每个矩形添加子图?

How to add a subplot to each rectangle in a Tree Map?

我使用 Matplotlib 和 Squarify 创建了这张树图:

现在我想在树状图中的每个矩形上添加一个线图。这可能吗?

是的,这是可能的。您将必须编写代码来提取要放置新绘图的确切位置。

您需要使用f.canvas.manager.window.SetPosition

设置新图形的位置

这个回答会很有帮助

Squarify 的 plot 是一个方便的函数,可以直接绘制给定值和标签的树状图。但是,这个过程也可以逐步执行。其中一个步骤是计算矩形的位置,为此我们假设一个图形从左下角到右上角的坐标为 0,0 到 1,1。

有了这些矩形,我们可以手动定位要绘制的坐标轴。目前尚不清楚是否需要蜱虫。如果需要,它们可以是 placed inside。或者可以将轴完全移动到每个子图的中心。或者只有刻度没有标签。

下面是一些演示代码:

import numpy as np
import matplotlib.pyplot as plt
import squarify

values = [500, 433, 331, 254, 119]
values.sort(reverse=True)  # values must be sorted descending (and positive)
# the sum of the values must equal the total area to be laid out; i.e., sum(values) == width * height
values = squarify.normalize_sizes(values, 1, 1)
rects = squarify.squarify(values, 0, 0, 1, 1)

fig = plt.figure(figsize=(7, 5))

axes = [fig.add_axes([rect['x'], rect['y'], rect['dx'], rect['dy'], ]) for rect in rects]

for ax, color in zip(axes, plt.cm.Pastel1.colors):
    x = np.linspace(0, 10, 100)
    y = np.random.normal(0.01, 0.1, 100).cumsum()
    ax.plot(x, y)
    ax.tick_params(axis="y", direction="in", pad=-15)
    ax.tick_params(axis="x", direction="in", pad=-15)
    plt.setp(ax.get_yticklabels(), ha="left")
    ax.set_facecolor(color)
plt.show()

这是另一个与问题中的图像相似的例子,有一个主图和一个颜色条。默认的 mplcursors 会与所有这些轴混淆,但 annotations while hovering 也可以手动添加。

import numpy as np
import matplotlib.pyplot as plt
import squarify

values = [4000, 1500, 1500, 1200, 1000, 500]

fig, mainax = plt.subplots(figsize=(6, 4))

mainax.set_xlim(0, 1000)
mainax.set_ylim(0, 1000)
mainax.grid(False)
cmap = plt.cm.get_cmap('Greens')
norm = plt.Normalize(vmin=0, vmax=max(values))
plt.colorbar(plt.cm.ScalarMappable(cmap=cmap, norm=norm))
pos = mainax.get_position()

values.sort(reverse=True)
normalized_values = squarify.normalize_sizes(values, pos.width, pos.height)
rects = squarify.squarify(normalized_values, pos.x0, pos.y0, pos.width, pos.height)

axes = [fig.add_axes([rect['x'], rect['y'], rect['dx'], rect['dy'], ]) for rect in rects]

for ax, val in zip(axes, values):
    x = np.linspace(0, 10, 100)
    y = np.random.normal(0.01, 0.1, 100).cumsum()
    ax.plot(x, y)
    ax.set_xticks([])
    ax.set_yticks([])
    ax.set_facecolor(cmap(norm(val)))
mainax.set_facecolor('none') # prevent that the mainax blocks the other axes
mainax.set_zorder(20) # high z-order because the annotations are drawn using this ax

labels = ['a', 'b', 'c', 'd', 'e', 'f']
sum_val = sum(values)
annotations = [mainax.annotate(f"'{lbl}': {val}\n{val * 100.0 / sum_val:.1f} %",
                               xy=(0, 0), xycoords='figure pixels',
                               xytext=(0, 0), textcoords='offset points',
                               bbox=dict(boxstyle='round', fc='lemonchiffon'),
                               ha='center', va='bottom')
               for ax, val, lbl in zip(axes, values, labels)]
for annot in annotations:
    annot.set_visible(False)

def hover(event):
    for ax, annot in zip(axes, annotations):
        if ax.bbox.contains(event.x, event.y):
            annot.xy = (event.x, event.y)
            annot.set_visible(True)
        else:
            annot.set_visible(False)
    fig.canvas.draw_idle()

fig.canvas.mpl_connect("motion_notify_event", hover)

plt.show()