├── .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 | [](https://pypi.org/project/spatialdata_xenium_explorer)
4 | [](https://pepy.tech/project/spatialdata_xenium_explorer)
5 | [](https://quentinblampey.github.io/spatialdata_xenium_explorer/)
6 | 
7 | [](https://github.com/python/black)
8 | [](https://github.com/quentinblampey/spatialdata_xenium_explorer/blob/master/LICENSE)
9 | [](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 |
--------------------------------------------------------------------------------