Legends not accessible via provided axis and misplaced (scatterplot, subplots)
Nobody has claimed this yet.
- Dominant language
- Python
- Stars
- 2.6k
- Forks
- 779
- Avg merge
- 1d 4h
- Merged PRs (30d)
- 27
Description
When 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
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with the sc.pl.scatter() entry point and reproduce the two-subplot example using the versions listed, especially matplotlib 3.1.2. Trace how continuous-colormap legends are created when an existing ax is supplied; done means each legend is accessible from the intended axis and the reported subplot behavior is covered by a regression test.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- matplotlib, python
- Domain
- data-visualization
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100