Skip to content

Commit ebf189d

Browse files
authored
Merge pull request #65 from HiDiHlabs/io
Support Atera output
2 parents a0fdc6a + 7009e3b commit ebf189d

1 file changed

Lines changed: 73 additions & 21 deletions

File tree

ovrlpy/io.py

Lines changed: 73 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,38 @@ def _filter_genes(df: pl.DataFrame, remove_features: Collection[str]) -> pl.Data
1818
return df
1919

2020

21-
# 10x Xenium
22-
_XENIUM_COLUMNS = {
21+
## 10x Genomics platforms
22+
_10X_GENOMICS_COLUMNS = {
2323
"feature_name": "gene",
2424
"x_location": "x",
2525
"y_location": "y",
2626
"z_location": "z",
2727
}
2828

29+
30+
def _read_10x_parquet(
31+
filepath: Path, *, min_qv: float | None = None, columns: Collection[str] = []
32+
) -> pl.DataFrame:
33+
transcripts = pl.scan_parquet(filepath)
34+
35+
# 'is_gene' column exists for Xenium OA v3 and v4 / Atera
36+
# but not Xenium OA v2 parquet (Xenium OA v1 does not have parquet)
37+
if "is_gene" in transcripts.collect_schema().names():
38+
transcripts = transcripts.filter(pl.col("is_gene"))
39+
40+
if min_qv is not None:
41+
transcripts = transcripts.filter(pl.col("qv") >= min_qv)
42+
43+
with pl.StringCache():
44+
transcripts = (
45+
transcripts.select(columns)
46+
.with_columns(pl.col("feature_name").cast(pl.String).cast(pl.Categorical))
47+
.collect()
48+
)
49+
return transcripts
50+
51+
52+
# 10x Xenium
2953
XENIUM_CTRLS = [
3054
"^BLANK",
3155
"^DeprecatedCodeword",
@@ -68,28 +92,13 @@ def read_Xenium(
6892
polars.DataFrame
6993
"""
7094
filepath = Path(filepath)
71-
columns = list(set(_XENIUM_COLUMNS.keys()) | set(additional_columns))
95+
columns = list(set(_10X_GENOMICS_COLUMNS.keys()) | set(additional_columns))
7296

7397
if filepath.suffix == ".parquet":
74-
transcripts = pl.scan_parquet(filepath)
75-
76-
# 'is_gene' column only exists for Xenium v3 which only has .parquet
77-
if "is_gene" in transcripts.collect_schema().names():
78-
transcripts = transcripts.filter(pl.col("is_gene"))
79-
80-
if min_qv is not None:
81-
transcripts = transcripts.filter(pl.col("qv") >= min_qv)
82-
83-
with pl.StringCache():
84-
transcripts = (
85-
transcripts.select(columns)
86-
.with_columns(
87-
pl.col("feature_name").cast(pl.String).cast(pl.Categorical)
88-
)
89-
.collect()
90-
)
98+
transcripts = _read_10x_parquet(filepath, min_qv=min_qv, columns=columns)
9199

92100
else:
101+
# read Xenium csv i.e. Xenium Onboard Analysis v1 output
93102
if min_qv is not None and "qv" not in additional_columns:
94103
columns.append("qv")
95104
transcripts = pl.read_csv(
@@ -104,7 +113,50 @@ def read_Xenium(
104113
if "qv" not in additional_columns:
105114
transcripts = transcripts.drop("qv")
106115

107-
transcripts = transcripts.rename(_XENIUM_COLUMNS)
116+
transcripts = transcripts.rename(_10X_GENOMICS_COLUMNS)
117+
transcripts = _filter_genes(transcripts, remove_features)
118+
119+
return transcripts
120+
121+
122+
# 10x Atera
123+
124+
# technically they are all dropped due to is_gene filter anyways
125+
ATERA_CTRLS = ["^Intergenic", "^NegControl", "^UnassignedCodeword"]
126+
127+
128+
def read_Atera(
129+
filepath: str | os.PathLike,
130+
*,
131+
min_qv: float | None = None,
132+
remove_features: Collection[str] = ATERA_CTRLS,
133+
additional_columns: Collection[str] = [],
134+
) -> pl.DataFrame:
135+
"""
136+
Read a Xenium transcripts file.
137+
138+
Parameters
139+
----------
140+
filepath : os.PathLike or str
141+
Path to the Atera transcripts file (.parquet). The Zarr-store is currently not supported.
142+
min_qv : float | None, optional
143+
Minimum Phred-scaled quality value (Q-Score) of a transcript to be included.
144+
If `None` no filtering is performed.
145+
remove_features : collections.abc.Collection[str], optional
146+
List of regex patterns to filter the 'feature_name' column,
147+
:py:attr:`ovrlpy.io.ATERA_CTRLS` by default.
148+
additional_columns : collections.abc.Collection[str], optional
149+
Additional columns to load from the transcripts file.
150+
151+
Returns
152+
-------
153+
polars.DataFrame
154+
"""
155+
filepath = Path(filepath)
156+
columns = list(set(_10X_GENOMICS_COLUMNS.keys()) | set(additional_columns))
157+
158+
transcripts = _read_10x_parquet(filepath, min_qv=min_qv, columns=columns)
159+
transcripts = transcripts.rename(_10X_GENOMICS_COLUMNS)
108160
transcripts = _filter_genes(transcripts, remove_features)
109161

110162
return transcripts

0 commit comments

Comments
 (0)