Marine poster legend designation for x values
I am drawing categorical data and number of values sns.countplot()
I am trying to add a legend for x-values ββto a shape like this: descriptors are set to x-value, labels are descriptions of x-values.
ax = sns.countplot(x = df.GARAGE_DOM)
handles, labels = ax.get_legend_handles_labels()
handles = ["VP", "BC", "GC", "GP", "JC", "PO"]
labels = ["Voie Publique", "box", "Garage couvert", "garage particulier clos", "Jardin clos", "parking ouvert"]
by_label = OrderedDict(zip(handles,labels))
ax.legend(by_label.keys(), by_label.values())
However, I got a warning that
UserWarning:
The legend does not support VP instances. A proxy executor can be used instead. See: http://matplotlib.org/users/legend_guide.html#using-proxy-artist
I read the doc of the proxy executor, but in my case I didn't find any examples.
Thank you for your help.
source to share
Here's a possible solution by creating a textbox as a legend handler. The following will create TextHandler
which will be used to create a legend artist that is a simple instance matplotlib.text.Text
. Legend descriptors are represented as tuples (text, color) from which it TextHandler
creates the desired one Text
.
import seaborn as sns
import matplotlib.pyplot as plt
from matplotlib.legend_handler import HandlerBase
from matplotlib.text import Text
import numpy as np
import pandas as pd
class TextHandler(HandlerBase):
def create_artists(self, legend, tup ,xdescent, ydescent,
width, height, fontsize,trans):
tx = Text(width/2.,height/2,tup[0], fontsize=fontsize,
ha="center", va="center", color=tup[1], fontweight="bold")
return [tx]
a = np.random.choice(["VP", "BC", "GC", "GP", "JC", "PO"], size=100,
p=np.arange(1,7)/21. )
df = pd.DataFrame(a, columns=["GARAGE_DOM"])
ax = sns.countplot(x = df.GARAGE_DOM)
handltext = ["VP", "BC", "GC", "GP", "JC", "PO"]
labels = ["Voie Publique", "box", "Garage couvert", "garage particulier clos", "Jardin clos", "parking ouvert"]
t = ax.get_xticklabels()
labeldic = dict(zip(handltext, labels))
labels = [labeldic[h.get_text()] for h in t]
handles = [(h.get_text(),c.get_fc()) for h,c in zip(t,ax.patches)]
ax.legend(handles, labels, handler_map={tuple : TextHandler()})
plt.show()
The above solution is an updated version of the original version below, which seems more complicated. Below is an original solution that uses
TextArea
and to place the text inside the legend
AnchoredOffsetbox
.
import seaborn.apionly as sns
import matplotlib.pyplot as plt
import matplotlib.patches as patches
from matplotlib.offsetbox import TextArea, AnchoredOffsetbox
from matplotlib.transforms import TransformedBbox, Bbox
from matplotlib.legend_handler import HandlerBase
import numpy as np
import pandas as pd
class TextHandler(HandlerBase):
def __init__(self, text, color="k"):
self.text = text
self.color = color
super(TextHandler, self).__init__()
def create_artists(self, legend, orig_handle,xdescent, ydescent,
width, height, fontsize,trans):
bb = Bbox.from_bounds(xdescent,ydescent, width,height)
tbb = TransformedBbox(bb, trans)
textbox = TextArea(self.text, textprops={"weight":"bold","color":self.color})
ab = AnchoredOffsetbox(loc=10,child=textbox, bbox_to_anchor=tbb, frameon=False)
return [ab]
a = np.random.choice(["VP", "BC", "GC", "GP", "JC", "PO"], size=100,
p=np.arange(1,7)/21. )
df = pd.DataFrame(a, columns=["GARAGE_DOM"])
ax = sns.countplot(x = df.GARAGE_DOM)
handltext = ["VP", "BC", "GC", "GP", "JC", "PO"]
labels = ["Voie Publique", "box", "Garage couvert", "garage particulier clos", "Jardin clos", "parking ouvert"]
handles = [ patches.Rectangle((0,0),1,1) for h in handltext]
t = ax.get_xticklabels()
labeldic = dict(zip(handltext, labels))
labels = [labeldic[h.get_text()] for h in t]
handlers = [TextHandler(h.get_text(),c.get_fc()) for h,c in zip(t,ax.patches)]
handlermap = dict(zip(handles, handlers))
ax.legend(handles, labels, handler_map=handlermap,)
plt.show()
Also see this more general answer
source to share