cellmaps-utils 0.5.0__tar.gz → 0.6.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.
Files changed (84) hide show
  1. {cellmaps_utils-0.5.0 → cellmaps_utils-0.6.0}/HISTORY.rst +13 -0
  2. {cellmaps_utils-0.5.0 → cellmaps_utils-0.6.0}/PKG-INFO +22 -9
  3. {cellmaps_utils-0.5.0 → cellmaps_utils-0.6.0}/cellmaps_utils/__init__.py +1 -1
  4. {cellmaps_utils-0.5.0 → cellmaps_utils-0.6.0}/cellmaps_utils/apmstool.py +22 -3
  5. {cellmaps_utils-0.5.0 → cellmaps_utils-0.6.0}/cellmaps_utils/crisprtool.py +3 -3
  6. cellmaps_utils-0.6.0/cellmaps_utils/hcx_utils.py +198 -0
  7. cellmaps_utils-0.6.0/cellmaps_utils/hierdiff.py +81 -0
  8. {cellmaps_utils-0.5.0 → cellmaps_utils-0.6.0}/cellmaps_utils/ifimage_readme.txt +4 -4
  9. {cellmaps_utils-0.5.0 → cellmaps_utils-0.6.0}/cellmaps_utils/iftool.py +35 -16
  10. {cellmaps_utils-0.5.0 → cellmaps_utils-0.6.0}/cellmaps_utils.egg-info/PKG-INFO +22 -9
  11. {cellmaps_utils-0.5.0 → cellmaps_utils-0.6.0}/cellmaps_utils.egg-info/SOURCES.txt +1 -0
  12. cellmaps_utils-0.6.0/cellmaps_utils.egg-info/requires.txt +9 -0
  13. {cellmaps_utils-0.5.0 → cellmaps_utils-0.6.0}/docs/cellmaps_utils.rst +40 -0
  14. cellmaps_utils-0.6.0/docs/outputs.rst +139 -0
  15. {cellmaps_utils-0.5.0 → cellmaps_utils-0.6.0}/docs/usage.rst +311 -2
  16. {cellmaps_utils-0.5.0 → cellmaps_utils-0.6.0}/setup.py +8 -8
  17. {cellmaps_utils-0.5.0 → cellmaps_utils-0.6.0}/tests/test_apmstool.py +12 -0
  18. {cellmaps_utils-0.5.0 → cellmaps_utils-0.6.0}/tests/test_crisprtool.py +2 -2
  19. {cellmaps_utils-0.5.0 → cellmaps_utils-0.6.0}/tests/test_iftool.py +2 -1
  20. cellmaps_utils-0.5.0/cellmaps_utils/hcx_utils.py +0 -92
  21. cellmaps_utils-0.5.0/cellmaps_utils.egg-info/requires.txt +0 -9
  22. cellmaps_utils-0.5.0/docs/outputs.rst +0 -48
  23. {cellmaps_utils-0.5.0 → cellmaps_utils-0.6.0}/AUTHORS.rst +0 -0
  24. {cellmaps_utils-0.5.0 → cellmaps_utils-0.6.0}/CONTRIBUTING.rst +0 -0
  25. {cellmaps_utils-0.5.0 → cellmaps_utils-0.6.0}/LICENSE +0 -0
  26. {cellmaps_utils-0.5.0 → cellmaps_utils-0.6.0}/MANIFEST.in +0 -0
  27. {cellmaps_utils-0.5.0 → cellmaps_utils-0.6.0}/README.rst +0 -0
  28. {cellmaps_utils-0.5.0 → cellmaps_utils-0.6.0}/cellmaps_utils/apms_readme.txt +0 -0
  29. {cellmaps_utils-0.5.0 → cellmaps_utils-0.6.0}/cellmaps_utils/basecmdtool.py +0 -0
  30. {cellmaps_utils-0.5.0 → cellmaps_utils-0.6.0}/cellmaps_utils/cellmaps_utilscmd.py +0 -0
  31. {cellmaps_utils-0.5.0 → cellmaps_utils-0.6.0}/cellmaps_utils/constants.py +0 -0
  32. {cellmaps_utils-0.5.0 → cellmaps_utils-0.6.0}/cellmaps_utils/crispr_readme.txt +0 -0
  33. {cellmaps_utils-0.5.0 → cellmaps_utils-0.6.0}/cellmaps_utils/ddotconverter.py +0 -0
  34. {cellmaps_utils-0.5.0 → cellmaps_utils-0.6.0}/cellmaps_utils/exceptions.py +0 -0
  35. {cellmaps_utils-0.5.0 → cellmaps_utils-0.6.0}/cellmaps_utils/hidefconverter.py +0 -0
  36. {cellmaps_utils-0.5.0 → cellmaps_utils-0.6.0}/cellmaps_utils/logutils.py +0 -0
  37. {cellmaps_utils-0.5.0 → cellmaps_utils-0.6.0}/cellmaps_utils/music_utils.py +0 -0
  38. {cellmaps_utils-0.5.0 → cellmaps_utils-0.6.0}/cellmaps_utils/ndexupload.py +0 -0
  39. {cellmaps_utils-0.5.0 → cellmaps_utils-0.6.0}/cellmaps_utils/provenance.py +0 -0
  40. {cellmaps_utils-0.5.0 → cellmaps_utils-0.6.0}/cellmaps_utils/tabletool.py +0 -0
  41. {cellmaps_utils-0.5.0 → cellmaps_utils-0.6.0}/cellmaps_utils.egg-info/dependency_links.txt +0 -0
  42. {cellmaps_utils-0.5.0 → cellmaps_utils-0.6.0}/cellmaps_utils.egg-info/not-zip-safe +0 -0
  43. {cellmaps_utils-0.5.0 → cellmaps_utils-0.6.0}/cellmaps_utils.egg-info/top_level.txt +0 -0
  44. {cellmaps_utils-0.5.0 → cellmaps_utils-0.6.0}/docs/Makefile +0 -0
  45. {cellmaps_utils-0.5.0 → cellmaps_utils-0.6.0}/docs/_build/html/_static/file.png +0 -0
  46. {cellmaps_utils-0.5.0 → cellmaps_utils-0.6.0}/docs/_build/html/_static/minus.png +0 -0
  47. {cellmaps_utils-0.5.0 → cellmaps_utils-0.6.0}/docs/_build/html/_static/plus.png +0 -0
  48. {cellmaps_utils-0.5.0 → cellmaps_utils-0.6.0}/docs/authors.rst +0 -0
  49. {cellmaps_utils-0.5.0 → cellmaps_utils-0.6.0}/docs/conf.py +0 -0
  50. {cellmaps_utils-0.5.0 → cellmaps_utils-0.6.0}/docs/contributing.rst +0 -0
  51. {cellmaps_utils-0.5.0 → cellmaps_utils-0.6.0}/docs/devbranches.rst +0 -0
  52. {cellmaps_utils-0.5.0 → cellmaps_utils-0.6.0}/docs/developer.rst +0 -0
  53. {cellmaps_utils-0.5.0 → cellmaps_utils-0.6.0}/docs/history.rst +0 -0
  54. {cellmaps_utils-0.5.0 → cellmaps_utils-0.6.0}/docs/index.rst +0 -0
  55. {cellmaps_utils-0.5.0 → cellmaps_utils-0.6.0}/docs/installation.rst +0 -0
  56. {cellmaps_utils-0.5.0 → cellmaps_utils-0.6.0}/docs/integrationtesting.rst +0 -0
  57. {cellmaps_utils-0.5.0 → cellmaps_utils-0.6.0}/docs/make.bat +0 -0
  58. {cellmaps_utils-0.5.0 → cellmaps_utils-0.6.0}/docs/modules.rst +0 -0
  59. {cellmaps_utils-0.5.0 → cellmaps_utils-0.6.0}/docs/newrelease.rst +0 -0
  60. {cellmaps_utils-0.5.0 → cellmaps_utils-0.6.0}/docs/pypircfile.rst +0 -0
  61. {cellmaps_utils-0.5.0 → cellmaps_utils-0.6.0}/docs/versioningscheme.rst +0 -0
  62. {cellmaps_utils-0.5.0 → cellmaps_utils-0.6.0}/setup.cfg +0 -0
  63. {cellmaps_utils-0.5.0 → cellmaps_utils-0.6.0}/tests/.DS_Store +0 -0
  64. {cellmaps_utils-0.5.0 → cellmaps_utils-0.6.0}/tests/__init__.py +0 -0
  65. {cellmaps_utils-0.5.0 → cellmaps_utils-0.6.0}/tests/data/ddot_output.ont +0 -0
  66. {cellmaps_utils-0.5.0 → cellmaps_utils-0.6.0}/tests/data/hidef_output.edges +0 -0
  67. {cellmaps_utils-0.5.0 → cellmaps_utils-0.6.0}/tests/data/hidef_output.nodes +0 -0
  68. {cellmaps_utils-0.5.0 → cellmaps_utils-0.6.0}/tests/data/images/blue.jpg +0 -0
  69. {cellmaps_utils-0.5.0 → cellmaps_utils-0.6.0}/tests/data/images/green.jpg +0 -0
  70. {cellmaps_utils-0.5.0 → cellmaps_utils-0.6.0}/tests/data/images/red.jpg +0 -0
  71. {cellmaps_utils-0.5.0 → cellmaps_utils-0.6.0}/tests/data/images/yellow.jpg +0 -0
  72. {cellmaps_utils-0.5.0 → cellmaps_utils-0.6.0}/tests/data/parent_edgelist +0 -0
  73. {cellmaps_utils-0.5.0 → cellmaps_utils-0.6.0}/tests/data/reactome_ddot.txt +0 -0
  74. {cellmaps_utils-0.5.0 → cellmaps_utils-0.6.0}/tests/data/ro-crate-metadata.json +0 -0
  75. {cellmaps_utils-0.5.0 → cellmaps_utils-0.6.0}/tests/data/test_password +0 -0
  76. {cellmaps_utils-0.5.0 → cellmaps_utils-0.6.0}/tests/test_basecommandlinetool.py +0 -0
  77. {cellmaps_utils-0.5.0 → cellmaps_utils-0.6.0}/tests/test_ddotconverter.py +0 -0
  78. {cellmaps_utils-0.5.0 → cellmaps_utils-0.6.0}/tests/test_helloworldcommand.py +0 -0
  79. {cellmaps_utils-0.5.0 → cellmaps_utils-0.6.0}/tests/test_hidefconverter.py +0 -0
  80. {cellmaps_utils-0.5.0 → cellmaps_utils-0.6.0}/tests/test_integation_provenance.py +0 -0
  81. {cellmaps_utils-0.5.0 → cellmaps_utils-0.6.0}/tests/test_logutils.py +0 -0
  82. {cellmaps_utils-0.5.0 → cellmaps_utils-0.6.0}/tests/test_ndexupload.py +0 -0
  83. {cellmaps_utils-0.5.0 → cellmaps_utils-0.6.0}/tests/test_provenance.py +0 -0
  84. {cellmaps_utils-0.5.0 → cellmaps_utils-0.6.0}/tests/test_tabletool.py +0 -0
@@ -2,6 +2,19 @@
2
2
  History
3
3
  =======
4
4
 
5
+ 0.6.0 (2025-02-05)
6
+ ------------------
7
+
8
+ * Add ``HierarchyDiff`` class with ``compare_hierarchies`` method to compare two hierarchies in CX2 format, and output
9
+ a hierarchy with robustness score of each system.
10
+
11
+ * Add version bounds for required packages.
12
+
13
+ * Add ``convert_hierarchical_network_to_hcx`` (utility function that aids convertion of a hierarchical network in CX2 to
14
+ HCX) and ``add_hcx_members_annotation`` (adds 'HCX::members' attribute to nodes) to ``hcx_utils``
15
+
16
+ * Fixes in IF and APMS tools
17
+
5
18
  0.5.0 (2024-09-05)
6
19
  ------------------
7
20
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: cellmaps_utils
3
- Version: 0.5.0
3
+ Version: 0.6.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
@@ -18,15 +18,15 @@ Classifier: Programming Language :: Python :: 3.11
18
18
  Description-Content-Type: text/x-rst
19
19
  License-File: LICENSE
20
20
  License-File: AUTHORS.rst
21
- Requires-Dist: fairscape-cli==0.2.0
21
+ Requires-Dist: fairscape-cli==1.0.2
22
22
  Requires-Dist: ndex2<4.0.0,>=3.8.0
23
- Requires-Dist: scipy
24
- Requires-Dist: scikit-learn
25
- Requires-Dist: pandas
26
- Requires-Dist: numpy
27
- Requires-Dist: dill
28
- Requires-Dist: tqdm
29
- Requires-Dist: requests
23
+ Requires-Dist: scipy<1.11,>=1.10.1
24
+ Requires-Dist: scikit-learn<1.4.0,>=0.19.0
25
+ Requires-Dist: pandas<3.0.0,>=2.0.0
26
+ Requires-Dist: numpy<2.0.0,>=1.24.4
27
+ Requires-Dist: dill<0.4.0,>=0.3.8
28
+ Requires-Dist: tqdm<5.0.0,>=4.66.6
29
+ Requires-Dist: requests<3.0.0,>=2.31.0
30
30
 
31
31
  =============================
32
32
  Cell Maps Pipeline Utilities
@@ -145,6 +145,19 @@ This package was created with Cookiecutter_ and the `audreyr/cookiecutter-pypack
145
145
  History
146
146
  =======
147
147
 
148
+ 0.6.0 (2025-02-05)
149
+ ------------------
150
+
151
+ * Add ``HierarchyDiff`` class with ``compare_hierarchies`` method to compare two hierarchies in CX2 format, and output
152
+ a hierarchy with robustness score of each system.
153
+
154
+ * Add version bounds for required packages.
155
+
156
+ * Add ``convert_hierarchical_network_to_hcx`` (utility function that aids convertion of a hierarchical network in CX2 to
157
+ HCX) and ``add_hcx_members_annotation`` (adds 'HCX::members' attribute to nodes) to ``hcx_utils``
158
+
159
+ * Fixes in IF and APMS tools
160
+
148
161
  0.5.0 (2024-09-05)
149
162
  ------------------
150
163
 
@@ -4,6 +4,6 @@
4
4
 
5
5
  __author__ = 'Cell Maps team'
6
6
  __email__ = 'tools@cm4ai.org'
7
- __version__ = '0.5.0'
7
+ __version__ = '0.6.0'
8
8
  __repo_url__ = 'https://github.com/idekerlab/cellmaps_utils'
9
9
  __description__ = 'Contains utilities needed by Cell Maps tools'
@@ -22,6 +22,8 @@ class APMSDataLoader(BaseCommandLineTool):
22
22
  """
23
23
  COMMAND = 'apmsconverter'
24
24
 
25
+ BAIT_COL_NAME = 'Bait'
26
+
25
27
  def __init__(self, theargs,
26
28
  provenance_utils=ProvenanceUtil()):
27
29
  """
@@ -43,6 +45,8 @@ class APMSDataLoader(BaseCommandLineTool):
43
45
  self._tissue = theargs.tissue
44
46
  self._author = theargs.author
45
47
  self._gene_set = theargs.gene_set
48
+ self._baitcolname = theargs.baitcolname
49
+ self._set_name = theargs.set_name
46
50
  self._provenance_utils = provenance_utils
47
51
  self._softwareid = None
48
52
  self._input_data_dict = theargs.__dict__
@@ -139,9 +143,11 @@ class APMSDataLoader(BaseCommandLineTool):
139
143
  if self._gene_set is not None:
140
144
  dir_name += self._gene_set.lower() + '_'
141
145
  dir_name += self._cell_line.lower() + '_'
142
- dir_name += self._treatment.lower() + '_apms_'
146
+ dir_name += self._treatment.lower()
147
+ if self._set_name is not None:
148
+ dir_name += '_' + self._set_name
149
+ dir_name += '_apms_'
143
150
  dir_name += self._release.lower()
144
-
145
151
  dir_name = dir_name.replace(' ', '_')
146
152
  self._outdir = os.path.join(self._outdir, dir_name)
147
153
 
@@ -154,7 +160,13 @@ class APMSDataLoader(BaseCommandLineTool):
154
160
  """
155
161
  df_list = []
156
162
  for input in self._inputs:
157
- cur_df = pd.read_csv(input, sep='\t')
163
+ thesep = '\t'
164
+ if input.endswith('.csv'):
165
+ thesep = ','
166
+ cur_df = pd.read_csv(input, sep=thesep, na_filter=False)
167
+ if self._baitcolname != 'Bait':
168
+ if self._baitcolname in cur_df.columns:
169
+ cur_df.rename({self._baitcolname: 'Bait'}, axis=1, inplace=True)
158
170
  # Handles case where HDAC2 in initial cm4ai dataset
159
171
  # had several columns lacking .x suffix
160
172
  # we are fixing this by checking for those columns and if
@@ -173,6 +185,7 @@ class APMSDataLoader(BaseCommandLineTool):
173
185
  df = pd.concat(df_list)
174
186
 
175
187
  apms_path = os.path.join(self._outdir, constants.APMS_TSV_FILE)
188
+
176
189
  df.to_csv(apms_path, sep='\t', index=False)
177
190
  return apms_path
178
191
 
@@ -263,11 +276,17 @@ class APMSDataLoader(BaseCommandLineTool):
263
276
  parser.add_argument('--gene_set', choices=['chromatin', 'metabolic'],
264
277
  default='chromatin',
265
278
  help='Gene set for dataset')
279
+ parser.add_argument('--set_name',
280
+ help='If set, adds value to RO-Crate folder name before _apms_<version>. '
281
+ 'Example values set1')
266
282
  parser.add_argument('--tissue', choices=['undifferentiated', 'neuron',
267
283
  'cardiomyocytes', ''],
268
284
  default='breast; mammary gland',
269
285
  help='Tissue for dataset. Since the default --cell_line '
270
286
  'is MDA-MB-468, this value is set to the tissue '
271
287
  'for that cell line')
288
+ parser.add_argument('--baitcolname', default='Bait',
289
+ help='Name of bait column in input file(s)')
290
+
272
291
  return parser
273
292
 
@@ -246,7 +246,7 @@ class CRISPRDataLoader(BaseCommandLineTool):
246
246
  if self._gene_set is not None:
247
247
  dir_name += self._gene_set.lower() + '_'
248
248
  dir_name += self._cell_line.lower() + '_'
249
- dir_name += re.sub('[^a-zA-Z0-9\w\n\.]', '_', self._tissue.lower()) + '_'
249
+ dir_name += re.sub(r'[^a-zA-Z0-9\w\n\.]', '_', self._tissue.lower()) + '_'
250
250
  dir_name += self._treatment.lower() + '_crispr_'
251
251
  if self._dataset.lower() != '':
252
252
  dir_name += self._dataset.lower() + '_'
@@ -308,8 +308,8 @@ class CRISPRDataLoader(BaseCommandLineTool):
308
308
 
309
309
  Version {version}
310
310
 
311
- {cmd} Loads CRISPR data into a RO-Crate by creating a
312
- directory, copying over relevant and using FAIRSCAPE CLI to
311
+ {cmd} Loads CRISPR data into a RO-Crate by creating a
312
+ directory, copying over relevant and using FAIRSCAPE CLI to
313
313
  register the data files in the directory known as an RO-Crate
314
314
 
315
315
  """.format(version=cellmaps_utils.__version__,
@@ -0,0 +1,198 @@
1
+ import json
2
+ import os
3
+
4
+ import ndex2
5
+ from ndex2.cx2 import CX2Network, RawCX2NetworkFactory
6
+
7
+ from cellmaps_utils.exceptions import CellMapsError
8
+
9
+
10
+ def get_host_and_uuid_from_network_url(network_url):
11
+ """
12
+ Extracts the host and UUID from a given NDEx network URL.
13
+
14
+ :param network_url: The URL of the NDEx network.
15
+ :type network_url: str
16
+ :return: A tuple containing the host and the UUID of the network.
17
+ :rtype: tuple
18
+ """
19
+ if network_url is None:
20
+ return None, None
21
+ parts = network_url.replace('https://', '').replace('http:/', '').split('/')
22
+ host, uuid = parts[0], parts[-1]
23
+ return host, uuid
24
+
25
+
26
+ def get_interactome(host, uuid, username, password, parent_edgelist):
27
+ """
28
+ Retrieves the interactome either from NDEx or from a local edge list.
29
+
30
+ :param host: The NDEx server host.
31
+ :type host: str
32
+ :param uuid: The UUID of the interactome network in NDEx.
33
+ :type uuid: str
34
+ :param username: The NDEx username for authentication.
35
+ :type username: str
36
+ :param password: The NDEx password for authentication.
37
+ :type password: str
38
+ :param parent_edgelist: Path to a file containing the interactome edge list.
39
+ :type parent_edgelist: str
40
+ :return: A CX2Network object representing the interactome.
41
+ :rtype: CX2Network
42
+ """
43
+ interactome = CX2Network()
44
+ if parent_edgelist is not None:
45
+ with open(parent_edgelist, 'r') as f:
46
+ for line in f:
47
+ parts = line.split()
48
+ id_source_node = interactome.lookup_node_id_by_name(parts[0])
49
+ source_node = id_source_node if id_source_node is not None else interactome.add_node(
50
+ attributes={'name': parts[0]})
51
+ id_target_node = interactome.lookup_node_id_by_name(parts[1])
52
+ target_node = id_target_node if id_target_node is not None else interactome.add_node(
53
+ attributes={'name': parts[1]})
54
+ interactome.add_edge(source=source_node, target=target_node)
55
+ elif host is not None and uuid is not None:
56
+ client = ndex2.client.Ndex2(host=host, username=username, password=password)
57
+ factory = RawCX2NetworkFactory()
58
+ client_resp = client.get_network_as_cx2_stream(uuid)
59
+ interactome = factory.get_cx2network(json.loads(client_resp.content))
60
+ return interactome
61
+
62
+
63
+ def get_root_nodes(hierarchy):
64
+ """
65
+ Identifies the root nodes in a hierarchical network.
66
+
67
+ In CDAPS the root node has only source edges to children
68
+ so this function counts up number of target edges for each node
69
+ and the one with 0 is the root
70
+
71
+ :return: root node ids
72
+ :rtype: set
73
+ """
74
+ all_nodes = set()
75
+ for node_id, node_obj in hierarchy.get_nodes().items():
76
+ all_nodes.add(node_id)
77
+
78
+ nodes_with_targets = set()
79
+ for edge_id, edge_obj in hierarchy.get_edges().items():
80
+ nodes_with_targets.add(edge_obj['t'])
81
+ return all_nodes.difference(nodes_with_targets)
82
+
83
+
84
+ def add_isroot_node_attribute(hierarchy, root_nodes):
85
+ """
86
+ Using the **root_nodes** set or list, add
87
+ ``HCX::isRoot`` to
88
+ every node setting value to ``True``
89
+ if node id is in **root_nodes**
90
+ otherwise set the value to ``False``
91
+ """
92
+ attr_name = 'HCX::isRoot'
93
+ for node_id, node_obj in hierarchy.get_nodes().items():
94
+ if node_id in root_nodes:
95
+ hierarchy.add_node_attribute(node_id, attr_name, True)
96
+ else:
97
+ hierarchy.add_node_attribute(node_id, attr_name, False)
98
+
99
+
100
+ def add_hcx_network_annotations(hierarchy, interactome=None, output_dir='.',
101
+ interactome_name='hierarchy_parent.cx2', host='www.ndexbio.org', uuid=None):
102
+ """
103
+ Adds HCX network annotations to the hierarchy.
104
+
105
+ :param hierarchy: The hierarchical network in CX2 format.
106
+ :type hierarchy: CX2Network
107
+ :param interactome: The interactome network.
108
+ :type interactome: CX2Network, optional
109
+ :param output_dir: Directory where the interactome file will be saved.
110
+ :type output_dir: str, optional
111
+ :param interactome_name: Name of the interactome file.
112
+ :type interactome_name: str, optional
113
+ :param host: NDEx host for interactome retrieval.
114
+ :type host: str, optional
115
+ :param uuid: UUID of the interactome in NDEx.
116
+ :type uuid: str, optional
117
+ :return: The updated hierarchy network with annotations.
118
+ :rtype: CX2Network
119
+ """
120
+ if not isinstance(hierarchy, CX2Network):
121
+ raise CellMapsError("Hierarchy must be an instance of CX2Network.")
122
+ if uuid is None and interactome is None:
123
+ raise CellMapsError("UUID of interactome or interactome (instance of CX2Network) must be specified!")
124
+
125
+ hierarchy.add_network_attribute('ndexSchema', 'hierarchy_v0.1',
126
+ datatype='string')
127
+ hierarchy.add_network_attribute('HCX::modelFileCount', '2',
128
+ datatype='integer')
129
+
130
+ if host and uuid:
131
+ hierarchy.add_network_attribute('HCX::interactionNetworkHost', host)
132
+ hierarchy.add_network_attribute('HCX::interactionNetworkUUID', uuid)
133
+ else:
134
+ interactome_path = os.path.join(output_dir, interactome_name)
135
+ interactome.write_as_raw_cx2(interactome_path)
136
+ hierarchy.add_network_attribute('HCX::interactionNetworkName', interactome_name,
137
+ datatype='string')
138
+ return hierarchy
139
+
140
+
141
+ def add_hcx_members_annotation(hierarchy, interactome, gene_column='CD_MemberList'):
142
+ """
143
+ Adds the 'HCX::members' attribute to nodes in the hierarchy based on the interactome.
144
+
145
+ :param hierarchy: The hierarchical network in CX2 format.
146
+ :type hierarchy: CX2Network
147
+ :param interactome: The interactome network.
148
+ :type interactome: CX2Network
149
+ :param gene_column: Column name containing gene members.
150
+ :type gene_column: str, optional
151
+ """
152
+ for node_id, node_obj in hierarchy.get_nodes().items():
153
+ memberlist = hierarchy.get_node(node_id).get('v', {}).get(gene_column, '').split(' ')
154
+ membersids = []
155
+ for member in memberlist:
156
+ id_interactome = interactome.lookup_node_id_by_name(member)
157
+ if id_interactome is not None:
158
+ membersids.append(id_interactome)
159
+ hierarchy.add_node_attribute(node_id, 'HCX::members', membersids, datatype='list_of_integer')
160
+
161
+
162
+ def convert_hierarchical_network_to_hcx(hierarchy, interactome_url, ndex_username=None, ndex_password=None,
163
+ gene_column='CD_MemberList'):
164
+ """
165
+ Converts a hierarchical network into HCX format by adding necessary annotations and interactome details.
166
+
167
+ :param hierarchy: The hierarchical network in CX2 format or a path to the CX2 file.
168
+ :type hierarchy: CX2Network or str
169
+ :param interactome_url: URL of the interactome network in NDEx.
170
+ :type interactome_url: str
171
+ :param ndex_username: NDEx username for authentication.
172
+ :type ndex_username: str, optional
173
+ :param ndex_password: NDEx password for authentication.
174
+ :type ndex_password: str, optional
175
+ :param gene_column: Column name containing gene members.
176
+ :type gene_column: str, optional
177
+ :return: The updated hierarchy network in CX2 format with HCX annotations.
178
+ :rtype: CX2Network
179
+ """
180
+ factory = RawCX2NetworkFactory()
181
+ if isinstance(hierarchy, str):
182
+ hierarchy = factory.get_cx2network(hierarchy)
183
+
184
+ # Get the host and uuid of the interactome
185
+ host, uuid = get_host_and_uuid_from_network_url(interactome_url)
186
+ hierarchy = add_hcx_network_annotations(hierarchy, host=host, uuid=uuid)
187
+
188
+ # Create CX2Network object from interactome from NDEx
189
+ client = ndex2.client.Ndex2(host=host, username=ndex_username, password=ndex_password)
190
+ client_resp = client.get_network_as_cx2_stream(uuid)
191
+ interactome = factory.get_cx2network(json.loads(client_resp.content))
192
+
193
+ # Add HCX::members attribute to node attributes
194
+ add_hcx_members_annotation(hierarchy, interactome, gene_column)
195
+
196
+ # Add isRoot attribute to node attributes
197
+ add_isroot_node_attribute(hierarchy, get_root_nodes(hierarchy))
198
+ return hierarchy
@@ -0,0 +1,81 @@
1
+ import logging
2
+ from ndex2.cx2 import RawCX2NetworkFactory, CX2Network
3
+
4
+ logger = logging.getLogger(__name__)
5
+
6
+
7
+ class HierarchyDiff:
8
+ """
9
+ A class to compare two hierarchies in CX2 (HCX)
10
+ """
11
+
12
+ def __init__(self):
13
+ """
14
+ Constructor
15
+ """
16
+ pass
17
+
18
+ def compare_hierarchies_from_files(self, hierarchy_a_path=None, hierarchy_b_path=None):
19
+ """
20
+ Compare two hierarchies from file paths.
21
+
22
+ :param hierarchy_a_path: Path to hierarchy to compare
23
+ :type hierarchy_a_path: str
24
+ :param hierarchy_b_path: Path to hierarchy to compare
25
+ :type hierarchy_b_path: str
26
+ """
27
+ factory = RawCX2NetworkFactory()
28
+ hierarchy_a = factory.get_cx2network(hierarchy_a_path)
29
+ hierarchy_b = factory.get_cx2network(hierarchy_b_path)
30
+ return self.compare_hierarchies(hierarchy_a, hierarchy_b)
31
+
32
+ @staticmethod
33
+ def compare_hierarchies(hierarchy_a=None, hierarchy_b=None):
34
+ """
35
+ Compare two hierarchies in CX2 format. It looks at each system and outputs a robustness score
36
+ which correlates to how stable the system is in the hierarchies generated. It outputs a hierarchy with
37
+ the robustness score added to the systems.
38
+
39
+ :param hierarchy_a: Hierarchy to compare
40
+ :type hierarchy_a: ndex2.cx2.CX2Network
41
+ :param hierarchy_b: Hierarchy to compare
42
+ :type hierarchy_b: ndex2.cx2.CX2Network
43
+ """
44
+ nodes_a = hierarchy_a.get_nodes()
45
+ nodes_b = hierarchy_b.get_nodes()
46
+
47
+ # Compute overlap scores for nodes based on CD_MemberList
48
+ all_node_ids = set(nodes_a.keys()).union(nodes_b.keys())
49
+ node_overlap_scores = {}
50
+
51
+ for node_id in all_node_ids:
52
+ members_a = set(nodes_a.get(node_id, {}).get('v', {}).get('CD_MemberList', '').split())
53
+ members_b = set(nodes_b.get(node_id, {}).get('v', {}).get('CD_MemberList', '').split())
54
+
55
+ if members_a and members_b:
56
+ # Calculate overlap score based on intersection and union
57
+ overlap = len(members_a.intersection(members_b))
58
+ union = len(members_a.union(members_b))
59
+ score = overlap / union if union > 0 else 0
60
+ else:
61
+ score = 0
62
+
63
+ node_overlap_scores[node_id] = score
64
+
65
+ # Create a new hierarchy to store results
66
+ result_hierarchy = CX2Network()
67
+
68
+ # Add nodes from hierarchy_a with robustness scores
69
+ for node_id, data in nodes_a.items():
70
+ score = node_overlap_scores.get(node_id, 0)
71
+ data['v']['robustness_score'] = score
72
+ result_hierarchy.add_node(node_id=node_id, attributes=data['v'])
73
+
74
+ # Add nodes from hierarchy_b if they don't already exist in the result hierarchy
75
+ for node_id, data in nodes_b.items():
76
+ if node_id not in nodes_a:
77
+ score = node_overlap_scores.get(node_id, 0)
78
+ data['v']['robustness_score'] = score
79
+ result_hierarchy.add_node(node_id=node_id, attributes=data['v'])
80
+
81
+ return result_hierarchy
@@ -1,8 +1,8 @@
1
1
  This data set has been generated in the Emma Lundberg lab (https://ell-core.stanford.edu/)
2
- in July-August 2023 as part of the CM4AI Research Program (https://cm4ai.org/).
2
+ as part of the CM4AI Research Program (https://cm4ai.org/).
3
3
 
4
- This data set displays the spatial localization of 95 proteins in the breast cancer
5
- cell line MDA-MB-468. MDA-MB-468 cells were seeded into 96-well glass bottom plates
4
+ This data set displays the spatial localization of proteins in the cell line MDA-MB-468.
5
+ MDA-MB-468 cells were seeded into 96-well glass bottom plates
6
6
  and treated either with the chemotherapeutic drug Paclitaxel or with the chemotherapeutic
7
7
  drug Vorinostat (SAHA) or not treated. For drug treatment, higher cell numbers were
8
8
  seeded than for the cells that were not treated to reach a sufficient amount of
@@ -14,7 +14,7 @@ DAPI (to label nuclei, "blue" channel),
14
14
  a Calreticulin antibody (to label the endoplasmic reticulum, "yellow" channel),
15
15
  a Tubulin antibody (to label Microtubuli, "red" channel), and an antibody against a
16
16
  protein of interest (a different antibody / protein for each well, "green" channel).
17
- In the green channel, for each well, one of 95 proteins was stained with an antibody
17
+ In the green channel, for each well, each one of the proteins was stained with an antibody
18
18
  from the proteinatlas.org project.
19
19
 
20
20
  The .tsv file describes each image in the data set. Each row represents one image.
@@ -138,13 +138,13 @@ class FakeImageDownloader(ImageDownloader):
138
138
  raise CellMapsError('Unable to download ' +
139
139
  str(entry))
140
140
  fname = os.path.basename(entry[1])
141
- color = re.sub('\..*$', '', re.sub('^.*_', '', fname))
141
+ color = re.sub(r'\..*$', '', re.sub('^.*_', '', fname))
142
142
  src_image_dict[color] = entry[1]
143
143
 
144
144
  for entry in download_list[5:]:
145
145
  t.update()
146
146
  fname = os.path.basename(entry[1])
147
- color = re.sub('\..*$', '', re.sub('^.*_', '', fname))
147
+ color = re.sub(r'\..*$', '', re.sub('^.*_', '', fname))
148
148
  shutil.copy(src_image_dict[color], entry[1])
149
149
  return []
150
150
 
@@ -316,14 +316,20 @@ class IFImageDataConverter(BaseCommandLineTool):
316
316
  os.path.join(self._outdir, 'readme.txt'))
317
317
 
318
318
  # download the images
319
- self._download_data(filtered_df['Baselink'].values.tolist())
319
+ if 'Baselink' in filtered_df:
320
+ baselink_name = 'Baselink'
321
+ else:
322
+ baselink_name = 'base_web_link'
323
+
324
+ self._download_data(filtered_df[baselink_name].values.tolist())
320
325
 
321
326
  # remove Baselink column
322
- filtered_df.drop('Baselink', axis=1, inplace=True)
327
+ filtered_df.drop(baselink_name, axis=1, inplace=True)
323
328
 
324
329
  # remove Slice column if set
325
330
  if self._slice is not None:
326
- filtered_df.drop('Slice', axis=1, inplace=True)
331
+ if 'Slice' in filtered_df:
332
+ filtered_df.drop('Slice', axis=1, inplace=True)
327
333
 
328
334
  file_path = os.path.join(self._outdir, constants.ANTIBODY_GENE_TABLE_FILE)
329
335
 
@@ -346,7 +352,8 @@ class IFImageDataConverter(BaseCommandLineTool):
346
352
  gen_dsets.extend(self._register_downloaded_images(description=description,
347
353
  keywords=keywords))
348
354
  self._register_software(keywords=keywords, description=description)
349
- self._register_computation(generated_dataset_ids=gen_dsets,
355
+ logger.info('Registering only 1st 1000 datasets into computation due to limitations of fairscape-cli')
356
+ self._register_computation(generated_dataset_ids=gen_dsets[:1000],
350
357
  description=description,
351
358
  keywords=keywords)
352
359
  return 0
@@ -491,15 +498,26 @@ class IFImageDataConverter(BaseCommandLineTool):
491
498
 
492
499
  # keep only slice specified
493
500
  if self._slice is not None:
494
- logger.info('Keeping only slice: ' + str(self._slice))
495
- df = df[df['Slice'] == self._slice]
496
- logger.debug(str(len(df)) + ' rows remain after slice filter')
501
+ if 'Slice' in df:
502
+ logger.info('Keeping only slice: ' + str(self._slice))
503
+ df = df[df['Slice'] == self._slice]
504
+ logger.debug(str(len(df)) + ' rows remain after slice filter')
505
+ else:
506
+ # attempt to find slice from base_web_link
507
+ if 'base_web_link' in df:
508
+ logger.info('Keeping only slice: ' + str(self._slice) + ' by parsing base_web_link')
509
+ df = df[df['base_web_link'].str.contains('_' + self._slice + '_', case=False)]
510
+ logger.debug(str(len(df)) + ' rows remain after slice filter')
497
511
 
498
512
  # keep only treatment specified
499
513
  logger.info('Keeping only treatment: ' + str(self._treatment))
500
514
  df = df[df['Treatment'].str.contains(self._treatment, case=False)]
501
515
  logger.debug(str(len(df)) + ' rows remain after treatment filter')
502
516
 
517
+ # remove negative-ctrl rows
518
+ logger.debug('Removing NEGATIVE rows')
519
+ df = df[df["Antibody ID"].str.contains('NEGATIVE') == False]
520
+
503
521
  return df
504
522
 
505
523
  def _register_downloaded_images(self,
@@ -601,11 +619,10 @@ class IFImageDataConverter(BaseCommandLineTool):
601
619
  description=desc,
602
620
  formatter_class=constants.ArgParseFormatter)
603
621
  parser.add_argument('outdir',
604
- help='Directory where RO-Crate will be created '
605
- 'store results in')
622
+ help='Directory where RO-Crate will be created')
606
623
  parser.add_argument('--input', required=True,
607
624
  help='Table file with the following '
608
- 'fields: [Antibody ID, ENSEMBL ID, Treatment, Well, Region, Slice, Baselink] ')
625
+ 'fields: [Antibody ID, ENSEMBL ID|ENSG, Treatment, Well, Region, Slice, Baselink|base_web_link] ')
609
626
  parser.add_argument('--author', default='Lundberg Lab',
610
627
  help='Author that created this data')
611
628
  parser.add_argument('--name', default='IF images',
@@ -624,15 +641,17 @@ class IFImageDataConverter(BaseCommandLineTool):
624
641
  parser.add_argument('--cell_line', default='MDA-MB-468',
625
642
  help='Name of cell line. For example MDA-MB-468')
626
643
  parser.add_argument('--gene_set', choices=['chromatin', 'metabolic'],
627
- default='chromatin',
628
- help='Gene set for dataset')
644
+ help='Gene set for dataset, standard names are '
645
+ + 'chromatin, metabolic, or leave it unset')
629
646
  parser.add_argument('--tissue', choices=['undifferentiated', 'neuron',
630
647
  'cardiomyocytes', ''],
631
648
  default='breast; mammary gland',
632
649
  help='Tissue for dataset. Since the default --cell_line '
633
650
  'is MDA-MB-468, this value is set to the tissue '
634
651
  'for that cell line')
635
- parser.add_argument('--slice', default='z01',
636
- help='Slice to keep')
652
+ parser.add_argument('--slice',
653
+ help='Slice to keep. Example names are z01, z02. If unset '
654
+ + 'all slices are kept')
655
+
637
656
  return parser
638
657
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: cellmaps_utils
3
- Version: 0.5.0
3
+ Version: 0.6.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
@@ -18,15 +18,15 @@ Classifier: Programming Language :: Python :: 3.11
18
18
  Description-Content-Type: text/x-rst
19
19
  License-File: LICENSE
20
20
  License-File: AUTHORS.rst
21
- Requires-Dist: fairscape-cli==0.2.0
21
+ Requires-Dist: fairscape-cli==1.0.2
22
22
  Requires-Dist: ndex2<4.0.0,>=3.8.0
23
- Requires-Dist: scipy
24
- Requires-Dist: scikit-learn
25
- Requires-Dist: pandas
26
- Requires-Dist: numpy
27
- Requires-Dist: dill
28
- Requires-Dist: tqdm
29
- Requires-Dist: requests
23
+ Requires-Dist: scipy<1.11,>=1.10.1
24
+ Requires-Dist: scikit-learn<1.4.0,>=0.19.0
25
+ Requires-Dist: pandas<3.0.0,>=2.0.0
26
+ Requires-Dist: numpy<2.0.0,>=1.24.4
27
+ Requires-Dist: dill<0.4.0,>=0.3.8
28
+ Requires-Dist: tqdm<5.0.0,>=4.66.6
29
+ Requires-Dist: requests<3.0.0,>=2.31.0
30
30
 
31
31
  =============================
32
32
  Cell Maps Pipeline Utilities
@@ -145,6 +145,19 @@ This package was created with Cookiecutter_ and the `audreyr/cookiecutter-pypack
145
145
  History
146
146
  =======
147
147
 
148
+ 0.6.0 (2025-02-05)
149
+ ------------------
150
+
151
+ * Add ``HierarchyDiff`` class with ``compare_hierarchies`` method to compare two hierarchies in CX2 format, and output
152
+ a hierarchy with robustness score of each system.
153
+
154
+ * Add version bounds for required packages.
155
+
156
+ * Add ``convert_hierarchical_network_to_hcx`` (utility function that aids convertion of a hierarchical network in CX2 to
157
+ HCX) and ``add_hcx_members_annotation`` (adds 'HCX::members' attribute to nodes) to ``hcx_utils``
158
+
159
+ * Fixes in IF and APMS tools
160
+
148
161
  0.5.0 (2024-09-05)
149
162
  ------------------
150
163
 
@@ -18,6 +18,7 @@ cellmaps_utils/ddotconverter.py
18
18
  cellmaps_utils/exceptions.py
19
19
  cellmaps_utils/hcx_utils.py
20
20
  cellmaps_utils/hidefconverter.py
21
+ cellmaps_utils/hierdiff.py
21
22
  cellmaps_utils/ifimage_readme.txt
22
23
  cellmaps_utils/iftool.py
23
24
  cellmaps_utils/logutils.py
@@ -0,0 +1,9 @@
1
+ fairscape-cli==1.0.2
2
+ ndex2<4.0.0,>=3.8.0
3
+ scipy<1.11,>=1.10.1
4
+ scikit-learn<1.4.0,>=0.19.0
5
+ pandas<3.0.0,>=2.0.0
6
+ numpy<2.0.0,>=1.24.4
7
+ dill<0.4.0,>=0.3.8
8
+ tqdm<5.0.0,>=4.66.6
9
+ requests<3.0.0,>=2.31.0