matplotlib numbers in legend - matplotlib

I have a couple of lines and I want to show a legend. The problem is, I can't use different styles (--, :, -.) because there are too few of them, and I can't use markers (+, *, etc.) because I need them to show some points on the lines.
So the best idea I've come up with is to use numbers. But I can't figure how I can create legends with numbers. I can even draw numbers near lines myself (to place them in the best position), but how can I then draw a legend with the numbers?
I.e. instead of:
-- H
-.- Li
I'd like something like:
1 H
2 Li

Perhaps a little Latex thrown into the mix?
#In which we make a legend; not with lines, but numbers!
import pylab as pl
pl.rc('text', usetex=True)
pl.figure(1)
pl.clf()
ax = pl.subplot(111)
pl.plot(range(0,10), 'k', label = r'\makebox[25]{1\hfill}Bla')
pl.plot(range(1,11), 'k', label = r'\makebox[25]{12\hfill}Bla12')
lgd = pl.legend(handlelength = -0.4)
for k in lgd.get_lines():
k.set_linewidth(0)
pl.draw()
pl.show()
The numbers/labels are aligned by using \makebox with specific width and \hfill to take up the space not used by your labels. Numbers are not automatic, but if you use a loop to draw your lines then you could add a counter to keep track of the numbers.
Don't know if this is part of your requirement, but the lines are removed by setting their linewidth to 0 and making the space reserved in the legend negative. Couldn't find a neater way of doing this as I believe a legend is always meant to show a line (e.g. you can't set numpoints to 0).
You could of course also just add some text in the right spot in your plot and not use a legend at all.

Related

TramineR legend position and axis

I'm working with TraMineR and I don't know how to arrange my plot. So basically what i would like to have the legend under the plot and to remove the space between the x and y axis. Any help is welcomed.
The plot:
Sample code:
seqdplot(Activities.seq, with.legend=FALSE)
legend("bottom", legend=attr(Activities.seq, "labels"),
fill=attr(Activities.seq, "cpal"),
inset=-.1, bty="o", xpd=NA, cex=.75,ncol=3)
The family of seqplot functions offers a series of arguments to control the legend as well as the axes. Look at the help page of seqplot (and of plot.stslist.statd for specific seqdplot parameters).
For instance, you can suppress the x-axis with axes=FALSE, and the y-axis with yaxis=FALSE.
To print the legend you can let seqdplot display it automatically using the default with.legend=TRUE option and control it with for examples cex.legend for the font size, ltext for the text. You can also use the ncol argument to set the number of columns in the legend.
The seqplot functions use by default layout to organize the graphic area between the plots and the legend. If you need more fine tuning (e.g. to change the default par(mar=c(5.1,4.1,4.1,2.1)) margins around the plot and the legend), you should create separately the plot(s) and the legend and then organize them yourself using e.g. layout or par(mfrow=...). In that case, the separate graphics should be created by setting with.legend=FALSE, which prevents the display of the legend and disables the automatic use of layout.
The color legend is easiest obtained with seqlegend.
I illustrate with the mvad data that ships with TraMineR. First the default plot with the legend. Note the use of border=NA to suppress the too many vertical black lines.
library(TraMineR)
data(mvad)
mvad.scode <- c("EM", "FE", "HE", "JL", "SC", "TR")
mvad.seq <- seqdef(mvad, 17:86,
states = mvad.scode,
xtstep = 6)
# Default plot with the legend,
seqdplot(mvad.seq, border=NA)
Now, we suppress the x and y axes and modify the display of the legend
seqdplot(mvad.seq, border=NA,
axes=FALSE, yaxis=FALSE, ylab="",
cex.legend=1.3, ncol=6, legend.prop=.11)
Here is how you can control the space between the plot and the x and y axes
seqdplot(mvad.seq, border=NA, yaxis=FALSE, xaxis=FALSE, with.legend=FALSE)
axis(2, line=-1)
axis(1, line=0)
Creating the legend separately and reducing the left, top, and right margins around the legend
op <- par(mar=c(5.1,0.1,0.1,0.1))
seqlegend(mvad.seq, ncol=2, cex=2)
par(op)

Matplotlib/Seaborn: Boxplot collapses on x axis

I am creating a series of boxplots in order to compare different cancer types with each other (based on 5 categories). For plotting I use seaborn/matplotlib. It works fine for most of the cancer types (see image right) however in some the x axis collapses slightly (see image left) or strongly (see image middle)
https://i.imgur.com/dxLR4B4.png
Looking into the code how seaborn plots a box/violin plot https://github.com/mwaskom/seaborn/blob/36964d7ffba3683de2117d25f224f8ebef015298/seaborn/categorical.py (line 961)
violin_data = remove_na(group_data[hue_mask])
I realized that this happens when there are too many nans
Is there any possibility to prevent this collapsing by code only
I do not want to modify my dataframe (replace the nans by zero)
Below you find my code:
boxp_df=pd.read_csv(pf_in,sep="\t",skip_blank_lines=False)
fig, ax = plt.subplots(figsize=(10, 10))
sns.violinplot(data=boxp_df, ax=ax)
plt.xticks(rotation=-45)
plt.ylabel("label")
plt.tight_layout()
plt.savefig(pf_out)
The output is a per cancer type differently sized plot
(depending on if there is any category completely nan)
I am expecting each plot to be in the same width.
Update
trying to use the order parameter as suggested leads to the following output:
https://i.imgur.com/uSm13Qw.png
Maybe this toy example helps ?
|Cat1|Cat2|Cat3|Cat4|Cat5
|3.93| |0.52| |6.01
|3.34| |0.89| |2.89
|3.39| |1.96| |4.63
|1.59| |3.66| |3.75
|2.73| |0.39| |2.87
|0.08| |1.25| |-0.27
Update
Apparently, the problem is not the data but the length of the title
https://github.com/matplotlib/matplotlib/issues/4413
Therefore I would close the question
#Diziet should I delete it or does my issue might help other ones?
Sorry for not including the line below in the code example:
ax.set_title("VERY LONG TITLE", fontsize=20)
It's hard to be sure without data to test it with, but I think you can pass the names of your categories/cancers to the order= parameter. This forces seaborn to use/display those, even if they are empty.
for instance:
tips = sns.load_dataset("tips")
ax = sns.violinplot(x="day", y="total_bill", data=tips, order=['Thur','Fri','Sat','Freedom Day','Sun','Durin\'s Day'])

Efficiently Plotting Many Lines in VisPy

From all example code/demos I have seen in the VisPy library, I only see one way that people plot many lines, for example:
for i in range(N):
pos = pos.copy()
pos[:, 1] = np.random.normal(scale=5, loc=(i+1)*30, size=N)
line = scene.visuals.Line(pos=pos, color=color, parent=canvas.scene)
lines.append(line)
canvas.show()
My issue is that I have many lines to plot (each several hundred thousand points). Matplotlib proved too slow because of the total number of points plotted was in the millions, hence I switched to VisPy. But VisPy is even slower when you plot thousands of lines each with thousands of points (the speed-up comes when you have millions of points).
The root cause is in the way lines are drawn. When you create a plot widget and then plot a line, each line is rendered to the canvas. In matplotlib you can explicitly state to not show the canvas until all lines are drawn in memory, but there doesn't appear to be the same functionality in VisPy, making it useless.
Is there any way around this? I need to plot multiple lines so that I can change properties interactively, so flattening all the data points into one plot call won't work.
(I am using a PyQt4 to embed the plot in a GUI. I have also considered pyqtgraph.)
You should pass an array to the "connect" parameter of the Line() function.
xy = np.random.rand(5,2) # 2D positions
# Create an array of point connections :
toconnect = np.array([[0,1], [0,2], [1,4], [2,3], [2,4]])
# Point 0 in your xy will be connected with 1 and 2, point
# 1 with 4 and point 2 with 3 and 4.
line = scene.visuals.Line(pos=xy, connect=toconnect)
You only add one object to your canvas but the control pear line is more limited.

Change the labels of a colorbar from increasing to decreasing values

I'd like to change the labels for the colorbar from increasing to decreasing values. When I try to do this via vmin and vmax I get the error message:
minvalue must be less than or equal to maxvalue
So, for example I'd like the colorbar to start at 20 on the left and go up to 15 on the right.
This is my code for the colorbar so far, but in this example the values go from 15 to 20 and I'd like to reverse that order:
cmap1 = mpl.cm.YlOrBr_r
norm1 = mpl.colors.Normalize(15,20)
cb1 = mpl.colorbar.ColorbarBase(colorbar1, cmap=cmap1, norm=norm1, orientation='horizontal')
cb1.set_label('magnitude')
The colorbars displayed below are probably not exactly like yours, as they are just example colorbars to function as a proof of concept.
In the following I assume you have a colorbar similar to this, with increasing values to the right:
Method 1: Inverting the x-axis
Inverts the whole x-axis of the colorbar
If you want to invert the x-axis, meaning that the values on the x-axis are descending to the right, making the colorbar "mirrored", you can make use of the ColorbarBase's ax attribute:
cb1 = mpl.colorbar.ColorbarBase(colorbar1,
cmap=cmap1,
norm=norm1,
orientation='horizontal')
cb1.ax.invert_xaxis()
This gives.the output below.
It is also possible to change the number of ticklabels by setting the colorbars locator. Here the MultipleLocator is used, although you can use many other locators as well.
from matplotlib.ticker import MultipleLocator
cb1.locator = MultipleLocator(1) # Show ticks only for each multiple of 1
cb1.update_ticks()
cb1.ax.invert_xaxis()
Method 2: Using custom ticklabels
Reverses the order of the ticklabels, keeping the orientation of the colorbar
If you want the orientation of the colorbar itself as it is, and only reverse the order in which the ticklabels appear, you can use the set_ticks and set_ticklabels methods. This is more of a "brute force" approach than the previous solution.
cb1.set_ticks(np.arange(15, 21))
cb1.set_ticklabels(np.arange(20, 14, -1))
This gives the colorbar seen below. Note that the colors are kept intact, only the tick locations and ticklabels have changed.
An alternative solution for producing the colorbar in Method 2:
cmap1 = cmap1.reversed()
cb1.ax.invert_yaxis()
works for me: variable_you_want.ax.invert_yaxis()

Draw points and lines in legend text?

Is there any possibility to get lines and points into a legend text in matplotlib?
I had something in mind like the following
x=np.linspace(0,10,100)
ys=np.sin(x)
yc=np.cos(x)
pl.plot(x,ys,'--',label='sin')
pl.plot(x,yc,':',label='derivative of --')
pl.legend()
pl.show()
except that instead of the -- there should be the same symbol with the corresponding color just as in front of the legend label sin.
After reading around in the matplotlib source code I finally found a solution that works perfect for me and that does not need any position tweaking etc. as it used matplotlibs internal V- and HPackers.
import numpy as np
import pylab as pl
x=np.linspace(0,10,100)
ys=np.sin(x)
yc=np.cos(x)
pl.plot(x,ys,'--',label='sin')
pl.plot(x,yc,':',label='derivative of')
leg=pl.legend()
# let the hacking begin
legrows = leg.get_children()[0].get_children()[1]\
.get_children()[0].get_children()
symbol = legrows[0].get_children()[0]
childs = legrows[1].get_children().append(symbol)
pl.show()
The result looks as follows:
This is a little bit of a hack, but it accomplishes your goal and places all of the pieces (i.e. the legend and the text) on the plot in the appropriate order.
import pylab
pl.plot(x,ys,'--',label='sin', color='green')
pl.plot(x,yc,':',label='derivative of --',color='blue')
line1= pylab.Line2D(range(10), range(10), marker='None', linestyle='--',linewidth=2.0, color="green")
line2= pylab.Line2D(range(10), range(10), marker='None', linestyle=':',linewidth=2.0, color="blue")
leg = pl.legend((line1,line2),('sin','derivative of '),numpoints=1, loc=1)
pylab.text(9.4, 0.73, '- -', color='green')
leg.set_zorder(2)
pl.show()
Instead of relying on the default colors for the lines, I set them such that they can be referenced specifically in the legend. There are extra spaces left in the text for 'the derivative' for the second line in the legend, so we can place text (aka corresponding symbol/color of the sin line) on top of it. Then you specify the symbol/color of the text and place it such that it lines up with the text in the legend. Finally you specify the order, via zorder, to set the text on top.