Skip to content

Commit 444ff00

Browse files
committed
Update to version 0.0.3 and modify parts of the Spot2Cell object
1 parent 7bd75aa commit 444ff00

9 files changed

Lines changed: 295 additions & 90 deletions

File tree

.gitignore

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,4 +159,8 @@ cython_debug/
159159
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
160160
# and can be added to the global gitignore or merged into this file. For a more nuclear
161161
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
162-
#.idea/
162+
.idea/
163+
# pixi environments
164+
.pixi/*
165+
!.pixi/config.toml
166+
.envrc

README.md

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,49 @@
55
[![PyPI - Downloads](https://img.shields.io/pypi/dm/spot2cell)](https://pypistats.org/packages/spot2cell)
66
[![main](https://github.com/saezlab/liana-py/actions/workflows/main.yml/badge.svg)](https://github.com/schapirolabor/spot2cell/actions)
77

8-
This tool assigns single-molecule detected spots to their respective cells in a tissue. It is designed to work with single-molecule FISH data technologies, but can be used with other types of data as well.
8+
Spot2cell is a tool that assigns spots to labeled masks with minimum instalation requirements.
9+
It aims to provide a simple, easy-to-use way for assigning spots to cell masks.
910

1011
## Installation
12+
spot2cell is available on PyPI and Conda-Forge and can be installed using `pip` or `conda`.
1113

14+
Install using `pip`:
1215
```bash
1316
pip install spot2cell
1417
```
1518

19+
Install using `conda`:
20+
```bash
21+
conda install -c conda-forge spot2cell
22+
```
23+
1624
## Docker
25+
1726
Build Docker image from Dockerfile
1827
```bash
1928
docker build -t spot2cell .
2029
```
30+
2131
## CLI
2232

2333
```bash
2434
spot2cell --help
35+
36+
usage: spots2cells.py [-h] [-s SPOTS] [-m MASK] [-x X_COL] [-y Y_COL] [-o OUTPUT] [--verbose] [--version]
37+
38+
This scripts assigns a list of spots to the objects in an instance mask.
39+
40+
options:
41+
-h, --help show this help message and exit
42+
-s, --spots SPOTS Path to the spot table file.
43+
-m, --mask MASK Path to the isntance mask file.
44+
-x, --x-col X_COL Column index of the x coordinate in the spots table [default: 0].
45+
-y, --y-col Y_COL Column index of the y coordinate in the spots table [default: 1].
46+
-o, --output OUTPUT Path to the output csv file.
47+
--verbose Verbose logging.
48+
--version show programs version number and exit
2549
```
50+
## Expected Inputs
51+
Spot2cell expects two inputs:
52+
- `spots`: A comma-separated `.csv` table of spots with x and y coordinates.
53+
- `mask`: An instance mask `.tif` file. In which each object is labeled with a unique positive number and each pixel belongs to exactly one object.

pyproject.toml

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,9 @@
22
requires = ["setuptools>=61.0"]
33
build-backend = "setuptools.build_meta"
44

5-
[tool.setuptools]
6-
packages = ["spot2cell"]
7-
85
[project]
96
name = "spot2cell"
10-
version = "0.0.2"
7+
dynamic = ["version"]
118
authors = [{ name="Miguel A. Ibarra-Arellano", email="c180l058j@mozmail.com"},]
129
license = {text = "AGPL-3.0-or-later"}
1310
description = "From a X,Y list of detected spots, assigns each spot to a cell based on a segmentation mask"
@@ -17,6 +14,11 @@ classifiers = [
1714
"Programming Language :: Python :: 3.9",
1815
"Programming Language :: Python :: 3.10",
1916
"Programming Language :: Python :: 3.11",
17+
"Programming Language :: Python :: 3.12",
18+
"Programming Language :: Python :: 3.13",
19+
"Programming Language :: Python :: 3.14",
20+
"Programming Language :: Python :: 3.15",
21+
"Programming Language :: Python :: 3.16",
2022
"Operating System :: OS Independent",
2123
"Intended Audience :: Science/Research",
2224
"License :: OSI Approved :: GNU Affero General Public License v3"
@@ -33,12 +35,16 @@ keywords = [
3335
"Xenium"
3436
]
3537

38+
[tool.setuptools]
39+
packages = ["spot2cell"]
40+
41+
[tool.setuptools.dynamic]
42+
version = { attr = "spot2cell.__version__" }
43+
3644
[project.scripts]
3745
spots2cells = "spot2cell.scripts.spots2cells:main"
3846

3947
[project.urls]
4048
Repository = "https://github.com/SchapiroLabor/spot2cell"
4149
Homepage = "https://github.com/SchapiroLabor/spot2cell"
4250
Issues = "https://github.com/SchapiroLabor/spot2cell/issues"
43-
44-

spot2cell/__init__.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,4 @@
1+
from . import logger
12
from .spot2cell import Spot2Cell
2-
from .version import __version__
3+
4+
__version__ = "0.0.3"

spot2cell/logger.py

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@
22
import sys
33

44

5-
def set_logger(logger=None,
6-
log_level='info',
7-
log_format='%(asctime)s - %(levelname)s - %(message)s'):
8-
""" Function to set up the handle error logging.
5+
def set_logger(
6+
logger=None,
7+
log_level="info",
8+
log_format="%(asctime)s - %(levelname)s - %(message)s",
9+
):
10+
"""Function to set up the handle error logging.
911
1012
logger (obj) = a logger object (optional, creates a default logger if not provided)
1113
log_level (str) = level of information to print out, options are {info, debug} [Default: info]
@@ -17,9 +19,9 @@ def set_logger(logger=None,
1719
logger = logging.getLogger(__name__)
1820

1921
# Determine log level
20-
if log_level == 'info':
22+
if log_level == "info":
2123
_level = logging.INFO
22-
elif log_level == 'debug':
24+
elif log_level == "debug":
2325
_level = logging.DEBUG
2426
else:
2527
raise ValueError(f"Log level {log_level} not recognized.")

spot2cell/scripts/spots2cells.py

Lines changed: 80 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,96 @@
1-
import os
21
import argparse
3-
from spot2cell import __version__
4-
from spot2cell import Spot2Cell
2+
import os
53
import pathlib
4+
from math import log
65
from pathlib import Path
7-
if os.name == 'nt':
6+
from re import L
7+
from typing import Dict, List, Optional, Sequence, Tuple
8+
9+
from spot2cell import Spot2Cell, __version__, logger
10+
11+
if os.name == "nt":
812
pathlib.PosixPath = pathlib.WindowsPath
913

1014

11-
def get_args():
15+
def CLI() -> argparse.ArgumentParser:
1216
# Create an argument parser
13-
parser = argparse.ArgumentParser(description='Assign spots to cells.')
14-
parser.add_argument('-s', '--spots', type=str, help='Path to the spot table csv file.')
15-
parser.add_argument('-m', '--mask', type=str, help='Path to the cell mask tif file.')
16-
parser.add_argument('-o', '--output', type=str, help='Path to the output csv file.')
17-
parser.add_argument('--version', action='version', version=f'{__version__}')
18-
args = parser.parse_args()
17+
parser = argparse.ArgumentParser(
18+
description="This scripts assigns a list of spots to the objects in an instance mask."
19+
)
20+
parser.add_argument(
21+
"-s",
22+
"--spots",
23+
type=Path,
24+
help="Path to the spot table file.",
25+
)
26+
parser.add_argument(
27+
"-m",
28+
"--mask",
29+
type=Path,
30+
help="Path to the isntance mask file.",
31+
)
32+
parser.add_argument(
33+
"-x",
34+
"--x-col",
35+
type=int,
36+
default=0,
37+
help="Column index of the x coordinate in the spots table [default: 0].",
38+
)
39+
parser.add_argument(
40+
"-y",
41+
"--y-col",
42+
type=int,
43+
default=1,
44+
help="Column index of the y coordinate in the spots table [default: 1].",
45+
)
46+
parser.add_argument(
47+
"-o",
48+
"--output",
49+
type=Path,
50+
help="Path to the output csv file.",
51+
)
52+
parser.add_argument("--verbose", action="store_true", help="Verbose logging.")
53+
parser.add_argument("--version", action="version", version=f"{__version__}")
54+
return parser
55+
56+
57+
def main(argv: Optional[Sequence[str]] = None) -> int:
58+
# Get CLI arguments
59+
parser = CLI()
60+
args = parser.parse_args(argv)
1961

20-
# Standardize paths
21-
# Convert WindowsPath to PosixPath
22-
args.spots = Path(args.spots).resolve()
23-
args.mask = Path(args.mask).resolve()
24-
args.output = Path(args.output).resolve()
62+
# Resolves the paths to absolute paths
63+
args.spots = args.spots.resolve()
64+
args.mask = args.mask.resolve()
65+
args.output = args.output.resolve()
2566

26-
return args
67+
# Set logger
68+
LOGGER = logger.set_logger(log_level="debug" if args.verbose else "info")
69+
LOGGER.info(f"spot2cell version: {__version__}")
70+
LOGGER.info(f"Reading mask from: {args.mask}")
71+
LOGGER.info(f"Reading spots from: {args.spots}")
2772

73+
try:
74+
# Create an instance of the Spot2Cell class.
75+
LOGGER.info(f"Creating Spot2Cell instance")
76+
Spots = Spot2Cell(
77+
args.spots,
78+
args.mask,
79+
x_col=args.x_col,
80+
y_col=args.y_col,
81+
logger=LOGGER,
82+
)
2883

29-
def main():
30-
# Get the arguments
31-
args = get_args()
84+
# Save the assigned spots to a csv file.
85+
LOGGER.info(f"Writing assigned spots to: {args.output}")
86+
Spots.save(args.output)
3287

33-
# Create an instance of the Spot2Cell class.
34-
spots = Spot2Cell(args.spots, args.mask)
88+
return 0
3589

36-
# Save the assigned spots to a csv file.
37-
spots.save(args.output)
90+
except Exception as e:
91+
print(f"spot2cells exit with an error: {e}")
92+
return 1
3893

3994

40-
if __name__ == '__main__':
95+
if __name__ == "__main__":
4196
main()

0 commit comments

Comments
 (0)