Reputation: 1409
I am making a hexbin plot with the following Python script:
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
)
The above code produces the following output:
Now I want to add borders around the hexagons with the most frequent values, which will look something like this. Note that in the below image the white borders are hand drawn to show how the result will look like. I don't know how to do this. What should I add in the code to produce such result.
Edit:
I am getting some results but they are not perfect, here is the updated script:
## 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)
This is producing the following result:
Upvotes: 5
Views: 798
Reputation: 734
I worked out two versions for plotting a contour line for the hexagons.
(header)
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) white glow
This approach is similar to your edit. Call plt.hexbin
multiple time with different lines styles as well as with the parameter 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 ...
Strictly speaking the "glow" adds a highlight to hexagons with many counts.
2) Contours
Drawing only white contour lines on top of the original hexagons is more complicated. You can solve this by
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)
Note: Finding matching contour lines depends on the float accuracy np.unique(lines.round(5),...)
, here a rounded to 4 decimals. Depending on input data this might have to be adjusted.
Upvotes: 2