Modifying xarray plot legend in python - python

I am new to using the plotting capabilities of xarray, I usually use matplotlib.
I am trying to move the legend that xarray.plot.scatter creates. It would also be cool to be able to remove the legend title.
data.plot.scatter(x = 'HGT', y = var, hue = 'time', add_guide = True)
If i make a separate legend through ax.legend, when add_guide = False, my legend order gets messed up. My plot is labeling different hours pairs (hours2 - hours1) with different colors and the order is very important for ease of understanding.
ax.legend(labels, bbox_to_anchor=(-1.15, 2.4, -1., .102), loc='lower left', ncol = 6)
So I am trying to understand better how to modify the legend xarray.plot.scatter and I am having trouble finding information about it thus far. Any suggestions? :)

Related

ax.locator_params(nbins=k) does not work in matplotlib

I have this simple piece of code where I try to plot simple graph while limiting number of x ticks. There are hundreds of items in iters variable and if they get plotted it would just create one fat black line.
However, ax.locator_params does not work and the number of ticks aren't reduced.
I have tried setting it on plt object, but no help.
I also tried specifying x and y axes in locator_params, but no help as well.
Finally, I have tried moving ax.locator_params before and after ax.plot, but nothing seemed to help. I am completely out of ideas.
fig, ax = plt.subplots(1, 1, figsize=(20,10))
ax.locator_params(tight=True, nbins=4)
ax.plot(iters, vals)
plt.xticks(rotation=30)
plt.show()
locator_params() with nbins= is only supported for numerical axes where the tick positions are set via MaxNLocator.
To get the same effect with text ticks, the current ticks can be stored in a list (get_xticks) and then be replaced by a subset. Note that changes to ticks (and to limits) should be called after the main plot functions.
xticks = ax.get_xticks()
ax.set_xticks(xticks[::len(xticks) // 4]) # set new tick positions
ax.tick_params(axis='x', rotation=30) # set tick rotation
ax.margins(x=0) # set tight margins

How to reproduce this legend with multiple curves?

I've been working hard on a package of functions for my work, and I'm stuck on a layout problem. Sometimes I need to work with a lot of columns subplots (1 row x N columns) and the standard matplotlib legend sometimes is not helpful and makes it hard to visualize all the data.
I've been trying to create something like the picture below. I already tried to create a subplot for the curves and another one for the legends (and display the x-axis scale as a horizontal plot). Also, I tried to spine the x-axis, but when I have a lot of curves plotted inside the same subplots the legend becomes huge.
The following image is from a software. I'd like to create a similar look. Notice that these legends are "static": it remains fixed independent of the zooming. Another observation is, I don't need all the ticks or anything like that.
What I'm already have is the following (the code is a mess, becouse I'm trying many different solutions and it is not organized nor pythonic yet.
import matplotlib.pyplot as plt
fig, ax = plt.subplots(1,2, sharey = True)
ax[0].semilogx(np.zeros_like(dados.Depth)+0.02, dados.Depth)
ax[0].semilogx(dados.AHT90, dados.Depth, label = 'aht90')
ax[0].set_xlim(0.2,2000)
ax[0].grid(True, which = 'both', axis = 'both')
axres1 = ax[0].twiny()
axres1.semilogx(dados.AHT90, dados.Depth, label = 'aht90')
axres1.set_xlim(0.2 , 2000)
axres1.set_xticks(np.logspace(np.log10(0.2),np.log10(2000),2))
axres1.spines["top"].set_position(("axes", 1.02))
axres1.get_xaxis().set_major_formatter(matplotlib.ticker.ScalarFormatter())
axres1.tick_params(axis='both', which='both', labelsize=6)
axres1.set_xlabel('sss')#, labelsize = 5)
axres2 = ax[0].twiny()
axres2.semilogx(dados.AHT10, dados.Depth, label = 'aht90')
axres2.set_xlim(0.2 , 2000)
axres2.set_xticks(np.logspace(np.log10(0.2),np.log10(2000),2))
axres2.spines["top"].set_position(("axes", 1.1))
axres2.get_xaxis().set_major_formatter(matplotlib.ticker.ScalarFormatter())
axres2.tick_params(axis='both', which='both', labelsize=6)
axres2.set_xlabel('aht10')#, labelsize = 5)
fig.show()
and the result is:
But well, I'm facing some issues on make a kind of make it automatic. If I add more curves, the prameter "set position" it is not practical to keep setting the position "by hand"
set_position(("axes", 1.02))
and another problem is, more curves I add, that kind of "legend" keep growing upward, and I have to adjust the subplot size with
fig.subplots_adjust(top=0.75)
And I'm also want to make the adjustment automatic, without keeping updating that parameter whenever I add more curves

Specify the exact size of matplotlib figure

I'm having a hard time to make some of my figures in my paper aligned appropriately.
Here is the problem. I have two figures that I want to use the same x-axis. I plot two figures separately and include them as two subfigures in Latex. The problem with this: the yticklabels of the second graph take more room, which makes it look smaller than the first graph.
I specified the figsize as the same using the following code
fig, ax = plt.subplots(figsize=(6,4))
But obviously when other things like titles, labels, tick labels take more space, the "main plot" appears smaller, making two subfigures misaligned. Is there any way to specify the size of the "main plot" ignoring other labels, titles, etc.?
P.S.: I didn't use matplotlib's subplots function because I noticed a bug in pandas or matplotlib: whenever I use an inset axis, the xlabel and xticklabels will not show. So I have to get around this by plotting two figures respectively and include them as two subfigures in latex.
When I am faced with this situation, I simply hard code the axes placement on the figure, then things will line up...
import matplotlib.pyplot as plt
ax = plt.axes([0.15, 0.65, 0.8, 0.3])
ax.plot(range(100), range(100))
ax2 = plt.axes([0.15, 0.15, 0.8, 0.3])
ax2.plot(range(100), range(10000, 110000, 1000))
If you set the left value of fig.subplots_adjust to a constant for both plots, the left edges should be in the same place, e.g.:
fig.subplots_adjust(left = 0.12) # you might need to adjust 0.12 to your needs
Put that in your scripts for both your figures and that should align them both nicely.

How to express classes on the axis of a heatmap in Seaborn

I created a very simple heatmap chart with Seaborn displaying a similarity square matrix. Here is the one line of code I used:
sns.heatmap(sim_mat, linewidths=0, square=True, robust=True)
sns.plt.show()
and this is the output I get:
What I'd like to do is to represent on the x and y axis not the labels of my instances but a colored indicator (imagine something like a small palplot on each axis) where each color represents another variable associated to each instance (let's say I have this info stored a list named labels) plus another legend for this kind of information next to the one specifying the colors of the heatmap (one like that for the lmplot). It is important that the two informations have different color palettes.
Is this possible in Seaborn?
UPDATE
What I am looking for is a clustermap as correctly suggested.
sns.clustermap(sim_mat, row_colors=label_cols, col_colors=label_cols
row_cluster=False, col_cluster=False)
Here is what I am getting btw, the dots and lines are too small and I do not see a way to enlarge them in the documentation. I'd like to
Plus, how can I add a legend and put the two one next to the other in the same position?
There are two options:
First, heatmap is an Axes level figure, so you could set up a main large main heatmap axes for the correlation matrix and flank it with heatmaps that you then pass class colors to yourself. This will be a little bit of work, but gives you lots of control over how everything works.
This is more or less an option in clustermap though, so I'm going to demonstrate how to do it that way here. It's a bit of a hack, but it will work.
First, we'll load the sample data and do a bit of roundabout transformations to get colors for the class labels.
networks = sns.load_dataset("brain_networks", index_col=0, header=[0, 1, 2])
network_labels = networks.columns.get_level_values("network")
network_pal = sns.cubehelix_palette(network_labels.unique().size,
light=.9, dark=.1, reverse=True,
start=1, rot=-2)
network_lut = dict(zip(map(str, network_labels.unique()), network_pal))
network_colors = pd.Series(network_labels).map(network_lut)
Next we call clustermap to make the main plot.
g = sns.clustermap(networks.corr(),
# Turn off the clustering
row_cluster=False, col_cluster=False,
# Add colored class labels
row_colors=network_colors, col_colors=network_colors,
# Make the plot look better when many rows/cols
linewidths=0, xticklabels=False, yticklabels=False)
The side colors are drawn with a heatmap, which matplotlib thinks of as quantitative data and thus there's not a straightforward way to get a legend directly from it. Instead of that, we'll add an invisible barplot with the right colors and labels, then add a legend for that.
for label in network_labels.unique():
g.ax_col_dendrogram.bar(0, 0, color=network_lut[label],
label=label, linewidth=0)
g.ax_col_dendrogram.legend(loc="center", ncol=6)
Finally, let's move the colorbar to take up the empty space where the row dendrogram would normally be and save the figure.
g.cax.set_position([.15, .2, .03, .45])
g.savefig("clustermap.png")
Building on the above answer, I think it's worth noting the possibility of multiple colour levels for labels - as noted in the clustermap docs ({row,col}_colors). I couldn't find an example of multiple levels, so I thought I'd share an example here.
networks = sns.load_dataset("brain_networks", index_col=0, header=[0, 1, 2])
network level
network_labels = networks.columns.get_level_values("network")
network_pal = sns.cubehelix_palette(network_labels.unique().size, light=.9, dark=.1, reverse=True, start=1, rot=-2)
network_lut = dict(zip(map(str, network_labels.unique()), network_pal))
Create index using the columns for networks
network_colors = pd.Series(network_labels, index=networks.columns).map(network_lut)
node level
node_labels = networks.columns.get_level_values("node")
node_pal = sns.cubehelix_palette(node_labels.unique().size)
node_lut = dict(zip(map(str, node_labels.unique()), node_pal))
Create index using the columns for nodes
node_colors = pd.Series(node_labels, index=networks.columns).map(node_lut)
Create dataframe for row and column color levels
network_node_colors = pd.DataFrame(network_colors).join(pd.DataFrame(node_colors))
create clustermap
g = sns.clustermap(networks.corr(),
# Turn off the clustering
row_cluster=False, col_cluster=False,
# Add colored class labels using data frame created from node and network colors
row_colors = network_node_colors,
col_colors = network_node_colors,
# Make the plot look better when many rows/cols
linewidths=0,
xticklabels=False, yticklabels=False,
center=0, cmap="vlag")
create two legends - one for each level by creating invisible column and row barplots (as per above)
network legend
from matplotlib.pyplot import gcf
for label in network_labels.unique():
g.ax_col_dendrogram.bar(0, 0, color=network_lut[label], label=label, linewidth=0)
l1 = g.ax_col_dendrogram.legend(title='Network', loc="center", ncol=5, bbox_to_anchor=(0.47, 0.8), bbox_transform=gcf().transFigure)
node legend
for label in node_labels.unique():
g.ax_row_dendrogram.bar(0, 0, color=node_lut[label], label=label, linewidth=0)
l2 = g.ax_row_dendrogram.legend(title='Node', loc="center", ncol=2, bbox_to_anchor=(0.8, 0.8), bbox_transform=gcf().transFigure)
plt.show()
When both dendrograms are used one can also add a new hidden axis and draw the legend.
ax= f.add_axes((0,0,0,0))
ax.xaxis.set_visible(False)
ax.yaxis.set_visible(False)
for label in node_labels.unique():
ax.bar(0, 0, color=node_lut[label], label=label, linewidth=0)
l2 = g.ax_row_dendrogram.legend(title='Node', loc="center", ncol=2, bbox_to_anchor=(0.8, 0.8), bbox_transform=f.transFigure)

matplotlib colorbar in subplots: labels are vanishing

I am developing some code to produce an arbitrary number of 2D plots (maps and simple contour plots) on a figure. The matplotlib subplots routine works great for this. In the simplified example below, everything works as it should. However, in my real application - which uses the exact same commands for subplots, contourf and colorbar, only that these are dispersed across several routines - the labels on the colorbars are not showing up (the color patches seem to be ok though). Even after hours of reading documentation and searching the web, I don't even have a clue where I could start looking for what the problem is. If I have my colorbar instance (cbar), I should be able to find out if the ticklabel position makes sense, if the ticklabels are set to visible, if my font settings make sense, etc.... But how do I actually check these properties? Has anyone encountered similar problems already? (and even better: found a solution?) Oh yes: if I manually create a new figure and axes in the actual plotting routine (where the contourf command is issued), then it will work again. But that means losing all control over the figure layout etc. Could it be that I am not passing my axes instance correctly? Here is what I do:
fig, ax = plt.subplots(nrows, ncols)
row, col = getCurrent(...)
plotMap(x, y, data, ax=ax[row,col], ...)
Then, inside plotMap:
c = ax.contourf(x, y, data, ...)
ax.figure.colorbar(c, ax=ax, orientation="horizontal", shrink=0.8)
As said above, the example below with simplified plots and artificial data works fine:
import numpy as np
import matplotlib.pyplot as plt
x = np.arange(0.,360.,5.)*np.pi/180.
y = np.arange(0.,360.,5.)*np.pi/180.
data = np.zeros((y.size, x.size))
for i in range(x.size):
data[:,i] = np.sin(x[i]**2*y**2)
fig, ax = plt.subplots(2,1)
contour = ax[0].contourf(x, y, data)
cbar = ax[0].figure.colorbar(contour, ax=ax[0], orientation='horizontal', shrink=0.8)
contour = ax[1].contourf(x, y, data, levels=[0.01,0.05,0.1,0.05])
cbar = ax[1].figure.colorbar(contour, ax=ax[1], orientation='horizontal', shrink=0.8)
plt.show()
Thanks for any help!
Addition after some further poking around:
for t in cbar.ax.get_xticklabels():
print t.get_position(), t.get_text(), t.get_visible()
shows me the correct text and visible=True, but all positions are (0.,0.). Could this be a problem?
BTW: axis labels are also missing sometimes... and I am using matplotlib version 1.1.1 with python 2.7.3 on windows.
OK - I could track it down: matplotlib is working as it should!
The error was embedded in a utility routine that adds some finishing touches to each page (=figure) once the given number of plot panels has been produced. In this routine I wanted to hide empty plot panels (i.e. on the last page) and I did this with
ax = fig.axes
for i in range(axCurrent, len(ax)):
ax[i].set_axis_off()
However, axCurrent was already reset to zero when the program entered this routine for any page but the last, hence the axes were switched off for all axes in figure. Adding
if axCurrent > 0:
before the for i... solves the problem.
Sorry if I stole anyone's time. Thanks anyway to everyone who was considering to help!

Categories