How to 'bottom' align within matplotlib multi column legend? - matplotlib

Given a multi column legend created like this:
plot.fig.legend(handles, labels, ncol=2 ....)
Is there a way to define the inner alignment?
I'm getting a legend that has a 'top' alignment:
But I wish for a 'bottom' alignment like this:
Their is a similar question that never received an answer here:
aligning matplotlib subplots legends

The question that #Sheldore linked to, was different but its solution similar.
I could adopt it for my issue.
handles.insert(2, plt.plot([], [], color=(0, 0, 0, 0), label=" ")[0])
labels.insert(2, '')
plot.fig.legend(handles, labels, ncol=2, ....)
The idea is that the legend is table like. If you want an element to move to a different position you have to fill in an empty entry into the other positions.

Related

make specific data points in scatter plot seaborn more visible [duplicate]

I have a Seaborn scatterplot and am trying to control the plotting order with 'hue_order', but it is not working as I would have expected (I can't get the blue dot to show on top of the gray).
x = [1, 2, 3, 1, 2, 3]
cat = ['N','Y','N','N','N']
test = pd.DataFrame(list(zip(x,cat)),
columns =['x','cat']
)
display(test)
colors = {'N': 'gray', 'Y': 'blue'}
sns.scatterplot(data=test, x='x', y='x',
hue='cat', hue_order=['Y', 'N', ],
palette=colors,
)
Flipping the 'hue_order' to hue_order=['N', 'Y', ] doesn't change the plot. How can I get the 'Y' category to plot on top of the 'N' category? My actual data has duplicate x,y ordinates that are differentiated by the category column.
The reason this is happening is that, unlike most plotting functions, scatterplot doesn't (internally) iterate over the hue levels when it's constructing the plot. It draws a single scatterplot and then sets the color of the elements with a vector. It does this so that you don't end up with all of the points from the final hue level on top of all the points from the penultimate hue level on top of all the ... etc. But it means that the scatterplot z-ordering is insensitive to the hue ordering and reflects only the order in the input data.
So you could use your desired hue order to sort the input data:
hue_order = ["N", "Y"]
colors = {'N': 'gray', 'Y': 'blue'}
sns.scatterplot(
data=test.sort_values('cat', key=np.vectorize(hue_order.index)),
x='x', y='x',
hue='cat', hue_order=hue_order,
palette=colors, s=100, # Embiggen the points to see what's happening
)
There may be a more efficient way to do that "sort by list of unique values" built into pandas; I am not sure.
TLDR: Before plotting, sort the data so that the dominant color appears last in the data. Here, it could just be:
test = test.sort_values('cat') # ascending = True
Then you get:
It seems that hue_order doesn't affect the order (or z-order) in which things are plotted. Rather, it affects how colors are assigned. E.g., if you don't specify a specific mapping of categories to colors (i.e. you just use a list of colors or a color palette), this parameter can determine whether 'N' or 'Y' gets the first (and which gets the second) color of the palette. There's an example showing this behavior here in the hue_order section. When you have the dict already linking categories to colors (colors = {'N': 'gray', 'Y': 'blue'}), it seems to just affect the order of labels in the legend, as you probably have seen.
So the key is to make sure the color you want on top is plotted last (and thus "on top"). I would have also assumed the hue_order parameter would do as you expected, but apparently not!

Extra entries ignored in axis legend

I’m trying to reproduce some plots from this video with up-to-date data and superimposing points on the lines for measures taken by governments. Using pandas for the data, and to call the plot commands.
wI have no trouble plotting the lines and appropriate legends. I then add superimposed points, for which I defined these properties:
point_opts = lambda marker, color: {'label': '', 'color': 'w', 'marker': marker, 'markeredgecolor': color, 'markeredgewidth': 3, 'linestyle': None}
I would like to only add those to the legend once, instead of once per country, hence the empty label.
I then try to modify the legend as follows:
handles, labels = ax.get_legend_handles_labels()
for props in ({**point_opts(marker, 'black'), 'label': measure} for measure, marker in points.items()):
handles.append(matplotlib.lines.Line2D([], [], **props))
labels.append(props['label'])
ax.legend(handles=handles, labels=labels)
However this does not change the axis legends (and no error messages are shown). The values seem right however. For example, if I add a second plot, on the Figure:
fig.legend(handles=handles, labels=labels, loc='center left')
I then get the result below.
Why is this happening? How can I actually modify my plot axis? Using python 3.7.3 and matplotlib 3.1.3 on OpenSuse x64, if that’s of any relevance.
Ugh alright, I’ve found it… I was, somewhere later, moving the legend around with:
ax.legend(loc='center left', bbox_to_anchor=(1, 0.5))
Apparently that resets the legend content to whatever the plot commands put there, erasing andy manual additions.

How to Superimpose on Matplotlib

I want to draw a triangle with two points inside using matplotlib. Here is the code I'm using:
plt.figure()
triangleEdges = np.array([[0,0],[1,0],[0.5,0.5*np.sqrt(3)]])
colors = ['red', 'green', 'blue']
t1 = plt.Polygon(triangleEdges, facecolor="none",
edgecolor='black', linewidth=2)
t1.set_facecolor('xkcd:salmon')
plt.gca().add_patch(t1)
drawSoftmaxPoint('blue',100,np.array([0.2,0.1,0.7]) )
drawSoftmaxPoint('red',100,np.array([0.5,0.1,0.7]))
plt.show()
Picture
According to the code, there should be two points inside the triangle, but it looks like the background is covering them. How can I make them visible?
Thank you!
you could use alpha and z-order in your polygon to make it happen (from the doc of matplotlib). just try to set the alpha value between 0 and 1 to check if you can see your points. and then maybe use z-order on your different elements to make sure the fill of the polygon is deepest (most behind). example of zorder:
https://matplotlib.org/gallery/misc/zorder_demo.html

Matplotlib - transform bbox

I printed some text into a plot. Now I want to make a copy of this text and move it to different coordinates. I guess I'll have to do this with tranform, but did not find a solution yet.
here is the code:
props = dict( facecolor='#DDDDDD', alpha=1,edgecolor='#FFFFFF',boxstyle="Square,pad=0.5")
text2=plt.text(4, 4, "text",va='top', ha='left',bbox=props)
plt.draw()
bb2=text2.get_bbox_patch().get_window_extent().transformed(ax.transData.inverted()).get_points()
To move the text to different coordinates you only need:
text2.set_position((new_x,new_y))
you could also use:
text2.set_x(new_x)
text2.set_y(new_y)

How to create a scatter plot legend with only one symbol for each label?

How can I create a scatter plot legend without two symbols showing up each time? I can understand why you'd want this when you're joining symbols by lines, but for a pure scatter plot, all I want in the legend is one example of the symbol. This plot from a previous stackoverflow post shows the kind of thing I mean:
In the legend command you can use the scatterpoints option:
ax.legend(loc=0, scatterpoints = 1)
For a normal plot, it is the option numpoints.
Here you can find more information about the keyword arguments for the legend: http://matplotlib.sourceforge.net/api/pyplot_api.html#matplotlib.pyplot.legend