首页 > 解决方案 > Matplotlib:在 hexbin 图中使用最常见值的 bin 组周围添加边框

问题描述

我正在使用以下 Python 脚本制作一个 hexbin 图:

pitch = Pitch(
    line_color="#747474", pitch_color="#222222", orientation="vertical", half=True, plot_arrow=False
)

fig, ax = pitch.create_pitch()

## color-map
cmap = [
    "#222222", "#3A2527", "#52282B", "#6A2B30", 
    "#762C32", "#822D34", "#8E2F37", "#9A3039", 
    "#B2323D", "#BE3440", "#CA3542", "#E13746"
]
cmap = colors.ListedColormap(cmap)

hexbin = ax.hexbin(
    68 - shots_data['Y'], shots_data['X'], zorder=3, cmap=cmap,
    extent=(0, 68, 52, 104), gridsize=22, bins=13, ec="#222222", lw=3
)

上面的代码产生以下输出:

在此处输入图像描述

现在我想在六边形周围添加最常见值的边框,看起来像这样。请注意,在下图中,白色边框是手绘的,以显示结果的外观。我不知道该怎么做。我应该在代码中添加什么来产生这样的结果。 在此处输入图像描述

编辑:

我得到了一些结果,但它们并不完美,这是更新后的脚本:

## Pitch obejct
pitch = Pitch(
    line_color="#747474", pitch_color="#222222", orientation="vertical", half=True, plot_arrow=False
)

## create-pitch
fig, ax = pitch.create_pitch()

## colormap
cmap = [
    "#3A2527", "#52282B", "#6A2B30", "#822D34",
    "#822D34","#882E36", "#8E2F37", "#9A3039", "#B2323D", "#E13746"
]
cmap = colors.ListedColormap(cmap)

## extent
extent = (
    shots_data['Y'].min(), shots_data['Y'].max(),
    shots_data['X'].min(), shots_data['X'].max(),
)

## main hexbin
hexbin = ax.hexbin(
    68 - shots_data['Y'], shots_data['X'], zorder=3, cmap=cmap,
    extent=extent, gridsize=22, ec="#222222", lw=1, bins="log", mincnt=1
)

## hexbin with mincnt=6
cmap = [
     "#822D34", "#882E36", "#8E2F37", "#9A3039", "#B2323D", "#E13746"
]
cmap = colors.ListedColormap(cmap)
ax.hexbin(
    68 - shots_data['Y'], shots_data['X'], zorder=3, cmap=cmap,
    extent=extent, gridsize=22, ec="#bce7ef", lw=1, bins="log", mincnt=6
)


## add rectangle 
rect = plt.Rectangle(
    xy=(-0.1, 104), width=68.1, height=1, zorder=3, fc="#222222"
)
ax.add_patch(rect)

这将产生以下结果: 在此处输入图像描述

标签: pythonmatplotlib

解决方案


我制定了两个版本来绘制六边形的等高线。

(标题)

import numpy as np, matplotlib.pyplot as plt, matplotlib.colors

# color-map
cmap = [ "#222222", "#3A2527", "#52282B", "#6A2B30", "#762C32", "#822D34", 
        "#8E2F37", "#9A3039", "#B2323D", "#BE3440", "#CA3542", "#E13746"]
cmap = matplotlib.colors.ListedColormap(cmap)

#prepare data
np.random.seed(10)
shotsX = np.random.randn(1000)*20+10
shotsY = np.random.randn(1000)*15+50

#original plot
cfg = dict(x=shotsX, y=shotsY, cmap=cmap, gridsize=22, extent=[0,100,0,100])
h   = plt.hexbin( ec="#222222",lw=2,zorder=-3,**cfg)
plt.axis('off');

1) 白光

这种方法类似于您的编辑。plt.hexbin使用不同的线条样式以及参数多次调用mincnt

#draw thick white contours + overlay previous style
cfg = {**cfg,'vmin':h.get_clim()[0], 'vmax':h.get_clim()[1]}
plt.hexbin( ec="white"  ,lw=5,zorder=-2,mincnt=10,**cfg)
plt.hexbin( ec="#222222",lw=2,zorder=-1,mincnt=10,**cfg)
plt.xlim(-3,103) #required as second call of plt.hexbin()
plt.ylim(-3,103) #strangely affects the limits ...

在此处输入图像描述

严格来说,“发光”为具有许多计数的六边形增添了亮点。

2) 轮廓

在原始六边形上仅绘制白色轮廓线更加复杂。您可以通过以下方式解决此问题

  • 找到六边形的顶点(即中心)
  • 为每个顶点计算线段
  • 提取外线
def hexLines(a=None,i=None,off=[0,0]):
    '''regular hexagon segment lines as `(xy1,xy2)` in clockwise 
    order with points in line sorted top to bottom
    for irregular hexagon pass both `a` (vertical) and `i` (horizontal)'''
    if a is None: a = 2 / np.sqrt(3) * i;    
    if i is None: i = np.sqrt(3) / 2 * a;     
    h  = a / 2 
    xy = np.array([ [ [ 0, a], [ i, h] ], 
                    [ [ i, h], [ i,-h] ], 
                    [ [ i,-h], [ 0,-a] ], 
                    [ [-i,-h], [ 0,-a] ], #flipped
                    [ [-i, h], [-i,-h] ], #flipped
                    [ [ 0, a], [-i, h] ]  #flipped
                  ])  
    return xy+off;


#get hexagon centers that should be highlighted
verts = h.get_offsets()
cnts  = h.get_array()
highl = verts[cnts > .5*cnts.max()]

#create hexagon lines
a = ((verts[0,1]-verts[1,1])/3).round(6)
i = ((verts[1:,0]-verts[:-1,0])/2).round(6)
i = i[i>0][0]
lines = np.concatenate([hexLines(a,i,off) for off in highl])

#select contour lines and draw
uls,c = np.unique(lines.round(4),axis=0,return_counts=True)
for l in uls[c==1]: plt.plot(*l.transpose(),'w-',lw=2,scalex=False,scaley=False)

在此处输入图像描述

注意:查找匹配的等高线取决于浮点精度np.unique(lines.round(5),...),这里是四舍五入到小数点后 4 位。根据输入数据,这可能需要进行调整。


推荐阅读