cellmaps-utils 0.8.0__tar.gz → 0.9.0__tar.gz
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.
- {cellmaps_utils-0.8.0 → cellmaps_utils-0.9.0}/HISTORY.rst +7 -0
- {cellmaps_utils-0.8.0 → cellmaps_utils-0.9.0}/PKG-INFO +10 -14
- {cellmaps_utils-0.8.0 → cellmaps_utils-0.9.0}/README.rst +1 -1
- {cellmaps_utils-0.8.0 → cellmaps_utils-0.9.0}/cellmaps_utils/__init__.py +1 -1
- {cellmaps_utils-0.8.0 → cellmaps_utils-0.9.0}/cellmaps_utils/provenance.py +10 -1
- cellmaps_utils-0.9.0/cellmaps_utils/statsgenerator.py +237 -0
- {cellmaps_utils-0.8.0 → cellmaps_utils-0.9.0}/cellmaps_utils.egg-info/PKG-INFO +10 -14
- {cellmaps_utils-0.8.0 → cellmaps_utils-0.9.0}/cellmaps_utils.egg-info/SOURCES.txt +3 -0
- cellmaps_utils-0.9.0/tests/.DS_Store +0 -0
- {cellmaps_utils-0.8.0 → cellmaps_utils-0.9.0}/tests/test_provenance.py +54 -0
- cellmaps_utils-0.9.0/tests/test_statsgenerator.py +127 -0
- {cellmaps_utils-0.8.0 → cellmaps_utils-0.9.0}/AUTHORS.rst +0 -0
- {cellmaps_utils-0.8.0 → cellmaps_utils-0.9.0}/CONTRIBUTING.rst +0 -0
- {cellmaps_utils-0.8.0 → cellmaps_utils-0.9.0}/LICENSE +0 -0
- {cellmaps_utils-0.8.0 → cellmaps_utils-0.9.0}/MANIFEST.in +0 -0
- {cellmaps_utils-0.8.0 → cellmaps_utils-0.9.0}/cellmaps_utils/apms_readme.txt +0 -0
- {cellmaps_utils-0.8.0 → cellmaps_utils-0.9.0}/cellmaps_utils/apmstool.py +0 -0
- {cellmaps_utils-0.8.0 → cellmaps_utils-0.9.0}/cellmaps_utils/basecmdtool.py +0 -0
- {cellmaps_utils-0.8.0 → cellmaps_utils-0.9.0}/cellmaps_utils/cellmaps_utilscmd.py +0 -0
- {cellmaps_utils-0.8.0 → cellmaps_utils-0.9.0}/cellmaps_utils/challenge.py +0 -0
- {cellmaps_utils-0.8.0 → cellmaps_utils-0.9.0}/cellmaps_utils/constants.py +0 -0
- {cellmaps_utils-0.8.0 → cellmaps_utils-0.9.0}/cellmaps_utils/crispr_readme.txt +0 -0
- {cellmaps_utils-0.8.0 → cellmaps_utils-0.9.0}/cellmaps_utils/crisprtool.py +0 -0
- {cellmaps_utils-0.8.0 → cellmaps_utils-0.9.0}/cellmaps_utils/ddotconverter.py +0 -0
- {cellmaps_utils-0.8.0 → cellmaps_utils-0.9.0}/cellmaps_utils/exceptions.py +0 -0
- {cellmaps_utils-0.8.0 → cellmaps_utils-0.9.0}/cellmaps_utils/hcx_utils.py +0 -0
- {cellmaps_utils-0.8.0 → cellmaps_utils-0.9.0}/cellmaps_utils/hidefconverter.py +0 -0
- {cellmaps_utils-0.8.0 → cellmaps_utils-0.9.0}/cellmaps_utils/hierdiff.py +0 -0
- {cellmaps_utils-0.8.0 → cellmaps_utils-0.9.0}/cellmaps_utils/ifimage_readme.txt +0 -0
- {cellmaps_utils-0.8.0 → cellmaps_utils-0.9.0}/cellmaps_utils/iftool.py +0 -0
- {cellmaps_utils-0.8.0 → cellmaps_utils-0.9.0}/cellmaps_utils/logutils.py +0 -0
- {cellmaps_utils-0.8.0 → cellmaps_utils-0.9.0}/cellmaps_utils/music_utils.py +0 -0
- {cellmaps_utils-0.8.0 → cellmaps_utils-0.9.0}/cellmaps_utils/ndexupload.py +0 -0
- {cellmaps_utils-0.8.0 → cellmaps_utils-0.9.0}/cellmaps_utils/tabletool.py +0 -0
- {cellmaps_utils-0.8.0 → cellmaps_utils-0.9.0}/cellmaps_utils.egg-info/dependency_links.txt +0 -0
- {cellmaps_utils-0.8.0 → cellmaps_utils-0.9.0}/cellmaps_utils.egg-info/not-zip-safe +0 -0
- {cellmaps_utils-0.8.0 → cellmaps_utils-0.9.0}/cellmaps_utils.egg-info/requires.txt +0 -0
- {cellmaps_utils-0.8.0 → cellmaps_utils-0.9.0}/cellmaps_utils.egg-info/top_level.txt +0 -0
- {cellmaps_utils-0.8.0 → cellmaps_utils-0.9.0}/docs/Makefile +0 -0
- {cellmaps_utils-0.8.0 → cellmaps_utils-0.9.0}/docs/_build/html/_static/file.png +0 -0
- {cellmaps_utils-0.8.0 → cellmaps_utils-0.9.0}/docs/_build/html/_static/minus.png +0 -0
- {cellmaps_utils-0.8.0 → cellmaps_utils-0.9.0}/docs/_build/html/_static/plus.png +0 -0
- {cellmaps_utils-0.8.0 → cellmaps_utils-0.9.0}/docs/authors.rst +0 -0
- {cellmaps_utils-0.8.0 → cellmaps_utils-0.9.0}/docs/cellmaps_utils.rst +0 -0
- {cellmaps_utils-0.8.0 → cellmaps_utils-0.9.0}/docs/cm4ai_data_release.rst +0 -0
- {cellmaps_utils-0.8.0 → cellmaps_utils-0.9.0}/docs/conf.py +0 -0
- {cellmaps_utils-0.8.0 → cellmaps_utils-0.9.0}/docs/contributing.rst +0 -0
- {cellmaps_utils-0.8.0 → cellmaps_utils-0.9.0}/docs/cx2_to_hcx_conversion.rst +0 -0
- {cellmaps_utils-0.8.0 → cellmaps_utils-0.9.0}/docs/ddot_converters.rst +0 -0
- {cellmaps_utils-0.8.0 → cellmaps_utils-0.9.0}/docs/devbranches.rst +0 -0
- {cellmaps_utils-0.8.0 → cellmaps_utils-0.9.0}/docs/developer.rst +0 -0
- {cellmaps_utils-0.8.0 → cellmaps_utils-0.9.0}/docs/hidef_converters.rst +0 -0
- {cellmaps_utils-0.8.0 → cellmaps_utils-0.9.0}/docs/hierarchical_networks.rst +0 -0
- {cellmaps_utils-0.8.0 → cellmaps_utils-0.9.0}/docs/hierarchy_robustness.rst +0 -0
- {cellmaps_utils-0.8.0 → cellmaps_utils-0.9.0}/docs/history.rst +0 -0
- {cellmaps_utils-0.8.0 → cellmaps_utils-0.9.0}/docs/in_project.rst +0 -0
- {cellmaps_utils-0.8.0 → cellmaps_utils-0.9.0}/docs/index.rst +0 -0
- {cellmaps_utils-0.8.0 → cellmaps_utils-0.9.0}/docs/installation.rst +0 -0
- {cellmaps_utils-0.8.0 → cellmaps_utils-0.9.0}/docs/integrationtesting.rst +0 -0
- {cellmaps_utils-0.8.0 → cellmaps_utils-0.9.0}/docs/make.bat +0 -0
- {cellmaps_utils-0.8.0 → cellmaps_utils-0.9.0}/docs/modules.rst +0 -0
- {cellmaps_utils-0.8.0 → cellmaps_utils-0.9.0}/docs/newrelease.rst +0 -0
- {cellmaps_utils-0.8.0 → cellmaps_utils-0.9.0}/docs/outputs.rst +0 -0
- {cellmaps_utils-0.8.0 → cellmaps_utils-0.9.0}/docs/pypircfile.rst +0 -0
- {cellmaps_utils-0.8.0 → cellmaps_utils-0.9.0}/docs/upload_to_ndex.rst +0 -0
- {cellmaps_utils-0.8.0 → cellmaps_utils-0.9.0}/docs/usage.rst +0 -0
- {cellmaps_utils-0.8.0 → cellmaps_utils-0.9.0}/docs/versioningscheme.rst +0 -0
- {cellmaps_utils-0.8.0 → cellmaps_utils-0.9.0}/pyproject.toml +0 -0
- {cellmaps_utils-0.8.0 → cellmaps_utils-0.9.0}/setup.cfg +0 -0
- {cellmaps_utils-0.8.0 → cellmaps_utils-0.9.0}/setup.py +0 -0
- {cellmaps_utils-0.8.0 → cellmaps_utils-0.9.0}/tests/__init__.py +0 -0
- {cellmaps_utils-0.8.0 → cellmaps_utils-0.9.0}/tests/data/ddot_output.ont +0 -0
- {cellmaps_utils-0.8.0 → cellmaps_utils-0.9.0}/tests/data/hidef_output.edges +0 -0
- {cellmaps_utils-0.8.0 → cellmaps_utils-0.9.0}/tests/data/hidef_output.nodes +0 -0
- {cellmaps_utils-0.8.0 → cellmaps_utils-0.9.0}/tests/data/images/blue.jpg +0 -0
- {cellmaps_utils-0.8.0 → cellmaps_utils-0.9.0}/tests/data/images/green.jpg +0 -0
- {cellmaps_utils-0.8.0 → cellmaps_utils-0.9.0}/tests/data/images/red.jpg +0 -0
- {cellmaps_utils-0.8.0 → cellmaps_utils-0.9.0}/tests/data/images/yellow.jpg +0 -0
- {cellmaps_utils-0.8.0 → cellmaps_utils-0.9.0}/tests/data/parent_edgelist +0 -0
- {cellmaps_utils-0.8.0 → cellmaps_utils-0.9.0}/tests/data/reactome_ddot.txt +0 -0
- {cellmaps_utils-0.8.0 → cellmaps_utils-0.9.0}/tests/data/repl1_example.tsv +0 -0
- {cellmaps_utils-0.8.0 → cellmaps_utils-0.9.0}/tests/data/repl1_repl2_combined.tsv +0 -0
- {cellmaps_utils-0.8.0 → cellmaps_utils-0.9.0}/tests/data/repl1_repl2_id_mapping.json +0 -0
- {cellmaps_utils-0.8.0 → cellmaps_utils-0.9.0}/tests/data/repl2_example.tsv +0 -0
- {cellmaps_utils-0.8.0 → cellmaps_utils-0.9.0}/tests/data/ro-crate-metadata.json +0 -0
- {cellmaps_utils-0.8.0 → cellmaps_utils-0.9.0}/tests/data/test_password +0 -0
- {cellmaps_utils-0.8.0 → cellmaps_utils-0.9.0}/tests/data/testsolrepl1.tsv +0 -0
- {cellmaps_utils-0.8.0 → cellmaps_utils-0.9.0}/tests/data/testsolrepl2.tsv +0 -0
- {cellmaps_utils-0.8.0 → cellmaps_utils-0.9.0}/tests/data/testsolution.csv +0 -0
- {cellmaps_utils-0.8.0 → cellmaps_utils-0.9.0}/tests/data/testsolution_genes.cx2 +0 -0
- {cellmaps_utils-0.8.0 → cellmaps_utils-0.9.0}/tests/test_apmstool.py +0 -0
- {cellmaps_utils-0.8.0 → cellmaps_utils-0.9.0}/tests/test_basecommandlinetool.py +0 -0
- {cellmaps_utils-0.8.0 → cellmaps_utils-0.9.0}/tests/test_challenge.py +0 -0
- {cellmaps_utils-0.8.0 → cellmaps_utils-0.9.0}/tests/test_crisprtool.py +0 -0
- {cellmaps_utils-0.8.0 → cellmaps_utils-0.9.0}/tests/test_ddotconverter.py +0 -0
- {cellmaps_utils-0.8.0 → cellmaps_utils-0.9.0}/tests/test_helloworldcommand.py +0 -0
- {cellmaps_utils-0.8.0 → cellmaps_utils-0.9.0}/tests/test_hidefconverter.py +0 -0
- {cellmaps_utils-0.8.0 → cellmaps_utils-0.9.0}/tests/test_iftool.py +0 -0
- {cellmaps_utils-0.8.0 → cellmaps_utils-0.9.0}/tests/test_integation_provenance.py +0 -0
- {cellmaps_utils-0.8.0 → cellmaps_utils-0.9.0}/tests/test_logutils.py +0 -0
- {cellmaps_utils-0.8.0 → cellmaps_utils-0.9.0}/tests/test_ndexupload.py +0 -0
- {cellmaps_utils-0.8.0 → cellmaps_utils-0.9.0}/tests/test_solutiongenerator.py +0 -0
- {cellmaps_utils-0.8.0 → cellmaps_utils-0.9.0}/tests/test_tabletool.py +0 -0
- {cellmaps_utils-0.8.0 → cellmaps_utils-0.9.0}/tests/test_tworeplcoelutionchallengegenerator.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
2
|
Name: cellmaps_utils
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.9.0
|
|
4
4
|
Summary: Contains utilities needed by Cell Maps tools
|
|
5
5
|
Home-page: https://github.com/idekerlab/cellmaps_utils
|
|
6
6
|
Author: Cell Maps team
|
|
@@ -27,17 +27,6 @@ Requires-Dist: numpy<2.0.0,>=1.24.4
|
|
|
27
27
|
Requires-Dist: dill<0.4.0,>=0.3.8
|
|
28
28
|
Requires-Dist: tqdm<5.0.0,>=4.66.6
|
|
29
29
|
Requires-Dist: requests<3.0.0,>=2.31.0
|
|
30
|
-
Dynamic: author
|
|
31
|
-
Dynamic: author-email
|
|
32
|
-
Dynamic: classifier
|
|
33
|
-
Dynamic: description
|
|
34
|
-
Dynamic: description-content-type
|
|
35
|
-
Dynamic: home-page
|
|
36
|
-
Dynamic: keywords
|
|
37
|
-
Dynamic: license
|
|
38
|
-
Dynamic: license-file
|
|
39
|
-
Dynamic: requires-dist
|
|
40
|
-
Dynamic: summary
|
|
41
30
|
|
|
42
31
|
=============================
|
|
43
32
|
Cell Maps Pipeline Utilities
|
|
@@ -120,7 +109,7 @@ Output:
|
|
|
120
109
|
dist builds source and wheel package
|
|
121
110
|
install install the package to the active Python's site-packages
|
|
122
111
|
|
|
123
|
-
Before running tests, please install ``pip install -r requirements_dev``
|
|
112
|
+
Before running tests, please install ``pip install -r requirements_dev.txt``
|
|
124
113
|
|
|
125
114
|
For developers
|
|
126
115
|
-------------------------------------------
|
|
@@ -160,6 +149,13 @@ This package was created with Cookiecutter_ and the `audreyr/cookiecutter-pypack
|
|
|
160
149
|
History
|
|
161
150
|
=======
|
|
162
151
|
|
|
152
|
+
0.9.0 (2025-07-03)
|
|
153
|
+
-------------------
|
|
154
|
+
|
|
155
|
+
* Add statistics generator for ro-crates
|
|
156
|
+
|
|
157
|
+
* Fix provenance bugs for new versions of ro-crate
|
|
158
|
+
|
|
163
159
|
0.8.0 (2025-04-23)
|
|
164
160
|
-------------------
|
|
165
161
|
|
|
@@ -79,7 +79,7 @@ Output:
|
|
|
79
79
|
dist builds source and wheel package
|
|
80
80
|
install install the package to the active Python's site-packages
|
|
81
81
|
|
|
82
|
-
Before running tests, please install ``pip install -r requirements_dev``
|
|
82
|
+
Before running tests, please install ``pip install -r requirements_dev.txt``
|
|
83
83
|
|
|
84
84
|
For developers
|
|
85
85
|
-------------------------------------------
|
|
@@ -361,7 +361,13 @@ class ProvenanceUtil(object):
|
|
|
361
361
|
data = rocrate
|
|
362
362
|
else:
|
|
363
363
|
data = self.get_rocrate_as_dict(rocrate)
|
|
364
|
-
|
|
364
|
+
if '@id' not in data.keys():
|
|
365
|
+
rocrate_id = data.get('@graph')[1].get('@id', None)
|
|
366
|
+
if rocrate_id is None:
|
|
367
|
+
raise CellMapsProvenanceError('ID of ro-crate not found')
|
|
368
|
+
return rocrate_id
|
|
369
|
+
else:
|
|
370
|
+
return data['@id']
|
|
365
371
|
|
|
366
372
|
def get_name_project_org_of_rocrate(self, rocrate):
|
|
367
373
|
"""
|
|
@@ -395,6 +401,9 @@ class ProvenanceUtil(object):
|
|
|
395
401
|
else:
|
|
396
402
|
data = self.get_rocrate_as_dict(rocrate)
|
|
397
403
|
|
|
404
|
+
if 'name' not in data.keys():
|
|
405
|
+
data = data.get('@graph')[1]
|
|
406
|
+
|
|
398
407
|
name = data['name']
|
|
399
408
|
description = data['description']
|
|
400
409
|
keywords = data['keywords']
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import argparse
|
|
3
|
+
import json
|
|
4
|
+
import pandas as pd
|
|
5
|
+
import re
|
|
6
|
+
from collections import defaultdict
|
|
7
|
+
from functools import lru_cache
|
|
8
|
+
import mygene
|
|
9
|
+
|
|
10
|
+
CELL_LINES = ['MDA-MB-468', 'Kolf2.1J']
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@lru_cache(maxsize=10000)
|
|
14
|
+
def query_gene_symbols(ensembl_id_tuple):
|
|
15
|
+
mg = mygene.MyGeneInfo()
|
|
16
|
+
results = mg.querymany(list(ensembl_id_tuple), scopes='ensembl.gene', fields='symbol', species='human')
|
|
17
|
+
mapping = {}
|
|
18
|
+
for entry in results:
|
|
19
|
+
if not entry.get('notfound') and 'symbol' in entry:
|
|
20
|
+
mapping[entry['query']] = entry['symbol']
|
|
21
|
+
else:
|
|
22
|
+
mapping[entry['query']] = f"UNKNOWN:{entry['query']}"
|
|
23
|
+
return mapping
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def get_gene_symbols_from_ensembl_ids(ensembl_ids):
|
|
27
|
+
unique_ids = set(ensembl_ids)
|
|
28
|
+
return query_gene_symbols(tuple(unique_ids))
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def extract_cell_line_names(json_path):
|
|
32
|
+
known_cell_lines = CELL_LINES
|
|
33
|
+
found = []
|
|
34
|
+
try:
|
|
35
|
+
with open(json_path, "r", encoding="utf-8") as f:
|
|
36
|
+
data = json.load(f)
|
|
37
|
+
|
|
38
|
+
def collect_texts(d):
|
|
39
|
+
texts = []
|
|
40
|
+
for value in d.values():
|
|
41
|
+
if isinstance(value, str):
|
|
42
|
+
texts.append(value)
|
|
43
|
+
elif isinstance(value, list):
|
|
44
|
+
texts.extend([v for v in value if isinstance(v, str)])
|
|
45
|
+
elif isinstance(value, dict):
|
|
46
|
+
texts.extend(collect_texts(value))
|
|
47
|
+
return texts
|
|
48
|
+
|
|
49
|
+
all_text = collect_texts(data)
|
|
50
|
+
if "@graph" in data and isinstance(data["@graph"], list):
|
|
51
|
+
for entry in data["@graph"]:
|
|
52
|
+
if isinstance(entry, dict):
|
|
53
|
+
all_text.extend(collect_texts(entry))
|
|
54
|
+
combined_text = " ".join(all_text)
|
|
55
|
+
|
|
56
|
+
for cl in known_cell_lines:
|
|
57
|
+
if re.search(rf'\b{re.escape(cl)}\b', combined_text, re.IGNORECASE):
|
|
58
|
+
found.append(cl)
|
|
59
|
+
|
|
60
|
+
return sorted(found) if found else ["<not found>"]
|
|
61
|
+
except Exception as e:
|
|
62
|
+
return [f"Error reading file: {e}"]
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def find_first_csv_or_tsv(folder):
|
|
66
|
+
for fname in os.listdir(folder):
|
|
67
|
+
if fname.lower().endswith(".csv") or fname.lower().endswith(".tsv"):
|
|
68
|
+
return os.path.join(folder, fname)
|
|
69
|
+
return None
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def get_ensembl_ids(file_path):
|
|
73
|
+
try:
|
|
74
|
+
if file_path.endswith(".tsv"):
|
|
75
|
+
df = pd.read_csv(file_path, sep='\t')
|
|
76
|
+
else:
|
|
77
|
+
df = pd.read_csv(file_path)
|
|
78
|
+
if "ENSEMBL ID" in df.columns:
|
|
79
|
+
return df["ENSEMBL ID"].dropna().tolist()
|
|
80
|
+
except Exception:
|
|
81
|
+
pass
|
|
82
|
+
return []
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def get_protein_names(file_path):
|
|
86
|
+
try:
|
|
87
|
+
if file_path.endswith(".tsv"):
|
|
88
|
+
df = pd.read_csv(file_path, sep='\t')
|
|
89
|
+
else:
|
|
90
|
+
df = pd.read_csv(file_path)
|
|
91
|
+
if "Bait" in df.columns and "PreyGene.x" in df.columns:
|
|
92
|
+
bait = df["Bait"].dropna().tolist()
|
|
93
|
+
prey = df["PreyGene.x"].dropna().tolist()
|
|
94
|
+
all_proteins = bait + prey
|
|
95
|
+
cleaned = {p.split("_")[0] for p in all_proteins}
|
|
96
|
+
return cleaned
|
|
97
|
+
except Exception:
|
|
98
|
+
pass
|
|
99
|
+
return set()
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def count_unique_image_filenames_per_cell_line(images_crates):
|
|
103
|
+
channels = ['blue', 'green', 'red', 'yellow']
|
|
104
|
+
cell_line_to_filenames = defaultdict(set)
|
|
105
|
+
|
|
106
|
+
for crate in images_crates:
|
|
107
|
+
crate = crate.strip()
|
|
108
|
+
if not os.path.isdir(crate):
|
|
109
|
+
continue
|
|
110
|
+
metadata_path = os.path.join(crate, "ro-crate-metadata.json")
|
|
111
|
+
if not os.path.isfile(metadata_path):
|
|
112
|
+
continue
|
|
113
|
+
cell_lines = extract_cell_line_names(metadata_path)
|
|
114
|
+
for color in channels:
|
|
115
|
+
color_path = os.path.join(crate, color)
|
|
116
|
+
if not os.path.isdir(color_path):
|
|
117
|
+
continue
|
|
118
|
+
filenames = [f for f in os.listdir(color_path) if os.path.isfile(os.path.join(color_path, f))]
|
|
119
|
+
for cl in cell_lines:
|
|
120
|
+
cell_line_to_filenames[cl].update(filenames)
|
|
121
|
+
|
|
122
|
+
return {cl: len(files) for cl, files in cell_line_to_filenames.items()}
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def check_and_aggregate_images(images_crates):
|
|
126
|
+
cell_line_to_ensembl = defaultdict(set)
|
|
127
|
+
|
|
128
|
+
for path in images_crates:
|
|
129
|
+
path = path.strip()
|
|
130
|
+
metadata_path = os.path.join(path, "ro-crate-metadata.json")
|
|
131
|
+
if not os.path.isfile(metadata_path):
|
|
132
|
+
continue
|
|
133
|
+
cell_lines = extract_cell_line_names(metadata_path)
|
|
134
|
+
if not cell_lines or cell_lines == ["<not found>"]:
|
|
135
|
+
continue
|
|
136
|
+
ensembl_file = find_first_csv_or_tsv(path)
|
|
137
|
+
if not ensembl_file:
|
|
138
|
+
continue
|
|
139
|
+
ensembl_ids = get_ensembl_ids(ensembl_file)
|
|
140
|
+
if not ensembl_ids:
|
|
141
|
+
continue
|
|
142
|
+
for cell_line in cell_lines:
|
|
143
|
+
cell_line_to_ensembl[cell_line].update(ensembl_ids)
|
|
144
|
+
|
|
145
|
+
all_gene_symbols = defaultdict(set)
|
|
146
|
+
for cl, ids in cell_line_to_ensembl.items():
|
|
147
|
+
symbol_map = get_gene_symbols_from_ensembl_ids(list(ids))
|
|
148
|
+
all_gene_symbols[cl] = set(symbol_map.values())
|
|
149
|
+
|
|
150
|
+
print("\nImages Crates:")
|
|
151
|
+
for cl in CELL_LINES:
|
|
152
|
+
print(f"{cl}: {len(all_gene_symbols[cl])} unique proteins")
|
|
153
|
+
return all_gene_symbols
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def check_and_aggregate_interactions(interaction_crates):
|
|
157
|
+
cell_line_to_proteins = defaultdict(set)
|
|
158
|
+
|
|
159
|
+
for path in interaction_crates:
|
|
160
|
+
path = path.strip()
|
|
161
|
+
metadata_path = os.path.join(path, "ro-crate-metadata.json")
|
|
162
|
+
if not os.path.isfile(metadata_path):
|
|
163
|
+
continue
|
|
164
|
+
cell_lines = extract_cell_line_names(metadata_path)
|
|
165
|
+
if not cell_lines or cell_lines == ["<not found>"]:
|
|
166
|
+
continue
|
|
167
|
+
interaction_file = find_first_csv_or_tsv(path)
|
|
168
|
+
if not interaction_file:
|
|
169
|
+
continue
|
|
170
|
+
names = get_protein_names(interaction_file)
|
|
171
|
+
if not names:
|
|
172
|
+
continue
|
|
173
|
+
for cell_line in cell_lines:
|
|
174
|
+
cell_line_to_proteins[cell_line].update(names)
|
|
175
|
+
|
|
176
|
+
print("\nInteraction Crates:")
|
|
177
|
+
for cl in CELL_LINES:
|
|
178
|
+
print(f"{cl}: {len(cell_line_to_proteins[cl])} unique proteins")
|
|
179
|
+
return cell_line_to_proteins
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
def save_summary_table(proteins, interactions, image_counts, output_file="statistics.csv"):
|
|
183
|
+
data = []
|
|
184
|
+
differentiation = {
|
|
185
|
+
"MDA-MB-468": ["NA"],
|
|
186
|
+
"Kolf2.1J": ["undifferentiated", "neuron", "cardiomycyte"]
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
for cl in CELL_LINES:
|
|
190
|
+
interaction_proteins = interactions.get(cl, set())
|
|
191
|
+
protein_crate_proteins = proteins.get(cl, set())
|
|
192
|
+
total = interaction_proteins.union(protein_crate_proteins)
|
|
193
|
+
image_count = image_counts.get(cl, "")
|
|
194
|
+
|
|
195
|
+
for diff in differentiation[cl]:
|
|
196
|
+
row = {
|
|
197
|
+
"Cell Line/Type": cl,
|
|
198
|
+
"Differentiation": diff,
|
|
199
|
+
"Protein interactions detected": len(interaction_proteins),
|
|
200
|
+
"Number of immunofluorescent images": image_count,
|
|
201
|
+
"Number immunofluorescent proteins detected": len(protein_crate_proteins),
|
|
202
|
+
"Number of genes knocked down in perturb seq": "",
|
|
203
|
+
"Total proteins investigated": len(total)
|
|
204
|
+
}
|
|
205
|
+
data.append(row)
|
|
206
|
+
|
|
207
|
+
df = pd.DataFrame(data)
|
|
208
|
+
df.to_csv(output_file, index=False)
|
|
209
|
+
print(f"\nSummary table saved to: {output_file}")
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
def generate_dataset_statistics(images_crates, interaction_crates):
|
|
213
|
+
proteins = defaultdict(set)
|
|
214
|
+
interactions = defaultdict(set)
|
|
215
|
+
image_counts = defaultdict(int)
|
|
216
|
+
|
|
217
|
+
if images_crates:
|
|
218
|
+
proteins = check_and_aggregate_images(images_crates)
|
|
219
|
+
image_counts = count_unique_image_filenames_per_cell_line(images_crates)
|
|
220
|
+
|
|
221
|
+
if interaction_crates:
|
|
222
|
+
interactions = check_and_aggregate_interactions(interaction_crates)
|
|
223
|
+
|
|
224
|
+
print("\nAll:")
|
|
225
|
+
for cl in CELL_LINES:
|
|
226
|
+
union = proteins[cl] | interactions[cl]
|
|
227
|
+
print(f"{cl}: {len(union)} unique proteins")
|
|
228
|
+
|
|
229
|
+
save_summary_table(proteins, interactions, image_counts)
|
|
230
|
+
|
|
231
|
+
if __name__ == "__main__":
|
|
232
|
+
parser = argparse.ArgumentParser(description="Generate statistics for datasets")
|
|
233
|
+
parser.add_argument("--interaction_crates", nargs='+', help="List of interaction RO-Crate folder paths.")
|
|
234
|
+
parser.add_argument("--images_crates", nargs='+', help="List of image RO-Crate folder paths.")
|
|
235
|
+
args = parser.parse_args()
|
|
236
|
+
|
|
237
|
+
generate_dataset_statistics(args.images_crates, args.interaction_crates)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
2
|
Name: cellmaps_utils
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.9.0
|
|
4
4
|
Summary: Contains utilities needed by Cell Maps tools
|
|
5
5
|
Home-page: https://github.com/idekerlab/cellmaps_utils
|
|
6
6
|
Author: Cell Maps team
|
|
@@ -27,17 +27,6 @@ Requires-Dist: numpy<2.0.0,>=1.24.4
|
|
|
27
27
|
Requires-Dist: dill<0.4.0,>=0.3.8
|
|
28
28
|
Requires-Dist: tqdm<5.0.0,>=4.66.6
|
|
29
29
|
Requires-Dist: requests<3.0.0,>=2.31.0
|
|
30
|
-
Dynamic: author
|
|
31
|
-
Dynamic: author-email
|
|
32
|
-
Dynamic: classifier
|
|
33
|
-
Dynamic: description
|
|
34
|
-
Dynamic: description-content-type
|
|
35
|
-
Dynamic: home-page
|
|
36
|
-
Dynamic: keywords
|
|
37
|
-
Dynamic: license
|
|
38
|
-
Dynamic: license-file
|
|
39
|
-
Dynamic: requires-dist
|
|
40
|
-
Dynamic: summary
|
|
41
30
|
|
|
42
31
|
=============================
|
|
43
32
|
Cell Maps Pipeline Utilities
|
|
@@ -120,7 +109,7 @@ Output:
|
|
|
120
109
|
dist builds source and wheel package
|
|
121
110
|
install install the package to the active Python's site-packages
|
|
122
111
|
|
|
123
|
-
Before running tests, please install ``pip install -r requirements_dev``
|
|
112
|
+
Before running tests, please install ``pip install -r requirements_dev.txt``
|
|
124
113
|
|
|
125
114
|
For developers
|
|
126
115
|
-------------------------------------------
|
|
@@ -160,6 +149,13 @@ This package was created with Cookiecutter_ and the `audreyr/cookiecutter-pypack
|
|
|
160
149
|
History
|
|
161
150
|
=======
|
|
162
151
|
|
|
152
|
+
0.9.0 (2025-07-03)
|
|
153
|
+
-------------------
|
|
154
|
+
|
|
155
|
+
* Add statistics generator for ro-crates
|
|
156
|
+
|
|
157
|
+
* Fix provenance bugs for new versions of ro-crate
|
|
158
|
+
|
|
163
159
|
0.8.0 (2025-04-23)
|
|
164
160
|
-------------------
|
|
165
161
|
|
|
@@ -27,6 +27,7 @@ cellmaps_utils/logutils.py
|
|
|
27
27
|
cellmaps_utils/music_utils.py
|
|
28
28
|
cellmaps_utils/ndexupload.py
|
|
29
29
|
cellmaps_utils/provenance.py
|
|
30
|
+
cellmaps_utils/statsgenerator.py
|
|
30
31
|
cellmaps_utils/tabletool.py
|
|
31
32
|
cellmaps_utils.egg-info/PKG-INFO
|
|
32
33
|
cellmaps_utils.egg-info/SOURCES.txt
|
|
@@ -63,6 +64,7 @@ docs/versioningscheme.rst
|
|
|
63
64
|
docs/_build/html/_static/file.png
|
|
64
65
|
docs/_build/html/_static/minus.png
|
|
65
66
|
docs/_build/html/_static/plus.png
|
|
67
|
+
tests/.DS_Store
|
|
66
68
|
tests/__init__.py
|
|
67
69
|
tests/test_apmstool.py
|
|
68
70
|
tests/test_basecommandlinetool.py
|
|
@@ -77,6 +79,7 @@ tests/test_logutils.py
|
|
|
77
79
|
tests/test_ndexupload.py
|
|
78
80
|
tests/test_provenance.py
|
|
79
81
|
tests/test_solutiongenerator.py
|
|
82
|
+
tests/test_statsgenerator.py
|
|
80
83
|
tests/test_tabletool.py
|
|
81
84
|
tests/test_tworeplcoelutionchallengegenerator.py
|
|
82
85
|
tests/data/ddot_output.ont
|
|
Binary file
|
|
@@ -753,3 +753,57 @@ class TestProvenanceUtil(unittest.TestCase):
|
|
|
753
753
|
import time
|
|
754
754
|
print(os.listdir(os.path.join(temp_dir, 'test_rocrate')))
|
|
755
755
|
shutil.rmtree(temp_dir)
|
|
756
|
+
|
|
757
|
+
def test_get_rocrate_provenance_attributes_from_valid_dict(self):
|
|
758
|
+
prov = ProvenanceUtil()
|
|
759
|
+
rocrate_dict = {
|
|
760
|
+
'name': 'Test Crate',
|
|
761
|
+
'description': 'Crate for testing',
|
|
762
|
+
'keywords': ['proj', 'v1.0', 'HeLa', 'drugA', 'embedding'],
|
|
763
|
+
'isPartOf': [
|
|
764
|
+
{'@type': 'Organization', 'name': 'TestOrg'},
|
|
765
|
+
{'@type': 'Project', 'name': 'TestProject'}
|
|
766
|
+
]
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
attrs = prov.get_rocrate_provenance_attributes(rocrate_dict)
|
|
770
|
+
self.assertEqual(attrs.get_name(), 'Test Crate')
|
|
771
|
+
self.assertEqual(attrs.get_description(), 'Crate for testing')
|
|
772
|
+
self.assertEqual(attrs.get_organization_name(), 'TestOrg')
|
|
773
|
+
self.assertEqual(attrs.get_project_name(), 'TestProject')
|
|
774
|
+
self.assertEqual(attrs.get_keywords(), ['proj', 'v1.0', 'HeLa', 'drugA', 'embedding'])
|
|
775
|
+
|
|
776
|
+
def test_get_rocrate_provenance_attributes_missing_fields(self):
|
|
777
|
+
prov = ProvenanceUtil()
|
|
778
|
+
rocrate_dict = {
|
|
779
|
+
'name': 'Test Crate',
|
|
780
|
+
'description': 'Missing project/org',
|
|
781
|
+
'keywords': ['x'],
|
|
782
|
+
'isPartOf': []
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
attrs = prov.get_rocrate_provenance_attributes(rocrate_dict)
|
|
786
|
+
self.assertEqual(attrs.get_project_name(), None)
|
|
787
|
+
self.assertEqual(attrs.get_organization_name(), None)
|
|
788
|
+
|
|
789
|
+
def test_get_id_of_rocrate_from_graph_structure(self):
|
|
790
|
+
prov = ProvenanceUtil()
|
|
791
|
+
crate_dict = {
|
|
792
|
+
'@graph': [
|
|
793
|
+
{'@id': 'ignored'},
|
|
794
|
+
{'@id': 'expected_id'}
|
|
795
|
+
]
|
|
796
|
+
}
|
|
797
|
+
result = prov.get_id_of_rocrate(crate_dict)
|
|
798
|
+
self.assertEqual(result, 'expected_id')
|
|
799
|
+
|
|
800
|
+
def test_get_id_of_rocrate_fails_if_id_not_found(self):
|
|
801
|
+
prov = ProvenanceUtil()
|
|
802
|
+
crate_dict = {
|
|
803
|
+
'@graph': [
|
|
804
|
+
{'@id': 'ignored'},
|
|
805
|
+
{'wrongkey': 'oops'}
|
|
806
|
+
]
|
|
807
|
+
}
|
|
808
|
+
with self.assertRaises(CellMapsProvenanceError):
|
|
809
|
+
prov.get_id_of_rocrate(crate_dict)
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import unittest
|
|
2
|
+
import tempfile
|
|
3
|
+
import os
|
|
4
|
+
import json
|
|
5
|
+
from unittest.mock import patch
|
|
6
|
+
|
|
7
|
+
import pandas as pd
|
|
8
|
+
from cellmaps_utils.statsgenerator import (
|
|
9
|
+
extract_cell_line_names,
|
|
10
|
+
find_first_csv_or_tsv,
|
|
11
|
+
get_ensembl_ids,
|
|
12
|
+
count_unique_image_filenames_per_cell_line,
|
|
13
|
+
get_protein_names,
|
|
14
|
+
save_summary_table,
|
|
15
|
+
generate_dataset_statistics
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
class TestDatasetStatistics(unittest.TestCase):
|
|
19
|
+
|
|
20
|
+
def setUp(self):
|
|
21
|
+
self.test_dir = tempfile.TemporaryDirectory()
|
|
22
|
+
self.base_path = self.test_dir.name
|
|
23
|
+
|
|
24
|
+
def tearDown(self):
|
|
25
|
+
self.test_dir.cleanup()
|
|
26
|
+
|
|
27
|
+
def test_extract_cell_line_names_found(self):
|
|
28
|
+
metadata = {
|
|
29
|
+
"@graph": [{"description": "Experiment using MDA-MB-468 cell line"}]
|
|
30
|
+
}
|
|
31
|
+
file_path = os.path.join(self.base_path, "ro-crate-metadata.json")
|
|
32
|
+
with open(file_path, "w") as f:
|
|
33
|
+
json.dump(metadata, f)
|
|
34
|
+
|
|
35
|
+
result = extract_cell_line_names(file_path)
|
|
36
|
+
self.assertEqual(result, ["MDA-MB-468"])
|
|
37
|
+
|
|
38
|
+
def test_extract_cell_line_names_not_found(self):
|
|
39
|
+
metadata = {
|
|
40
|
+
"@graph": [{"description": "No matching cell line"}]
|
|
41
|
+
}
|
|
42
|
+
file_path = os.path.join(self.base_path, "ro-crate-metadata.json")
|
|
43
|
+
with open(file_path, "w") as f:
|
|
44
|
+
json.dump(metadata, f)
|
|
45
|
+
|
|
46
|
+
result = extract_cell_line_names(file_path)
|
|
47
|
+
self.assertEqual(result, ["<not found>"])
|
|
48
|
+
|
|
49
|
+
def test_find_first_csv_or_tsv(self):
|
|
50
|
+
file_path = os.path.join(self.base_path, "data.tsv")
|
|
51
|
+
with open(file_path, "w") as f:
|
|
52
|
+
f.write("ENSEMBL ID\nENSG000001234\n")
|
|
53
|
+
|
|
54
|
+
found = find_first_csv_or_tsv(self.base_path)
|
|
55
|
+
self.assertTrue(found.endswith("data.tsv"))
|
|
56
|
+
|
|
57
|
+
def test_get_ensembl_ids(self):
|
|
58
|
+
df = pd.DataFrame({"ENSEMBL ID": ["ENSG000001", "ENSG000002"]})
|
|
59
|
+
file_path = os.path.join(self.base_path, "mock.csv")
|
|
60
|
+
df.to_csv(file_path, index=False)
|
|
61
|
+
|
|
62
|
+
result = get_ensembl_ids(file_path)
|
|
63
|
+
self.assertEqual(result, ["ENSG000001", "ENSG000002"])
|
|
64
|
+
|
|
65
|
+
def test_count_unique_image_filenames_per_cell_line(self):
|
|
66
|
+
meta = {
|
|
67
|
+
"@graph": [{"name": "image crate for Kolf2.1J"}]
|
|
68
|
+
}
|
|
69
|
+
metadata_path = os.path.join(self.base_path, "ro-crate-metadata.json")
|
|
70
|
+
with open(metadata_path, "w") as f:
|
|
71
|
+
json.dump(meta, f)
|
|
72
|
+
|
|
73
|
+
# Setup: image file in "green"
|
|
74
|
+
green_path = os.path.join(self.base_path, "green")
|
|
75
|
+
os.makedirs(green_path)
|
|
76
|
+
with open(os.path.join(green_path, "img1.png"), "w") as f:
|
|
77
|
+
f.write("fake image")
|
|
78
|
+
|
|
79
|
+
result = count_unique_image_filenames_per_cell_line([self.base_path])
|
|
80
|
+
self.assertEqual(result["Kolf2.1J"], 1)
|
|
81
|
+
|
|
82
|
+
def test_get_protein_names(self):
|
|
83
|
+
df = pd.DataFrame({
|
|
84
|
+
"Bait": ["TP53_abc", "BRCA1_123"],
|
|
85
|
+
"PreyGene.x": ["EGFR_1", "MYC_test"]
|
|
86
|
+
})
|
|
87
|
+
file_path = os.path.join(self.base_path, "interact.csv")
|
|
88
|
+
df.to_csv(file_path, index=False)
|
|
89
|
+
|
|
90
|
+
result = get_protein_names(file_path)
|
|
91
|
+
self.assertEqual(result, {"TP53", "BRCA1", "EGFR", "MYC"})
|
|
92
|
+
|
|
93
|
+
def test_save_summary_table(self):
|
|
94
|
+
proteins = {"MDA-MB-468": {"A", "B"}, "Kolf2.1J": {"X", "Y"}}
|
|
95
|
+
interactions = {"MDA-MB-468": {"B", "C"}, "Kolf2.1J": {"Y"}}
|
|
96
|
+
image_counts = {"MDA-MB-468": 4, "Kolf2.1J": 10}
|
|
97
|
+
|
|
98
|
+
file_path = os.path.join(self.base_path, "output.csv")
|
|
99
|
+
save_summary_table(proteins, interactions, image_counts, output_file=file_path)
|
|
100
|
+
|
|
101
|
+
df = pd.read_csv(file_path)
|
|
102
|
+
self.assertEqual(len(df), 4) # 1 MDA + 3 Kolf2 rows
|
|
103
|
+
self.assertIn("Total proteins investigated", df.columns)
|
|
104
|
+
self.assertEqual(df[df["Cell Line/Type"] == "MDA-MB-468"]["Total proteins investigated"].values[0], 3)
|
|
105
|
+
|
|
106
|
+
def test_generate_dataset_statistics_workflow(self):
|
|
107
|
+
# Setup minimal image crate
|
|
108
|
+
img_dir = os.path.join(self.base_path, "blue")
|
|
109
|
+
os.makedirs(img_dir)
|
|
110
|
+
with open(os.path.join(img_dir, "img.tif"), "w") as f:
|
|
111
|
+
f.write("fake")
|
|
112
|
+
|
|
113
|
+
# Add metadata with a cell line
|
|
114
|
+
with open(os.path.join(self.base_path, "ro-crate-metadata.json"), "w") as f:
|
|
115
|
+
json.dump({"@graph": [{"name": "Study with Kolf2.1J"}]}, f)
|
|
116
|
+
|
|
117
|
+
# Add ENSEMBL file
|
|
118
|
+
df = pd.DataFrame({"ENSEMBL ID": ["ENSG000001"]})
|
|
119
|
+
df.to_csv(os.path.join(self.base_path, "table.tsv"), sep="\t", index=False)
|
|
120
|
+
|
|
121
|
+
# Patch gene symbol lookup to avoid mygene dependency
|
|
122
|
+
with patch("cellmaps_utils.statsgenerator.get_gene_symbols_from_ensembl_ids") as mock_mapper:
|
|
123
|
+
mock_mapper.return_value = {"ENSG000001": "TESTGENE"}
|
|
124
|
+
generate_dataset_statistics(images_crates=[self.base_path], interaction_crates=[])
|
|
125
|
+
|
|
126
|
+
if __name__ == "__main__":
|
|
127
|
+
unittest.main()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{cellmaps_utils-0.8.0 → cellmaps_utils-0.9.0}/tests/test_tworeplcoelutionchallengegenerator.py
RENAMED
|
File without changes
|