Legends not accessible via provided axis and misplaced (scatterplot, subplots)
See original GitHub issueWhen using sc.pl.scatter()
and providing an existing axis object, the legend doesn’t always appear correctly and cannot be accessed.
This doesn’t seem to happen with a categorial coloring however, only with a continous colormap.
This code works as expected:
sc.pp.calculate_qc_metrics(adata_raw, qc_vars=['mt'], percent_top=None, log1p=False, inplace=True)
sc_fig, (sc_ax1, sc_ax2) = plt.subplots(1,2, figsize=(12,5))
sc.pl.scatter(adata_raw, 'total_counts','n_genes_by_counts', color='batch', size = 10, ax=sc_ax1, show=False, title="all counts")
sc_ax1.get_legend().remove()
sc.pl.scatter(adata_raw[adata_raw.obs['total_counts']<1000],'total_counts','n_genes_by_counts', color='batch', size = 10, ax=sc_ax2, show=False, title="< 1000 counts")
plt.show()
It creates some metrics and stores them in adata_raw.obs
, then plots these metrics for all counts and for counts < 1000 on the two axes created by plt.subplots()
. The legend from the first axis is then removed.
This is an example of this output:
Now the code that doesn’t work:
sc_fig, (sc_ax1, sc_ax2) = plt.subplots(1,2, figsize=(12,5))
sc.pl.scatter(adata_raw, 'total_counts','n_genes_by_counts', color='pct_counts_mt', size = 10, ax=sc_ax1, show=False, title="all counts")
#sc_ax1.get_legend().remove()
sc.pl.scatter(adata_raw[adata_raw.obs['total_counts']<1000],'total_counts','n_genes_by_counts', color='pct_counts_mt', size = 10, ax=sc_ax2, show=False, title="< 1000 counts")
plt.show()
Essentially the same thing but colored by the percentage of mitochondrial counts.
Only one legend seems to be drawn and this one is not looking as expected. Plus, I cannot remove the legend from the first plot.
This is how it looks:
Why doesn’t it behave in the same way like in the example above?
Is there a way I can share the same legend with a scale from 0 to 1 (0%-100%) for both plots in this case?
As you can see, the line removing the legend from sc_ax1
is commented out because get_legend()
returns None
in this case, which would lead to the error below:
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-154-702da93b63cb> in <module>
2 sc_fig, (sc_ax1, sc_ax2) = plt.subplots(1,2, figsize=(12,5))
3 sc.pl.scatter(adata_raw, 'total_counts','n_genes_by_counts', color='pct_counts_mt', size = 10, ax=sc_ax1, show=False, title="all counts")
----> 4 sc_ax1.get_legend().remove()
5 sc.pl.scatter(adata_raw[adata_raw.obs['total_counts']<1000],'total_counts','n_genes_by_counts', color='pct_counts_mt', size = 10, ax=sc_ax2, show=False, title="< 1000 counts")
6 plt.show()
AttributeError: 'NoneType' object has no attribute 'remove'
Shouldn’t the legends be attached to the individual axes objects? I cannot access them and I wonder where they are stored in this case.
Versions:
scanpy==1.5.2.dev5+ge5d246aa anndata==0.7.1 umap==0.4.3 numpy==1.18.4 scipy==1.4.1 pandas==0.25.3 scikit-learn==0.23.0 statsmodels==0.11.1 python-igraph==0.8.2 louvain==0.7.0 matplotlib==3.1.2
Issue Analytics
- State:
- Created 3 years ago
- Comments:6
Totally forgot about this one sorry 😦
The problem In
scatter_base
: https://github.com/theislab/scanpy/blob/040e61ff50836d4a6cdd7da7482dcb4ee50d05ae/scanpy/plotting/_utils.py#L736-L740For non categorical variables, this code gets the current figure and adds a separate axis on which the colorbar is plotted. Therefore, the axes objects on which the data is plotted do not contain a legend object. Instead,
fig
should contain the colorbar axis and we could maybe manage to manipulate it as a workaround.There is also this DeprecationWarning popping up.
Current workaround (also for all other sort of plots) The problem here is really that we don’t have two separate figures / axes aren’t handled correctly. Good news is, that there is a way around using
plt.subplots
and using givenAxes
objects. even if we want to plot 2 plots side by side in a jupyter notebook (original post here: https://stackoverflow.com/questions/21754976/ipython-notebook-arrange-plots-horizontally). However,sc.pl.scatter
isn’t exposing the figure object but only the axis. But if we specifyshow=False
, it returns the axis and we can obtain the figure object usingmatplotlib.pyplot.gcf()
. Store these figures in a list and pass them to theplot_nice()
function which will plot all your figures side by side until it runs out of space, after which it will create a linebreak and continue. Therefore, you can specify how many figures you want to plot per line, using the individualfigsize
argument.For my example it would look like this:
Result:
In order to import the function, place the following code in a file called
flow_layout.py
in the same folder as your notebooks:Hope this is useful 😃
Additional information: Changing
legend_loc
doesn’t have any effect here either.