├── .gitattributes ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── custom.md │ └── feature_request.md └── workflows │ └── ci.yml ├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── docs ├── 10x_tutorials │ ├── visium │ │ ├── Selection_1_cells_stats.csv │ │ ├── Selection_1_coordinates.csv │ │ ├── assets │ │ │ ├── clusters.png │ │ │ ├── density_map.png │ │ │ ├── lasso.png │ │ │ ├── lasso2.png │ │ │ ├── one_spot.png │ │ │ └── transcripts_subset.png │ │ └── visium_tuto.ipynb │ └── xenium │ │ ├── Selection_1_cells_stats.csv │ │ ├── Selection_1_coordinates.csv │ │ ├── Selection_2_cells_stats.csv │ │ ├── Xeniumranger_V1_hSkin_Melanoma_Add_on_FFPE_he_imagealignment.csv │ │ ├── alignment_matrix.csv │ │ ├── assets │ │ ├── alignment_1.png │ │ ├── alignment_2.png │ │ ├── alignment_3.png │ │ ├── alignment_4.png │ │ ├── alignment_5.png │ │ ├── from_spatialdata.png │ │ ├── lasso.png │ │ ├── lasso2.png │ │ └── leiden.png │ │ └── xenium_tuto.ipynb ├── README.md ├── api.md ├── cite_us.md ├── cli.md ├── css │ ├── custom.css │ └── termynal.css ├── getting_started.md ├── index.md └── js │ ├── custom.js │ └── termynal.js ├── mkdocs.yml ├── poetry.lock ├── pyproject.toml ├── setup.py ├── spatialdata_xenium_explorer ├── __init__.py ├── _constants.py ├── _logging.py ├── cli │ └── app.py ├── converter.py ├── core │ ├── __init__.py │ ├── images.py │ ├── points.py │ ├── shapes.py │ └── table.py ├── main.py └── utils.py └── tests └── test_instance.py /.gitattributes: -------------------------------------------------------------------------------- 1 | # Ignoring notebooks for language statistics on Github 2 | .ipynb -linguist-detectable 3 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: "[Bug]" 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | ## Description 11 | A clear and concise description of what the bug is. 12 | 13 | ## Reproducing the issue 14 | Steps to reproduce the behavior. 15 | 16 | ## Expected behavior 17 | A clear and concise description of what you expected to happen. 18 | 19 | ## System 20 | - OS: [e.g. Linux] 21 | - Python version [e.g. 3.9.7] 22 |
23 | Dependencies versions 24 |
25 | Paste here what 'pip list' gives you. 26 |
27 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/custom.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Custom issue template 3 | about: Describe this issue template's purpose here. 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | 11 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: "[Feature]" 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: ci 2 | on: 3 | push: 4 | tags: 5 | - v* 6 | jobs: 7 | deploy-doc: 8 | runs-on: ubuntu-latest 9 | steps: 10 | #---------------------------------------------- 11 | # check-out repo and set-up python 12 | #---------------------------------------------- 13 | - name: Check out repository 14 | uses: actions/checkout@v3 15 | - name: Set up python 16 | id: setup-python 17 | uses: actions/setup-python@v4 18 | with: 19 | python-version: 3.9 20 | #---------------------------------------------- 21 | # install & configure poetry 22 | #---------------------------------------------- 23 | - name: Install Poetry 24 | uses: snok/install-poetry@v1 25 | #---------------------------------------------- 26 | # load cached venv if cache exists 27 | #---------------------------------------------- 28 | - name: Load cached venv 29 | id: cached-poetry-dependencies 30 | uses: actions/cache@v3 31 | with: 32 | path: .venv 33 | key: venv-${{ runner.os }}-${{ steps.setup-python.outputs.python-version }}-${{ hashFiles('**/poetry.lock') }} 34 | #---------------------------------------------- 35 | # install dependencies if cache does not exist 36 | #---------------------------------------------- 37 | - name: Install dependencies 38 | if: steps.cached-poetry-dependencies.outputs.cache-hit != 'true' 39 | run: poetry install 40 | #---------------------------------------------- 41 | # run test suite 42 | #---------------------------------------------- 43 | - name: Tests 44 | run: poetry run pytest 45 | - name: Deploy doc 46 | run: poetry run mkdocs gh-deploy --force 47 | build: 48 | needs: [deploy-doc] 49 | runs-on: ubuntu-latest 50 | steps: 51 | - uses: actions/checkout@v3 52 | - name: Build and publish to pypi 53 | uses: JRubics/poetry-publish@v1.17 54 | with: 55 | python_version: "3.10" 56 | pypi_token: ${{ secrets.PYPI_TOKEN }} 57 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # OS related 2 | .DS_Store 3 | 4 | # Misc 5 | *ignore** 6 | visium_data_directory 7 | xenium_data_directory 8 | *.explorer 9 | *.zip 10 | *_outs 11 | 12 | # IDE related 13 | .vscode/* 14 | !.vscode/settings.json 15 | !.vscode/tasks.json 16 | !.vscode/launch.json 17 | !.vscode/extensions.json 18 | *.code-workspace 19 | **/.vscode 20 | 21 | # Documentation 22 | site 23 | 24 | # Logs 25 | outputs 26 | multirun 27 | lightning_logs 28 | 29 | # Data files 30 | data/* 31 | 32 | # Jupyter Notebook 33 | .ipynb_checkpoints 34 | *.ipynb 35 | !docs/10x_tutorials/*/*.ipynb 36 | 37 | # Misc 38 | logs/ 39 | wandb/ 40 | .env 41 | .autoenv 42 | !**/.gitkeep 43 | 44 | # pyenv 45 | .python-version 46 | 47 | # Byte-compiled / optimized / DLL files 48 | __pycache__/ 49 | *.py[cod] 50 | *$py.class 51 | 52 | # C extensions 53 | *.so 54 | 55 | # Distribution / packaging 56 | .Python 57 | build/ 58 | develop-eggs/ 59 | dist/ 60 | downloads/ 61 | eggs/ 62 | .eggs/ 63 | lib/ 64 | lib64/ 65 | parts/ 66 | sdist/ 67 | var/ 68 | wheels/ 69 | pip-wheel-metadata/ 70 | share/python-wheels/ 71 | *.egg-info/ 72 | .installed.cfg 73 | *.egg 74 | MANIFEST 75 | 76 | # PyInstaller 77 | # Usually these files are written by a python script from a template 78 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 79 | *.manifest 80 | *.spec 81 | 82 | # Installer logs 83 | pip-log.txt 84 | pip-delete-this-directory.txt 85 | 86 | # Unit test / coverage reports 87 | htmlcov/ 88 | .tox/ 89 | .nox/ 90 | .coverage 91 | .coverage.* 92 | .cache 93 | nosetests.xml 94 | coverage.xml 95 | *.cover 96 | *.py,cover 97 | .hypothesis/ 98 | .pytest_cache/ -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## [0.1.7] - 2024-04-22 2 | 3 | Hotfix: support newer versions of SpatialData (#6) 4 | 5 | ## [0.1.6] - 2024-02-14 6 | 7 | ### Breaking changes 8 | - `pixelsize` is now renamed `pixel_size` 9 | 10 | ### Changed 11 | - Explorer images should have a higher contrast (not clipping values anymore) 12 | 13 | ## [0.1.5] - 2024-01-30 14 | 15 | ### Added 16 | - Support spot-based technologies like Visium 17 | - Faster table conversion (using native CSR matrix arguments) 18 | 19 | ### Fix 20 | - Keep right image name during image alignment 21 | 22 | ## [0.1.4] - 2024-01-11 23 | 24 | ### Fix 25 | - When a table is sorted or filtered, the polygons are written in the right order (#2) 26 | 27 | ### Change 28 | - Now forces using `spatialdata>=0.0.15` 29 | 30 | ## [0.1.3] - 2023-12-08 31 | 32 | ### Added 33 | - New CLI 34 | - Documentation 35 | - Support both spatial images and multi-scale spatial images 36 | - Better memory management during conversion 37 | - Support image alignment with the explorer 38 | - Conversion between explorer ID and AnnData ID -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | BSD 3-Clause License 5 | 6 | Copyright (c) 2023, Quentin Blampey 7 | All rights reserved. 8 | 9 | Redistribution and use in source and binary forms, with or without 10 | modification, are permitted provided that the following conditions are met: 11 | 12 | 1. Redistributions of source code must retain the above copyright notice, this 13 | list of conditions and the following disclaimer. 14 | 15 | 2. Redistributions in binary form must reproduce the above copyright notice, 16 | this list of conditions and the following disclaimer in the documentation 17 | and/or other materials provided with the distribution. 18 | 19 | 3. Neither the name of the copyright holder nor the names of its 20 | contributors may be used to endorse or promote products derived from 21 | this software without specific prior written permission. 22 | 23 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 24 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 26 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 27 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 29 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 30 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 31 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 32 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 33 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SpatialData to Xenium Explorer 2 | 3 | [![PyPI](https://img.shields.io/pypi/v/spatialdata_xenium_explorer.svg)](https://pypi.org/project/spatialdata_xenium_explorer) 4 | [![Downloads](https://static.pepy.tech/badge/spatialdata_xenium_explorer)](https://pepy.tech/project/spatialdata_xenium_explorer) 5 | [![Docs](https://img.shields.io/badge/docs-mkdocs-blue)](https://quentinblampey.github.io/spatialdata_xenium_explorer/) 6 | ![Build](https://github.com/quentinblampey/spatialdata_xenium_explorer/workflows/ci/badge.svg) 7 | [![Code Style](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/python/black) 8 | [![License](https://img.shields.io/pypi/l/spatialdata_xenium_explorer.svg)](https://github.com/quentinblampey/spatialdata_xenium_explorer/blob/master/LICENSE) 9 | [![Imports: isort](https://img.shields.io/badge/imports-isort-blueviolet)](https://pycqa.github.io/isort/) 10 | 11 | Converting any [`SpatialData`](https://github.com/scverse/spatialdata) object into files that can be opened by the [Xenium Explorer](https://www.10xgenomics.com/support/software/xenium-explorer). 12 | 13 | 14 | Check [the documentation](https://quentinblampey.github.io/spatialdata_xenium_explorer/) to get started quickly. 15 | 16 | > *Xenium Explorer* is a registered trademark of 10x Genomics. The Xenium Explorer is licensed for usage on Xenium data (more details [here](https://www.10xgenomics.com/legal/end-user-software-license-agreement)). 17 | 18 | ⚠️ This repository is not active anymore. Please refer to [Sopa](https://github.com/gustaveroussy/sopa), which includes this conversion tool, and is still actively maintained. 19 | 20 | ## Installation 21 | 22 | Requirement: `python>=3.9` 23 | 24 | ```sh 25 | pip install spatialdata_xenium_explorer 26 | ``` 27 | 28 | ## Features 29 | 30 | - Conversion of the following data: images, cell boundaries (polygons or spot), transcripts, cell-by-gene table, and cell categories (or observations). 31 | - Image alignment can be made on the Xenium Explorer, and then the `SpatialData` object can be updated 32 | - When working on the `SpatialData` or `AnnData` object, new cell categories can be easily and quickly added to the Explorer 33 | - When selecting cells with the "lasso tool" on the Explorer, it's easy to select back these cells on the `SpatialData` or `AnnData` object 34 | 35 | ## Usage 36 | 37 | You can use our CLI or API, see examples below. It will create up to 6 files, among which a file called `experiment.xenium`. Double-click on this file to open it on the [Xenium Explorer](https://www.10xgenomics.com/support/software/xenium-explorer/downloads) (make sure you have the latest version of the software). 38 | 39 | ### CLI 40 | 41 | ```sh 42 | spatialdata_xenium_explorer write /path/to/sdata.zarr 43 | ``` 44 | 45 | Check [our documentation](https://quentinblampey.github.io/spatialdata_xenium_explorer/cli) for more details. 46 | 47 | ### API 48 | 49 | ```python 50 | import spatialdata 51 | import spatialdata_xenium_explorer 52 | 53 | sdata = spatialdata.read_zarr("...") 54 | 55 | spatialdata_xenium_explorer.write("/path/to/directory", sdata, image_key, shapes_key, points_key, gene_column) 56 | ``` 57 | 58 | Check [our documentation](https://quentinblampey.github.io/spatialdata_xenium_explorer/api) for more details. 59 | 60 | ## Contributing 61 | 62 | This package is still in early development. Contributions are welcome (new issues, pull requests, ...). 63 | 64 | ## Cite us 65 | 66 | This library has been detailed in a more general article spatial omics analysis, see the [Sopa library](https://github.com/gustaveroussy/sopa). 67 | The latter article is not published yet, but you can cite our [preprint](https://www.biorxiv.org/content/10.1101/2023.12.22.571863v1): 68 | 69 | ```txt 70 | @article {Blampey2023.12.22.571863, 71 | author = {Quentin Blampey & Kevin Mulder et al.}, 72 | title = {Sopa: a technology-invariant pipeline for analyses of image-based spatial-omics}, 73 | elocation-id = {2023.12.22.571863}, 74 | year = {2023}, 75 | doi = {10.1101/2023.12.22.571863}, 76 | publisher = {Cold Spring Harbor Laboratory}, 77 | URL = {https://www.biorxiv.org/content/early/2023/12/23/2023.12.22.571863}, 78 | eprint = {https://www.biorxiv.org/content/early/2023/12/23/2023.12.22.571863.full.pdf}, 79 | journal = {bioRxiv} 80 | } 81 | ``` 82 | -------------------------------------------------------------------------------- /docs/10x_tutorials/visium/Selection_1_cells_stats.csv: -------------------------------------------------------------------------------- 1 | #Selection name: Selection 1 2 | #Area (µm^2): 2261.18 3 | Cell ID,Cluster,Transcripts,Area (µm^2) 4 | aaaaanaj-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,13,4.38 5 | aaaaabpb-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,13,4.38 6 | aaaabcci-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,27,4.38 7 | aaaaannn-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,10,4.38 8 | aaaabeia-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,14,4.38 9 | aaaaadak-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,11,4.38 10 | aaaabdnc-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,8,4.38 11 | aaaaalmm-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,5,4.38 12 | aaaabekh-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,22,4.38 13 | aaaaandb-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,19,4.38 14 | aaaaadgo-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,29,4.38 15 | aaaabbje-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,17,4.38 16 | aaaabacc-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,9,4.38 17 | aaaabcpo-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,11,4.38 18 | aaaaaldi-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,18,4.38 19 | aaaaakho-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,7,4.38 20 | aaaaadbm-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,14,4.38 21 | aaaaafko-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,19,4.38 22 | aaaabihl-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,18,4.38 23 | aaaaagdp-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,24,4.38 24 | aaaabcph-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,13,4.38 25 | aaaabgnf-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,12,4.38 26 | aaaabiem-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,6,4.38 27 | aaaabgnl-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,9,4.38 28 | aaaaaoek-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,23,4.38 29 | aaaaafgl-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,9,4.38 30 | aaaaadhe-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,15,4.38 31 | aaaaaoje-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,19,4.38 32 | aaaaaeao-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,29,4.38 33 | aaaaachp-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,62,4.38 34 | aaaaalhj-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,13,4.38 35 | aaaaaphc-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,10,4.38 36 | aaaabboi-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,41,4.38 37 | aaaaafcf-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,22,4.38 38 | aaaaabon-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,12,4.38 39 | aaaabhap-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,24,4.38 40 | aaaaakhh-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,11,4.38 41 | aaaabjhb-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,36,4.38 42 | aaaaaeja-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,16,4.38 43 | aaaaaika-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,10,4.38 44 | aaaabego-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,21,4.38 45 | aaaaaekj-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,6,4.38 46 | aaaaafdg-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,14,4.38 47 | aaaaadac-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,12,4.38 48 | aaaaabii-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,27,4.38 49 | aaaaancl-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,14,4.38 50 | aaaaakam-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,11,4.38 51 | aaaaaimb-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,10,4.38 52 | aaaabhlc-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,6,4.38 53 | aaaaafnj-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,3,4.38 54 | aaaaancb-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,1,4.38 55 | aaaaaabi-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,5,4.38 56 | aaaabend-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,22,4.38 57 | aaaaadlk-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,23,4.38 58 | aaaabani-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,5,4.38 59 | aaaabiod-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,35,4.38 60 | aaaaameh-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,14,4.38 61 | aaaaacpo-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,22,4.38 62 | aaaaanib-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,26,4.38 63 | aaaabfch-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,16,4.38 64 | aaaaagjb-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,29,4.38 65 | aaaabiea-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,22,4.38 66 | aaaaacka-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,33,4.38 67 | aaaabbfn-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,30,4.38 68 | aaaaafca-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,11,4.38 69 | aaaaagej-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,12,4.38 70 | aaaabejh-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,45,4.38 71 | aaaabifl-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,30,4.38 72 | aaaaacdd-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,11,4.38 73 | aaaabgbb-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,25,4.38 74 | aaaaampj-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,13,4.38 75 | aaaabjfl-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,5,4.38 76 | aaaaabeg-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,8,4.38 77 | aaaabiic-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,15,4.38 78 | aaaabanj-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,34,4.38 79 | aaaabaam-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,24,4.38 80 | aaaabihk-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,18,4.38 81 | aaaaacfp-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,42,4.38 82 | aaaaaanp-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,22,4.38 83 | aaaabcdh-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,14,4.38 84 | aaaabfgj-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,12,4.38 85 | aaaabagc-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,32,4.38 86 | aaaabech-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,19,4.38 87 | aaaabbai-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,49,4.38 88 | aaaaajef-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,63,4.38 89 | aaaaammc-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,23,4.38 90 | aaaabcha-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,46,4.38 91 | aaaaafhj-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,45,4.38 92 | aaaaajnp-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,15,4.38 93 | aaaaanhf-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,11,4.38 94 | aaaaaigc-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,10,4.38 95 | aaaaapeb-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,14,4.38 96 | aaaaachl-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,49,4.38 97 | aaaabgln-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,22,4.38 98 | aaaaappi-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,18,4.38 99 | aaaaaoia-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,64,4.38 100 | aaaaamnf-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,24,4.38 101 | aaaaacag-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,43,4.38 102 | aaaabcfm-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,5,4.38 103 | aaaabijf-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,19,4.38 104 | aaaaalpo-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,14,4.38 105 | aaaabecb-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,14,4.38 106 | aaaabigi-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,16,4.38 107 | aaaaaofh-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,9,4.38 108 | aaaabfpk-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,4,4.38 109 | aaaabakd-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,13,4.38 110 | aaaaagmo-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,15,4.38 111 | aaaaaenn-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,15,4.38 112 | aaaaaaha-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,27,4.38 113 | aaaaaolp-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,62,4.38 114 | aaaabhlg-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,17,4.38 115 | aaaabhil-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,9,4.38 116 | aaaaagpf-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,27,4.38 117 | aaaabhda-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,25,4.38 118 | aaaabigf-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,16,4.38 119 | aaaaalbm-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,50,4.38 120 | aaaabblc-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,19,4.38 121 | aaaabbgf-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,61,4.38 122 | aaaaaibj-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,14,4.38 123 | aaaabfmd-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,50,4.38 124 | aaaaabml-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,13,4.38 125 | aaaaabeb-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,46,4.38 126 | aaaaabgi-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,27,4.38 127 | aaaaapmf-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,15,4.38 128 | aaaaaako-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,7,4.38 129 | aaaabfeb-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,21,4.38 130 | aaaaangn-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,21,4.38 131 | aaaaaghl-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,18,4.38 132 | aaaabcgj-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,15,4.38 133 | aaaaallk-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,6,4.38 134 | aaaaamif-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,2,4.38 135 | aaaaadpg-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,8,4.38 136 | aaaaanga-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,1,4.38 -------------------------------------------------------------------------------- /docs/10x_tutorials/visium/Selection_1_coordinates.csv: -------------------------------------------------------------------------------- 1 | #Selection name: Selection 1 2 | #Area (µm^2): 682.78 3 | X,Y 4 | 149.85,261.92 5 | 151.91,261.69 6 | 152.05,261.75 7 | 153.49,262.43 8 | 154.63,263.70 9 | 155.52,265.25 10 | 155.97,266.85 11 | 155.82,268.54 12 | 155.69,270.08 13 | 155.51,272.35 14 | 154.94,274.88 15 | 154.42,276.15 16 | 153.09,278.00 17 | 151.62,280.26 18 | 149.96,281.62 19 | 148.11,282.16 20 | 146.15,283.58 21 | 144.93,285.21 22 | 143.35,286.20 23 | 141.59,286.46 24 | 139.74,287.19 25 | 137.37,288.49 26 | 136.01,289.13 27 | 134.56,290.37 28 | 133.72,291.75 29 | 133.54,293.01 30 | 133.54,294.83 31 | 134.00,297.87 32 | 134.22,300.10 33 | 134.64,301.65 34 | 135.14,302.95 35 | 135.52,304.25 36 | 136.44,305.74 37 | 136.45,307.49 38 | 135.50,308.60 39 | 134.88,309.82 40 | 134.03,310.97 41 | 132.54,310.74 42 | 132.09,309.24 43 | 131.02,308.44 44 | 129.33,307.78 45 | 127.63,306.91 46 | 126.02,305.49 47 | 124.28,303.79 48 | 122.52,302.08 49 | 121.90,300.98 50 | 120.44,298.40 51 | 119.77,296.60 52 | 119.34,293.46 53 | 119.83,291.26 54 | 120.50,288.94 55 | 121.26,286.74 56 | 122.76,284.81 57 | 123.98,281.79 58 | 125.56,280.38 59 | 127.44,278.59 60 | 128.93,277.84 61 | 132.27,276.96 62 | 135.67,276.39 63 | 137.03,276.37 64 | 138.60,276.31 65 | 140.06,276.19 66 | 141.93,275.85 67 | 143.16,274.92 68 | 144.68,273.07 69 | 145.47,271.68 70 | 146.36,270.02 71 | 146.15,268.49 72 | 145.22,267.03 73 | 144.87,265.86 74 | 145.34,264.64 75 | 145.97,263.22 76 | 146.80,261.90 77 | 148.45,261.71 78 | 149.85,261.92 -------------------------------------------------------------------------------- /docs/10x_tutorials/visium/assets/clusters.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quentinblampey/spatialdata_xenium_explorer/e62aa7cac1aee9be081eb2fca73efb228bfb3f06/docs/10x_tutorials/visium/assets/clusters.png -------------------------------------------------------------------------------- /docs/10x_tutorials/visium/assets/density_map.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quentinblampey/spatialdata_xenium_explorer/e62aa7cac1aee9be081eb2fca73efb228bfb3f06/docs/10x_tutorials/visium/assets/density_map.png -------------------------------------------------------------------------------- /docs/10x_tutorials/visium/assets/lasso.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quentinblampey/spatialdata_xenium_explorer/e62aa7cac1aee9be081eb2fca73efb228bfb3f06/docs/10x_tutorials/visium/assets/lasso.png -------------------------------------------------------------------------------- /docs/10x_tutorials/visium/assets/lasso2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quentinblampey/spatialdata_xenium_explorer/e62aa7cac1aee9be081eb2fca73efb228bfb3f06/docs/10x_tutorials/visium/assets/lasso2.png -------------------------------------------------------------------------------- /docs/10x_tutorials/visium/assets/one_spot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quentinblampey/spatialdata_xenium_explorer/e62aa7cac1aee9be081eb2fca73efb228bfb3f06/docs/10x_tutorials/visium/assets/one_spot.png -------------------------------------------------------------------------------- /docs/10x_tutorials/visium/assets/transcripts_subset.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quentinblampey/spatialdata_xenium_explorer/e62aa7cac1aee9be081eb2fca73efb228bfb3f06/docs/10x_tutorials/visium/assets/transcripts_subset.png -------------------------------------------------------------------------------- /docs/10x_tutorials/xenium/Selection_1_cells_stats.csv: -------------------------------------------------------------------------------- 1 | #Selection name: Selection 2 2 | #Area (µm^2): 16793.06 3 | Cell ID,Cluster,Transcripts,Area (µm^2) 4 | aaaaaibb-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,1,4.38 5 | aaaabcmj-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,3,4.38 6 | aaaaabgd-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,1,4.38 7 | aaaaaamj-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,1,4.38 8 | aaaaakon-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,23,4.38 9 | aaaaaaoi-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,3,4.38 10 | aaaabhce-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,2,4.38 11 | aaaaaffb-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,2,4.38 12 | aaaaacjf-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,1,4.38 13 | aaaabclo-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,1,4.38 14 | aaaaagjp-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,1,4.38 15 | aaaaafjh-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,0,4.38 16 | aaaabaoo-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,0,4.38 17 | aaaabcdf-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,5,4.38 18 | aaaabgdf-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,1,4.38 19 | aaaaaghp-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,2,4.38 20 | aaaabjee-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,0,4.38 21 | aaaaadii-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,4,4.38 22 | aaaaaplo-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,2,4.38 23 | aaaabafj-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,3,4.38 24 | aaaaancp-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,4,4.38 25 | aaaabhki-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,1,4.38 26 | aaaabdbe-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,4,4.38 27 | aaaabhdn-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,4,4.38 28 | aaaaaeia-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,2,4.38 29 | aaaabifh-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,5,4.38 30 | aaaaaehn-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,3,4.38 31 | aaaaanfi-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,1,4.38 32 | aaaaakpe-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,1,4.38 33 | aaaabdjh-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,1,4.38 34 | aaaabcki-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,3,4.38 35 | aaaaalob-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,2,4.38 36 | aaaaahck-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,4,4.38 37 | aaaabiko-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,5,4.38 38 | aaaaaael-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,2,4.38 39 | aaaabhdg-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,8,4.38 40 | aaaaafgi-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,2,4.38 41 | aaaaadfd-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,0,4.38 42 | aaaaacld-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,2,4.38 43 | aaaabhdi-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,1,4.38 44 | aaaabhfe-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,4,4.38 45 | aaaaajcc-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,1,4.38 46 | aaaaaijh-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,2,4.38 47 | aaaaabdk-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,1,4.38 48 | aaaaaleo-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,4,4.38 49 | aaaabcdc-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,5,4.38 50 | aaaaajdo-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,1,4.38 51 | aaaaakhl-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,3,4.38 52 | aaaaaacg-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,2,4.38 53 | aaaaamgd-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,3,4.38 54 | aaaabeed-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,2,4.38 55 | aaaabcee-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,4,4.38 56 | aaaabfcp-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,5,4.38 57 | aaaabcga-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,5,4.38 58 | aaaabhja-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,4,4.38 59 | aaaabikd-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,1,4.38 60 | aaaaaljf-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,1,4.38 61 | aaaaacpf-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,8,4.38 62 | aaaaajfe-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,1,4.38 63 | aaaaageo-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,3,4.38 64 | aaaaaakg-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,1,4.38 65 | aaaaaehg-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,1,4.38 66 | aaaabhbp-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,4,4.38 67 | aaaaaafd-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,3,4.38 68 | aaaaadgk-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,1,4.38 69 | aaaaaalh-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,3,4.38 70 | aaaaaddf-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,3,4.38 71 | aaaabeic-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,29,4.38 72 | aaaaanag-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,4,4.38 73 | aaaaaacp-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,37,4.38 74 | aaaabgkh-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,7,4.38 75 | aaaabcoi-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,11,4.38 76 | aaaabhen-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,11,4.38 77 | aaaaalhb-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,2,4.38 78 | aaaabgad-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,16,4.38 79 | aaaabagf-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,5,4.38 80 | aaaaafng-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,6,4.38 81 | aaaaajdl-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,4,4.38 82 | aaaaaggm-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,3,4.38 83 | aaaaajck-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,4,4.38 84 | aaaaaenh-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,3,4.38 85 | aaaaaplj-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,2,4.38 86 | aaaabhff-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,2,4.38 87 | aaaababa-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,4,4.38 88 | aaaaajlk-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,3,4.38 89 | aaaabini-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,3,4.38 90 | aaaabeel-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,4,4.38 91 | aaaaaooo-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,5,4.38 92 | aaaaaiao-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,5,4.38 93 | aaaaacmh-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,3,4.38 94 | aaaaalkl-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,2,4.38 95 | aaaaabbe-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,6,4.38 96 | aaaaaapc-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,10,4.38 97 | aaaaajgc-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,3,4.38 98 | aaaabamm-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,30,4.38 99 | aaaaabeo-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,5,4.38 100 | aaaabfgh-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,17,4.38 101 | aaaabhbo-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,3,4.38 102 | aaaaamio-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,6,4.38 103 | aaaaabfm-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,7,4.38 104 | aaaabjhc-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,2,4.38 105 | aaaabajh-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,7,4.38 106 | aaaaaepd-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,5,4.38 107 | aaaaainm-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,1,4.38 108 | aaaabcal-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,2,4.38 109 | aaaabcad-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,3,4.38 110 | aaaabdoo-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,0,4.38 111 | aaaaacfd-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,4,4.38 112 | aaaabikg-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,1,4.38 113 | aaaabgmd-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,9,4.38 114 | aaaabhco-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,1,4.38 115 | aaaaacoi-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,3,4.38 116 | aaaabgek-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,3,4.38 117 | aaaaaage-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,0,4.38 118 | aaaaaiob-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,4,4.38 119 | aaaaagae-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,1,4.38 120 | aaaabioo-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,2,4.38 121 | aaaabfdf-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,10,4.38 122 | aaaabcfk-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,2,4.38 123 | aaaabdok-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,1,4.38 124 | aaaaailf-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,2,4.38 125 | aaaaapco-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,2,4.38 126 | aaaaahgi-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,4,4.38 127 | aaaabbof-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,7,4.38 128 | aaaaapia-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,11,4.38 129 | aaaababj-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,2,4.38 130 | aaaabcbf-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,5,4.38 131 | aaaaadoe-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,1,4.38 132 | aaaabdnh-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,1,4.38 133 | aaaaaghf-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,1,4.38 134 | aaaaaime-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,4,4.38 135 | aaaabimk-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,3,4.38 136 | aaaabikb-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,2,4.38 137 | aaaaaoic-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,3,4.38 138 | aaaaanho-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,5,4.38 139 | aaaaapab-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,2,4.38 140 | aaaabefb-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,3,4.38 141 | aaaabjen-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,15,4.38 142 | aaaaaign-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,3,4.38 143 | aaaabchi-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,2,4.38 144 | aaaaaicp-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,13,4.38 145 | aaaaanle-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,31,4.38 146 | aaaaacae-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,1,4.38 147 | aaaabijn-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,8,4.38 148 | aaaaaanb-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,5,4.38 149 | aaaaaobk-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,6,4.38 150 | aaaaapeo-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,3,4.38 151 | aaaaadck-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,3,4.38 152 | aaaabccc-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,9,4.38 153 | aaaabfaa-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,0,4.38 154 | aaaabepa-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,9,4.38 155 | aaaaahmj-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,6,4.38 156 | aaaabbae-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,2,4.38 157 | aaaaabmh-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,19,4.38 158 | aaaaapil-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,10,4.38 159 | aaaaaijn-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,12,4.38 160 | aaaaaajb-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,12,4.38 161 | aaaaalgk-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,0,4.38 162 | aaaaajlj-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,4,4.38 163 | aaaaaibf-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,22,4.38 164 | aaaabcll-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,18,4.38 165 | aaaaaoai-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,6,4.38 166 | aaaabdib-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,22,4.38 167 | aaaaaece-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,4,4.38 168 | aaaaamoe-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,5,4.38 169 | aaaabdip-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,0,4.38 170 | aaaaaoph-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,11,4.38 171 | aaaaajak-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,6,4.38 172 | aaaabihh-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,5,4.38 173 | aaaaaled-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,1,4.38 174 | aaaabeih-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,4,4.38 175 | aaaabaog-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,1,4.38 176 | aaaabadd-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,6,4.38 177 | aaaabfbm-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,13,4.38 178 | aaaabidc-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,2,4.38 179 | aaaaanjm-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,16,4.38 180 | aaaabdeg-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,9,4.38 181 | aaaaagkn-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,19,4.38 182 | aaaaadeg-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,19,4.38 183 | aaaabhcd-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,11,4.38 184 | aaaaaljh-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,10,4.38 185 | aaaabhml-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,6,4.38 186 | aaaaafnp-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,8,4.38 187 | aaaaafdk-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,7,4.38 188 | aaaabdmm-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,7,4.38 189 | aaaabehg-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,11,4.38 190 | aaaaaopd-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,6,4.38 191 | aaaabama-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,6,4.38 192 | aaaabfmg-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,15,4.38 193 | aaaaaleb-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,8,4.38 194 | aaaaalhk-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,12,4.38 195 | aaaabhip-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,8,4.38 196 | aaaabbdi-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,7,4.38 197 | aaaabhaj-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,8,4.38 198 | aaaaaojo-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,17,4.38 199 | aaaaaeij-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,10,4.38 200 | aaaaakbn-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,5,4.38 201 | aaaabdkl-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,18,4.38 202 | aaaabgba-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,9,4.38 203 | aaaaapjp-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,10,4.38 204 | aaaabiim-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,6,4.38 205 | aaaabhga-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,10,4.38 206 | aaaabdnk-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,28,4.38 207 | aaaaadpn-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,6,4.38 208 | aaaabhbn-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,14,4.38 209 | aaaaadmb-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,0,4.38 210 | aaaaahgc-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,3,4.38 211 | aaaaahgl-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,0,4.38 212 | aaaabhbk-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,1,4.38 213 | aaaabflf-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,7,4.38 214 | aaaaagfd-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,2,4.38 215 | aaaaajei-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,9,4.38 216 | aaaaajol-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,7,4.38 217 | aaaabbfh-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,3,4.38 218 | aaaaaiaj-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,2,4.38 219 | aaaaahdh-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,11,4.38 220 | aaaaaiko-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,22,4.38 221 | aaaabjbf-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,17,4.38 222 | aaaaaffg-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,9,4.38 223 | aaaaagla-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,6,4.38 224 | aaaabbeb-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,29,4.38 225 | aaaabchf-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,31,4.38 226 | aaaaahah-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,1,4.38 227 | aaaabcja-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,20,4.38 228 | aaaaabkj-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,10,4.38 229 | aaaaahdg-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,6,4.38 230 | aaaabamg-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,19,4.38 231 | aaaaanch-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,7,4.38 232 | aaaaalpe-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,6,4.38 233 | aaaaackc-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,9,4.38 234 | aaaabfem-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,19,4.38 235 | aaaaadjg-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,14,4.38 236 | aaaabhhm-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,4,4.38 237 | aaaabeno-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,5,4.38 238 | aaaaapkc-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,6,4.38 239 | aaaabgia-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,5,4.38 240 | aaaaakcl-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,11,4.38 241 | aaaabdbh-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,3,4.38 242 | aaaaaegf-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,9,4.38 243 | aaaabino-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,5,4.38 244 | aaaabikp-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,14,4.38 245 | aaaabfca-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,7,4.38 246 | aaaaacgg-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,9,4.38 247 | aaaaamfl-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,10,4.38 248 | aaaabdjf-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,3,4.38 249 | aaaaapbd-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,4,4.38 250 | aaaaagal-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,10,4.38 251 | aaaaadjb-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,4,4.38 252 | aaaaaknh-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,11,4.38 253 | aaaabgic-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,4,4.38 254 | aaaaafem-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,3,4.38 255 | aaaabjae-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,6,4.38 256 | aaaaaekg-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,11,4.38 257 | aaaabaie-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,2,4.38 258 | aaaabefa-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,2,4.38 259 | aaaabean-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,7,4.38 260 | aaaabeoe-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,8,4.38 261 | aaaaabbk-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,11,4.38 262 | aaaaaddc-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,8,4.38 263 | aaaaaapa-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,7,4.38 264 | aaaaahho-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,5,4.38 265 | aaaaabka-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,4,4.38 266 | aaaaajki-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,4,4.38 267 | aaaaailb-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,3,4.38 268 | aaaababn-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,3,4.38 269 | aaaaalei-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,5,4.38 270 | aaaaadld-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,7,4.38 271 | aaaaaake-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,9,4.38 272 | aaaabcgf-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,13,4.38 273 | aaaabaep-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,9,4.38 274 | aaaabhgc-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,7,4.38 275 | aaaaapdh-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,5,4.38 276 | aaaaaijf-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,7,4.38 277 | aaaaanbl-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,8,4.38 278 | aaaaaegc-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,9,4.38 279 | aaaaalga-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,6,4.38 280 | aaaabcpj-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,2,4.38 281 | aaaabenk-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,2,4.38 282 | aaaabcdm-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,0,4.38 283 | aaaaachb-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,5,4.38 284 | aaaabdoe-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,0,4.38 285 | aaaaajec-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,0,4.38 286 | aaaabihd-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,2,4.38 287 | aaaaajdm-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,2,4.38 288 | aaaaakpc-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,4,4.38 289 | aaaabedf-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,4,4.38 290 | aaaabboj-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,2,4.38 291 | aaaaaofe-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,1,4.38 292 | aaaabaik-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,1,4.38 293 | aaaaahdm-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,12,4.38 294 | aaaabbmb-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,8,4.38 295 | aaaaaonk-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,5,4.38 296 | aaaaadjc-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,7,4.38 297 | aaaaahbo-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,4,4.38 298 | aaaaacnc-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,3,4.38 299 | aaaaambk-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,5,4.38 300 | aaaabgoj-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,1,4.38 301 | aaaaaoga-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,0,4.38 302 | aaaaacam-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,1,4.38 303 | aaaaafop-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,0,4.38 304 | aaaaahhf-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,1,4.38 305 | aaaaaoof-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,7,4.38 306 | aaaabbdo-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,6,4.38 307 | aaaaaonb-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,11,4.38 308 | aaaaaemo-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,8,4.38 309 | aaaaaamk-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,8,4.38 310 | aaaaapne-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,19,4.38 311 | aaaaapbc-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,6,4.38 312 | aaaabidm-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,4,4.38 313 | aaaabile-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,5,4.38 314 | aaaabhkm-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,13,4.38 315 | aaaaaeig-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,8,4.38 316 | aaaaagjd-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,8,4.38 317 | aaaabekl-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,7,4.38 318 | aaaabdjj-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,12,4.38 319 | aaaaapll-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,4,4.38 320 | aaaaakgk-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,7,4.38 321 | aaaabclb-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,3,4.38 322 | aaaabape-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,4,4.38 323 | aaaabgpm-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,4,4.38 324 | aaaabcnp-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,11,4.38 325 | aaaaandi-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,9,4.38 326 | aaaabhfd-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,7,4.38 327 | aaaaaiad-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,4,4.38 328 | aaaaagke-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,5,4.38 329 | aaaaaigf-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,6,4.38 330 | aaaabiie-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,3,4.38 331 | aaaabige-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,5,4.38 332 | aaaaajjn-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,8,4.38 333 | aaaabcfg-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,4,4.38 334 | aaaaankn-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,5,4.38 335 | aaaaagnn-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,3,4.38 336 | aaaaalaj-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,3,4.38 337 | aaaaampo-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,1,4.38 338 | aaaaahkc-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,6,4.38 339 | aaaaajll-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,6,4.38 340 | aaaaaoea-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,2,4.38 341 | aaaaaeif-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,8,4.38 342 | aaaaabhh-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,7,4.38 343 | aaaaannd-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,9,4.38 344 | aaaaaafb-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,11,4.38 345 | aaaaaicm-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,8,4.38 346 | aaaaajbp-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,8,4.38 347 | aaaaajbe-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,2,4.38 348 | aaaaakii-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,8,4.38 349 | aaaaanel-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,23,4.38 350 | aaaaadca-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,3,4.38 351 | aaaaaajd-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,7,4.38 352 | aaaabcod-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,12,4.38 353 | aaaabiak-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,7,4.38 354 | aaaaanim-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,12,4.38 355 | aaaaaejl-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,0,4.38 356 | aaaaamna-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,0,4.38 357 | aaaaanep-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,1,4.38 358 | aaaaahok-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,5,4.38 359 | aaaaapdo-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,17,4.38 360 | aaaabhlo-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,4,4.38 361 | aaaaafmd-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,0,4.38 362 | aaaabhbf-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,1,4.38 363 | aaaabiid-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,1,4.38 364 | aaaaahog-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,4,4.38 365 | aaaabdjd-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,8,4.38 366 | aaaaaldg-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,2,4.38 367 | aaaabhkc-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,1,4.38 368 | aaaaanpo-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,11,4.38 369 | aaaaacch-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,1,4.38 370 | aaaaaaao-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,4,4.38 371 | aaaaahni-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,18,4.38 372 | aaaaakba-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,2,4.38 373 | aaaaakmh-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,17,4.38 374 | aaaaaoib-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,7,4.38 375 | aaaaahmm-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,13,4.38 376 | aaaabchj-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,6,4.38 377 | aaaaapio-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,7,4.38 378 | aaaaajpf-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,12,4.38 379 | aaaaanje-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,7,4.38 380 | aaaaagck-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,20,4.38 381 | aaaaafcl-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,14,4.38 382 | aaaaaeeh-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,7,4.38 383 | aaaabjgc-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,4,4.38 384 | aaaaamem-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,2,4.38 385 | aaaabikk-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,7,4.38 386 | aaaaablg-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,3,4.38 387 | aaaabgnc-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,20,4.38 388 | aaaabaed-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,13,4.38 389 | aaaaakcp-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,6,4.38 390 | aaaaaalj-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,9,4.38 391 | aaaabaab-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,7,4.38 392 | aaaabfmh-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,5,4.38 393 | aaaaaoah-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,16,4.38 394 | aaaaalig-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,8,4.38 395 | aaaaaafj-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,9,4.38 396 | aaaabihn-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,2,4.38 397 | aaaaafck-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,7,4.38 398 | aaaabebp-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,18,4.38 399 | aaaaakao-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,11,4.38 400 | aaaabipo-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,2,4.38 401 | aaaabcml-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,1,4.38 402 | aaaaadob-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,2,4.38 403 | aaaabchp-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,14,4.38 404 | aaaaagnp-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,3,4.38 405 | aaaaagaf-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,10,4.38 406 | aaaaaaak-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,3,4.38 407 | aaaaakcn-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,18,4.38 408 | aaaabcbc-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,7,4.38 409 | aaaabidn-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,0,4.38 410 | aaaaakdc-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,5,4.38 411 | aaaaaefd-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,6,4.38 412 | aaaaanli-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,5,4.38 413 | aaaaaoco-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,2,4.38 414 | aaaabbpk-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,8,4.38 415 | aaaaamja-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,8,4.38 416 | aaaaammo-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,1,4.38 417 | aaaaapcg-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,13,4.38 418 | aaaaainj-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,22,4.38 419 | aaaabbki-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,15,4.38 420 | aaaabjcj-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,12,4.38 421 | aaaabchc-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,8,4.38 422 | aaaaamlm-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,8,4.38 423 | aaaaaefj-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,6,4.38 424 | aaaabemn-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,8,4.38 425 | aaaabjfa-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,10,4.38 426 | aaaaanha-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,15,4.38 427 | aaaaadpd-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,16,4.38 428 | aaaaajcn-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,20,4.38 429 | aaaaabmm-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,10,4.38 430 | aaaabfek-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,6,4.38 431 | aaaaaikd-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,13,4.38 432 | aaaaamac-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,11,4.38 433 | aaaabblh-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,6,4.38 434 | aaaaaphk-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,22,4.38 435 | aaaaaoli-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,12,4.38 436 | aaaaahgf-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,4,4.38 437 | aaaaacki-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,2,4.38 438 | aaaabglg-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,12,4.38 439 | aaaaamkg-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,15,4.38 440 | aaaaagaj-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,16,4.38 441 | aaaaalci-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,15,4.38 442 | aaaaaeal-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,11,4.38 443 | aaaaalki-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,3,4.38 444 | aaaabfej-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,8,4.38 445 | aaaaaeol-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,6,4.38 446 | aaaaamom-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,10,4.38 447 | aaaaadib-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,27,4.38 448 | aaaabfab-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,6,4.38 449 | aaaaalij-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,14,4.38 450 | aaaaaolf-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,16,4.38 451 | aaaaacph-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,4,4.38 452 | aaaaalma-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,5,4.38 453 | aaaaaiee-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,2,4.38 454 | aaaabfha-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,4,4.38 455 | aaaabble-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,33,4.38 456 | aaaaapbp-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,17,4.38 457 | aaaaaaig-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,21,4.38 458 | aaaaaopg-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,9,4.38 459 | aaaaalcn-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,18,4.38 460 | aaaabfpi-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,21,4.38 461 | aaaaahpo-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,9,4.38 462 | aaaaahkh-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,6,4.38 463 | aaaaaiih-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,5,4.38 464 | aaaabgfb-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,20,4.38 465 | aaaabchh-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,10,4.38 466 | aaaaaknp-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,13,4.38 467 | aaaaadbb-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,10,4.38 468 | aaaabbgn-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,20,4.38 469 | aaaaapja-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,11,4.38 470 | aaaaaljp-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,4,4.38 471 | aaaaaiph-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,8,4.38 472 | aaaaaimg-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,6,4.38 473 | aaaabgge-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,5,4.38 474 | aaaabacb-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,5,4.38 475 | aaaabfjm-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,8,4.38 476 | aaaaaphg-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,3,4.38 477 | aaaaapbo-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,3,4.38 478 | aaaaaepi-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,5,4.38 479 | aaaaajeo-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,6,4.38 480 | aaaaapci-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,6,4.38 481 | aaaabbee-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,5,4.38 482 | aaaaabol-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,4,4.38 483 | aaaaadlg-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,6,4.38 484 | aaaabgoo-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,18,4.38 485 | aaaaanhg-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,5,4.38 486 | aaaabajl-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,7,4.38 487 | aaaaaadh-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,3,4.38 488 | aaaabhlk-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,4,4.38 489 | aaaabdbj-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,6,4.38 490 | aaaaapln-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,5,4.38 491 | aaaaajpo-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,5,4.38 492 | aaaaalap-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,3,4.38 493 | aaaaacdc-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,1,4.38 494 | aaaabbkh-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,1,4.38 495 | aaaabbpp-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,3,4.38 496 | aaaaaiek-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,1,4.38 497 | aaaaaopl-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,5,4.38 498 | aaaaaaol-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,4,4.38 499 | aaaabaok-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,14,4.38 500 | aaaabfoa-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,4,4.38 501 | aaaaabbb-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,18,4.38 502 | aaaabbcf-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,7,4.38 503 | aaaabfgg-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,11,4.38 504 | aaaaagbh-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,7,4.38 505 | aaaaamhb-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,3,4.38 506 | aaaaamdb-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,6,4.38 507 | aaaaalld-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,12,4.38 508 | aaaabfbd-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,10,4.38 509 | aaaaacmn-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,5,4.38 510 | aaaabanb-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,7,4.38 511 | aaaaaipb-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,12,4.38 512 | aaaaafmb-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,9,4.38 513 | aaaaaimc-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,13,4.38 514 | aaaabbbp-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,11,4.38 515 | aaaabebi-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,7,4.38 516 | aaaaalkk-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,3,4.38 517 | aaaabelh-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,9,4.38 518 | aaaaagab-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,6,4.38 519 | aaaaaafn-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,3,4.38 520 | aaaabhfc-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,7,4.38 521 | aaaaanmm-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,12,4.38 522 | aaaabdla-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,11,4.38 523 | aaaaaljk-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,8,4.38 524 | aaaabiif-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,13,4.38 525 | aaaabghl-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,15,4.38 526 | aaaaanaj-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,18,4.38 527 | aaaaabpb-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,19,4.38 528 | aaaabcci-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,33,4.38 529 | aaaaadcn-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,7,4.38 530 | aaaaanfa-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,4,4.38 531 | aaaaaebj-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,16,4.38 532 | aaaaacho-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,8,4.38 533 | aaaaaeog-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,12,4.38 534 | aaaaadnf-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,5,4.38 535 | aaaaannn-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,16,4.38 536 | aaaabemi-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,2,4.38 537 | aaaaadli-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,7,4.38 538 | aaaabeia-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,15,4.38 539 | aaaabill-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,10,4.38 540 | aaaabejf-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,14,4.38 541 | aaaaadak-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,14,4.38 542 | aaaabdnc-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,12,4.38 543 | aaaaakpp-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,7,4.38 544 | aaaaaljd-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,12,4.38 545 | aaaabgai-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,12,4.38 546 | aaaaafhe-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,12,4.38 547 | aaaaalmm-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,9,4.38 548 | aaaaahhk-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,19,4.38 549 | aaaaaphf-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,8,4.38 550 | aaaabekh-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,25,4.38 551 | aaaaandb-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,25,4.38 552 | aaaaaina-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,8,4.38 553 | aaaaanlo-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,11,4.38 554 | aaaaanmo-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,5,4.38 555 | aaaaadgo-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,33,4.38 556 | aaaabbje-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,20,4.38 557 | aaaabacc-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,29,4.38 558 | aaaabcpo-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,15,4.38 559 | aaaaaldi-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,23,4.38 560 | aaaaakho-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,10,4.38 561 | aaaaadbm-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,20,4.38 562 | aaaaafko-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,24,4.38 563 | aaaabihl-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,23,4.38 564 | aaaaagdp-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,31,4.38 565 | aaaabcph-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,16,4.38 566 | aaaaaaep-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,5,4.38 567 | aaaabgnf-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,13,4.38 568 | aaaaakhk-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,11,4.38 569 | aaaabiem-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,14,4.38 570 | aaaabgnl-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,10,4.38 571 | aaaaaoek-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,29,4.38 572 | aaaaafgl-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,14,4.38 573 | aaaaadhe-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,24,4.38 574 | aaaaaoje-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,25,4.38 575 | aaaaaeao-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,40,4.38 576 | aaaaachp-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,75,4.38 577 | aaaaalhj-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,20,4.38 578 | aaaaaphc-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,17,4.38 579 | aaaabboi-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,50,4.38 580 | aaaaafcf-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,32,4.38 581 | aaaaabon-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,17,4.38 582 | aaaabhap-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,28,4.38 583 | aaaaakhh-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,19,4.38 584 | aaaabjhb-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,39,4.38 585 | aaaaafee-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,7,4.38 586 | aaaabhbi-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,5,4.38 587 | aaaabeii-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,7,4.38 588 | aaaabgng-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,8,4.38 589 | aaaabbnk-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,0,4.38 590 | aaaaaiec-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,12,4.38 591 | aaaaaefh-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,3,4.38 592 | aaaaaoep-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,14,4.38 593 | aaaaapgo-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,6,4.38 594 | aaaaanoa-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,10,4.38 595 | aaaaafkd-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,11,4.38 596 | aaaabfjd-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,0,4.38 597 | aaaabfdh-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,4,4.38 598 | aaaaaemf-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,6,4.38 599 | aaaabein-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,19,4.38 600 | aaaabebm-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,4,4.38 601 | aaaaanfo-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,11,4.38 602 | aaaaapop-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,11,4.38 603 | aaaaaiba-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,1,4.38 604 | aaaaamhg-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,4,4.38 605 | aaaaabho-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,12,4.38 606 | aaaaappl-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,7,4.38 607 | aaaabcdi-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,3,4.38 608 | aaaabejk-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,3,4.38 609 | aaaaagdg-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,8,4.38 610 | aaaabgbo-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,9,4.38 611 | aaaaaofj-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,2,4.38 612 | aaaabimo-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,5,4.38 613 | aaaabapn-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,7,4.38 614 | aaaaaeja-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,21,4.38 615 | aaaabhdj-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,13,4.38 616 | aaaaahan-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,19,4.38 617 | aaaabcaa-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,6,4.38 618 | aaaabclg-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,10,4.38 619 | aaaaaika-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,14,4.38 620 | aaaaaklp-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,7,4.38 621 | aaaaamdg-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,14,4.38 622 | aaaabgga-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,2,4.38 623 | aaaabego-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,28,4.38 624 | aaaaakcf-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,3,4.38 625 | aaaaamje-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,6,4.38 626 | aaaaainc-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,8,4.38 627 | aaaabcnh-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,4,4.38 628 | aaaaaomm-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,12,4.38 629 | aaaaaolb-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,10,4.38 630 | aaaaaekj-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,10,4.38 631 | aaaaafdg-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,15,4.38 632 | aaaaabij-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,5,4.38 633 | aaaaaeei-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,11,4.38 634 | aaaaadac-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,14,4.38 635 | aaaabdeh-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,12,4.38 636 | aaaabfae-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,19,4.38 637 | aaaaajjl-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,6,4.38 638 | aaaababf-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,16,4.38 639 | aaaabdmg-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,2,4.38 640 | aaaaagbf-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,12,4.38 641 | aaaaafef-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,5,4.38 642 | aaaabbek-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,1,4.38 643 | aaaabdlf-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,1,4.38 644 | aaaaakgi-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,8,4.38 645 | aaaaamhm-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,15,4.38 646 | aaaaadhg-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,8,4.38 647 | aaaaaalc-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,6,4.38 648 | aaaaagim-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,4,4.38 649 | aaaaadgd-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,11,4.38 650 | aaaaadgl-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,8,4.38 651 | aaaaalmh-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,0,4.38 652 | aaaabhpc-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,4,4.38 653 | aaaaamkb-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,0,4.38 654 | aaaaacgh-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,2,4.38 655 | aaaaaobp-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,4,4.38 656 | aaaaapck-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,7,4.38 657 | aaaabanl-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,1,4.38 658 | aaaaappf-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,5,4.38 659 | aaaaapac-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,8,4.38 660 | aaaabcba-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,17,4.38 661 | aaaaabpi-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,5,4.38 662 | aaaabfpl-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,4,4.38 663 | aaaabhcl-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,8,4.38 664 | aaaaabii-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,28,4.38 665 | aaaaahbn-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,8,4.38 666 | aaaaancl-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,22,4.38 667 | aaaabimp-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,11,4.38 668 | aaaaakam-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,17,4.38 669 | aaaaaimb-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,12,4.38 670 | aaaaadjd-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,6,4.38 671 | aaaabboo-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,1,4.38 672 | aaaabhlc-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,13,4.38 673 | aaaaakgn-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,14,4.38 674 | aaaaafnj-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,7,4.38 675 | aaaaaogg-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,20,4.38 676 | aaaaamdh-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,0,4.38 677 | aaaabfhf-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,5,4.38 678 | aaaaamlg-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,4,4.38 679 | aaaabeli-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,5,4.38 680 | aaaaajaa-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,4,4.38 681 | aaaabhln-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,2,4.38 682 | aaaaancb-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,1,4.38 683 | aaaaabcn-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,2,4.38 684 | aaaaaabi-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,8,4.38 685 | aaaaaemp-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,7,4.38 686 | aaaaanoo-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,8,4.38 687 | aaaabend-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,22,4.38 688 | aaaaadlk-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,30,4.38 689 | aaaabani-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,5,4.38 690 | aaaabiod-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,38,4.38 691 | aaaaameh-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,18,4.38 692 | aaaaacpo-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,29,4.38 693 | aaaaanib-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,27,4.38 694 | aaaabfch-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,32,4.38 695 | aaaaagjb-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,36,4.38 696 | aaaabiea-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,25,4.38 697 | aaaaacka-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,43,4.38 698 | aaaabbfn-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,40,4.38 699 | aaaaafca-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,17,4.38 700 | aaaaagej-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,18,4.38 701 | aaaabejh-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,59,4.38 702 | aaaabifl-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,30,4.38 703 | aaaaacdd-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,11,4.38 704 | aaaabgbb-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,27,4.38 705 | aaaaampj-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,14,4.38 706 | aaaabjfl-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,9,4.38 707 | aaaaabeg-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,8,4.38 708 | aaaabiic-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,18,4.38 709 | aaaabanj-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,35,4.38 710 | aaaabaam-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,25,4.38 711 | aaaabihk-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,25,4.38 712 | aaaaacfp-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,45,4.38 713 | aaaaaanp-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,23,4.38 714 | aaaabapa-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,7,4.38 715 | aaaabcdh-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,25,4.38 716 | aaaaaihg-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,19,4.38 717 | aaaaahoj-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,23,4.38 718 | aaaabfgj-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,35,4.38 719 | aaaaammf-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,14,4.38 720 | aaaabagc-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,55,4.38 721 | aaaabech-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,23,4.38 722 | aaaabbai-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,64,4.38 723 | aaaaajef-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,69,4.38 724 | aaaaammc-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,36,4.38 725 | aaaabcha-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,57,4.38 726 | aaaaafhj-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,65,4.38 727 | aaaaajnp-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,16,4.38 728 | aaaaanhf-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,14,4.38 729 | aaaaadkj-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,20,4.38 730 | aaaabgck-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,5,4.38 731 | aaaaalba-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,3,4.38 732 | aaaaaigc-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,14,4.38 733 | aaaaambf-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,7,4.38 734 | aaaabhdd-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,10,4.38 735 | aaaaaodi-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,7,4.38 736 | aaaaancm-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,10,4.38 737 | aaaabdon-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,3,4.38 738 | aaaaapeb-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,16,4.38 739 | aaaaachl-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,50,4.38 740 | aaaabgln-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,24,4.38 741 | aaaaappi-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,18,4.38 742 | aaaabdfi-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,9,4.38 743 | aaaaaoia-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,66,4.38 744 | aaaaamnf-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,24,4.38 745 | aaaaacag-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,44,4.38 746 | aaaabcfm-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,6,4.38 747 | aaaabijf-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,22,4.38 748 | aaaaalpo-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,16,4.38 749 | aaaabecb-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,16,4.38 750 | aaaabigi-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,18,4.38 751 | aaaaaofh-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,11,4.38 752 | aaaabfdm-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,3,4.38 753 | aaaaabkf-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,2,4.38 754 | aaaaalna-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,9,4.38 755 | aaaabfpk-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,6,4.38 756 | aaaaalce-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,7,4.38 757 | aaaabbnb-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,2,4.38 758 | aaaaakai-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,6,4.38 759 | aaaaaefm-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,3,4.38 760 | aaaabakd-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,13,4.38 761 | aaaaagmo-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,18,4.38 762 | aaaaaenn-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,16,4.38 763 | aaaaamnk-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,6,4.38 764 | aaaaaaha-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,31,4.38 765 | aaaaaolp-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,64,4.38 766 | aaaabhdp-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,15,4.38 767 | aaaabhlg-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,26,4.38 768 | aaaabhil-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,21,4.38 769 | aaaaagpf-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,28,4.38 770 | aaaabhda-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,29,4.38 771 | aaaabigf-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,24,4.38 772 | aaaaalbm-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,55,4.38 773 | aaaabblc-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,32,4.38 774 | aaaabbgf-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,63,4.38 775 | aaaaaibj-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,26,4.38 776 | aaaabfmd-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,56,4.38 777 | aaaabfng-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,4,4.38 778 | aaaabggp-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,1,4.38 779 | aaaaafcn-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,2,4.38 780 | aaaaaiea-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,1,4.38 781 | aaaabdaf-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,3,4.38 782 | aaaaadpk-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,12,4.38 783 | aaaabgao-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,12,4.38 784 | aaaaabml-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,19,4.38 785 | aaaaabeb-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,47,4.38 786 | aaaaabgi-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,28,4.38 787 | aaaaapmf-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,22,4.38 788 | aaaaaenk-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,2,4.38 789 | aaaaakgb-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,3,4.38 790 | aaaaacal-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,3,4.38 791 | aaaaahhc-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,2,4.38 792 | aaaabgbp-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,21,4.38 793 | aaaaanhl-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,7,4.38 794 | aaaabhmk-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,2,4.38 795 | aaaaalgp-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,7,4.38 796 | aaaaacco-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,1,4.38 797 | aaaaaopn-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,2,4.38 798 | aaaabefj-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,6,4.38 799 | aaaabfhl-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,5,4.38 800 | aaaabbbb-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,10,4.38 801 | aaaabadp-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,3,4.38 802 | aaaaahin-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,12,4.38 803 | aaaaaako-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,12,4.38 804 | aaaaalhd-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,12,4.38 805 | aaaabdpd-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,5,4.38 806 | aaaaaajc-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,11,4.38 807 | aaaaapkk-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,2,4.38 808 | aaaaaplh-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,5,4.38 809 | aaaaagbk-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,4,4.38 810 | aaaabjck-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,18,4.38 811 | aaaaaedg-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,8,4.38 812 | aaaaaabh-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,11,4.38 813 | aaaabfeb-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,36,4.38 814 | aaaaalla-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,5,4.38 815 | aaaaakob-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,8,4.38 816 | aaaaangn-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,31,4.38 817 | aaaaamnn-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,21,4.38 818 | aaaabbjm-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,7,4.38 819 | aaaabeio-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,13,4.38 820 | aaaaaoon-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,9,4.38 821 | aaaabbca-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,13,4.38 822 | aaaaaeam-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,16,4.38 823 | aaaaaaka-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,9,4.38 824 | aaaaadin-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,7,4.38 825 | aaaaahgg-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,8,4.38 826 | aaaaaghl-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,28,4.38 827 | aaaaaiga-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,3,4.38 828 | aaaaaeim-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,19,4.38 829 | aaaaaioh-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,14,4.38 830 | aaaaadnp-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,12,4.38 831 | aaaabdmh-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,5,4.38 832 | aaaababe-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,10,4.38 833 | aaaaagag-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,9,4.38 834 | aaaaajgd-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,1,4.38 835 | aaaabgan-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,9,4.38 836 | aaaabeld-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,6,4.38 837 | aaaabeml-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,2,4.38 838 | aaaaabcj-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,2,4.38 839 | aaaabfgd-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,2,4.38 840 | aaaabcgj-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,24,4.38 841 | aaaabddf-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,21,4.38 842 | aaaaabjd-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,3,4.38 843 | aaaabahf-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,5,4.38 844 | aaaaakfj-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,1,4.38 845 | aaaaajcb-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,4,4.38 846 | aaaabdhm-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,11,4.38 847 | aaaaahie-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,5,4.38 848 | aaaabcej-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,3,4.38 849 | aaaaalfi-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,4,4.38 850 | aaaaabgp-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,12,4.38 851 | aaaaabgn-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,6,4.38 852 | aaaabijj-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,2,4.38 853 | aaaabjgk-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,2,4.38 854 | aaaabagb-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,5,4.38 855 | aaaaanka-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,9,4.38 856 | aaaaadnj-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,1,4.38 857 | aaaabaee-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,7,4.38 858 | aaaabcjm-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,1,4.38 859 | aaaaallk-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,9,4.38 860 | aaaaamif-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,4,4.38 861 | aaaaadpg-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,11,4.38 862 | aaaaanga-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,4,4.38 863 | aaaabcoe-1,CytAssist_FFPE_Human_Colon_Post_Xenium_Rep1,2,4.38 -------------------------------------------------------------------------------- /docs/10x_tutorials/xenium/Selection_1_coordinates.csv: -------------------------------------------------------------------------------- 1 | #Selection name: Selection 1 2 | #Area (µm^2): 33463.47 3 | X,Y 4 | 5484.56,1806.01 5 | 5503.51,1814.05 6 | 5504.77,1815.45 7 | 5511.13,1837.85 8 | 5508.32,1856.93 9 | 5500.61,1876.65 10 | 5493.87,1908.45 11 | 5490.33,1935.50 12 | 5502.51,1953.31 13 | 5525.40,1956.38 14 | 5544.36,1946.77 15 | 5555.82,1926.69 16 | 5575.61,1901.61 17 | 5599.79,1901.25 18 | 5606.21,1877.16 19 | 5623.94,1851.86 20 | 5648.84,1854.79 21 | 5649.98,1879.40 22 | 5636.98,1901.46 23 | 5648.32,1920.33 24 | 5643.74,1940.24 25 | 5637.76,1968.22 26 | 5644.78,1990.46 27 | 5639.75,2009.65 28 | 5618.62,2019.63 29 | 5600.85,2014.89 30 | 5592.43,1992.11 31 | 5589.38,1972.26 32 | 5568.01,1974.42 33 | 5549.57,1992.31 34 | 5535.09,2008.55 35 | 5515.19,2019.67 36 | 5474.70,2037.29 37 | 5441.98,2043.15 38 | 5421.09,2054.26 39 | 5400.25,2082.86 40 | 5387.58,2064.83 41 | 5385.95,2035.72 42 | 5379.44,2013.01 43 | 5379.44,2011.65 44 | 5379.34,1968.27 45 | 5385.26,1937.68 46 | 5409.23,1933.91 47 | 5409.99,1934.08 48 | 5425.50,1921.20 49 | 5436.95,1899.20 50 | 5442.08,1881.39 51 | 5449.91,1860.08 52 | 5462.62,1838.98 53 | 5484.56,1806.01 -------------------------------------------------------------------------------- /docs/10x_tutorials/xenium/Xeniumranger_V1_hSkin_Melanoma_Add_on_FFPE_he_imagealignment.csv: -------------------------------------------------------------------------------- 1 | 0.0018369815247865553,0.6440103822350116,1154.5213262768684 2 | -0.6440103822350116,0.0018369815247865553,13229.632649884079 3 | 0,0,1 -------------------------------------------------------------------------------- /docs/10x_tutorials/xenium/alignment_matrix.csv: -------------------------------------------------------------------------------- 1 | 1.836981524786555258e-03,6.440103822350116136e-01,1.154521326276868422e+03 2 | -6.440103822350116136e-01,1.836981524786555258e-03,1.322963264988407900e+04 3 | 0.000000000000000000e+00,0.000000000000000000e+00,1.000000000000000000e+00 4 | -------------------------------------------------------------------------------- /docs/10x_tutorials/xenium/assets/alignment_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quentinblampey/spatialdata_xenium_explorer/e62aa7cac1aee9be081eb2fca73efb228bfb3f06/docs/10x_tutorials/xenium/assets/alignment_1.png -------------------------------------------------------------------------------- /docs/10x_tutorials/xenium/assets/alignment_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quentinblampey/spatialdata_xenium_explorer/e62aa7cac1aee9be081eb2fca73efb228bfb3f06/docs/10x_tutorials/xenium/assets/alignment_2.png -------------------------------------------------------------------------------- /docs/10x_tutorials/xenium/assets/alignment_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quentinblampey/spatialdata_xenium_explorer/e62aa7cac1aee9be081eb2fca73efb228bfb3f06/docs/10x_tutorials/xenium/assets/alignment_3.png -------------------------------------------------------------------------------- /docs/10x_tutorials/xenium/assets/alignment_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quentinblampey/spatialdata_xenium_explorer/e62aa7cac1aee9be081eb2fca73efb228bfb3f06/docs/10x_tutorials/xenium/assets/alignment_4.png -------------------------------------------------------------------------------- /docs/10x_tutorials/xenium/assets/alignment_5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quentinblampey/spatialdata_xenium_explorer/e62aa7cac1aee9be081eb2fca73efb228bfb3f06/docs/10x_tutorials/xenium/assets/alignment_5.png -------------------------------------------------------------------------------- /docs/10x_tutorials/xenium/assets/from_spatialdata.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quentinblampey/spatialdata_xenium_explorer/e62aa7cac1aee9be081eb2fca73efb228bfb3f06/docs/10x_tutorials/xenium/assets/from_spatialdata.png -------------------------------------------------------------------------------- /docs/10x_tutorials/xenium/assets/lasso.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quentinblampey/spatialdata_xenium_explorer/e62aa7cac1aee9be081eb2fca73efb228bfb3f06/docs/10x_tutorials/xenium/assets/lasso.png -------------------------------------------------------------------------------- /docs/10x_tutorials/xenium/assets/lasso2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quentinblampey/spatialdata_xenium_explorer/e62aa7cac1aee9be081eb2fca73efb228bfb3f06/docs/10x_tutorials/xenium/assets/lasso2.png -------------------------------------------------------------------------------- /docs/10x_tutorials/xenium/assets/leiden.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quentinblampey/spatialdata_xenium_explorer/e62aa7cac1aee9be081eb2fca73efb228bfb3f06/docs/10x_tutorials/xenium/assets/leiden.png -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # `spatialdata_xenium_explorer` documentation 2 | 3 | The documentation is generated by [Mkdocs material](https://squidfunk.github.io/mkdocs-material/) and deployed when pushing a new tag on Github. 4 | 5 | ## Serve locally 6 | 7 | Clone the repository, and install all the dependencies: 8 | 9 | ``` 10 | poetry install 11 | ``` 12 | 13 | Then, run: 14 | ```sh 15 | mkdocs serve 16 | ``` 17 | 18 | You can also update the CLI docs (if you install `typer-cli`) with the following command line: 19 | 20 | ```sh 21 | typer spatialdata_xenium_explorer.main utils docs --output tmpfile --name spatialdata_xenium_explorer && sed '1,2d; s/## /### /g; s/Usage:/!!! note '\"'Usage'\"'/g' tmpfile >> docs/cli.md && rm tmpfile 22 | ``` -------------------------------------------------------------------------------- /docs/api.md: -------------------------------------------------------------------------------- 1 | ::: spatialdata_xenium_explorer.write 2 | options: 3 | show_root_heading: true 4 | 5 | ::: spatialdata_xenium_explorer.write_image 6 | options: 7 | show_root_heading: true 8 | 9 | ::: spatialdata_xenium_explorer.write_cell_categories 10 | options: 11 | show_root_heading: true 12 | 13 | ::: spatialdata_xenium_explorer.write_transcripts 14 | options: 15 | show_root_heading: true 16 | 17 | ::: spatialdata_xenium_explorer.write_gene_counts 18 | options: 19 | show_root_heading: true 20 | 21 | ::: spatialdata_xenium_explorer.write_polygons 22 | options: 23 | show_root_heading: true 24 | 25 | ::: spatialdata_xenium_explorer.write_metadata 26 | options: 27 | show_root_heading: true 28 | 29 | ::: spatialdata_xenium_explorer.int_cell_id 30 | options: 31 | show_root_heading: true 32 | 33 | ::: spatialdata_xenium_explorer.str_cell_id 34 | options: 35 | show_root_heading: true 36 | 37 | ::: spatialdata_xenium_explorer.align 38 | options: 39 | show_root_heading: true 40 | 41 | ::: spatialdata_xenium_explorer.save_column_csv 42 | options: 43 | show_root_heading: true -------------------------------------------------------------------------------- /docs/cite_us.md: -------------------------------------------------------------------------------- 1 | This library has been detailed in a more general article spatial omics analysis, see the [Sopa library](https://github.com/gustaveroussy/sopa). 2 | The latter article is not published yet, but you can cite our [preprint](https://www.biorxiv.org/content/10.1101/2023.12.22.571863v1): 3 | 4 | ```txt 5 | @article {Blampey2023.12.22.571863, 6 | author = {Quentin Blampey & Kevin Mulder et al.}, 7 | title = {Sopa: a technology-invariant pipeline for analyses of image-based spatial-omics}, 8 | elocation-id = {2023.12.22.571863}, 9 | year = {2023}, 10 | doi = {10.1101/2023.12.22.571863}, 11 | publisher = {Cold Spring Harbor Laboratory}, 12 | URL = {https://www.biorxiv.org/content/early/2023/12/23/2023.12.22.571863}, 13 | eprint = {https://www.biorxiv.org/content/early/2023/12/23/2023.12.22.571863.full.pdf}, 14 | journal = {bioRxiv} 15 | } 16 | ``` 17 | 18 | This library has been developed by Quentin Blampey, PhD student in Biomathematics / Deep Learning. The following institutions funded this work: 19 | 20 | - Lab of Mathematics and Computer Science (MICS), **CentraleSupélec** (Engineering School, Paris-Saclay University). 21 | - PRISM center, **Gustave Roussy Institute** (Cancer campus, Paris-Saclay University). -------------------------------------------------------------------------------- /docs/cli.md: -------------------------------------------------------------------------------- 1 | # CLI (command-line-interface) 2 | 3 | ## Usage 4 | 5 | When installing `spatialdata_xenium_explorer` are written in our [getting-started guidelines](../getting_started), a new command named `spatialdata_xenium_explorer` becomes available. 6 | 7 | !!! note "CLI helper" 8 | Run `spatialdata_xenium_explorer --help` to get details about all the command line purpose. You can also use this helper on any subcommand, for instance `spatialdata_xenium_explorer write --help`. 9 | 10 |
11 | ```console 12 | // Run the spatialdata_xenium_explorer CLI helper 13 | $ spatialdata_xenium_explorer --help 14 | Usage: spatialdata_xenium_explorer [OPTIONS] COMMAND [ARGS]... 15 | ╭─ Commands ──────────────────────────────────────────────────╮ 16 | │ write Convertion: spatialdata to the Xenium Explorer │ 17 | │ add-aligned Add image after alignment on the explorer │ 18 | │ update-obs Update the cell categories for the explorer │ 19 | ╰─────────────────────────────────────────────────────────────╯ 20 | // Example 21 | $ spatialdata_xenium_explorer write /path/to/sdata.zarr 22 | ... [Logs] ... 23 | ``` 24 |
25 | 26 | ## Commands 27 | 28 | **Usage**: 29 | 30 | ```console 31 | $ spatialdata_xenium_explorer [OPTIONS] COMMAND [ARGS]... 32 | ``` 33 | 34 | **Options**: 35 | 36 | * `--help`: Show this message and exit. 37 | 38 | **Commands**: 39 | 40 | * `add-aligned`: After alignment on the Xenium Explorer,... 41 | * `update-obs`: Update the cell categories for the Xenium... 42 | * `write`: Convert a spatialdata object to Xenium... 43 | 44 | ### `spatialdata_xenium_explorer add-aligned` 45 | 46 | After alignment on the Xenium Explorer, add an image to the SpatialData object 47 | 48 | **Usage**: 49 | 50 | ```console 51 | $ spatialdata_xenium_explorer add-aligned [OPTIONS] SDATA_PATH IMAGE_PATH TRANSFORMATION_MATRIX_PATH 52 | ``` 53 | 54 | **Arguments**: 55 | 56 | * `SDATA_PATH`: Path to the SpatialData `.zarr` directory [required] 57 | * `IMAGE_PATH`: Path to the image file to be added (`.ome.tif` used in the explorer during alignment) [required] 58 | * `TRANSFORMATION_MATRIX_PATH`: Path to the `matrix.csv` file returned by the Explorer after alignment [required] 59 | 60 | **Options**: 61 | 62 | * `--original-image-key TEXT`: Optional original-image key (of sdata.images) on which the new image will be aligned. This doesn't need to be provided if there is only one image 63 | * `--overwrite / --no-overwrite`: Whether to overwrite the image if existing [default: no-overwrite] 64 | * `--help`: Show this message and exit. 65 | 66 | ### `spatialdata_xenium_explorer update-obs` 67 | 68 | Update the cell categories for the Xenium Explorer's (i.e. what's in `adata.obs`). This is useful when you perform analysis and update your `AnnData` object 69 | 70 | !!! note "Usage" 71 | This command should only be used if you updated `adata.obs`, after creation of the other explorer files. 72 | 73 | **Usage**: 74 | 75 | ```console 76 | $ spatialdata_xenium_explorer update-obs [OPTIONS] ADATA_PATH OUTPUT_PATH 77 | ``` 78 | 79 | **Arguments**: 80 | 81 | * `ADATA_PATH`: Path to the anndata file (`zarr` or `h5ad`) containing the new observations [required] 82 | * `OUTPUT_PATH`: Path to the Xenium Explorer directory (it will update `analysis.zarr.zip`) [required] 83 | 84 | **Options**: 85 | 86 | * `--help`: Show this message and exit. 87 | 88 | ### `spatialdata_xenium_explorer write` 89 | 90 | Convert a spatialdata object to Xenium Explorer's inputs 91 | 92 | **Usage**: 93 | 94 | ```console 95 | $ spatialdata_xenium_explorer write [OPTIONS] SDATA_PATH 96 | ``` 97 | 98 | **Arguments**: 99 | 100 | * `SDATA_PATH`: Path to the SpatialData `.zarr` directory [required] 101 | 102 | **Options**: 103 | 104 | * `--output-path TEXT`: Path to a directory where Xenium Explorer's outputs will be saved. By default, writes to the same path as `sdata_path` but with the `.explorer` suffix 105 | * `--image-key TEXT`: Name of the image of interest (key of `sdata.images`). This argument doesn't need to be provided if there is only one image. 106 | * `--shapes-key TEXT`: Name of the cell shapes (key of `sdata.shapes`). This argument doesn't need to be provided if there is only one shapes key or a table with only one region. 107 | * `--points-key TEXT`: Name of the transcripts (key of `sdata.points`). This argument doesn't need to be provided if there is only one points key. 108 | * `--gene-column TEXT`: Column name of the points dataframe containing the gene names 109 | * `--pixel_size FLOAT`: Number of microns in a pixel. Invalid value can lead to inconsistent scales in the Explorer. [default: 0.2125] 110 | * `--spot / --no-spot`: Whether the technology is based on spots [default: no-spot] 111 | * `--layer TEXT`: Layer of `sdata.table` where the gene counts are saved. If `None`, uses `sdata.table.X`. 112 | * `--lazy / --no-lazy`: If `True`, will not load the full images in memory (except if the image memory is below `ram_threshold_gb`) [default: lazy] 113 | * `--ram-threshold-gb INTEGER`: Threshold (in gygabytes) from which image can be loaded in memory. If `None`, the image is never loaded in memory [default: 4] 114 | * `--mode TEXT`: string that indicated which files should be created. `'-ib'` means everything except images and boundaries, while `'+tocm'` means only transcripts/observations/counts/metadata (each letter corresponds to one explorer file). By default, keeps everything 115 | * `--help`: Show this message and exit. 116 | -------------------------------------------------------------------------------- /docs/css/custom.css: -------------------------------------------------------------------------------- 1 | .termynal-comment { 2 | color: #4a968f; 3 | font-style: italic; 4 | display: block; 5 | } 6 | 7 | .termy [data-termynal] { 8 | white-space: pre-wrap; 9 | } -------------------------------------------------------------------------------- /docs/css/termynal.css: -------------------------------------------------------------------------------- 1 | /** 2 | * termynal.js 3 | * 4 | * @author Ines Montani 5 | * @version 0.0.1 6 | * @license MIT 7 | */ 8 | 9 | :root { 10 | --color-bg: #252a33; 11 | --color-text: #eee; 12 | --color-text-subtle: #a2a2a2; 13 | } 14 | 15 | [data-termynal] { 16 | width: 750px; 17 | max-width: 100%; 18 | background: var(--color-bg); 19 | color: var(--color-text); 20 | /* font-size: 18px; */ 21 | font-size: 15px; 22 | /* font-family: 'Fira Mono', Consolas, Menlo, Monaco, 'Courier New', Courier, monospace; */ 23 | font-family: 'Roboto Mono', 'Fira Mono', Consolas, Menlo, Monaco, 'Courier New', Courier, monospace; 24 | border-radius: 4px; 25 | padding: 75px 45px 35px; 26 | position: relative; 27 | -webkit-box-sizing: border-box; 28 | box-sizing: border-box; 29 | line-height: 1.2; 30 | } 31 | 32 | [data-termynal]:before { 33 | content: ''; 34 | position: absolute; 35 | top: 15px; 36 | left: 15px; 37 | display: inline-block; 38 | width: 15px; 39 | height: 15px; 40 | border-radius: 50%; 41 | /* A little hack to display the window buttons in one pseudo element. */ 42 | background: #d9515d; 43 | -webkit-box-shadow: 25px 0 0 #f4c025, 50px 0 0 #3ec930; 44 | box-shadow: 25px 0 0 #f4c025, 50px 0 0 #3ec930; 45 | } 46 | 47 | [data-termynal]:after { 48 | content: 'bash'; 49 | position: absolute; 50 | color: var(--color-text-subtle); 51 | top: 5px; 52 | left: 0; 53 | width: 100%; 54 | text-align: center; 55 | } 56 | 57 | a[data-terminal-control] { 58 | text-align: right; 59 | display: block; 60 | color: #aebbff; 61 | } 62 | 63 | [data-ty] { 64 | display: block; 65 | line-height: 2; 66 | } 67 | 68 | [data-ty]:before { 69 | /* Set up defaults and ensure empty lines are displayed. */ 70 | content: ''; 71 | display: inline-block; 72 | vertical-align: middle; 73 | } 74 | 75 | [data-ty="input"]:before, 76 | [data-ty-prompt]:before { 77 | margin-right: 0.75em; 78 | color: var(--color-text-subtle); 79 | } 80 | 81 | [data-ty="input"]:before { 82 | content: '$'; 83 | } 84 | 85 | [data-ty][data-ty-prompt]:before { 86 | content: attr(data-ty-prompt); 87 | } 88 | 89 | [data-ty-cursor]:after { 90 | content: attr(data-ty-cursor); 91 | font-family: monospace; 92 | margin-left: 0.5em; 93 | -webkit-animation: blink 1s infinite; 94 | animation: blink 1s infinite; 95 | } 96 | 97 | 98 | /* Cursor animation */ 99 | 100 | @-webkit-keyframes blink { 101 | 50% { 102 | opacity: 0; 103 | } 104 | } 105 | 106 | @keyframes blink { 107 | 50% { 108 | opacity: 0; 109 | } 110 | } -------------------------------------------------------------------------------- /docs/getting_started.md: -------------------------------------------------------------------------------- 1 | ## Installation 2 | 3 | `spatialdata_xenium_explorer` can be installed on every OS with `pip` or [`poetry`](https://python-poetry.org/docs/) on `python>=3.9`. 4 | 5 | !!! note "Advice (optional)" 6 | 7 | We advise creating a new environment via a package manager (except if you use Poetry, which will automatically create the environment). 8 | 9 | For instance, you can create a new `conda` environment: 10 | 11 | ```bash 12 | conda create --name spatialdata_xenium_explorer python=3.10 13 | conda activate spatialdata_xenium_explorer 14 | ``` 15 | 16 | Choose one of the following, depending on your needs (it should take at most a few minutes): 17 | 18 | === "From PyPI" 19 | 20 | ``` bash 21 | pip install spatialdata_xenium_explorer 22 | ``` 23 | 24 | === "Pip in dev mode" 25 | 26 | ``` bash 27 | git clone https://github.com/quentinblampey/spatialdata_xenium_explorer.git 28 | cd spatialdata_xenium_explorer 29 | 30 | pip install -e . 31 | ``` 32 | 33 | === "Poetry in dev mode" 34 | 35 | ``` bash 36 | git clone https://github.com/quentinblampey/spatialdata_xenium_explorer.git 37 | cd spatialdata_xenium_explorer 38 | 39 | poetry install 40 | ``` 41 | 42 | ## Usage 43 | 44 | You can choose between these two options: 45 | 46 | - Our [command-line-interface](../cli) (CLI) 47 | - Our [python API](../api) -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | # SpatialData to Xenium Explorer 2 | 3 | Converting any [`SpatialData`](https://github.com/scverse/spatialdata) object into files that can be open by the [Xenium Explorer](https://www.10xgenomics.com/support/software/xenium-explorer). 4 | 5 | > *Xenium Explorer* is a registered trademark of 10x Genomics 6 | 7 | You may also be interested in a pipeline for spatial-omics that uses this conversion to Xenium Explorer: see [Sopa](https://github.com/gustaveroussy/sopa). 8 | 9 | ## Features 10 | - Conversion of the following data: images, cell boundaries (polygons), transcripts, cell-by-gene table, and cell categories (or observations). 11 | - Image alignment can be made on the Xenium Explorer, and then the `SpatialData` object can be updated 12 | - When working on the `SpatialData` or `AnnData` object, new cell categories can be easily and quickly added to the Explorer 13 | - When selecting cells with the "lasso tool" on the Explorer, it's easy to select back these cells on the `SpatialData` or `AnnData` object 14 | -------------------------------------------------------------------------------- /docs/js/custom.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll(".use-termynal").forEach(node => { 2 | node.style.display = "block"; 3 | new Termynal(node, { 4 | lineDelay: 500 5 | }); 6 | }); 7 | const progressLiteralStart = "---> 100%"; 8 | const promptLiteralStart = "$ "; 9 | const customPromptLiteralStart = "# "; 10 | const termynalActivateClass = "termy"; 11 | let termynals = []; 12 | 13 | function createTermynals() { 14 | document 15 | .querySelectorAll(`.${termynalActivateClass} .highlight`) 16 | .forEach(node => { 17 | const text = node.textContent; 18 | const lines = text.split("\n"); 19 | const useLines = []; 20 | let buffer = []; 21 | function saveBuffer() { 22 | if (buffer.length) { 23 | let isBlankSpace = true; 24 | buffer.forEach(line => { 25 | if (line) { 26 | isBlankSpace = false; 27 | } 28 | }); 29 | dataValue = {}; 30 | if (isBlankSpace) { 31 | dataValue["delay"] = 0; 32 | } 33 | if (buffer[buffer.length - 1] === "") { 34 | // A last single
won't have effect 35 | // so put an additional one 36 | buffer.push(""); 37 | } 38 | const bufferValue = buffer.join("
"); 39 | dataValue["value"] = bufferValue; 40 | useLines.push(dataValue); 41 | buffer = []; 42 | } 43 | } 44 | for (let line of lines) { 45 | if (line === progressLiteralStart) { 46 | saveBuffer(); 47 | useLines.push({ 48 | type: "progress" 49 | }); 50 | } else if (line.startsWith(promptLiteralStart)) { 51 | saveBuffer(); 52 | const value = line.replace(promptLiteralStart, "").trimEnd(); 53 | useLines.push({ 54 | type: "input", 55 | value: value 56 | }); 57 | } else if (line.startsWith("// ")) { 58 | saveBuffer(); 59 | const value = line.replace("// ", "").trimEnd(); 60 | useLines.push({ 61 | value: value, 62 | class: "termynal-comment", 63 | delay: 0 64 | }); 65 | } else if (line.startsWith(customPromptLiteralStart)) { 66 | saveBuffer(); 67 | const promptStart = line.indexOf(promptLiteralStart); 68 | if (promptStart === -1) { 69 | console.error("Custom prompt found but no end delimiter", line) 70 | } 71 | const prompt = line.slice(0, promptStart).replace(customPromptLiteralStart, "") 72 | let value = line.slice(promptStart + promptLiteralStart.length); 73 | useLines.push({ 74 | type: "input", 75 | value: value, 76 | prompt: prompt 77 | }); 78 | } else { 79 | buffer.push(line); 80 | } 81 | } 82 | saveBuffer(); 83 | const div = document.createElement("div"); 84 | node.replaceWith(div); 85 | const termynal = new Termynal(div, { 86 | lineData: useLines, 87 | noInit: true, 88 | lineDelay: 500 89 | }); 90 | termynals.push(termynal); 91 | }); 92 | } 93 | 94 | function loadVisibleTermynals() { 95 | termynals = termynals.filter(termynal => { 96 | if (termynal.container.getBoundingClientRect().top - innerHeight <= 0) { 97 | termynal.init(); 98 | return false; 99 | } 100 | return true; 101 | }); 102 | } 103 | window.addEventListener("scroll", loadVisibleTermynals); 104 | createTermynals(); 105 | loadVisibleTermynals(); -------------------------------------------------------------------------------- /docs/js/termynal.js: -------------------------------------------------------------------------------- 1 | /** 2 | * termynal.js 3 | * A lightweight, modern and extensible animated terminal window, using 4 | * async/await. 5 | * 6 | * @author Ines Montani 7 | * @version 0.0.1 8 | * @license MIT 9 | */ 10 | 11 | 'use strict'; 12 | 13 | /** Generate a terminal widget. */ 14 | class Termynal { 15 | /** 16 | * Construct the widget's settings. 17 | * @param {(string|Node)=} container - Query selector or container element. 18 | * @param {Object=} options - Custom settings. 19 | * @param {string} options.prefix - Prefix to use for data attributes. 20 | * @param {number} options.startDelay - Delay before animation, in ms. 21 | * @param {number} options.typeDelay - Delay between each typed character, in ms. 22 | * @param {number} options.lineDelay - Delay between each line, in ms. 23 | * @param {number} options.progressLength - Number of characters displayed as progress bar. 24 | * @param {string} options.progressChar – Character to use for progress bar, defaults to █. 25 | * @param {number} options.progressPercent - Max percent of progress. 26 | * @param {string} options.cursor – Character to use for cursor, defaults to ▋. 27 | * @param {Object[]} lineData - Dynamically loaded line data objects. 28 | * @param {boolean} options.noInit - Don't initialise the animation. 29 | */ 30 | constructor(container = '#termynal', options = {}) { 31 | this.container = (typeof container === 'string') ? document.querySelector(container) : container; 32 | this.pfx = `data-${options.prefix || 'ty'}`; 33 | this.originalStartDelay = this.startDelay = options.startDelay 34 | || parseFloat(this.container.getAttribute(`${this.pfx}-startDelay`)) || 600; 35 | this.originalTypeDelay = this.typeDelay = options.typeDelay 36 | || parseFloat(this.container.getAttribute(`${this.pfx}-typeDelay`)) || 90; 37 | this.originalLineDelay = this.lineDelay = options.lineDelay 38 | || parseFloat(this.container.getAttribute(`${this.pfx}-lineDelay`)) || 1500; 39 | this.progressLength = options.progressLength 40 | || parseFloat(this.container.getAttribute(`${this.pfx}-progressLength`)) || 40; 41 | this.progressChar = options.progressChar 42 | || this.container.getAttribute(`${this.pfx}-progressChar`) || '█'; 43 | this.progressPercent = options.progressPercent 44 | || parseFloat(this.container.getAttribute(`${this.pfx}-progressPercent`)) || 100; 45 | this.cursor = options.cursor 46 | || this.container.getAttribute(`${this.pfx}-cursor`) || '▋'; 47 | this.lineData = this.lineDataToElements(options.lineData || []); 48 | this.loadLines() 49 | if (!options.noInit) this.init() 50 | } 51 | 52 | loadLines() { 53 | // Load all the lines and create the container so that the size is fixed 54 | // Otherwise it would be changing and the user viewport would be constantly 55 | // moving as she/he scrolls 56 | const finish = this.generateFinish() 57 | finish.style.visibility = 'hidden' 58 | this.container.appendChild(finish) 59 | // Appends dynamically loaded lines to existing line elements. 60 | this.lines = [...this.container.querySelectorAll(`[${this.pfx}]`)].concat(this.lineData); 61 | for (let line of this.lines) { 62 | line.style.visibility = 'hidden' 63 | this.container.appendChild(line) 64 | } 65 | const restart = this.generateRestart() 66 | restart.style.visibility = 'hidden' 67 | this.container.appendChild(restart) 68 | this.container.setAttribute('data-termynal', ''); 69 | } 70 | 71 | /** 72 | * Initialise the widget, get lines, clear container and start animation. 73 | */ 74 | init() { 75 | /** 76 | * Calculates width and height of Termynal container. 77 | * If container is empty and lines are dynamically loaded, defaults to browser `auto` or CSS. 78 | */ 79 | const containerStyle = getComputedStyle(this.container); 80 | this.container.style.width = containerStyle.width !== '0px' ? 81 | containerStyle.width : undefined; 82 | this.container.style.minHeight = containerStyle.height !== '0px' ? 83 | containerStyle.height : undefined; 84 | 85 | this.container.setAttribute('data-termynal', ''); 86 | this.container.innerHTML = ''; 87 | for (let line of this.lines) { 88 | line.style.visibility = 'visible' 89 | } 90 | this.start(); 91 | } 92 | 93 | /** 94 | * Start the animation and rener the lines depending on their data attributes. 95 | */ 96 | async start() { 97 | this.addFinish() 98 | await this._wait(this.startDelay); 99 | 100 | for (let line of this.lines) { 101 | const type = line.getAttribute(this.pfx); 102 | const delay = line.getAttribute(`${this.pfx}-delay`) || this.lineDelay; 103 | 104 | if (type == 'input') { 105 | line.setAttribute(`${this.pfx}-cursor`, this.cursor); 106 | await this.type(line); 107 | await this._wait(delay); 108 | } 109 | 110 | else if (type == 'progress') { 111 | await this.progress(line); 112 | await this._wait(delay); 113 | } 114 | 115 | else { 116 | this.container.appendChild(line); 117 | await this._wait(delay); 118 | } 119 | 120 | line.removeAttribute(`${this.pfx}-cursor`); 121 | } 122 | this.addRestart() 123 | this.finishElement.style.visibility = 'hidden' 124 | this.lineDelay = this.originalLineDelay 125 | this.typeDelay = this.originalTypeDelay 126 | this.startDelay = this.originalStartDelay 127 | } 128 | 129 | generateRestart() { 130 | const restart = document.createElement('a') 131 | restart.onclick = (e) => { 132 | e.preventDefault() 133 | this.container.innerHTML = '' 134 | this.init() 135 | } 136 | restart.href = '#' 137 | restart.setAttribute('data-terminal-control', '') 138 | restart.innerHTML = "restart ↻" 139 | return restart 140 | } 141 | 142 | generateFinish() { 143 | const finish = document.createElement('a') 144 | finish.onclick = (e) => { 145 | e.preventDefault() 146 | this.lineDelay = 0 147 | this.typeDelay = 0 148 | this.startDelay = 0 149 | } 150 | finish.href = '#' 151 | finish.setAttribute('data-terminal-control', '') 152 | finish.innerHTML = "fast →" 153 | this.finishElement = finish 154 | return finish 155 | } 156 | 157 | addRestart() { 158 | const restart = this.generateRestart() 159 | this.container.appendChild(restart) 160 | } 161 | 162 | addFinish() { 163 | const finish = this.generateFinish() 164 | this.container.appendChild(finish) 165 | } 166 | 167 | /** 168 | * Animate a typed line. 169 | * @param {Node} line - The line element to render. 170 | */ 171 | async type(line) { 172 | const chars = [...line.textContent]; 173 | line.textContent = ''; 174 | this.container.appendChild(line); 175 | 176 | for (let char of chars) { 177 | const delay = line.getAttribute(`${this.pfx}-typeDelay`) || this.typeDelay; 178 | await this._wait(delay); 179 | line.textContent += char; 180 | } 181 | } 182 | 183 | /** 184 | * Animate a progress bar. 185 | * @param {Node} line - The line element to render. 186 | */ 187 | async progress(line) { 188 | const progressLength = line.getAttribute(`${this.pfx}-progressLength`) 189 | || this.progressLength; 190 | const progressChar = line.getAttribute(`${this.pfx}-progressChar`) 191 | || this.progressChar; 192 | const chars = progressChar.repeat(progressLength); 193 | const progressPercent = line.getAttribute(`${this.pfx}-progressPercent`) 194 | || this.progressPercent; 195 | line.textContent = ''; 196 | this.container.appendChild(line); 197 | 198 | for (let i = 1; i < chars.length + 1; i++) { 199 | await this._wait(this.typeDelay); 200 | const percent = Math.round(i / chars.length * 100); 201 | line.textContent = `${chars.slice(0, i)} ${percent}%`; 202 | if (percent>progressPercent) { 203 | break; 204 | } 205 | } 206 | } 207 | 208 | /** 209 | * Helper function for animation delays, called with `await`. 210 | * @param {number} time - Timeout, in ms. 211 | */ 212 | _wait(time) { 213 | return new Promise(resolve => setTimeout(resolve, time)); 214 | } 215 | 216 | /** 217 | * Converts line data objects into line elements. 218 | * 219 | * @param {Object[]} lineData - Dynamically loaded lines. 220 | * @param {Object} line - Line data object. 221 | * @returns {Element[]} - Array of line elements. 222 | */ 223 | lineDataToElements(lineData) { 224 | return lineData.map(line => { 225 | let div = document.createElement('div'); 226 | div.innerHTML = `${line.value || ''}`; 227 | 228 | return div.firstElementChild; 229 | }); 230 | } 231 | 232 | /** 233 | * Helper function for generating attributes string. 234 | * 235 | * @param {Object} line - Line data object. 236 | * @returns {string} - String of attributes. 237 | */ 238 | _attributes(line) { 239 | let attrs = ''; 240 | for (let prop in line) { 241 | // Custom add class 242 | if (prop === 'class') { 243 | attrs += ` class=${line[prop]} ` 244 | continue 245 | } 246 | if (prop === 'type') { 247 | attrs += `${this.pfx}="${line[prop]}" ` 248 | } else if (prop !== 'value') { 249 | attrs += `${this.pfx}-${prop}="${line[prop]}" ` 250 | } 251 | } 252 | 253 | return attrs; 254 | } 255 | } 256 | 257 | /** 258 | * HTML API: If current script has container(s) specified, initialise Termynal. 259 | */ 260 | if (document.currentScript.hasAttribute('data-termynal-container')) { 261 | const containers = document.currentScript.getAttribute('data-termynal-container'); 262 | containers.split('|') 263 | .forEach(container => new Termynal(container)) 264 | } -------------------------------------------------------------------------------- /mkdocs.yml: -------------------------------------------------------------------------------- 1 | site_name: spatialdata_xenium_explorer 2 | repo_name: spatialdata_xenium_explorer 3 | repo_url: https://github.com/quentinblampey/spatialdata_xenium_explorer 4 | copyright: Copyright © 2023 - 2024 Quentin Blampey 5 | theme: 6 | name: material 7 | nav: 8 | - Home: index.md 9 | - Getting Started: getting_started 10 | - CLI: cli 11 | - API: api 12 | - Cite us: cite_us 13 | plugins: 14 | - search 15 | - mkdocstrings 16 | markdown_extensions: 17 | - admonition 18 | - attr_list 19 | - md_in_html 20 | - pymdownx.details 21 | - pymdownx.highlight: 22 | anchor_linenums: true 23 | - pymdownx.inlinehilite 24 | - pymdownx.snippets 25 | - pymdownx.superfences 26 | - pymdownx.arithmatex: 27 | generic: true 28 | - pymdownx.tabbed: 29 | alternate_style: true 30 | extra_css: 31 | - css/termynal.css 32 | - css/custom.css 33 | extra_javascript: 34 | - js/termynal.js 35 | - js/custom.js 36 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "spatialdata_xenium_explorer" 3 | version = "0.1.7" 4 | description = "Converting any SpatialData object into files that can be open by the Xenium Explorer" 5 | documentation = "https://quentinblampey.github.io/spatialdata_xenium_explorer/" 6 | homepage = "https://quentinblampey.github.io/spatialdata_xenium_explorer/" 7 | repository = "https://github.com/quentinblampey/spatialdata_xenium_explorer" 8 | authors = ["Quentin Blampey "] 9 | packages = [{ include = "spatialdata_xenium_explorer" }] 10 | license = "BSD-3-Clause" 11 | readme = "README.md" 12 | classifiers = [ 13 | "License :: OSI Approved :: BSD License", 14 | "Operating System :: MacOS :: MacOS X", 15 | "Operating System :: POSIX :: Linux", 16 | "Operating System :: Microsoft :: Windows", 17 | "Programming Language :: Python :: 3", 18 | "Topic :: Scientific/Engineering", 19 | ] 20 | 21 | [tool.poetry.scripts] 22 | spatialdata_xenium_explorer = "spatialdata_xenium_explorer.main:app" 23 | 24 | [tool.poetry.dependencies] 25 | python = ">=3.9,<3.11" 26 | botocore = "1.34.19" 27 | spatialdata = ">=0.1.2" 28 | typer = ">=0.9.0" 29 | 30 | [tool.poetry.group.dev.dependencies] 31 | black = ">=22.8.0" 32 | isort = ">=5.10.1" 33 | pytest = ">=7.1.3" 34 | ipykernel = ">=6.25.2" 35 | mkdocs-material = ">=9.3.2" 36 | mkdocstrings = ">=0.23.0" 37 | mkdocstrings-python = ">=1.7.3" 38 | jupyter-black = ">=0.3.4" 39 | 40 | [build-system] 41 | requires = ["poetry-core>=1.0.0"] 42 | build-backend = "poetry.core.masonry.api" 43 | 44 | [tool.pytest.ini_options] 45 | testpaths = ["tests"] 46 | python_files = "test_*.py" 47 | 48 | [tool.black] 49 | line-length = 100 50 | include = '\.pyi?$' 51 | exclude = ''' 52 | /( 53 | \.eggs # exclude a few common directories in the 54 | | \.git # root of the project 55 | | \.hg 56 | | \.mypy_cache 57 | | \.tox 58 | | \.venv 59 | | _build 60 | | buck-out 61 | | build 62 | | dist 63 | )/ 64 | ''' 65 | 66 | [tool.isort] 67 | profile = "black" 68 | skip_glob = ["*/__init__.py"] 69 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup 2 | 3 | if __name__ == "__main__": 4 | setup() 5 | -------------------------------------------------------------------------------- /spatialdata_xenium_explorer/__init__.py: -------------------------------------------------------------------------------- 1 | import importlib.metadata 2 | import logging 3 | 4 | __version__ = importlib.metadata.version("spatialdata_xenium_explorer") 5 | 6 | from .core.images import write_image, align 7 | from .core.points import write_transcripts 8 | from .core.table import write_cell_categories, write_gene_counts, save_column_csv 9 | from .core.shapes import write_polygons 10 | from .converter import write, write_metadata, update_metadata 11 | from .utils import str_cell_id, int_cell_id 12 | from ._logging import configure_logger 13 | 14 | log = logging.getLogger("spatialdata_xenium_explorer") 15 | configure_logger(log) 16 | -------------------------------------------------------------------------------- /spatialdata_xenium_explorer/_constants.py: -------------------------------------------------------------------------------- 1 | class FileNames: 2 | IMAGE = "morphology.ome.tif" 3 | POINTS = "transcripts.zarr.zip" 4 | SHAPES = "cells.zarr.zip" 5 | TABLE = "cell_feature_matrix.zarr.zip" 6 | CELL_CATEGORIES = "analysis.zarr.zip" 7 | METADATA = "experiment.xenium" 8 | 9 | 10 | class ExplorerConstants: 11 | GRID_SIZE = 250 12 | QUALITY_SCORE = 40 13 | MICRONS_TO_PIXELS = 4.705882 14 | PIXELS_TO_MICRONS = 0.2125 15 | 16 | COLORS = ["white", 400, 500, 600, 700] 17 | NUCLEUS_COLOR = "white" 18 | KNOWN_CHANNELS = {"DAPI": "white", "DNA1": "white", "DNA2": "white", "DAPI (000)": "white"} 19 | 20 | 21 | class Versions: 22 | EXPERIMENT = [2, 0] 23 | GROUPS = [5, 0] 24 | CELL_CATEGORIES = [1, 0] 25 | 26 | 27 | class ShapesConstants: 28 | RADIUS = "radius" 29 | DEFAULT_POINT_RADIUS = 100 30 | 31 | 32 | def cell_categories_attrs() -> dict: 33 | return { 34 | "major_version": Versions.CELL_CATEGORIES[0], 35 | "minor_version": Versions.CELL_CATEGORIES[1], 36 | "number_groupings": 0, 37 | "grouping_names": [], 38 | "group_names": [], 39 | } 40 | 41 | 42 | def cell_summary_attrs() -> dict: 43 | return { 44 | "column_descriptions": [ 45 | "Cell centroid in X", 46 | "Cell centroid in Y", 47 | "Cell area", 48 | "Nucleus centroid in X", 49 | "Nucleus centroid in Y", 50 | "Nucleus area", 51 | "z_level", 52 | ], 53 | "column_names": [ 54 | "cell_centroid_x", 55 | "cell_centroid_y", 56 | "cell_area", 57 | "nucleus_centroid_x", 58 | "nucleus_centroid_y", 59 | "nucleus_area", 60 | "z_level", 61 | ], 62 | } 63 | 64 | 65 | def group_attrs() -> dict: 66 | return { 67 | "major_version": Versions.GROUPS[0], 68 | "minor_version": Versions.GROUPS[1], 69 | "name": "CellSegmentationDataset", 70 | "polygon_set_descriptions": [ 71 | "NA", 72 | "NA", 73 | ], 74 | "polygon_set_display_names": ["Nucleus boundaries", "Cell boundaries"], 75 | "polygon_set_names": ["nucleus", "cell"], 76 | "spatial_units": "microns", 77 | } 78 | 79 | 80 | def experiment_dict(run_name: str, region_name: str, num_cells: int, pixel_size: float) -> dict: 81 | return { 82 | "major_version": Versions.EXPERIMENT[0], 83 | "minor_version": Versions.EXPERIMENT[1], 84 | "run_name": run_name, 85 | "region_name": region_name, 86 | "experiment_uuid": "N/A", 87 | "panel_tissue_type": "N/A", 88 | "run_start_time": "N/A", 89 | "preservation_method": "N/A", 90 | "num_cells": num_cells, 91 | "transcripts_per_cell": 0, 92 | "transcripts_per_100um": 0, 93 | "cassette_name": "N/A", 94 | "slide_id": "N/A", 95 | "panel_design_id": "N/A", 96 | "panel_name": "N/A", 97 | "panel_organism": "Human", 98 | "panel_num_targets_predesigned": 0, 99 | "panel_num_targets_custom": 0, 100 | "pixel_size": pixel_size, 101 | "instrument_sn": "N/A", 102 | "instrument_sw_version": "N/A", 103 | "analysis_sw_version": "xenium-1.3.0.5", 104 | "experiment_uuid": "", 105 | "cassette_uuid": "", 106 | "roi_uuid": "", 107 | "z_step_size": 3.0, 108 | "well_uuid": "", 109 | "calibration_uuid": "N/A", 110 | "images": { 111 | "morphology_filepath": "morphology.ome.tif", 112 | "morphology_mip_filepath": "morphology_mip.ome.tif", 113 | "morphology_focus_filepath": "morphology_focus.ome.tif", 114 | }, 115 | "xenium_explorer_files": { 116 | "transcripts_zarr_filepath": "transcripts.zarr.zip", 117 | "cells_zarr_filepath": "cells.zarr.zip", 118 | "cell_features_zarr_filepath": "cell_feature_matrix.zarr.zip", 119 | "analysis_zarr_filepath": "analysis.zarr.zip", 120 | "analysis_summary_filepath": "analysis_summary.html", 121 | }, 122 | } 123 | 124 | 125 | def image_metadata(channel_names: list[str], pixel_size: float) -> dict: 126 | return { 127 | "SignificantBits": 8, 128 | "PhysicalSizeX": pixel_size, 129 | "PhysicalSizeXUnit": "µm", 130 | "PhysicalSizeY": pixel_size, 131 | "PhysicalSizeYUnit": "µm", 132 | "Channel": {"Name": channel_names}, 133 | } 134 | -------------------------------------------------------------------------------- /spatialdata_xenium_explorer/_logging.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | log = logging.getLogger(__name__) 4 | 5 | 6 | class ColorFormatter(logging.Formatter): 7 | grey = "\x1b[38;20m" 8 | blue = "\x1b[36;20m" 9 | yellow = "\x1b[33;20m" 10 | red = "\x1b[31;20m" 11 | bold_red = "\x1b[31;1m" 12 | reset = "\x1b[0m" 13 | 14 | prefix = "[%(levelname)s] (%(name)s)" 15 | suffix = "%(message)s" 16 | 17 | FORMATS = { 18 | logging.DEBUG: f"{grey}{prefix}{reset} {suffix}", 19 | logging.INFO: f"{blue}{prefix}{reset} {suffix}", 20 | logging.WARNING: f"{yellow}{prefix}{reset} {suffix}", 21 | logging.ERROR: f"{red}{prefix}{reset} {suffix}", 22 | logging.CRITICAL: f"{bold_red}{prefix}{reset} {suffix}", 23 | } 24 | 25 | def format(self, record): 26 | log_fmt = self.FORMATS.get(record.levelno) 27 | formatter = logging.Formatter(log_fmt) 28 | return formatter.format(record) 29 | 30 | 31 | def configure_logger(log: logging.Logger): 32 | log.setLevel(logging.INFO) 33 | 34 | consoleHandler = logging.StreamHandler() 35 | consoleHandler.setFormatter(ColorFormatter()) 36 | 37 | log.addHandler(consoleHandler) 38 | log.propagate = False 39 | -------------------------------------------------------------------------------- /spatialdata_xenium_explorer/cli/app.py: -------------------------------------------------------------------------------- 1 | import typer 2 | 3 | app = typer.Typer() 4 | 5 | SDATA_HELPER = "Path to the SpatialData `.zarr` directory" 6 | 7 | 8 | @app.command() 9 | def write( 10 | sdata_path: str = typer.Argument(help=SDATA_HELPER), 11 | output_path: str = typer.Option( 12 | None, 13 | help="Path to a directory where Xenium Explorer's outputs will be saved. By default, writes to the same path as `sdata_path` but with the `.explorer` suffix", 14 | ), 15 | image_key: str = typer.Option( 16 | None, 17 | help="Name of the image of interest (key of `sdata.images`). This argument doesn't need to be provided if there is only one image.", 18 | ), 19 | shapes_key: str = typer.Option( 20 | None, 21 | help="Name of the cell shapes (key of `sdata.shapes`). This argument doesn't need to be provided if there is only one shapes key or a table with only one region.", 22 | ), 23 | points_key: str = typer.Option( 24 | None, 25 | help="Name of the transcripts (key of `sdata.points`). This argument doesn't need to be provided if there is only one points key.", 26 | ), 27 | gene_column: str = typer.Option( 28 | None, help="Column name of the points dataframe containing the gene names" 29 | ), 30 | pixel_size: float = typer.Option( 31 | 0.2125, 32 | help="Number of microns in a pixel. Invalid value can lead to inconsistent scales in the Explorer.", 33 | ), 34 | spot: bool = typer.Option(False, help="Whether the technology is based on spots"), 35 | layer: str = typer.Option( 36 | None, 37 | help="Layer of `sdata.table` where the gene counts are saved. If `None`, uses `sdata.table.X`.", 38 | ), 39 | lazy: bool = typer.Option( 40 | True, 41 | help="If `True`, will not load the full images in memory (except if the image memory is below `ram_threshold_gb`)", 42 | ), 43 | ram_threshold_gb: int = typer.Option( 44 | 4, 45 | help="Threshold (in gygabytes) from which image can be loaded in memory. If `None`, the image is never loaded in memory", 46 | ), 47 | mode: str = typer.Option( 48 | None, 49 | help="string that indicated which files should be created. `'-ib'` means everything except images and boundaries, while `'+tocm'` means only transcripts/observations/counts/metadata (each letter corresponds to one explorer file). By default, keeps everything", 50 | ), 51 | ): 52 | """Convert a spatialdata object to Xenium Explorer's inputs""" 53 | from pathlib import Path 54 | 55 | import spatialdata 56 | 57 | from spatialdata_xenium_explorer import write 58 | 59 | sdata = spatialdata.read_zarr(sdata_path) 60 | 61 | if output_path is None: 62 | output_path = Path(sdata_path).with_suffix(".explorer") 63 | 64 | write( 65 | output_path, 66 | sdata, 67 | image_key=image_key, 68 | shapes_key=shapes_key, 69 | points_key=points_key, 70 | gene_column=gene_column, 71 | pixel_size=pixel_size, 72 | spot=spot, 73 | layer=layer, 74 | lazy=lazy, 75 | ram_threshold_gb=ram_threshold_gb, 76 | mode=mode, 77 | ) 78 | 79 | 80 | @app.command() 81 | def update_obs( 82 | adata_path: str = typer.Argument( 83 | help="Path to the anndata file (`zarr` or `h5ad`) containing the new observations" 84 | ), 85 | output_path: str = typer.Argument( 86 | help="Path to the Xenium Explorer directory (it will update `analysis.zarr.zip`)", 87 | ), 88 | ): 89 | """Update the cell categories for the Xenium Explorer's (i.e. what's in `adata.obs`). This is useful when you perform analysis and update your `AnnData` object 90 | 91 | Usage: 92 | This command should only be used if you updated `adata.obs`, after creation of the other explorer files. 93 | """ 94 | from pathlib import Path 95 | 96 | import anndata 97 | 98 | from spatialdata_xenium_explorer import write_cell_categories 99 | 100 | path = Path(adata_path) 101 | 102 | if path.is_dir(): 103 | adata = anndata.read_zarr(path) 104 | else: 105 | adata = anndata.read_h5ad(path) 106 | 107 | write_cell_categories(output_path, adata) 108 | 109 | 110 | @app.command() 111 | def add_aligned( 112 | sdata_path: str = typer.Argument(help=SDATA_HELPER), 113 | image_path: str = typer.Argument( 114 | help="Path to the image file to be added (`.ome.tif` used in the explorer during alignment)" 115 | ), 116 | transformation_matrix_path: str = typer.Argument( 117 | help="Path to the `matrix.csv` file returned by the Explorer after alignment" 118 | ), 119 | original_image_key: str = typer.Option( 120 | None, 121 | help="Optional original-image key (of sdata.images) on which the new image will be aligned. This doesn't need to be provided if there is only one image", 122 | ), 123 | overwrite: bool = typer.Option(False, help="Whether to overwrite the image if existing"), 124 | ): 125 | """After alignment on the Xenium Explorer, add an image to the SpatialData object""" 126 | import spatialdata 127 | 128 | from spatialdata_xenium_explorer import align 129 | from spatialdata_xenium_explorer.core.images import ome_tif 130 | 131 | sdata = spatialdata.read_zarr(sdata_path) 132 | image = ome_tif(image_path) 133 | 134 | align( 135 | sdata, image, transformation_matrix_path, overwrite=overwrite, image_key=original_image_key 136 | ) 137 | -------------------------------------------------------------------------------- /spatialdata_xenium_explorer/converter.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import json 4 | import logging 5 | from pathlib import Path 6 | 7 | import geopandas as gpd 8 | from spatialdata import SpatialData 9 | 10 | from . import ( 11 | utils, 12 | write_cell_categories, 13 | write_gene_counts, 14 | write_image, 15 | write_polygons, 16 | write_transcripts, 17 | ) 18 | from ._constants import FileNames, experiment_dict 19 | 20 | log = logging.getLogger(__name__) 21 | 22 | 23 | def write( 24 | path: str, 25 | sdata: SpatialData, 26 | image_key: str | None = None, 27 | shapes_key: str | None = None, 28 | points_key: str | None = None, 29 | gene_column: str | None = None, 30 | pixel_size: float = 0.2125, 31 | spot: bool = False, 32 | layer: str | None = None, 33 | polygon_max_vertices: int = 13, 34 | lazy: bool = True, 35 | ram_threshold_gb: int | None = 4, 36 | mode: str = None, 37 | ) -> None: 38 | """ 39 | Transform a SpatialData object into inputs for the Xenium Explorer. 40 | After running this function, double-click on the `experiment.xenium` file to open it. 41 | 42 | !!! note "Software download" 43 | Make sure you have the latest version of the [Xenium Explorer](https://www.10xgenomics.com/support/software/xenium-explorer) 44 | 45 | Note: 46 | This function will create up to 6 files, depending on the `SpatialData` object and the arguments: 47 | 48 | - `experiment.xenium` contains some experiment metadata. Double-click on this file to open the Xenium Explorer. This file can also be created with [`write_metadata`](./#spatialdata_xenium_explorer.write_metadata). 49 | 50 | - `morphology.ome.tif` is the primary image. This file can also be created with [`write_image`](./#spatialdata_xenium_explorer.write_image). Add more images with `align`. 51 | 52 | - `analysis.zarr.zip` contains the cells categories (or clusters), i.e. `adata.obs`. This file can also be created with [`write_cell_categories`](./#spatialdata_xenium_explorer.write_cell_categories). 53 | 54 | - `cell_feature_matrix.zarr.zip` contains the cell-by-gene counts. This file can also be created with [`write_gene_counts`](./#spatialdata_xenium_explorer.write_gene_counts). 55 | 56 | - `cells.zarr.zip` contains the cells polygon boundaries. This file can also be created with [`write_polygons`](./#spatialdata_xenium_explorer.write_polygons). 57 | 58 | - `transcripts.zarr.zip` contains transcripts locations. This file can also be created with [`write_transcripts`](./#spatialdata_xenium_explorer.write_transcripts). 59 | 60 | Args: 61 | path: Path to the directory where files will be saved. 62 | sdata: A `SpatialData` object. 63 | image_key: Name of the image of interest (key of `sdata.images`). This argument doesn't need to be provided if there is only one image. 64 | shapes_key: Name of the cell shapes (key of `sdata.shapes`). This argument doesn't need to be provided if there is only one shapes key or a table with only one region. 65 | points_key: Name of the transcripts (key of `sdata.points`). This argument doesn't need to be provided if there is only one points key. 66 | gene_column: Column name of the points dataframe containing the gene names. 67 | pixel_size: Number of microns in a pixel. Invalid value can lead to inconsistent scales in the Explorer. 68 | spot: Whether the technology is based on spots 69 | layer: Layer of `sdata.table` where the gene counts are saved. If `None`, uses `sdata.table.X`. 70 | polygon_max_vertices: Maximum number of vertices for the cell polygons. A higher value will display smoother cells. 71 | lazy: If `True`, will not load the full images in memory (except if the image memory is below `ram_threshold_gb`). 72 | ram_threshold_gb: Threshold (in gygabytes) from which image can be loaded in memory. If `None`, the image is never loaded in memory. 73 | mode: string that indicated which files should be created. "-ib" means everything except images and boundaries, while "+tocm" means only transcripts/observations/counts/metadata (each letter corresponds to one explorer file). By default, keeps everything. 74 | """ 75 | path: Path = Path(path) 76 | _check_explorer_directory(path) 77 | 78 | image_key, image = utils.get_spatial_image(sdata, image_key, return_key=True) 79 | 80 | ### Saving cell categories and gene counts 81 | if sdata.table is not None: 82 | adata = sdata.table 83 | 84 | region = adata.uns["spatialdata_attrs"]["region"] 85 | region = region if isinstance(region, list) else [region] 86 | 87 | if len(region) == 1: 88 | assert ( 89 | shapes_key is None or shapes_key == region[0] 90 | ), f"Found only one region ({region[0]}), but `shapes_key` was provided with a different value ({shapes_key})" 91 | shapes_key = region[0] 92 | 93 | if _should_save(mode, "c"): 94 | write_gene_counts(path, adata, layer=layer) 95 | if _should_save(mode, "o"): 96 | write_cell_categories(path, adata) 97 | 98 | ### Saving cell boundaries 99 | shapes_key, geo_df = utils.get_element(sdata, "shapes", shapes_key, return_key=True) 100 | 101 | if _should_save(mode, "b") and geo_df is not None: 102 | geo_df = utils.to_intrinsic(sdata, geo_df, image_key) 103 | 104 | if sdata.table is not None: 105 | geo_df = geo_df.loc[adata.obs[adata.uns["spatialdata_attrs"]["instance_key"]]] 106 | 107 | geo_df = utils._standardize_shapes(geo_df) 108 | 109 | write_polygons(path, geo_df.geometry, polygon_max_vertices, pixel_size=pixel_size) 110 | 111 | ### Saving transcripts 112 | if spot and sdata.table is not None: 113 | df, gene_column = utils._spot_transcripts_origin(adata) 114 | else: 115 | df = utils.get_element(sdata, "points", points_key) 116 | df = utils.to_intrinsic(sdata, df, image_key) 117 | 118 | if _should_save(mode, "t") and df is not None: 119 | if gene_column is not None: 120 | write_transcripts(path, df, gene_column, pixel_size=pixel_size) 121 | else: 122 | log.warn("The argument 'gene_column' has to be provided to save the transcripts") 123 | 124 | ### Saving image 125 | if _should_save(mode, "i"): 126 | write_image( 127 | path, image, lazy=lazy, ram_threshold_gb=ram_threshold_gb, pixel_size=pixel_size 128 | ) 129 | 130 | ### Saving experiment.xenium file 131 | if _should_save(mode, "m"): 132 | write_metadata(path, image_key, shapes_key, _get_n_obs(sdata, geo_df), pixel_size) 133 | 134 | log.info(f"Saved files in the following directory: {path}") 135 | log.info(f"You can open the experiment with 'open {path / FileNames.METADATA}'") 136 | 137 | 138 | def _check_explorer_directory(path: Path): 139 | assert ( 140 | not path.exists() or path.is_dir() 141 | ), f"A path to an existing file was provided. It should be a path to a directory." 142 | path.mkdir(parents=True, exist_ok=True) 143 | 144 | 145 | def _should_save(mode: str | None, character: str): 146 | if mode is None: 147 | return True 148 | 149 | assert len(mode) and mode[0] in [ 150 | "-", 151 | "+", 152 | ], f"Mode should be a string that starts with '+' or '-'" 153 | 154 | return character in mode if mode[0] == "+" else character not in mode 155 | 156 | 157 | def _get_n_obs(sdata: SpatialData, geo_df: gpd.GeoDataFrame) -> int: 158 | if sdata.table is not None: 159 | return sdata.table.n_obs 160 | return len(geo_df) if geo_df is not None else 0 161 | 162 | 163 | def write_metadata( 164 | path: str, 165 | image_key: str = "NA", 166 | shapes_key: str = "NA", 167 | n_obs: int = 0, 168 | is_dir: bool = True, 169 | pixel_size: float = 0.2125, 170 | ): 171 | """Create an `experiment.xenium` file that can be open by the Xenium Explorer. 172 | 173 | Note: 174 | This function alone is not enough to actually open an experiment. You will need at least to wrun `write_image`, or create all the outputs with `write_explorer`. 175 | 176 | Args: 177 | path: Path to the Xenium Explorer directory where the metadata file will be written 178 | image_key: Key of `SpatialData` object containing the primary image used on the explorer. 179 | shapes_key: Key of `SpatialData` object containing the boundaries shown on the explorer. 180 | n_obs: Number of cells 181 | is_dir: If `False`, then `path` is a path to a single file, not to the Xenium Explorer directory. 182 | pixel_size: Number of microns in a pixel. Invalid value can lead to inconsistent scales in the Explorer. 183 | """ 184 | path = utils.explorer_file_path(path, FileNames.METADATA, is_dir) 185 | 186 | with open(path, "w") as f: 187 | metadata = experiment_dict(image_key, shapes_key, n_obs, pixel_size) 188 | json.dump(metadata, f, indent=4) 189 | 190 | 191 | def update_metadata( 192 | path: str, 193 | sdata: SpatialData, 194 | table_name: str | None = None, 195 | is_dir: bool = True, 196 | ): 197 | """Update an `experiment.xenium` file that can be open by the Xenium Explorer. 198 | 199 | Args: 200 | path: Path to the Xenium Explorer directory where the metadata file will be written 201 | sdata: A `SpatialData` object. 202 | table_name: Optional name of the table containing the cells to be visualized. 203 | is_dir: If `False`, then `path` is a path to a single file, not to the Xenium Explorer directory. 204 | """ 205 | path = utils.explorer_file_path(path, FileNames.METADATA, is_dir) 206 | 207 | table_name = "table" if table_name is None else table_name 208 | adata = sdata.tables[table_name] 209 | assert adata is not None 210 | 211 | with open(path, "r") as f: 212 | data = json.load(f) 213 | 214 | data["num_cells"] = adata.n_obs 215 | 216 | with open(path, "w") as f: 217 | json.dump(data, f, indent=4) 218 | -------------------------------------------------------------------------------- /spatialdata_xenium_explorer/core/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/quentinblampey/spatialdata_xenium_explorer/e62aa7cac1aee9be081eb2fca73efb228bfb3f06/spatialdata_xenium_explorer/core/__init__.py -------------------------------------------------------------------------------- /spatialdata_xenium_explorer/core/images.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import logging 4 | import re 5 | from math import ceil 6 | from pathlib import Path 7 | 8 | import dask.array as da 9 | import numpy as np 10 | import tifffile as tf 11 | import xarray as xr 12 | from dask_image.imread import imread 13 | from multiscale_spatial_image import MultiscaleSpatialImage, to_multiscale 14 | from spatial_image import SpatialImage 15 | from spatialdata import SpatialData 16 | from spatialdata.models import Image2DModel 17 | from spatialdata.transformations import Affine 18 | from tqdm import tqdm 19 | 20 | from .. import utils 21 | from .._constants import ExplorerConstants, FileNames, image_metadata 22 | 23 | log = logging.getLogger(__name__) 24 | 25 | 26 | class MultiscaleImageWriter: 27 | photometric = "minisblack" 28 | compression = "jpeg2000" 29 | resolutionunit = "CENTIMETER" 30 | dtype = np.uint8 31 | 32 | def __init__(self, image: MultiscaleSpatialImage, tile_width: int, pixel_size: float): 33 | self.image = image 34 | self.tile_width = tile_width 35 | self.pixel_size = pixel_size 36 | 37 | self.scale_names = list(image.children) 38 | self.channel_names = list(map(str, image[self.scale_names[0]].c.values)) 39 | self.channel_names = _set_colors(self.channel_names) 40 | self.metadata = image_metadata(self.channel_names, pixel_size) 41 | self.data = None 42 | 43 | self.lazy = True 44 | self.ram_threshold_gb = None 45 | 46 | def _n_tiles_axis(self, xarr: xr.DataArray, axis: int) -> int: 47 | return ceil(xarr.shape[axis] / self.tile_width) 48 | 49 | def _get_tiles(self, xarr: xr.DataArray): 50 | for c in range(xarr.shape[0]): 51 | for index_y in range(self._n_tiles_axis(xarr, 1)): 52 | for index_x in range(self._n_tiles_axis(xarr, 2)): 53 | tile = xarr[ 54 | c, 55 | self.tile_width * index_y : self.tile_width * (index_y + 1), 56 | self.tile_width * index_x : self.tile_width * (index_x + 1), 57 | ].values 58 | yield self._scale(tile) 59 | 60 | def _should_load_memory(self, shape: tuple[int, int, int], dtype: np.dtype): 61 | if not self.lazy: 62 | return True 63 | 64 | if self.ram_threshold_gb is None: 65 | return False 66 | 67 | itemsize = max(np.dtype(dtype).itemsize, np.dtype(self.dtype).itemsize) 68 | size = shape[0] * shape[1] * shape[2] * itemsize 69 | 70 | return size <= self.ram_threshold_gb * 1024**3 71 | 72 | def _scale(self, array: np.ndarray): 73 | return utils.scale_dtype(array, self.dtype) 74 | 75 | def _write_image_level(self, tif: tf.TiffWriter, scale_index: int, **kwargs): 76 | xarr: xr.DataArray = next(iter(self.image[self.scale_names[scale_index]].values())) 77 | resolution = 1e4 * 2**scale_index / self.pixel_size 78 | 79 | if not self._should_load_memory(xarr.shape, xarr.dtype): 80 | n_tiles = xarr.shape[0] * self._n_tiles_axis(xarr, 1) * self._n_tiles_axis(xarr, 2) 81 | data = self._get_tiles(xarr) 82 | data = iter(tqdm(data, total=n_tiles - 1, desc="Writing tiles")) 83 | else: 84 | if self.data is not None: 85 | self.data = utils.resize_numpy(self.data, 2, xarr.dims, xarr.shape) 86 | else: 87 | log.info(f" (Loading image of shape {xarr.shape}) in memory") 88 | self.data = self._scale(xarr.values) 89 | 90 | data = self.data 91 | 92 | log.info(f" > Image of shape {xarr.shape}") 93 | tif.write( 94 | data, 95 | tile=(self.tile_width, self.tile_width), 96 | resolution=(resolution, resolution), 97 | metadata=self.metadata, 98 | shape=xarr.shape, 99 | dtype=self.dtype, 100 | photometric=self.photometric, 101 | compression=self.compression, 102 | resolutionunit=self.resolutionunit, 103 | **kwargs, 104 | ) 105 | 106 | def __len__(self): 107 | return len(self.scale_names) 108 | 109 | def procedure(self): 110 | if not self.lazy: 111 | return "in-memory (consider lazy procedure if it crashes because of RAM)" 112 | if self.ram_threshold_gb is None: 113 | return "lazy (slower but low RAM usage)" 114 | return "semi-lazy (load in memory when possible)" 115 | 116 | def write(self, path, lazy=True, ram_threshold_gb=None): 117 | self.lazy = lazy 118 | self.ram_threshold_gb = ram_threshold_gb 119 | 120 | log.info(f"Writing multiscale image with procedure={self.procedure()}") 121 | 122 | with tf.TiffWriter(path, bigtiff=True) as tif: 123 | self._write_image_level(tif, 0, subifds=len(self) - 1) 124 | 125 | for i in range(1, len(self)): 126 | self._write_image_level(tif, i, subfiletype=1) 127 | 128 | 129 | def _default_image_models_kwargs(image_models_kwargs: dict | None): 130 | image_models_kwargs = {} if image_models_kwargs is None else image_models_kwargs 131 | 132 | if "chunks" not in image_models_kwargs: 133 | image_models_kwargs["chunks"] = (1, 4096, 4096) 134 | 135 | return image_models_kwargs 136 | 137 | 138 | def _to_color(channel_name: str, is_wavelength: bool, colors_iterator: list): 139 | if is_wavelength: 140 | return channel_name 141 | if channel_name in ExplorerConstants.KNOWN_CHANNELS: 142 | return f"{channel_name} (color={ExplorerConstants.KNOWN_CHANNELS[channel_name]})" 143 | return f"{channel_name} (color={colors_iterator.pop()})" 144 | 145 | 146 | def _set_colors(channel_names: list[str]) -> list[str]: 147 | """ 148 | Trick to provide a color to all channels on the Xenium Explorer. 149 | 150 | But some channels colors are set to white by default. This functions allows to color these 151 | channels with an available wavelength color (e.g., `550`). 152 | """ 153 | existing_wavelength = [ 154 | bool(re.search(r"(? SpatialImage: 259 | """Read an `.ome.tif` image. This image should be a 2D image (with possibly multiple channels). 260 | Typically, this function can be used to open Xenium IF images. 261 | 262 | Args: 263 | path: Path to the `.ome.tif` image 264 | 265 | Returns: 266 | A `SpatialImage` 267 | """ 268 | image_models_kwargs = _default_image_models_kwargs(None) 269 | image_name = Path(path).absolute().name.split(".")[0] 270 | image: da.Array = imread(path) 271 | 272 | if image.ndim == 4: 273 | assert image.shape[0] == 1, f"4D images not supported" 274 | image = da.moveaxis(image[0], 2, 0) 275 | log.info(f"Transformed 4D image into a 3D image of shape (c, y, x) = {image.shape}") 276 | elif image.ndim != 3: 277 | raise ValueError(f"Number of dimensions not supported: {image.ndim}") 278 | 279 | image = image.rechunk(chunks=image_models_kwargs["chunks"]) 280 | 281 | channel_names = _ome_channels_names(path) 282 | if len(channel_names) != len(image): 283 | channel_names = [str(i) for i in range(len(image))] 284 | log.warn(f"Channel names couldn't be read. Using {channel_names} instead.") 285 | 286 | return SpatialImage(image, dims=["c", "y", "x"], name=image_name, coords={"c": channel_names}) 287 | -------------------------------------------------------------------------------- /spatialdata_xenium_explorer/core/points.py: -------------------------------------------------------------------------------- 1 | import logging 2 | from math import ceil 3 | from pathlib import Path 4 | 5 | import dask.dataframe as dd 6 | import numpy as np 7 | import zarr 8 | 9 | from .._constants import ExplorerConstants, FileNames 10 | from ..utils import explorer_file_path 11 | 12 | log = logging.getLogger(__name__) 13 | 14 | 15 | def subsample_indices(n_samples, factor: int = 4): 16 | n_sub = n_samples // factor 17 | return np.random.choice(n_samples, n_sub, replace=False) 18 | 19 | 20 | def write_transcripts( 21 | path: Path, 22 | df: dd.DataFrame, 23 | gene: str = "gene", 24 | max_levels: int = 15, 25 | is_dir: bool = True, 26 | pixel_size: float = 0.2125, 27 | ): 28 | """Write a `transcripts.zarr.zip` file containing pyramidal transcript locations 29 | 30 | Args: 31 | path: Path to the Xenium Explorer directory where the transcript file will be written 32 | df: DataFrame representing the transcripts, with `"x"`, `"y"` column required, as well as the `gene` column (see the corresponding argument) 33 | gene: Column of `df` containing the genes names. 34 | max_levels: Maximum number of levels in the pyramid. 35 | is_dir: If `False`, then `path` is a path to a single file, not to the Xenium Explorer directory. 36 | pixel_size: Number of microns in a pixel. Invalid value can lead to inconsistent scales in the Explorer. 37 | """ 38 | path = explorer_file_path(path, FileNames.POINTS, is_dir) 39 | 40 | # TODO: make everything using dask instead of pandas 41 | df = df.compute() 42 | 43 | num_transcripts = len(df) 44 | grid_size = ExplorerConstants.GRID_SIZE / ExplorerConstants.PIXELS_TO_MICRONS * pixel_size 45 | df[gene] = df[gene].astype("category") 46 | 47 | location = df[["x", "y"]] 48 | location *= pixel_size 49 | location = np.concatenate([location, np.zeros((num_transcripts, 1))], axis=1) 50 | 51 | if location.min() < 0: 52 | log.warn("Some transcripts are located outside of the image (pixels < 0)") 53 | log.info(f"Writing {len(df)} transcripts") 54 | 55 | xmax, ymax = location[:, :2].max(axis=0) 56 | 57 | gene_names = list(df[gene].cat.categories) 58 | num_genes = len(gene_names) 59 | 60 | codeword_gene_mapping = list(range(num_genes)) 61 | 62 | valid = np.ones((num_transcripts, 1)) 63 | uuid = np.stack([np.arange(num_transcripts), np.full(num_transcripts, 65535)], axis=1) 64 | transcript_id = np.stack([np.arange(num_transcripts), np.full(num_transcripts, 65535)], axis=1) 65 | gene_identity = df[gene].cat.codes.values[:, None] 66 | codeword_identity = np.stack([gene_identity[:, 0], np.full(num_transcripts, 65535)], axis=1) 67 | status = np.zeros((num_transcripts, 1)) 68 | quality_score = np.full((num_transcripts, 1), ExplorerConstants.QUALITY_SCORE) 69 | 70 | ATTRS = { 71 | "codeword_count": num_genes, 72 | "codeword_gene_mapping": codeword_gene_mapping, 73 | "codeword_gene_names": gene_names, 74 | "gene_names": gene_names, 75 | "gene_index_map": {name: index for name, index in zip(gene_names, codeword_gene_mapping)}, 76 | "number_genes": num_genes, 77 | "spatial_units": "micron", 78 | "coordinate_space": "refined-final_global_micron", 79 | "major_version": 4, 80 | "minor_version": 1, 81 | "name": "RnaDataset", 82 | "number_rnas": num_transcripts, 83 | "dataset_uuid": "unique-id-test", 84 | "data_format": 0, 85 | } 86 | 87 | GRIDS_ATTRS = { 88 | "grid_key_names": ["grid_x_loc", "grid_y_loc"], 89 | "grid_zip": False, 90 | "grid_size": [grid_size], 91 | "grid_array_shapes": [], 92 | "grid_number_objects": [], 93 | "grid_keys": [], 94 | } 95 | 96 | with zarr.ZipStore(path, mode="w") as store: 97 | g = zarr.group(store=store) 98 | g.attrs.put(ATTRS) 99 | 100 | grids = g.create_group("grids") 101 | 102 | for level in range(max_levels): 103 | log.info(f" > Level {level}: {len(location)} transcripts") 104 | level_group = grids.create_group(level) 105 | 106 | tile_size = grid_size * 2**level 107 | 108 | indices = np.floor(location[:, :2] / tile_size).clip(0).astype(int) 109 | tiles_str_indices = np.array([f"{tx},{ty}" for (tx, ty) in indices]) 110 | 111 | GRIDS_ATTRS["grid_array_shapes"].append([]) 112 | GRIDS_ATTRS["grid_number_objects"].append([]) 113 | GRIDS_ATTRS["grid_keys"].append([]) 114 | 115 | n_tiles_x, n_tiles_y = max(1, ceil(xmax / tile_size)), max(1, ceil(ymax / tile_size)) 116 | 117 | for tx in range(n_tiles_x): 118 | for ty in range(n_tiles_y): 119 | str_index = f"{tx},{ty}" 120 | loc = np.where(tiles_str_indices == str_index)[0] 121 | 122 | n_points_tile = len(loc) 123 | chunks = (n_points_tile, 1) 124 | 125 | if n_points_tile == 0: 126 | continue 127 | 128 | GRIDS_ATTRS["grid_array_shapes"][-1].append({}) 129 | GRIDS_ATTRS["grid_keys"][-1].append(str_index) 130 | GRIDS_ATTRS["grid_number_objects"][-1].append(n_points_tile) 131 | 132 | tile_group = level_group.create_group(str_index) 133 | tile_group.array( 134 | "valid", 135 | valid[loc], 136 | dtype="uint8", 137 | chunks=chunks, 138 | ) 139 | tile_group.array( 140 | "status", 141 | status[loc], 142 | dtype="uint8", 143 | chunks=chunks, 144 | ) 145 | tile_group.array( 146 | "location", 147 | location[loc], 148 | dtype="float32", 149 | chunks=chunks, 150 | ) 151 | tile_group.array( 152 | "gene_identity", 153 | gene_identity[loc], 154 | dtype="uint16", 155 | chunks=chunks, 156 | ) 157 | tile_group.array( 158 | "quality_score", 159 | quality_score[loc], 160 | dtype="float32", 161 | chunks=chunks, 162 | ) 163 | tile_group.array( 164 | "codeword_identity", 165 | codeword_identity[loc], 166 | dtype="uint16", 167 | chunks=chunks, 168 | ) 169 | tile_group.array( 170 | "uuid", 171 | uuid[loc], 172 | dtype="uint32", 173 | chunks=chunks, 174 | ) 175 | tile_group.array( 176 | "id", 177 | transcript_id[loc], 178 | dtype="uint32", 179 | chunks=chunks, 180 | ) 181 | 182 | if n_tiles_x * n_tiles_y == 1 and level > 0: 183 | GRIDS_ATTRS["number_levels"] = level + 1 184 | break 185 | 186 | sub_indices = subsample_indices(len(location)) 187 | 188 | location = location[sub_indices] 189 | valid = valid[sub_indices] 190 | status = status[sub_indices] 191 | gene_identity = gene_identity[sub_indices] 192 | quality_score = quality_score[sub_indices] 193 | codeword_identity = codeword_identity[sub_indices] 194 | uuid = uuid[sub_indices] 195 | transcript_id = transcript_id[sub_indices] 196 | 197 | grids.attrs.put(GRIDS_ATTRS) 198 | -------------------------------------------------------------------------------- /spatialdata_xenium_explorer/core/shapes.py: -------------------------------------------------------------------------------- 1 | import logging 2 | from math import ceil 3 | from pathlib import Path 4 | from typing import Iterable 5 | 6 | import numpy as np 7 | import zarr 8 | from shapely.geometry import Polygon 9 | 10 | from .._constants import ExplorerConstants, FileNames, cell_summary_attrs, group_attrs 11 | from ..utils import explorer_file_path 12 | 13 | log = logging.getLogger(__name__) 14 | 15 | TOLERANCE_STEP = 0.5 16 | 17 | 18 | def pad_polygon( 19 | polygon: Polygon, max_vertices: int, tolerance: float = TOLERANCE_STEP 20 | ) -> np.ndarray: 21 | """Transform the polygon to have the desired number of vertices 22 | 23 | Args: 24 | polygon: A `shapely` polygon 25 | max_vertices: The desired number of vertices 26 | tolerance: The step of tolerance used for simplification. At each step, we increase the tolerance of this value until the polygon is simplified enough. 27 | 28 | Returns: 29 | A 2D array representing the polygon vertices 30 | """ 31 | n_vertices = len(polygon.exterior.coords) 32 | assert n_vertices >= 3 33 | 34 | coords = polygon.exterior.coords._coords 35 | 36 | if n_vertices == max_vertices: 37 | return coords.flatten() 38 | 39 | if n_vertices < max_vertices: 40 | return np.pad(coords, ((0, max_vertices - n_vertices), (0, 0)), mode="edge").flatten() 41 | 42 | # TODO: improve it: how to choose the right tolerance? 43 | polygon = polygon.simplify(tolerance=tolerance) 44 | return pad_polygon(polygon, max_vertices, tolerance + TOLERANCE_STEP) 45 | 46 | 47 | def write_polygons( 48 | path: Path, 49 | polygons: Iterable[Polygon], 50 | max_vertices: int, 51 | is_dir: bool = True, 52 | pixel_size: float = 0.2125, 53 | ) -> None: 54 | """Write a `cells.zarr.zip` file containing the cell polygonal boundaries 55 | 56 | Args: 57 | path: Path to the Xenium Explorer directory where the transcript file will be written 58 | polygons: A list of `shapely` polygons to be written 59 | max_vertices: The number of vertices per polygon (they will be transformed to have the right number of vertices) 60 | is_dir: If `False`, then `path` is a path to a single file, not to the Xenium Explorer directory. 61 | pixel_size: Number of microns in a pixel. Invalid value can lead to inconsistent scales in the Explorer. 62 | """ 63 | path = explorer_file_path(path, FileNames.SHAPES, is_dir) 64 | 65 | assert all( 66 | isinstance(p, Polygon) for p in polygons 67 | ), f"All geometries must be a shapely Polygon" 68 | 69 | log.info(f"Writing {len(polygons)} cell polygons") 70 | coordinates = np.stack([pad_polygon(p, max_vertices) for p in polygons]) 71 | coordinates *= pixel_size 72 | 73 | num_cells = len(coordinates) 74 | cells_fourth = ceil(num_cells / 4) 75 | cells_half = ceil(num_cells / 2) 76 | 77 | GROUP_ATTRS = group_attrs() 78 | GROUP_ATTRS["number_cells"] = num_cells 79 | 80 | polygon_vertices = np.stack([coordinates, coordinates]) 81 | num_points = polygon_vertices.shape[2] 82 | n_vertices = num_points // 2 83 | 84 | with zarr.ZipStore(path, mode="w") as store: 85 | g = zarr.group(store=store) 86 | g.attrs.put(GROUP_ATTRS) 87 | 88 | g.array( 89 | "polygon_vertices", 90 | polygon_vertices, 91 | dtype="float32", 92 | chunks=(1, cells_fourth, ceil(num_points / 4)), 93 | ) 94 | 95 | cell_id = np.ones((num_cells, 2)) 96 | cell_id[:, 0] = np.arange(num_cells) 97 | g.array("cell_id", cell_id, dtype="uint32", chunks=(cells_half, 1)) 98 | 99 | cell_summary = np.zeros((num_cells, 7)) 100 | cell_summary[:, 2] = [p.area for p in polygons] 101 | g.array( 102 | "cell_summary", 103 | cell_summary, 104 | dtype="float64", 105 | chunks=(num_cells, 1), 106 | ) 107 | g["cell_summary"].attrs.put(cell_summary_attrs()) 108 | 109 | g.array( 110 | "polygon_num_vertices", 111 | np.full((2, num_cells), n_vertices), 112 | dtype="int32", 113 | chunks=(1, cells_half), 114 | ) 115 | 116 | g.array( 117 | "seg_mask_value", 118 | np.arange(num_cells), 119 | dtype="uint32", 120 | chunks=(cells_half,), 121 | ) 122 | -------------------------------------------------------------------------------- /spatialdata_xenium_explorer/core/table.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import logging 4 | 5 | import numpy as np 6 | import pandas as pd 7 | import zarr 8 | from anndata import AnnData 9 | from scipy.sparse import csr_matrix 10 | 11 | from .._constants import FileNames, cell_categories_attrs 12 | from ..utils import explorer_file_path 13 | 14 | log = logging.getLogger(__name__) 15 | 16 | 17 | def write_gene_counts( 18 | path: str, adata: AnnData, layer: str | None = None, is_dir: bool = True 19 | ) -> None: 20 | """Write a `cell_feature_matrix.zarr.zip` file containing the cell-by-gene transcript counts (i.e., from `adata.X`). 21 | 22 | Args: 23 | path: Path to the Xenium Explorer directory where the cell-by-gene file will be written 24 | adata: An `AnnData` object. Note that `adata.X` must contain raw counts. 25 | layer: If not `None`, `adata.layers[layer]` will be used instead of `adata.X`. This must contain raw counts. 26 | is_dir: If `False`, then `path` is a path to a single file, not to the Xenium Explorer directory. 27 | """ 28 | path = explorer_file_path(path, FileNames.TABLE, is_dir) 29 | 30 | log.info(f"Writing table with {adata.n_vars} columns") 31 | counts = adata.X if layer is None else adata.layers[layer] 32 | counts = csr_matrix(counts.T) 33 | 34 | feature_keys = list(adata.var_names) + ["Total transcripts"] 35 | feature_ids = feature_keys 36 | feature_types = ["gene"] * len(adata.var_names) + ["aggregate_gene"] 37 | 38 | ATTRS = { 39 | "major_version": 3, 40 | "minor_version": 0, 41 | "number_cells": adata.n_obs, 42 | "number_features": adata.n_vars + 1, 43 | "feature_keys": feature_keys, 44 | "feature_ids": feature_ids, 45 | "feature_types": feature_types, 46 | } 47 | 48 | total_counts = counts.sum(1).A1 49 | loc = total_counts > 0 50 | 51 | data = np.concatenate([counts.data, total_counts[loc]]) 52 | indices = np.concatenate([counts.indices, np.where(loc)[0]]) 53 | indptr = counts.indptr 54 | indptr = np.append(indptr, indptr[-1] + loc.sum()) 55 | 56 | cell_id = np.ones((adata.n_obs, 2)) 57 | cell_id[:, 0] = np.arange(adata.n_obs) 58 | 59 | with zarr.ZipStore(path, mode="w") as store: 60 | g = zarr.group(store=store) 61 | cells_group = g.create_group("cell_features") 62 | cells_group.attrs.put(ATTRS) 63 | 64 | cells_group.array("cell_id", cell_id, dtype="uint32", chunks=cell_id.shape) 65 | cells_group.array("data", data, dtype="uint32", chunks=data.shape) 66 | cells_group.array("indices", indices, dtype="uint32", chunks=indices.shape) 67 | cells_group.array("indptr", indptr, dtype="uint32", chunks=indptr.shape) 68 | 69 | 70 | def _write_categorical_column( 71 | root: zarr.Group, index: int, values: np.ndarray, categories: list[str] 72 | ) -> None: 73 | group = root.create_group(index) 74 | values_indices = [np.where(values == cat)[0] for cat in categories] 75 | values_cum_len = np.cumsum([len(indices) for indices in values_indices]) 76 | 77 | indices = np.concatenate(values_indices) 78 | indptr = np.concatenate([[0], values_cum_len[:-1]]) 79 | 80 | group.array("indices", indices, dtype="uint32", chunks=(len(indices),)) 81 | group.array("indptr", indptr, dtype="uint32", chunks=(len(indptr),)) 82 | 83 | 84 | def write_cell_categories(path: str, adata: AnnData, is_dir: bool = True) -> None: 85 | """Write a `analysis.zarr.zip` file containing the cell categories/clusters (i.e., from `adata.obs`) 86 | 87 | Args: 88 | path: Path to the Xenium Explorer directory where the cell-categories file will be written 89 | adata: An `AnnData` object 90 | is_dir: If `False`, then `path` is a path to a single file, not to the Xenium Explorer directory. 91 | """ 92 | path = explorer_file_path(path, FileNames.CELL_CATEGORIES, is_dir) 93 | 94 | adata.strings_to_categoricals() 95 | cat_columns = [name for name, cat in adata.obs.dtypes.items() if cat == "category"] 96 | 97 | log.info(f"Writing {len(cat_columns)} cell categories: {', '.join(cat_columns)}") 98 | 99 | ATTRS = cell_categories_attrs() 100 | ATTRS["number_groupings"] = len(cat_columns) 101 | 102 | with zarr.ZipStore(path, mode="w") as store: 103 | g = zarr.group(store=store) 104 | cell_groups = g.create_group("cell_groups") 105 | 106 | for i, name in enumerate(cat_columns): 107 | if adata.obs[name].isna().any(): 108 | NA = "NA" 109 | log.warn(f"Column {name} has nan values. They will be displayed as '{NA}'") 110 | adata.obs[name] = adata.obs[name].cat.add_categories(NA).fillna(NA) 111 | 112 | categories = list(adata.obs[name].cat.categories) 113 | ATTRS["grouping_names"].append(name) 114 | ATTRS["group_names"].append(categories) 115 | 116 | _write_categorical_column(cell_groups, i, adata.obs[name], categories) 117 | 118 | cell_groups.attrs.put(ATTRS) 119 | 120 | 121 | def save_column_csv(path: str, adata: AnnData, key: str): 122 | """Save one column of the AnnData object as a CSV that can be open interactively in the explorer, under the "cell" panel. 123 | 124 | Args: 125 | path: Path where to write the CSV that will be open in the Xenium Explorer 126 | adata: An `AnnData` object 127 | key: Key of `adata.obs` containing the column to convert 128 | """ 129 | df = pd.DataFrame({"cell_id": adata.obs_names, "group": adata.obs[key].values}) 130 | df.to_csv(path, index=None) 131 | -------------------------------------------------------------------------------- /spatialdata_xenium_explorer/main.py: -------------------------------------------------------------------------------- 1 | from .cli.app import app 2 | -------------------------------------------------------------------------------- /spatialdata_xenium_explorer/utils.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import logging 4 | from pathlib import Path 5 | 6 | import dask.array as da 7 | import dask.dataframe as dd 8 | import dask_image.ndinterp 9 | import geopandas as gpd 10 | import numpy as np 11 | import pandas as pd 12 | import xarray as xr 13 | from anndata import AnnData 14 | from multiscale_spatial_image import MultiscaleSpatialImage 15 | from shapely.geometry import MultiPolygon, Point, Polygon 16 | from spatial_image import SpatialImage 17 | from spatialdata import SpatialData 18 | from spatialdata.models import SpatialElement 19 | from spatialdata.transformations import Identity, get_transformation, set_transformation 20 | 21 | from ._constants import ShapesConstants 22 | 23 | log = logging.getLogger(__name__) 24 | 25 | 26 | def explorer_file_path(path: str, filename: str, is_dir: bool): 27 | path: Path = Path(path) 28 | 29 | if is_dir: 30 | path = path / filename 31 | 32 | return path 33 | 34 | 35 | def int_cell_id(explorer_cell_id: str) -> int: 36 | """Transforms an alphabetical cell id from the Xenium Explorer to an integer ID 37 | 38 | E.g., int_cell_id('aaaachba-1') = 10000""" 39 | code = explorer_cell_id[:-2] if explorer_cell_id[-2] == "-" else explorer_cell_id 40 | coefs = [ord(c) - 97 for c in code][::-1] 41 | return sum(value * 16**i for i, value in enumerate(coefs)) 42 | 43 | 44 | def str_cell_id(cell_id: int) -> str: 45 | """Transforms an integer cell ID into an Xenium Explorer alphabetical cell id 46 | 47 | E.g., str_cell_id(10000) = 'aaaachba-1'""" 48 | coefs = [] 49 | for _ in range(8): 50 | cell_id, coef = divmod(cell_id, 16) 51 | coefs.append(coef) 52 | return "".join([chr(97 + coef) for coef in coefs][::-1]) + "-1" 53 | 54 | 55 | def get_intrinsic_cs( 56 | sdata: SpatialData, element: SpatialElement | str, name: str | None = None 57 | ) -> str: 58 | """Gets the name of the intrinsic coordinate system of an element 59 | 60 | Args: 61 | sdata: A SpatialData object 62 | element: `SpatialElement`, or its key 63 | name: Name to provide to the intrinsic coordinate system if not existing. By default, uses the element id. 64 | 65 | Returns: 66 | Name of the intrinsic coordinate system 67 | """ 68 | if name is None: 69 | name = f"_{element if isinstance(element, str) else id(element)}_intrinsic" 70 | 71 | if isinstance(element, str): 72 | element = sdata[element] 73 | 74 | for cs, transform in get_transformation(element, get_all=True).items(): 75 | if isinstance(transform, Identity): 76 | return cs 77 | 78 | set_transformation(element, Identity(), name) 79 | return name 80 | 81 | 82 | def to_intrinsic( 83 | sdata: SpatialData, element: SpatialElement | str, element_cs: SpatialElement | str 84 | ): 85 | """Transforms a `SpatialElement` into the intrinsic coordinate system of another `SpatialElement` 86 | 87 | Args: 88 | sdata: A SpatialData object 89 | element: `SpatialElement` to transform, or its key 90 | element_cs: `SpatialElement` of the target coordinate system, or its key 91 | 92 | Returns: 93 | The `SpatialElement` after transformation in the target coordinate system 94 | """ 95 | if isinstance(element, str): 96 | element = sdata[element] 97 | cs = get_intrinsic_cs(sdata, element_cs) 98 | return sdata.transform_element_to_coordinate_system(element, cs) 99 | 100 | 101 | def get_key(sdata: SpatialData, attr: str, key: str | None = None): 102 | if key is not None: 103 | return key 104 | 105 | elements = getattr(sdata, attr) 106 | 107 | if len(elements) != 1: 108 | log.warn( 109 | f"Trying to get an element key of `sdata.{attr}`, but it contains multiple values and no key was provided. It will not be saved to the xenium explorer." 110 | ) 111 | return None 112 | 113 | return next(iter(elements.keys())) 114 | 115 | 116 | def get_element(sdata: SpatialData, attr: str, key: str | None = None, return_key: bool = False): 117 | key = get_key(sdata, attr, key) 118 | value = sdata[key] if key is not None else None 119 | return (key, value) if return_key else value 120 | 121 | 122 | def get_spatial_image( 123 | sdata: SpatialData, key: str | None = None, return_key: bool = False 124 | ) -> SpatialImage | tuple[str, SpatialImage]: 125 | """Gets a SpatialImage from a SpatialData object (if the image has multiple scale, the `scale0` is returned) 126 | 127 | Args: 128 | sdata: SpatialData object. 129 | key: Optional image key. If `None`, returns the only image (if only one), or raises an error. 130 | return_key: Whether to also return the key of the image. 131 | 132 | Returns: 133 | If `return_key` is False, only the image is returned, else a tuple `(image_key, image)` 134 | """ 135 | assert not ( 136 | key is None and len(sdata.images) > 1 137 | ), "When the SpatialData contains more than one image, please provide 'image_key'" 138 | 139 | key = get_key(sdata, "images", key) 140 | 141 | assert key is not None, "At least one image in `sdata.images` is required" 142 | 143 | image = sdata.images[key] 144 | if isinstance(image, MultiscaleSpatialImage): 145 | image = SpatialImage(next(iter(image["scale0"].values()))) 146 | 147 | if return_key: 148 | return key, image 149 | return image 150 | 151 | 152 | def resize(xarr: xr.DataArray, scale_factor: float) -> da.Array: 153 | """Resize a xarray image 154 | 155 | Args: 156 | xarr: A `xarray` array 157 | scale_factor: Scale factor of resizing, e.g. `2` will decrease the width by 2 158 | 159 | Returns: 160 | Resized dask array 161 | """ 162 | resize_dims = [dim in ["x", "y"] for dim in xarr.dims] 163 | transform = np.diag([scale_factor if resize_dim else 1 for resize_dim in resize_dims]) 164 | output_shape = [ 165 | size // scale_factor if resize_dim else size 166 | for size, resize_dim in zip(xarr.shape, resize_dims) 167 | ] 168 | 169 | return dask_image.ndinterp.affine_transform( 170 | xarr.data, matrix=transform, output_shape=output_shape 171 | ) 172 | 173 | 174 | def resize_numpy( 175 | arr: np.ndarray, scale_factor: float, dims: list[str], output_shape: list[int] 176 | ) -> np.ndarray: 177 | """Resize a numpy image 178 | 179 | Args: 180 | arr: a `numpy` array 181 | scale_factor: Scale factor of resizing, e.g. `2` will decrease the width by 2 182 | dims: List of dimension names. Only `"x"` and `"y"` are resized. 183 | output_shape: Size of the output array 184 | 185 | Returns: 186 | Resized array 187 | """ 188 | resize_dims = [dim in ["x", "y"] for dim in dims] 189 | transform = np.diag([scale_factor if resize_dim else 1 for resize_dim in resize_dims]) 190 | 191 | return dask_image.ndinterp.affine_transform( 192 | arr, matrix=transform, output_shape=output_shape 193 | ).compute() 194 | 195 | 196 | def _check_integer_dtype(dtype: np.dtype): 197 | assert np.issubdtype( 198 | dtype, np.integer 199 | ), f"Expecting image to have an intenger dtype, but found {dtype}" 200 | 201 | 202 | def scale_dtype(arr: np.ndarray, dtype: np.dtype) -> np.ndarray: 203 | """Change the dtype of an array but keep the scale compared to the type maximum value. 204 | 205 | !!! note "Example" 206 | For an array of dtype `uint8` being transformed to `np.uint16`, the value `255` will become `65535` 207 | 208 | Args: 209 | arr: A `numpy` array 210 | dtype: Target `numpy` data type 211 | 212 | Returns: 213 | A scaled `numpy` array with the dtype provided. 214 | """ 215 | _check_integer_dtype(arr.dtype) 216 | _check_integer_dtype(dtype) 217 | 218 | if arr.dtype == dtype: 219 | return arr 220 | 221 | factor = np.iinfo(dtype).max / np.iinfo(arr.dtype).max 222 | return (arr * factor).astype(dtype) 223 | 224 | 225 | def _standardize_shapes(geo_df: gpd.GeoDataFrame) -> gpd.GeoDataFrame: 226 | if geo_df.geometry.map(lambda geom: isinstance(geom, Polygon)).all(): 227 | return geo_df 228 | 229 | if geo_df.geometry.map(lambda geom: isinstance(geom, Point)).all(): 230 | if not ShapesConstants.RADIUS in geo_df: 231 | log.warn( 232 | f"GeoDataFrame contains only Point objects, but no column '{ShapesConstants.RADIUS}' was found. Using default {ShapesConstants.RADIUS}={ShapesConstants.DEFAULT_POINT_RADIUS}" 233 | ) 234 | geo_df[ShapesConstants.RADIUS] = ShapesConstants.DEFAULT_POINT_RADIUS 235 | 236 | geo_df.geometry = geo_df.apply(lambda row: row.geometry.buffer(row.radius), axis=1) 237 | return geo_df 238 | 239 | if geo_df.geometry.map(lambda geom: isinstance(geom, MultiPolygon)).all(): 240 | log.warn( 241 | "GeoDataFrame contains only MultiPolygon objects. For each MultiPolygon, only the Polygon with the largest area will be shown" 242 | ) 243 | 244 | geo_df.geometry = geo_df.geometry.map( 245 | lambda multi_polygon: max(multi_polygon.geoms, key=lambda geom: geom.area) 246 | ) 247 | return geo_df 248 | 249 | raise ValueError( 250 | "The provided shapes contain unsupported types, or contain a mix of multiple types. Supported types: Polygon, Point, MultiPolygon." 251 | ) 252 | 253 | 254 | def _spot_transcripts_origin(adata: AnnData) -> tuple[dd.DataFrame, str]: 255 | gene_column = "gene" 256 | df = pd.DataFrame( 257 | {"x": [0] * adata.n_vars, "y": [0] * adata.n_vars, gene_column: adata.var_names} 258 | ) 259 | df = dd.from_pandas(df, chunksize=10_000) 260 | return df, gene_column 261 | -------------------------------------------------------------------------------- /tests/test_instance.py: -------------------------------------------------------------------------------- 1 | # So that there is at least one test. TODO: Add some real tests. 2 | def test_nothing(): 3 | assert True 4 | --------------------------------------------------------------------------------