hca-schema-validator 0.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- hca_schema_validator/__init__.py +11 -0
- hca_schema_validator/_vendored/__init__.py +18 -0
- hca_schema_validator/_vendored/cellxgene_schema/LICENSE +20 -0
- hca_schema_validator/_vendored/cellxgene_schema/__init__.py +1 -0
- hca_schema_validator/_vendored/cellxgene_schema/atac_seq.py +675 -0
- hca_schema_validator/_vendored/cellxgene_schema/cli.py +206 -0
- hca_schema_validator/_vendored/cellxgene_schema/env.py +9 -0
- hca_schema_validator/_vendored/cellxgene_schema/gencode.py +163 -0
- hca_schema_validator/_vendored/cellxgene_schema/gencode_files/.gitignore +3 -0
- hca_schema_validator/_vendored/cellxgene_schema/gencode_files/drosophila_melanogaster_diff.txt +42 -0
- hca_schema_validator/_vendored/cellxgene_schema/gencode_files/gene_info.md +29 -0
- hca_schema_validator/_vendored/cellxgene_schema/gencode_files/gene_info.yml +51 -0
- hca_schema_validator/_vendored/cellxgene_schema/gencode_files/genes_caenorhabditis_elegans.csv.gz +0 -0
- hca_schema_validator/_vendored/cellxgene_schema/gencode_files/genes_callithrix_jacchus.csv.gz +0 -0
- hca_schema_validator/_vendored/cellxgene_schema/gencode_files/genes_danio_rerio.csv.gz +0 -0
- hca_schema_validator/_vendored/cellxgene_schema/gencode_files/genes_drosophila_melanogaster.csv.gz +0 -0
- hca_schema_validator/_vendored/cellxgene_schema/gencode_files/genes_ercc.csv.gz +0 -0
- hca_schema_validator/_vendored/cellxgene_schema/gencode_files/genes_gorilla_gorilla.csv.gz +0 -0
- hca_schema_validator/_vendored/cellxgene_schema/gencode_files/genes_homo_sapiens.csv.gz +0 -0
- hca_schema_validator/_vendored/cellxgene_schema/gencode_files/genes_macaca_fascicularis.csv.gz +0 -0
- hca_schema_validator/_vendored/cellxgene_schema/gencode_files/genes_macaca_mulatta.csv.gz +0 -0
- hca_schema_validator/_vendored/cellxgene_schema/gencode_files/genes_microcebus_murinus.csv.gz +0 -0
- hca_schema_validator/_vendored/cellxgene_schema/gencode_files/genes_mus_musculus.csv.gz +0 -0
- hca_schema_validator/_vendored/cellxgene_schema/gencode_files/genes_oryctolagus_cuniculus.csv.gz +0 -0
- hca_schema_validator/_vendored/cellxgene_schema/gencode_files/genes_pan_troglodytes.csv.gz +0 -0
- hca_schema_validator/_vendored/cellxgene_schema/gencode_files/genes_rattus_norvegicus.csv.gz +0 -0
- hca_schema_validator/_vendored/cellxgene_schema/gencode_files/genes_sars_cov_2.csv.gz +0 -0
- hca_schema_validator/_vendored/cellxgene_schema/gencode_files/genes_sus_scrofa.csv.gz +0 -0
- hca_schema_validator/_vendored/cellxgene_schema/gencode_files/homo_sapiens_diff.txt +1237 -0
- hca_schema_validator/_vendored/cellxgene_schema/gencode_files/mus_musculus_diff.txt +549 -0
- hca_schema_validator/_vendored/cellxgene_schema/gencode_files/rattus_norvegicus_diff.txt +7306 -0
- hca_schema_validator/_vendored/cellxgene_schema/map_species.py +40 -0
- hca_schema_validator/_vendored/cellxgene_schema/matrix_utils.py +134 -0
- hca_schema_validator/_vendored/cellxgene_schema/migrate.py +145 -0
- hca_schema_validator/_vendored/cellxgene_schema/migrate_files/automigrate_terms.json +2 -0
- hca_schema_validator/_vendored/cellxgene_schema/migrate_files/donor_updates.json +2 -0
- hca_schema_validator/_vendored/cellxgene_schema/migrate_files/non_csr_list.txt +54 -0
- hca_schema_validator/_vendored/cellxgene_schema/migrate_files/private_non_csr.json +1 -0
- hca_schema_validator/_vendored/cellxgene_schema/migrate_files/title_donor_updates.json +2 -0
- hca_schema_validator/_vendored/cellxgene_schema/ontology_files/.gitignore +1 -0
- hca_schema_validator/_vendored/cellxgene_schema/ontology_parser.py +3 -0
- hca_schema_validator/_vendored/cellxgene_schema/remove_labels.py +74 -0
- hca_schema_validator/_vendored/cellxgene_schema/schema.py +21 -0
- hca_schema_validator/_vendored/cellxgene_schema/schema_definitions/schema_definition.yaml +841 -0
- hca_schema_validator/_vendored/cellxgene_schema/utils.py +242 -0
- hca_schema_validator/_vendored/cellxgene_schema/validate.py +2389 -0
- hca_schema_validator/_vendored/cellxgene_schema/validation_internals/__init__.py +0 -0
- hca_schema_validator/_vendored/cellxgene_schema/validation_internals/check_duplicates.py +87 -0
- hca_schema_validator/_vendored/cellxgene_schema/write_labels.py +403 -0
- hca_schema_validator/schema_definitions/hca_schema_definition.yaml +839 -0
- hca_schema_validator/validator.py +47 -0
- hca_schema_validator-0.2.0.dist-info/METADATA +116 -0
- hca_schema_validator-0.2.0.dist-info/RECORD +55 -0
- hca_schema_validator-0.2.0.dist-info/WHEEL +4 -0
- hca_schema_validator-0.2.0.dist-info/licenses/NOTICE +15 -0
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
|
|
3
|
+
import anndata as ad
|
|
4
|
+
from cellxgene_ontology_guide.ontology_parser import OntologyParser
|
|
5
|
+
|
|
6
|
+
logger = logging.getLogger(__name__)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def map_species(input_file, output_file):
|
|
10
|
+
ontology_parser = OntologyParser("6.0.0")
|
|
11
|
+
adata = ad.read_h5ad(input_file, backed="r")
|
|
12
|
+
map_columns = [
|
|
13
|
+
("organism_cell_type_ontology_term_id", "cell_type_ontology_term_id", "CL"),
|
|
14
|
+
("organism_tissue_ontology_term_id", "tissue_ontology_term_id", "UBERON"),
|
|
15
|
+
]
|
|
16
|
+
for map_from, map_to, ontology in map_columns:
|
|
17
|
+
logger.info(f"Mapping {ontology} terms to {map_to}")
|
|
18
|
+
if not hasattr(adata.obs, map_to):
|
|
19
|
+
logger.info(f"No existing {map_to} column detected, creating one and defaulting to empty strings.")
|
|
20
|
+
adata.obs[map_to] = ""
|
|
21
|
+
for category, group in adata.obs.groupby(map_from):
|
|
22
|
+
try:
|
|
23
|
+
closest_terms = ontology_parser.get_closest_bridge_term_ids(category, ontology)
|
|
24
|
+
except Exception:
|
|
25
|
+
logger.error(f"{category} in {map_from} is not a valid ontology term ID, cannot find {ontology} map.")
|
|
26
|
+
continue
|
|
27
|
+
if len(closest_terms) < 1:
|
|
28
|
+
logger.warning(f"{category} has no closest match for {ontology} in its ancestry tree.")
|
|
29
|
+
elif len(closest_terms) == 1:
|
|
30
|
+
adata.obs.loc[group.index, map_to] = closest_terms[0]
|
|
31
|
+
logger.info(f"Setting {category} {map_to} rows to single closest match: {closest_terms[0]}")
|
|
32
|
+
else:
|
|
33
|
+
logger.warning(
|
|
34
|
+
f"{category} has multiple closest matches for {ontology} in its ancestry tree: "
|
|
35
|
+
f"{closest_terms}. Pick one based on biological context and assign manually."
|
|
36
|
+
)
|
|
37
|
+
adata.obs[map_to] = adata.obs[map_to].astype("category")
|
|
38
|
+
|
|
39
|
+
logger.info(f"Mappings complete, writing to {output_file}")
|
|
40
|
+
adata.write_h5ad(output_file, compression="gzip")
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
from typing import Union
|
|
2
|
+
|
|
3
|
+
import anndata as ad
|
|
4
|
+
import numpy as np
|
|
5
|
+
import sparse as sp
|
|
6
|
+
from anndata.compat import DaskArray
|
|
7
|
+
from dask.array import map_blocks
|
|
8
|
+
from scipy import sparse
|
|
9
|
+
|
|
10
|
+
SPARSE_MATRIX_TYPES = {"csr", "csc", "coo"}
|
|
11
|
+
SUPPORTED_SPARSE_MATRIX_TYPES = {"csr"}
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def determine_matrix_format(matrix: DaskArray) -> str:
|
|
15
|
+
"""
|
|
16
|
+
Given a matrix, returns the format as one of: csc, csr, coo, dense
|
|
17
|
+
or unknown.
|
|
18
|
+
|
|
19
|
+
This mimics the scipy.sparse `format` property, but extends it to
|
|
20
|
+
support ndarray and other classes AnnData may proxy the matrix with.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
# Note: the AnnData proxy classes DO support the `format_str` property, but
|
|
24
|
+
# doing a slice seemed safer, if less performant. Using `format_str`, which
|
|
25
|
+
# currently works, uses private API:
|
|
26
|
+
#
|
|
27
|
+
# >>> return getattr(matrix, "format_str", "dense)
|
|
28
|
+
#
|
|
29
|
+
matrix_format = "unknown"
|
|
30
|
+
try:
|
|
31
|
+
matrix_slice = matrix[0:1, 0:1].compute()
|
|
32
|
+
except AttributeError:
|
|
33
|
+
# compute() may fail on an unknown matrix value. if so, return "unknown"
|
|
34
|
+
return matrix_format
|
|
35
|
+
if isinstance(matrix_slice, sparse.spmatrix):
|
|
36
|
+
matrix_format = matrix_slice.format
|
|
37
|
+
elif isinstance(matrix_slice, np.ndarray):
|
|
38
|
+
matrix_format = "dense"
|
|
39
|
+
assert matrix_format in SPARSE_MATRIX_TYPES.union({"unknown", "dense"})
|
|
40
|
+
return matrix_format
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def compute_column_sums(matrix: Union[DaskArray, np.ndarray, sparse.spmatrix]) -> np.ndarray:
|
|
44
|
+
"""
|
|
45
|
+
Compute column-wise sums for a Dask array (dense or sparse), NumPy ndarray, or SciPy sparse matrix.
|
|
46
|
+
Returns a NumPy array of sums.
|
|
47
|
+
|
|
48
|
+
For example, this matrix:
|
|
49
|
+
[
|
|
50
|
+
[1, 0, 0],
|
|
51
|
+
[1, 2, 0],
|
|
52
|
+
[1, 0, 1],
|
|
53
|
+
]
|
|
54
|
+
|
|
55
|
+
would return a NumPy array of [3, 12, 1]
|
|
56
|
+
"""
|
|
57
|
+
if isinstance(matrix, np.ndarray):
|
|
58
|
+
return matrix.sum(axis=0).ravel()
|
|
59
|
+
|
|
60
|
+
if sparse.issparse(matrix):
|
|
61
|
+
return np.array(matrix.sum(axis=0)).ravel()
|
|
62
|
+
|
|
63
|
+
# Handle Dask array (could be sparse or dense)
|
|
64
|
+
if isinstance(matrix, DaskArray):
|
|
65
|
+
|
|
66
|
+
def to_coo(chunk: Union[np.ndarray, sparse.spmatrix]) -> Union[sp.COO, np.ndarray]:
|
|
67
|
+
# convert to sparse.COO, so we can use dask's internal chunked sum
|
|
68
|
+
if sparse.issparse(chunk):
|
|
69
|
+
return sp.COO.from_scipy_sparse(chunk)
|
|
70
|
+
else:
|
|
71
|
+
return chunk
|
|
72
|
+
|
|
73
|
+
x_coo = matrix.map_blocks(to_coo, dtype=matrix.dtype)
|
|
74
|
+
|
|
75
|
+
# 'split_every' is used to ensure that the computation is split into memory manageable chunks
|
|
76
|
+
col_sums = x_coo.sum(axis=0, split_every=8).compute()
|
|
77
|
+
return col_sums
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def calculate_matrix_nonzero(matrix: DaskArray) -> int:
|
|
81
|
+
def count_nonzeros(matrix_chunk: Union[np.ndarray, sparse.spmatrix], is_sparse_matrix: bool) -> np.array:
|
|
82
|
+
nnz = matrix_chunk.nnz if is_sparse_matrix else np.count_nonzero(matrix_chunk)
|
|
83
|
+
return np.array([nnz])
|
|
84
|
+
|
|
85
|
+
is_sparse_matrix = determine_matrix_format(matrix) in SPARSE_MATRIX_TYPES
|
|
86
|
+
if len(matrix.chunks[0]) > 1:
|
|
87
|
+
nonzeros = map_blocks(count_nonzeros, matrix, is_sparse_matrix, drop_axis=1, dtype=int).compute().sum()
|
|
88
|
+
else:
|
|
89
|
+
nonzeros = count_nonzeros(matrix.compute(), is_sparse_matrix)[0]
|
|
90
|
+
return nonzeros
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def check_non_csr_matrixes(adata: ad.AnnData):
|
|
94
|
+
"""
|
|
95
|
+
Check X, raw.X and layers matrices for having more than 50% zeros and not being csr_matrix
|
|
96
|
+
|
|
97
|
+
If found, convert to csr_matrix
|
|
98
|
+
"""
|
|
99
|
+
|
|
100
|
+
def get_sparsity(matrix: DaskArray, format: str):
|
|
101
|
+
is_sparse_matrix = format in SPARSE_MATRIX_TYPES
|
|
102
|
+
nnz = calculate_matrix_nonzero(matrix, is_sparse_matrix)
|
|
103
|
+
sparsity = 1 - nnz / np.prod(matrix.shape)
|
|
104
|
+
return sparsity
|
|
105
|
+
|
|
106
|
+
format = determine_matrix_format(adata.X)
|
|
107
|
+
if format != "csr" and get_sparsity(adata.X, format) >= 0.5:
|
|
108
|
+
adata.X = adata.X.map_blocks(sparse.csr_matrix, dtype=adata.X.dtype)
|
|
109
|
+
|
|
110
|
+
if adata.raw is not None:
|
|
111
|
+
format = determine_matrix_format(adata.raw.X)
|
|
112
|
+
if format != "csr" and get_sparsity(adata.raw.X, format) >= 0.5:
|
|
113
|
+
raw_adata = ad.AnnData(adata.raw.X, var=adata.raw.var, obs=adata.obs)
|
|
114
|
+
raw_adata.X = raw_adata.X.map_blocks(sparse.csr_matrix, dtype=raw_adata.X.dtype)
|
|
115
|
+
adata.raw = raw_adata
|
|
116
|
+
del raw_adata
|
|
117
|
+
|
|
118
|
+
for layer in adata.layers:
|
|
119
|
+
format = determine_matrix_format(adata.layers[layer])
|
|
120
|
+
if format != "csr" and get_sparsity(adata.layers[layer], format) >= 0.5:
|
|
121
|
+
adata.layers[layer] = adata.layers[layer].map_blocks(sparse.csr_matrix, dtype=adata.layers[layer].X.dtype)
|
|
122
|
+
|
|
123
|
+
return adata
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def debug_print_matrix(matrix: ad.AnnData, matrix_name: str, max_rows=20, max_cols=20):
|
|
127
|
+
computed = matrix[:max_rows, :max_cols].compute()
|
|
128
|
+
|
|
129
|
+
# If sparse, convert to dense for display
|
|
130
|
+
if hasattr(computed, "toarray"):
|
|
131
|
+
computed = computed.toarray()
|
|
132
|
+
|
|
133
|
+
print(f"{matrix_name} (first {max_rows} rows, {max_cols} cols):")
|
|
134
|
+
print(np.round(computed, 2)) # round for readability
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import dask
|
|
2
|
+
|
|
3
|
+
from . import utils
|
|
4
|
+
|
|
5
|
+
# fmt: off
|
|
6
|
+
# ONTOLOGY TERMS TO UPDATE ACROSS ALL DATASETS IN CORPUS
|
|
7
|
+
# Initialization is AUTOMATED for newly deprecated terms that have 'Replaced By' terms in their ontology files
|
|
8
|
+
|
|
9
|
+
# Curators should review the monthly 'Curator Report' and add deprecated term replacements to corresponding map if
|
|
10
|
+
# 'Replaced By' is not available for a deprecated term.
|
|
11
|
+
|
|
12
|
+
# If Curators have non-deprecated term changes to apply to all datasets in the corpus where applicable,
|
|
13
|
+
# add them here.
|
|
14
|
+
# added two CL terms here instead of as applicable to individual datasets
|
|
15
|
+
ONTOLOGY_TERM_OBS_MAPS = {
|
|
16
|
+
"assay": {
|
|
17
|
+
},
|
|
18
|
+
"cell_type": {
|
|
19
|
+
"CL:0000651": "CL:0002181",
|
|
20
|
+
"CL:0000212": "CL:0000677"
|
|
21
|
+
},
|
|
22
|
+
"development_stage": {
|
|
23
|
+
},
|
|
24
|
+
"disease": {
|
|
25
|
+
},
|
|
26
|
+
"self_reported_ethnicity": {
|
|
27
|
+
},
|
|
28
|
+
"sex": {
|
|
29
|
+
},
|
|
30
|
+
"tissue": {
|
|
31
|
+
},
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
ONTOLOGY_TERM_UNS_MAPS = {
|
|
35
|
+
"organism": {
|
|
36
|
+
},
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
DEPRECATED_FEATURE_IDS = [
|
|
40
|
+
]
|
|
41
|
+
|
|
42
|
+
# Dictionary for CURATOR-DEFINED remapping of deprecated feature IDs, if any, to new feature IDs.
|
|
43
|
+
GENCODE_MAPPER = {}
|
|
44
|
+
# fmt: on
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def migrate(input_file, output_file, collection_id, dataset_id):
|
|
48
|
+
print(f"Converting {input_file} into {output_file}")
|
|
49
|
+
|
|
50
|
+
dataset = utils.read_h5ad(input_file)
|
|
51
|
+
|
|
52
|
+
# Migrate organism_ontology_term_id for 6.0.0
|
|
53
|
+
dataset = utils.move_column_from_obs_to_uns(adata=dataset, column_name="organism_ontology_term_id")
|
|
54
|
+
|
|
55
|
+
# Migrate delimter from `,` to ` || ` for self_reported_ethnicity_ontology_term_id
|
|
56
|
+
utils.replace_delimiter(
|
|
57
|
+
dataframe=dataset.obs,
|
|
58
|
+
old_delimiter=",",
|
|
59
|
+
new_delimiter=" || ",
|
|
60
|
+
column_name="self_reported_ethnicity_ontology_term_id",
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
# AUTOMATED, DO NOT CHANGE
|
|
64
|
+
for ontology_name, deprecated_term_map in ONTOLOGY_TERM_OBS_MAPS.items():
|
|
65
|
+
utils.replace_ontology_term(dataset.obs, ontology_name, deprecated_term_map)
|
|
66
|
+
for ontology_name, deprecated_term_map in ONTOLOGY_TERM_UNS_MAPS.items():
|
|
67
|
+
dataset = utils.replace_ontology_term_uns(dataset, ontology_name, deprecated_term_map)
|
|
68
|
+
|
|
69
|
+
# CURATOR-DEFINED, DATASET-SPECIFIC UPDATES
|
|
70
|
+
# Use the template below to define dataset and collection specific ontology changes. Will only apply to dataset
|
|
71
|
+
# if it matches a condition.
|
|
72
|
+
# If no such changes are needed, leave blank
|
|
73
|
+
# Examples:
|
|
74
|
+
# if dataset_id == "<dataset_1_id>":
|
|
75
|
+
# <no further logic necessary>
|
|
76
|
+
# utils.replace_ontology_term(df, <ontology_name>, {"term_to_replace": "replacement_term", ...})
|
|
77
|
+
# elif dataset_id == "<dataset_2_id>":
|
|
78
|
+
# <custom transformation logic beyond scope of util functions>
|
|
79
|
+
# elif collection_id == "<collection_1_id>":
|
|
80
|
+
# <no further logic necessary>
|
|
81
|
+
# utils.replace_ontology_term(df, <ontology_name>, {"term_to_replace": "replacement_term", ...})
|
|
82
|
+
# elif collection_id == "<collection_2_id>":
|
|
83
|
+
# <custom transformation logic beyond scope of replace_ontology_term>
|
|
84
|
+
# ...
|
|
85
|
+
|
|
86
|
+
if (
|
|
87
|
+
dataset.uns["title"]
|
|
88
|
+
== "Time-resolved single-cell analysis of Brca1 associated mammary tumourigenesis reveals aberrant differentiation of luminal progenitors"
|
|
89
|
+
):
|
|
90
|
+
utils.replace_ontology_term(dataset.obs, "cell_type", {"CL:0000451": "CL:4047054"})
|
|
91
|
+
|
|
92
|
+
if collection_id == "126afc71-47fb-4e9d-8aaf-9d9f61e0ac77":
|
|
93
|
+
utils.map_ontology_term(
|
|
94
|
+
dataset.obs,
|
|
95
|
+
"disease",
|
|
96
|
+
"disease_group",
|
|
97
|
+
{
|
|
98
|
+
"Healthy": "PATO:0000461",
|
|
99
|
+
"HIV": "MONDO:0005109",
|
|
100
|
+
"VL_HIV": "MONDO:0005109 || MONDO:0005445",
|
|
101
|
+
"aL_HIV": "MONDO:0005109 || MONDO:0011989",
|
|
102
|
+
"cVL_HIV": "MONDO:0005109 || MONDO:0005445",
|
|
103
|
+
"pVL_HIV": "MONDO:0005109 || MONDO:0005445",
|
|
104
|
+
},
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
if collection_id == "eeba7193-4d32-46bd-a21b-089936d60601":
|
|
108
|
+
utils.map_ontology_term(
|
|
109
|
+
dataset.obs,
|
|
110
|
+
"disease",
|
|
111
|
+
"sample_id",
|
|
112
|
+
{
|
|
113
|
+
"mraf1": "MONDO:0004981 || MONDO:1030008",
|
|
114
|
+
"mraf2": "MONDO:0004981 || MONDO:1030008",
|
|
115
|
+
"mraf3": "MONDO:0004981 || MONDO:1030008",
|
|
116
|
+
"mraf4": "MONDO:0004981 || MONDO:1030008",
|
|
117
|
+
"mraf7": "MONDO:0004981 || MONDO:1030008",
|
|
118
|
+
"mraf13": "MONDO:0004981 || MONDO:1030008",
|
|
119
|
+
"mraf14": "MONDO:0004981 || MONDO:1030008",
|
|
120
|
+
},
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
# errors found in 3 dev migration runs
|
|
124
|
+
# will just check for key and delete instead of list of dataset_ids
|
|
125
|
+
if "organism" in dataset.uns:
|
|
126
|
+
del dataset.uns["organism"]
|
|
127
|
+
|
|
128
|
+
# feature_is_filtered set incorrectly for dataset with no raw.X matrix, should be all False
|
|
129
|
+
if dataset_id == "ee195b7d-184d-4dfa-9b1c-51a7e601ac11":
|
|
130
|
+
dataset.var["feature_is_filtered"] = False
|
|
131
|
+
|
|
132
|
+
# 11 datasets with "organism_ontology_term_id_colors" in uns, all can be removed
|
|
133
|
+
# seems easy enough to quickly check for key, can also list out the 11 dataset_ids
|
|
134
|
+
if "organism_ontology_term_id_colors" in dataset.uns:
|
|
135
|
+
del dataset.uns["organism_ontology_term_id_colors"]
|
|
136
|
+
|
|
137
|
+
if GENCODE_MAPPER:
|
|
138
|
+
dataset = utils.remap_deprecated_features(adata=dataset, remapped_features=GENCODE_MAPPER)
|
|
139
|
+
|
|
140
|
+
# AUTOMATED, DO NOT CHANGE -- IF GENCODE UPDATED, DEPRECATED FEATURE FILTERING ALGORITHM WILL GO HERE.
|
|
141
|
+
if DEPRECATED_FEATURE_IDS:
|
|
142
|
+
dataset = utils.remove_deprecated_features(adata=dataset, deprecated=DEPRECATED_FEATURE_IDS)
|
|
143
|
+
|
|
144
|
+
with dask.config.set(scheduler="single-threaded"):
|
|
145
|
+
dataset.write_h5ad(output_file, compression="gzip")
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
7f7faf6b-f11d-4f07-bc1c-188a4472748d
|
|
2
|
+
7c497c66-c2e2-4291-8b96-339e5e65f314
|
|
3
|
+
7b55fe5c-d5c8-48e0-a1a3-54c5b9074f3f
|
|
4
|
+
2ac76f1b-43ef-4271-8686-2f165570989f
|
|
5
|
+
f9cfac8d-bff6-47a2-a1f6-503827d375f5
|
|
6
|
+
ea95cdd5-cf7e-4d5d-a0bd-032776d32ae5
|
|
7
|
+
e34f3f89-3a48-4173-a06a-9e9803f2e2b9
|
|
8
|
+
e2a325e2-5633-4950-ae46-ebff70979e3b
|
|
9
|
+
d5a2d011-3765-4dad-ac36-3fff087589fe
|
|
10
|
+
b6e502d7-0d07-408e-a291-91b63af0a40a
|
|
11
|
+
a30db232-08a6-48f0-ab9f-ad6977b8a6b1
|
|
12
|
+
9ea52f85-a35e-4b5b-9207-e2f1de7dbbcd
|
|
13
|
+
93091496-be48-4122-b945-9af9c22a7535
|
|
14
|
+
78f40dd6-ed22-470d-8547-6fb5746c25da
|
|
15
|
+
6929d6d1-6f81-44d7-bd35-5a580259af81
|
|
16
|
+
5e7fff82-b8eb-4220-bf65-c6feb6765ea1
|
|
17
|
+
4e374783-0152-423e-bcb6-b87cc232f64d
|
|
18
|
+
4859642c-d155-437b-adf5-83ca6c1bef67
|
|
19
|
+
390b376d-8857-4b4b-991f-0d7326938172
|
|
20
|
+
34229bdc-a895-4394-8820-574e4028d8c6
|
|
21
|
+
31285bcd-9a28-48ce-8cc0-543af80261d3
|
|
22
|
+
3079e9b0-c6db-45c8-b998-a4555a73968e
|
|
23
|
+
1fc48734-35ee-4372-885c-95fd2674c75a
|
|
24
|
+
1a974af7-8b4b-4abb-8aba-23e2979d9518
|
|
25
|
+
171b03c9-516b-499c-a434-fa4f70191988
|
|
26
|
+
149b2c3f-ee11-47a7-984b-923570280bd7
|
|
27
|
+
0de831e0-a525-4ec5-b717-df56f2de2bf0
|
|
28
|
+
0087cde2-967d-4f7c-8e6e-40e4c9ad1891
|
|
29
|
+
e04cb7d9-7487-4bf7-99db-b16f236a351a
|
|
30
|
+
b2dd6bc9-36ed-4cd4-a2b3-51f6a2386afa
|
|
31
|
+
a19d1667-a7b5-4556-9e5f-f9bfa690c0f1
|
|
32
|
+
510151f7-0cf7-4679-a15b-b7cd824f19bb
|
|
33
|
+
30a2789f-7b76-4414-9c63-1deb3eacdde1
|
|
34
|
+
20de7fd1-ab21-45b8-a1e2-f7b36261f8e2
|
|
35
|
+
f0939ae1-0877-42b2-bc0e-3249a42c2c0f
|
|
36
|
+
e8d1c9cc-29e6-4b51-90f1-29e75de6098f
|
|
37
|
+
d6f3c096-a6f0-4e67-91ad-514bd286f13e
|
|
38
|
+
a12d95f7-480f-4c8a-bf24-302175b27bc4
|
|
39
|
+
8f83f50e-c091-4095-8456-a7acf6c9d423
|
|
40
|
+
52f0b078-0fb7-4cdb-81f8-d1bcf7b4468b
|
|
41
|
+
427bfbe6-1141-4505-98e3-38aa05339310
|
|
42
|
+
3ed61f1e-bd31-411e-b4aa-9729e8437732
|
|
43
|
+
38e5738d-3870-433b-858a-5c364f322439
|
|
44
|
+
346c5aad-b034-4248-8cbe-0a05fd634b9c
|
|
45
|
+
33da10b0-9c1d-4c82-9b14-c67cdcf9fae5
|
|
46
|
+
0500e103-38db-456d-9c3f-b96b8a693ab2
|
|
47
|
+
cb34cb2d-aee2-4272-86ed-f8e1af870e52
|
|
48
|
+
c20f1b97-0d47-4192-af0a-1e012621f8d9
|
|
49
|
+
3bbb6cf9-72b9-41be-b568-656de6eb18b5
|
|
50
|
+
e40c6272-af77-4a10-9385-62a398884f27
|
|
51
|
+
d6dfdef1-406d-4efb-808c-3c5eddbfe0cb
|
|
52
|
+
6a270451-b4d9-43e0-aa89-e33aac1ac74b
|
|
53
|
+
c888b684-6c51-431f-972a-6c963044cef0
|
|
54
|
+
ae4f8ddd-cac9-4172-9681-2175da462f2e
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
[["Single-cell characterization of the stress response in the adult ventral hippocampus", "Ctrl_401"], ["Single-cell RNA sequencing of human intestinal blood endothelial cells", "donor_ulxdkr"], ["Kidney-injury enriched MKA", "Kirita20"]]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
*owl
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
|
|
3
|
+
import anndata as ad
|
|
4
|
+
from pandas import DataFrame
|
|
5
|
+
|
|
6
|
+
from . import schema
|
|
7
|
+
from .utils import getattr_anndata
|
|
8
|
+
|
|
9
|
+
logger = logging.getLogger(__name__)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class AnnDataLabelRemover:
|
|
13
|
+
"""
|
|
14
|
+
From valid h5ad, handles writing a new h5ad file with appended ontology/gene labels removed
|
|
15
|
+
from adata.obs, adata.var, and adata.raw.var respectively as indicated in the schema definition
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
def __init__(self, adata: ad.AnnData = None):
|
|
19
|
+
self.adata = adata
|
|
20
|
+
self.schema_def = schema.get_schema_definition()
|
|
21
|
+
|
|
22
|
+
def remove_labels(self):
|
|
23
|
+
"""
|
|
24
|
+
From a valid (per cellxgene's schema) adata, this function parses the associated schema definition for
|
|
25
|
+
'add_labels.to_column' annotations, and removes specified columns from self.adata
|
|
26
|
+
"""
|
|
27
|
+
for component_name in ["obs", "var", "raw.var", "uns"]:
|
|
28
|
+
# If the component does not exist, skip (this is for raw.var)
|
|
29
|
+
component = getattr_anndata(self.adata, component_name)
|
|
30
|
+
if component is None:
|
|
31
|
+
continue
|
|
32
|
+
|
|
33
|
+
component_def = self.schema_def["components"][component_name]
|
|
34
|
+
|
|
35
|
+
# Doing it for columns
|
|
36
|
+
if "columns" in component_def:
|
|
37
|
+
for column_def in component_def["columns"].values():
|
|
38
|
+
if "add_labels" in column_def:
|
|
39
|
+
self._remove_columns(component, column_def)
|
|
40
|
+
|
|
41
|
+
# Remove automatically annotated columns
|
|
42
|
+
if "reserved_columns" in component_def:
|
|
43
|
+
for field in component_def["reserved_columns"]:
|
|
44
|
+
del component[field]
|
|
45
|
+
|
|
46
|
+
# Doing it for index
|
|
47
|
+
if "index" in component_def:
|
|
48
|
+
index_def = component_def["index"]
|
|
49
|
+
if "add_labels" in index_def:
|
|
50
|
+
self._remove_columns(component, index_def)
|
|
51
|
+
|
|
52
|
+
# Remove any labels added as dict keys
|
|
53
|
+
if "keys" in component_def:
|
|
54
|
+
for key in component_def["keys"]:
|
|
55
|
+
key_def = component_def["keys"][key]
|
|
56
|
+
if "add_labels" in key_def:
|
|
57
|
+
key_to_remove = key_def["add_labels"][0]["to_key"]
|
|
58
|
+
del component[key_to_remove]
|
|
59
|
+
|
|
60
|
+
def _remove_columns(self, component: DataFrame, subcomponent_definition: dict):
|
|
61
|
+
"""
|
|
62
|
+
Given an adata component and subcomponent definition, this function deletes all existing columns in the
|
|
63
|
+
self.adata component that are defined as added labels ('add_labels.to_column') in the subcomponent definition.
|
|
64
|
+
|
|
65
|
+
:param pd.Dataframe component: dataframe within adata dataset (i.e. 'obs', 'var', 'raw.var')
|
|
66
|
+
:param dict subcomponent_definition: yaml-defined schema for subcomponent of component (i.e. index, or a
|
|
67
|
+
particular column)
|
|
68
|
+
|
|
69
|
+
:rtype None
|
|
70
|
+
"""
|
|
71
|
+
for label_def in subcomponent_definition["add_labels"]:
|
|
72
|
+
column_name = label_def["to_column"]
|
|
73
|
+
if column_name in component:
|
|
74
|
+
del component[column_name]
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import semver
|
|
2
|
+
import yaml
|
|
3
|
+
|
|
4
|
+
from . import __schema_version__, env
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def get_schema_definition() -> dict:
|
|
8
|
+
"""
|
|
9
|
+
Look up and read the current schema definition
|
|
10
|
+
:return The schema definition
|
|
11
|
+
:rtype dict
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
path = env.SCHEMA_DEFINITION_FILE
|
|
15
|
+
with open(path) as fp:
|
|
16
|
+
return yaml.load(fp, Loader=yaml.FullLoader)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def get_current_schema_version() -> str:
|
|
20
|
+
current_version: semver.Version = semver.Version.parse(__schema_version__)
|
|
21
|
+
return f"{str(current_version.major)}.{str(current_version.minor)}.0"
|