Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Spatial data plot visualization requirements #913

Open
adkinsrs opened this issue Oct 16, 2024 · 15 comments
Open

Spatial data plot visualization requirements #913

adkinsrs opened this issue Oct 16, 2024 · 15 comments
Assignees

Comments

@adkinsrs
Copy link
Member

  1. spatial image
  2. UMAP and violin plots
  3. Focus on smaller region
  4. ProjectR integration
    1. This leads to visualizing cell types.

This is a list in flux

@adkinsrs adkinsrs self-assigned this Oct 16, 2024
@adkinsrs
Copy link
Member Author

Potentially 3-4 panel plot

  • Expression
  • UMAP
  • Violin
  • Cell type

@adkinsrs
Copy link
Member Author

Opened branch "feature-spatial-visualization" to address this

@adkinsrs
Copy link
Member Author

adkinsrs commented Oct 17, 2024

image

First pass at a 4-panel plot using a selection of genes. This was made in a Jupyter notebook, hence the dark theming. I used a combination of scanpy and matplotlib to make this. Note I removed the colorbar from the stacked violin plot because the scanpy people removed the colorbar with their own horizontal "legend" object, and I could not create a vertical colorbar using their StackedViolin figure object.

@adkinsrs
Copy link
Member Author

Notebook found at 809996e

@adkinsrs
Copy link
Member Author

adkinsrs commented Oct 28, 2024

Second iteration
image

Third iteration
image

Mike Hoa suggested a) dark background on UMAP, b) green gradient maybe similar to the spatial and c) use a bubble plot (dotplot) instead of a stacked_violin plot.

@adkinsrs
Copy link
Member Author

image

@adkinsrs
Copy link
Member Author

@hertzron wanted to see a full-tissue version of the plots, so here is one. I also added Atoh1 to this and the previous plot for funsies. With more cells to evaluate, the low-expression (dark) green is harder to see with the dark background

image

@adkinsrs
Copy link
Member Author

Version where we use the default "white" background but make the UMAP black (according the Mike Hoa's suggestion). Also good matplotlib practice for me.

image

#4th iteration

plt.style.use('default')

# Split into 2 top/bottom subfigures.  Top side will be spatial and umap plots, and bottom side will be "stacked violin" plot
fig = plt.figure(figsize=(30, 12))

subfigs = fig.subfigures(2,1)

#subfigs[0].subplots_adjust(hspace=0.5)

ax0 = subfigs[0].subplots(2, num_genes+2)
ax_col = 0

for gene in marker_genes:
    sc.pl.spatial(vis_adata, img_key="hires", color=gene, size=1, library_id="D1_hires_image", ax=ax0[0][ax_col], color_map="YlGn_r", show=False)
    sc.pl.umap(vis_adata, color=gene, ax=ax0[1][ax_col], color_map="YlGn_r", na_color="gray", show=False)

    # remove umap title (using title=None doesn't work)
    ax0[1][ax_col].set_title("")
    ax0[1][ax_col].set_facecolor("#000000")
    ax_col +=1

# clusters
sc.pl.spatial(vis_adata, img_key="hires", color="spatial_clusters", size=1, library_id="D1_hires_image", legend_loc=None, ax=ax0[0][ax_col], show=False)
sc.pl.umap(vis_adata, color="spatial_clusters", ax=ax0[1][ax_col], show=False)

# remove umap title (using title=None doesn't work)
ax0[1][ax_col].set_title("")
ax0[1][ax_col].set_facecolor("black")
ax_col +=1

# blank image
sc.pl.spatial(vis_adata, img_key="hires", color=None, size=1, library_id="D1_hires_image", ax=ax0[0][ax_col], show=False)

# remove axes for ax0[ax_row][1]
ax0[1][ax_col].axis("off")

# Stacked Violin plot
ax1 = subfigs[1].subplots(nrows=1, ncols=1)

dotplot_fig = sc.pl.dotplot(vis_adata, marker_genes, title="Marker gene expression per cluster", groupby="spatial_clusters", ax=ax1, swap_axes=True, cmap="YlGn_r", show=False, return_fig=True)
dotplot_fig.make_figure()

dotplot_axes = dotplot_fig.fig.get_axes()

# For some reason, deleting all axes and remaking the figure makes it without the spacer above the plot (which was in ax[2] I think)
for ax in dotplot_axes:
    dotplot_fig.fig.delaxes(ax)

dotplot_fig.make_figure()
#dotplot_fig.get_axes()["mainplot_ax"].patch.set_facecolor("white")

@adkinsrs
Copy link
Member Author

adkinsrs commented Oct 31, 2024

Minor adjustment between this plot and the last.

  1. removed the size=1 for spatial plots
  2. Used the visium_hd function to read in the dataset instead of visium, which should be better and treats the bins as squares instead of circles. And this seems to show better in the detail. Slightly more Leiden clusters predicted

image

@adkinsrs
Copy link
Member Author

At a recent gEAR planning meeting there were some suggestions for a slimmed down viewer. This would be separate from a curator and would be an extension of the current gene expression view functionality but just for spatial data

  • Require the user to provide pre-clustered data in their spatial upload instead of us trying to figure it out
  • Make two 2-panel figures
    • Top two are the full spatial image with gene and clusters
    • Bottom two are a zoomed in region with gene and clusters
  • Need to figure out some way to make the matplotlib image interactive so that users could select a region in the first figure that would be zoomed for the second figure.
    • Looking into Holoviews (https://holoviews.org/) as it supports Matplotlib as a backend. Matplotlib's interactivity features only support opening a GUI window in the current host matplotlib is running (unless some complicated forwarding stuff is done).

@adkinsrs
Copy link
Member Author

adkinsrs commented Nov 13, 2024

Matplotlib also does support event handling and the last example shows how to click a point to generate a new plot, which I guess we would specify to be in the "zoom" figure. Look into the ResizeEvent

It might also be worth exploring the WebAgg backend, which on show() will start a tornado server with an interactive figure.

@adkinsrs
Copy link
Member Author

Holoviews is cool but you cannot import a Matplotlib figure into the tool. You have to use Holoviews itself with a Matplotlib backend to render a figure. Since we use scanpy to create the spatial plot, this is not ideal. There are ways to convert the image data into numpy and try to render the spatial multi-panel plot without scanpy, but I deem that too complicated for the moment.

Next thing up to try is Panel (https://panel.holoviz.org/) which supports embedding Matplotlib within a panel. Almost like making a dashboard.

@adkinsrs
Copy link
Member Author

adkinsrs commented Nov 14, 2024

Also going to investigate mpld3 (https://github.com/mpld3/mpld3) which seems pretty well maintained, has a decent amount of stars, and seems exactly the tool for the job

EDIT: Tested this, and after 10 minutes the tool is still trying to convert my 3 panel figure into a d3 object. I believe the issue is trying to convert the image itself. However it does have the ability to create custom plugins. One of the tutorials (https://nbviewer.org/github/mpld3/mpld3/blob/master/notebooks/custom_plugins.ipynb) showcases how to make custom plugins, and their example injects text of Hello World into the plot, so this gives me hope that I could plot without the images and then inject the image as a background. However zooming in on the Hello World plot does not zoom in the text, so maybe this isn't the way to go.

I do believe there is some merit to using this for normal tSNE/UMAP plots though as there is the possibility of creating hoverable tooltips and zooming.

EDIT 2: From the FAQ for this tool.

Mpld3, like matplolib itself, is designed for small to medium-scale visualizations, and this is unlikely to change. The reason is that mpld3 is built upon the foundation of HTML’s SVG, which is not particularly well-suited for large datasets. Plots with more than a few thousand elements will have noticeably slow response for interactive features.

@adkinsrs
Copy link
Member Author

adkinsrs commented Nov 15, 2024

Panel seems nice. You could create a Pane with a Matplotlib spec and add some component/widget controls. It could serve our needs. However, Panel needs a server to run.... it won't run on the same Flask. Moreover since Panel has Flask under the hood (I think), we would have to set it as a different port.

I think the general flow would be:

  1. Send POST to Flask
  2. Flask route sends a POST to the Panel server
  3. Panel serves the panel, and returns HTML
  4. HTML is the returned from Flask to the browser

One way we could try to avoid balancing different port numbers is just to run the Panel server as a Cloud Run Function in a serverless capacity. Though we'd still have to provide a way to run it locally as well.

Example of flask embedding is in this command https://discourse.holoviz.org/t/panel-regression-embedding-panel-in-gunicorn-flask/1724/6

@adkinsrs
Copy link
Member Author

November 22, 2024 standup

Showed Panel-based viewer to Ronna and co. Feedback was well-received. She envisioned a situation where you can take unannotated clusters (like the single-cell workbench), and when you select a region to zoom in or select a specific cluster, it will recluster within that region. I did note the scope of the panel currently was for the gene expression viewer, but we can make it work for other tools too.

Other things:

  • Zoomed in expression panel should have a different colorbar color, in case user does not realize the original and zoomed in scales are different.
  • I need to fine-tune cell filtering to better filter out cells that are in the negative space of the image
  • I also need to figure out the bug where the zoomed in plot pans to another part of the axis where the selected data is. Hasn't occurred today (as of writing this) but when it did occur it wasn't repeatable.
  • When you select a region from one plot, move the selected region on the other plot, or disable selection on that plot.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant