每个句柄具有不同数量和颜色的标记的Matplotlib图例
鉴于以下情况:每个句柄具有不同数量和颜色的标记的Matplotlib图例,matplotlib,legend,markers,Matplotlib,Legend,Markers,鉴于以下情况: import pandas as pd import matplotlib.pyplot as plt d=pd.DataFrame({'category':['a','a','a','b','b','b'], 'year':[1,2,1,2,1,2], 'x':[2,4,5,1,2,3], 'y':[1,2,3,2,4,6], 'clr':['grey
import pandas as pd
import matplotlib.pyplot as plt
d=pd.DataFrame({'category':['a','a','a','b','b','b'],
'year':[1,2,1,2,1,2],
'x':[2,4,5,1,2,3],
'y':[1,2,3,2,4,6],
'clr':['grey','green','grey','blue','grey','orange']})
d
category clr x y year
0 a grey 2 1 1
1 a green 4 2 2
2 a grey 5 3 1
3 b blue 1 2 2
4 b grey 2 4 1
5 b orange 3 6 2
及
我希望图例在第一年标记上有一个灰点(目前是这样),但在第二年标记上,每种不同颜色都有一个灰点(在本例中,一个橙色、蓝色和绿色的点在同一行顺序上,此时不重要)。
像这样:
我尝试了以下方法,但没有效果:
lgnd.legendHandles[1]._legmarker.set_numpoints(len(d.clr.unique()))
lgnd.legendHandles[1]._legmarker.set_markeredgecolor(d.clr)
提前谢谢 我很高兴找到了解决你问题的方法(并在这个过程中学习了一些新技巧)。基本上,您可以创建自己的legend handler对象,将所有颜色映射到一年。制作自定义图例处理程序可以通过制作任何具有函数
legend\u-artist(self、legend、orig\u-handle、fontsize、handlebox)
的对象来完成。有关此工作原理的详细信息,请参阅的“实现自定义处理程序”部分。我对代码中的所有解释都进行了注释,因为有太多的内容需要用文字来解释,而没有代码来演示
示例代码:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import pdb
import matplotlib.patches as mpatches
class MyLegendHandler(object):
def __init__(self,color):
self.color = color
def legend_artist(self, legend, orig_handle, fontsize, handlebox):
x0, y0 = handlebox.xdescent, handlebox.ydescent #offset of the lower left corner
width, height = handlebox.width, handlebox.height #width, height bound box of legend, for now, it is the dimension of each circle legend
#NOTE: to be practicle, let's just set radius = height as if width != height, it's an ellipse
#NOTE: these will latter on be changed internally to accomdate adding text
handlebox.width += len(self.color)*height # reset width of handlebox to accomodate multiple legends
for i in range(len(self.color)): #loop through all colors
#for each color, draw a circle of that color
#NOTE: play around here to align the legends right rather than left :)
center = [0.5*(i + 1) * width - 0.5*x0, 0.5 * height - 0.5 * y0]
patch = mpatches.Ellipse(center, height, height, facecolor=self.color[i],
edgecolor=None, hatch=None, transform=handlebox.get_transform())
handlebox.add_artist(patch)
return patch
###################################
d=pd.DataFrame({'category':['a','a','a','b','b','b'],
'year':[1,2,1,2,1,2],
'x':[2,4,5,1,2,3],
'y':[1,2,3,2,4,6],
'clr':['grey','green','grey','blue','grey','orange']})
unique_year_elements = []
years_seen = []
tmp = None
my_map = {}
for i in np.arange(len(d)):
tmp, = plt.plot(d.x[i],d.y[i],marker='o',linestyle='none',markerfacecolor=d.clr[i],
markeredgecolor='none',markersize=15)
#collect the plot elements that are of unique years-- 1 year might have several plot element, we only need 1
if not (d.year[i] in years_seen):
years_seen.append(d.year[i])
unique_year_elements.append(tmp)
#build handler_map for plt.legend to map elements to its legend handler object
for i in np.arange(len(years_seen)):
color_list = d.loc[d['year'] == years_seen[i]].clr.unique().tolist()
#pdb.set_trace()
my_map[unique_year_elements[i]] = MyLegendHandler(color_list)
#creating the legend object
plt.legend( unique_year_elements, ["Year "+str(y) for y in years_seen],
handler_map=my_map)
#clean up axes
plt.tick_params(axis='x',which='both',bottom='off',top='off',color='none',labelcolor='none')
plt.tick_params(axis='y',which='both',left='off',right='off',color='none',labelcolor='none')
plt.show()
样本输出:
另一种对我有效的方法是绘制圆圈(省略号-看看为什么)和文本:
import matplotlib.patches as mpatches
#Set ellipse dimension coordinates
xmax_el=xmax/30
ymax_el=ymax/28
#Set ellipse y-location coordinates
yloc1=max(ind)+2.5
yloc2=max(ind)+1.75
#Create first circle in grey as just one grey circle is needed:
circlex=mpatches.Ellipse((pmax-.2*pmax,yloc1), xmax_el, ymax_el ,alpha=0.5,clip_on=False\
,edgecolor='grey',linewidth=2,facecolor='none')
#Start a list of patches (circles), with the grey one being the first:
patches=[circlex]
clrs=['g','r','b']
#Populate a list of circles, one for each unique color for patch names
circles=[]
for i in np.arange(len(clrs)):
circles.append('circle'+str(i))
#This list is for multiplying by the x-position dimension to space out the colored bubbles:
clrnum=np.arange(len(clrs))
#Reverse the order of colors so they plot in order on the chart (if clrs was based on real data that is being plotted)
clrs2=clrs[::-1]
#Iterate through the color, circle, and circle number lists, create patches, and plot.
for i,j,k in zip(clrs2,circles,clrnum):
j=mpatches.Ellipse((pmax-(.2+k*0.05)*pmax,yloc2),xmax_el,ymax_el,alpha=0.5,clip_on=False,edgecolor=i,linewidth=2,facecolor='none')
patches.append(j)
for i in patches:
ax.add_artist(i)
#Add text:
ax.text(pmax-.15*pmax,yloc1,'2015 Plan Offering',color='grey',ha='left',va='center')
ax.text(pmax-.15*pmax,yloc2,'2016 Plan Offering',color='grey',ha='left',va='center')
结果:
import matplotlib.patches as mpatches
#Set ellipse dimension coordinates
xmax_el=xmax/30
ymax_el=ymax/28
#Set ellipse y-location coordinates
yloc1=max(ind)+2.5
yloc2=max(ind)+1.75
#Create first circle in grey as just one grey circle is needed:
circlex=mpatches.Ellipse((pmax-.2*pmax,yloc1), xmax_el, ymax_el ,alpha=0.5,clip_on=False\
,edgecolor='grey',linewidth=2,facecolor='none')
#Start a list of patches (circles), with the grey one being the first:
patches=[circlex]
clrs=['g','r','b']
#Populate a list of circles, one for each unique color for patch names
circles=[]
for i in np.arange(len(clrs)):
circles.append('circle'+str(i))
#This list is for multiplying by the x-position dimension to space out the colored bubbles:
clrnum=np.arange(len(clrs))
#Reverse the order of colors so they plot in order on the chart (if clrs was based on real data that is being plotted)
clrs2=clrs[::-1]
#Iterate through the color, circle, and circle number lists, create patches, and plot.
for i,j,k in zip(clrs2,circles,clrnum):
j=mpatches.Ellipse((pmax-(.2+k*0.05)*pmax,yloc2),xmax_el,ymax_el,alpha=0.5,clip_on=False,edgecolor=i,linewidth=2,facecolor='none')
patches.append(j)
for i in patches:
ax.add_artist(i)
#Add text:
ax.text(pmax-.15*pmax,yloc1,'2015 Plan Offering',color='grey',ha='left',va='center')
ax.text(pmax-.15*pmax,yloc2,'2016 Plan Offering',color='grey',ha='left',va='center')