fSTG-Toolkit 1.3.0__tar.gz → 1.4.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 (68) hide show
  1. {fstg_toolkit-1.3.0 → fstg_toolkit-1.4.0}/PKG-INFO +2 -1
  2. {fstg_toolkit-1.3.0 → fstg_toolkit-1.4.0}/README.md +1 -0
  3. {fstg_toolkit-1.3.0 → fstg_toolkit-1.4.0}/pyproject.toml +1 -1
  4. {fstg_toolkit-1.3.0 → fstg_toolkit-1.4.0}/src/fstg_toolkit/__main__.py +33 -5
  5. {fstg_toolkit-1.3.0 → fstg_toolkit-1.4.0}/src/fstg_toolkit/frequent/spminer.py +3 -2
  6. {fstg_toolkit-1.3.0 → fstg_toolkit-1.4.0}/src/fstg_toolkit/io.py +6 -4
  7. {fstg_toolkit-1.3.0 → fstg_toolkit-1.4.0}/src/fstg_toolkit/spminer/Dockerfile +1 -1
  8. {fstg_toolkit-1.3.0 → fstg_toolkit-1.4.0}/src/fstg_toolkit/spminer/batch_process.py +39 -33
  9. {fstg_toolkit-1.3.0 → fstg_toolkit-1.4.0}/LICENSE +0 -0
  10. {fstg_toolkit-1.3.0 → fstg_toolkit-1.4.0}/src/fstg_toolkit/__init__.py +0 -0
  11. {fstg_toolkit-1.3.0 → fstg_toolkit-1.4.0}/src/fstg_toolkit/app/__init__.py +0 -0
  12. {fstg_toolkit-1.3.0 → fstg_toolkit-1.4.0}/src/fstg_toolkit/app/assets/common.js +0 -0
  13. {fstg_toolkit-1.3.0 → fstg_toolkit-1.4.0}/src/fstg_toolkit/app/assets/custom.css +0 -0
  14. {fstg_toolkit-1.3.0 → fstg_toolkit-1.4.0}/src/fstg_toolkit/app/assets/favicon.ico +0 -0
  15. {fstg_toolkit-1.3.0 → fstg_toolkit-1.4.0}/src/fstg_toolkit/app/core/__init__.py +0 -0
  16. {fstg_toolkit-1.3.0 → fstg_toolkit-1.4.0}/src/fstg_toolkit/app/core/color.py +0 -0
  17. {fstg_toolkit-1.3.0 → fstg_toolkit-1.4.0}/src/fstg_toolkit/app/core/config.py +0 -0
  18. {fstg_toolkit-1.3.0 → fstg_toolkit-1.4.0}/src/fstg_toolkit/app/core/datafilesdb.py +0 -0
  19. {fstg_toolkit-1.3.0 → fstg_toolkit-1.4.0}/src/fstg_toolkit/app/core/geometry.py +0 -0
  20. {fstg_toolkit-1.3.0 → fstg_toolkit-1.4.0}/src/fstg_toolkit/app/core/processing.py +0 -0
  21. {fstg_toolkit-1.3.0 → fstg_toolkit-1.4.0}/src/fstg_toolkit/app/core/utils.py +0 -0
  22. {fstg_toolkit-1.3.0 → fstg_toolkit-1.4.0}/src/fstg_toolkit/app/figures/__init__.py +0 -0
  23. {fstg_toolkit-1.3.0 → fstg_toolkit-1.4.0}/src/fstg_toolkit/app/figures/common.py +0 -0
  24. {fstg_toolkit-1.3.0 → fstg_toolkit-1.4.0}/src/fstg_toolkit/app/figures/data.py +0 -0
  25. {fstg_toolkit-1.3.0 → fstg_toolkit-1.4.0}/src/fstg_toolkit/app/figures/frequent.py +0 -0
  26. {fstg_toolkit-1.3.0 → fstg_toolkit-1.4.0}/src/fstg_toolkit/app/figures/matrices.py +0 -0
  27. {fstg_toolkit-1.3.0 → fstg_toolkit-1.4.0}/src/fstg_toolkit/app/figures/metrics.py +0 -0
  28. {fstg_toolkit-1.3.0 → fstg_toolkit-1.4.0}/src/fstg_toolkit/app/figures/subject.py +0 -0
  29. {fstg_toolkit-1.3.0 → fstg_toolkit-1.4.0}/src/fstg_toolkit/app/fstg_view.py +0 -0
  30. {fstg_toolkit-1.3.0 → fstg_toolkit-1.4.0}/src/fstg_toolkit/app/pages/dashboard.py +0 -0
  31. {fstg_toolkit-1.3.0 → fstg_toolkit-1.4.0}/src/fstg_toolkit/app/pages/home.py +0 -0
  32. {fstg_toolkit-1.3.0 → fstg_toolkit-1.4.0}/src/fstg_toolkit/app/pages/list.py +0 -0
  33. {fstg_toolkit-1.3.0 → fstg_toolkit-1.4.0}/src/fstg_toolkit/app/pages/submit.py +0 -0
  34. {fstg_toolkit-1.3.0 → fstg_toolkit-1.4.0}/src/fstg_toolkit/app/views/__init__.py +0 -0
  35. {fstg_toolkit-1.3.0 → fstg_toolkit-1.4.0}/src/fstg_toolkit/app/views/common.py +0 -0
  36. {fstg_toolkit-1.3.0 → fstg_toolkit-1.4.0}/src/fstg_toolkit/app/views/data.py +0 -0
  37. {fstg_toolkit-1.3.0 → fstg_toolkit-1.4.0}/src/fstg_toolkit/app/views/frequent.py +0 -0
  38. {fstg_toolkit-1.3.0 → fstg_toolkit-1.4.0}/src/fstg_toolkit/app/views/matrices.py +0 -0
  39. {fstg_toolkit-1.3.0 → fstg_toolkit-1.4.0}/src/fstg_toolkit/app/views/metrics.py +0 -0
  40. {fstg_toolkit-1.3.0 → fstg_toolkit-1.4.0}/src/fstg_toolkit/app/views/subject.py +0 -0
  41. {fstg_toolkit-1.3.0 → fstg_toolkit-1.4.0}/src/fstg_toolkit/factory.py +0 -0
  42. {fstg_toolkit-1.3.0 → fstg_toolkit-1.4.0}/src/fstg_toolkit/frequent/__init__.py +0 -0
  43. {fstg_toolkit-1.3.0 → fstg_toolkit-1.4.0}/src/fstg_toolkit/frequent/_docker_utils.py +0 -0
  44. {fstg_toolkit-1.3.0 → fstg_toolkit-1.4.0}/src/fstg_toolkit/frequent/patterns.py +0 -0
  45. {fstg_toolkit-1.3.0 → fstg_toolkit-1.4.0}/src/fstg_toolkit/graph.py +0 -0
  46. {fstg_toolkit-1.3.0 → fstg_toolkit-1.4.0}/src/fstg_toolkit/logging.yml +0 -0
  47. {fstg_toolkit-1.3.0 → fstg_toolkit-1.4.0}/src/fstg_toolkit/metrics.py +0 -0
  48. {fstg_toolkit-1.3.0 → fstg_toolkit-1.4.0}/src/fstg_toolkit/simulation.py +0 -0
  49. {fstg_toolkit-1.3.0 → fstg_toolkit-1.4.0}/src/fstg_toolkit/spminer/.env +0 -0
  50. {fstg_toolkit-1.3.0 → fstg_toolkit-1.4.0}/src/fstg_toolkit/spminer/NOTICE.md +0 -0
  51. {fstg_toolkit-1.3.0 → fstg_toolkit-1.4.0}/src/fstg_toolkit/spminer/ckpt/model.pt +0 -0
  52. {fstg_toolkit-1.3.0 → fstg_toolkit-1.4.0}/src/fstg_toolkit/spminer/common/combined_syn.py +0 -0
  53. {fstg_toolkit-1.3.0 → fstg_toolkit-1.4.0}/src/fstg_toolkit/spminer/common/data.py +0 -0
  54. {fstg_toolkit-1.3.0 → fstg_toolkit-1.4.0}/src/fstg_toolkit/spminer/common/feature_preprocess.py +0 -0
  55. {fstg_toolkit-1.3.0 → fstg_toolkit-1.4.0}/src/fstg_toolkit/spminer/common/models.py +0 -0
  56. {fstg_toolkit-1.3.0 → fstg_toolkit-1.4.0}/src/fstg_toolkit/spminer/common/utils.py +0 -0
  57. {fstg_toolkit-1.3.0 → fstg_toolkit-1.4.0}/src/fstg_toolkit/spminer/docker-compose.yml +0 -0
  58. {fstg_toolkit-1.3.0 → fstg_toolkit-1.4.0}/src/fstg_toolkit/spminer/requirements.txt +0 -0
  59. {fstg_toolkit-1.3.0 → fstg_toolkit-1.4.0}/src/fstg_toolkit/spminer/subgraph_matching/alignment.py +0 -0
  60. {fstg_toolkit-1.3.0 → fstg_toolkit-1.4.0}/src/fstg_toolkit/spminer/subgraph_matching/config.py +0 -0
  61. {fstg_toolkit-1.3.0 → fstg_toolkit-1.4.0}/src/fstg_toolkit/spminer/subgraph_matching/hyp_search.py +0 -0
  62. {fstg_toolkit-1.3.0 → fstg_toolkit-1.4.0}/src/fstg_toolkit/spminer/subgraph_matching/test.py +0 -0
  63. {fstg_toolkit-1.3.0 → fstg_toolkit-1.4.0}/src/fstg_toolkit/spminer/subgraph_matching/train.py +0 -0
  64. {fstg_toolkit-1.3.0 → fstg_toolkit-1.4.0}/src/fstg_toolkit/spminer/subgraph_mining/config.py +0 -0
  65. {fstg_toolkit-1.3.0 → fstg_toolkit-1.4.0}/src/fstg_toolkit/spminer/subgraph_mining/decoder.py +0 -0
  66. {fstg_toolkit-1.3.0 → fstg_toolkit-1.4.0}/src/fstg_toolkit/spminer/subgraph_mining/search_agents.py +0 -0
  67. {fstg_toolkit-1.3.0 → fstg_toolkit-1.4.0}/src/fstg_toolkit/utils.py +0 -0
  68. {fstg_toolkit-1.3.0 → fstg_toolkit-1.4.0}/src/fstg_toolkit/visualization.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fSTG-Toolkit
3
- Version: 1.3.0
3
+ Version: 1.4.0
4
4
  Summary: An Open-Source Software for Spatio-Temporal Graph Analysis of fMRI data.
5
5
  License-Expression: CECILL-2.1
6
6
  License-File: LICENSE
@@ -52,6 +52,7 @@ Description-Content-Type: text/markdown
52
52
  ![PyPI - Version](https://img.shields.io/pypi/v/fSTG-Toolkit)
53
53
  ![PyPI - License](https://img.shields.io/pypi/l/fSTG-Toolkit)
54
54
  [![Read the Docs](https://img.shields.io/readthedocs/fstg-toolkit)](https://fstg-toolkit.readthedocs.io/)
55
+ [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.22093918.svg)](https://doi.org/10.5281/zenodo.22093918)
55
56
 
56
57
  </div>
57
58
 
@@ -10,6 +10,7 @@
10
10
  ![PyPI - Version](https://img.shields.io/pypi/v/fSTG-Toolkit)
11
11
  ![PyPI - License](https://img.shields.io/pypi/l/fSTG-Toolkit)
12
12
  [![Read the Docs](https://img.shields.io/readthedocs/fstg-toolkit)](https://fstg-toolkit.readthedocs.io/)
13
+ [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.22093918.svg)](https://doi.org/10.5281/zenodo.22093918)
13
14
 
14
15
  </div>
15
16
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "fSTG-Toolkit"
3
- version = "1.3.0"
3
+ version = "1.4.0"
4
4
  description = "An Open-Source Software for Spatio-Temporal Graph Analysis of fMRI data."
5
5
  authors = [
6
6
  {name = "Julien PONTABRY", email = "jpontabry@unistra.fr"}
@@ -126,6 +126,24 @@ def __load_graph(filepath: Path) -> SpatioTemporalGraph:
126
126
  return loader.load_graph(areas, chosen)
127
127
 
128
128
 
129
+ def __with_zip_suffix(filepath: Path) -> Path:
130
+ """Add the '.zip' extension to a path of archive, if it is missing.
131
+
132
+ Parameters
133
+ ----------
134
+ filepath : Path
135
+ The path of the archive to write.
136
+
137
+ Returns
138
+ -------
139
+ Path
140
+ The path ending with the '.zip' extension.
141
+ """
142
+ if filepath.suffix.lower() == '.zip':
143
+ return filepath
144
+ return filepath.with_name(f'{filepath.name}.zip')
145
+
146
+
129
147
  ## building and computing #####################################################
130
148
 
131
149
 
@@ -224,6 +242,7 @@ def build(areas_description_path: Path, correlation_matrices_path: Tuple[Path],
224
242
 
225
243
  The CSV file for the description of areas and regions should have the columns: `Id_Area`, `Name_Area`, and `Name_Region`.
226
244
  """
245
+ output = __with_zip_suffix(output)
227
246
 
228
247
  # prepare the data saver
229
248
  saver = DataSaver()
@@ -234,6 +253,9 @@ def build(areas_description_path: Path, correlation_matrices_path: Tuple[Path],
234
253
  matrices = {}
235
254
  for cm in correlation_matrices_path:
236
255
  for name, matrix in __read_load_np(cm):
256
+ if name in matrices:
257
+ error_console.print(f"Warning: sequence '{name}' from '{cm.name}' "
258
+ f"replaces a previous sequence with the same name.")
237
259
  matrices[name] = matrix
238
260
  console.print(f"- red '{cm.name}'")
239
261
  console.print(f"Red {len(correlation_matrices_path)} files.")
@@ -640,7 +662,8 @@ GRAPH_SEQUENCE_DESCRIPTION = GraphSequenceDescription()
640
662
  @graph.group()
641
663
  @click.option('-o', '--output_path', type=click.Path(writable=True, path_type=Path),
642
664
  default="output", show_default="a file named 'output' in the current directory",
643
- help="Path where to write the simulated output.")
665
+ help="Path where to write the simulated output. The extension is added if missing: "
666
+ "'.zip' for patterns and sequences, '.npz' for correlations.")
644
667
  @click.pass_context
645
668
  def simulate(ctx: click.core.Context, output_path: Path):
646
669
  """Simulate a spatio-temporal graph."""
@@ -679,7 +702,7 @@ def pattern(ctx: click.core.Context, networks: List[List[Tuple[Tuple[int, int],
679
702
  pat = generate_pattern(networks_list=networks,
680
703
  spatial_edges=spatial_edges,
681
704
  temporal_edges=temporal_edges)
682
- save_spatio_temporal_graph(pat, ctx.obj)
705
+ save_spatio_temporal_graph(pat, __with_zip_suffix(ctx.obj))
683
706
 
684
707
 
685
708
  @simulate.command()
@@ -697,7 +720,7 @@ def sequence(ctx: click.core.Context, patterns: Tuple[Path], sequence_descriptio
697
720
  for i, filepath in enumerate(patterns)}
698
721
  simulator = SpatioTemporalGraphSimulator(**pattern_graphs)
699
722
  graph = simulator.simulate(*sequence_description)
700
- save_spatio_temporal_graph(graph, ctx.obj)
723
+ save_spatio_temporal_graph(graph, __with_zip_suffix(ctx.obj))
701
724
 
702
725
 
703
726
  @simulate.command()
@@ -707,11 +730,16 @@ def sequence(ctx: click.core.Context, patterns: Tuple[Path], sequence_descriptio
707
730
  help="The correlation threshold when building graph from matrices.")
708
731
  @click.pass_context
709
732
  def correlations(ctx: click.core.Context, graph_path: Path, threshold: float):
710
- """Simulate correlations matrices from a spatio-temporal graph."""
733
+ """Simulate correlations matrices from a spatio-temporal graph.
734
+
735
+ The sequence of matrices is saved under the name of the output file (without its
736
+ '.npz' extension), which is the name the command 'build' gives to its graph.
737
+ """
711
738
  graph = __load_graph(graph_path)
712
739
  simulator = CorrelationMatrixSequenceSimulator(graph, threshold=threshold)
713
740
  matrices = simulator.simulate()
714
- np.savez_compressed(ctx.obj, simulated=matrices)
741
+ name = Path(ctx.obj).name.removesuffix('.npz')
742
+ np.savez_compressed(ctx.obj, **{name: matrices})
715
743
 
716
744
 
717
745
  ## showing ####################################################################
@@ -33,6 +33,7 @@
33
33
 
34
34
  import logging
35
35
  import re
36
+ from importlib.metadata import version
36
37
  from pathlib import Path
37
38
  from typing import Optional
38
39
 
@@ -73,8 +74,8 @@ class SPMinerService:
73
74
  already loaded.
74
75
  """
75
76
  if self.__docker_image is None:
76
- # TODO use an external config file?
77
- tag = 'spminer:latest'
77
+ # tagging by package version rebuilds the image when the bundled code changes
78
+ tag = f'spminer:{version("fSTG-Toolkit")}'
78
79
  build_path = Path(__file__).parent.parent / 'spminer'
79
80
  self.__docker_image = self.__docker_helper.load_local_image(tag, build_path)
80
81
 
@@ -1279,7 +1279,7 @@ def load_spatio_temporal_graph(filepath: Path | str) -> SpatioTemporalGraph:
1279
1279
  If no graph is found in the zip file.
1280
1280
  """
1281
1281
  logger.debug(f"Loading STG from '{filepath}'.")
1282
- loader = DataLoader(filepath)
1282
+ loader = DataLoader(Path(filepath))
1283
1283
  filenames = loader.lazy_load_graphs()
1284
1284
 
1285
1285
  if len(filenames) > 0:
@@ -1331,7 +1331,7 @@ def save_spatio_temporal_graph(graph: SpatioTemporalGraph, filepath: Path | str)
1331
1331
  saver = DataSaver()
1332
1332
  saver.add_areas(graph.areas)
1333
1333
  saver.add_graphs({'graph.json': graph})
1334
- _, gen = saver.save(filepath)
1334
+ _, gen = saver.save(Path(filepath))
1335
1335
  list(gen)
1336
1336
 
1337
1337
 
@@ -1714,8 +1714,10 @@ class GraphsDataset:
1714
1714
  columns = [f'Factor{i+1}' for i in range(n)] + ['Subject', 'Graph']
1715
1715
  subjects = pd.DataFrame(data, columns=columns).set_index(columns[:n+1])
1716
1716
 
1717
- if len(matrices_filenames) == len(graphs_filenames):
1718
- subjects['Matrix'] = matrices_filenames
1717
+ # pair matrices with graphs by name, as files are not stored in the same order
1718
+ matrices_by_name = {filename.removesuffix('.npy'): filename for filename in matrices_filenames}
1719
+ if all(name in matrices_by_name for name in filenames_without_ext):
1720
+ subjects['Matrix'] = [matrices_by_name[name] for name in filenames_without_ext]
1719
1721
 
1720
1722
  return GraphsDataset(loader=loader,
1721
1723
  areas_desc=areas_desc,
@@ -46,7 +46,7 @@ RUN pip install --no-cache-dir -r requirements.txt
46
46
  COPY . .
47
47
 
48
48
  # 5. Setup the permissionsgp
49
- RUN mkdir data_preprocessed_batch && chmod 1777 data_preprocessed_batch
49
+ RUN mkdir -p data_preprocessed_batch && chmod 1777 data_preprocessed_batch
50
50
 
51
51
  ## select either "t" or "st"
52
52
  CMD ["python", "batch_process.py", "t"]
@@ -57,31 +57,32 @@ OUTPUT_FOLDER = 'results_batch'
57
57
  PREPROCESSED_FOLDER = 'data_preprocessed_batch'
58
58
  NODE_ANCHORED = True
59
59
 
60
- REGION_ENCODING = {
61
- 'Prefrontal cortex': 1,
62
- 'Retrosplenial': 2,
63
- 'Insula': 3,
64
- 'Thalamus': 4,
65
- 'Hippocampe': 5,
66
- 'hippocampal formation': 6,
67
- 'Cortical subplate': 7,
68
- 'Sensory cortex': 8,
69
- 'Olfactory area': 9,
70
- 'Striatum': 10,
71
- 'Pallidum': 11,
72
- 'Hypothalamus': 12,
73
- 'Midbrain': 13,
74
- 'Unknown': 14
75
- }
76
-
77
60
  TEMPORAL_EDGE_ENCODING = {
78
61
  'PP': 0, 'PO': 1, 'EQ': 2, 'DC': 3, 'PPi': 4
79
62
  }
80
63
 
81
64
  SPATIAL_EDGE_TYPE = 5
82
65
 
66
+ UNKNOWN_REGION = 'Unknown'
67
+
83
68
 
84
- def preprocess_graph_temporal(input_json, output_pkl):
69
+ def build_region_encoding(json_files):
70
+ """Build the region encoding shared by all the graphs of a run.
71
+
72
+ Codes start at 1 and follow the sorted order of the region names, so that
73
+ the encoding does not depend on the order of the files.
74
+ """
75
+ regions = set()
76
+ for json_file in json_files:
77
+ with open(json_file, 'r') as f:
78
+ data = json.load(f)
79
+ for node in data['nodes']:
80
+ regions.add(node.get('region', UNKNOWN_REGION))
81
+
82
+ return {region: code for code, region in enumerate(sorted(regions), 1)}
83
+
84
+
85
+ def preprocess_graph_temporal(input_json, output_pkl, region_encoding):
85
86
  with open(input_json, 'r') as f:
86
87
  data = json.load(f)
87
88
 
@@ -89,8 +90,8 @@ def preprocess_graph_temporal(input_json, output_pkl):
89
90
 
90
91
  for node in data['nodes']:
91
92
  node_id = node['id']
92
- region = node.get('region', 'Unknown')
93
- region_code = REGION_ENCODING.get(region, 14)
93
+ region = node.get('region', UNKNOWN_REGION)
94
+ region_code = region_encoding[region]
94
95
  G.add_node(node_id, node_feature=region_code)
95
96
 
96
97
  temporal_count = 0
@@ -109,7 +110,7 @@ def preprocess_graph_temporal(input_json, output_pkl):
109
110
 
110
111
  return G, temporal_count
111
112
 
112
- def preprocess_graph_spatial(input_json, output_pkl):
113
+ def preprocess_graph_spatial(input_json, output_pkl, region_encoding):
113
114
  with open(input_json, 'r') as f:
114
115
  data = json.load(f)
115
116
 
@@ -121,8 +122,8 @@ def preprocess_graph_spatial(input_json, output_pkl):
121
122
  for node in data['nodes']:
122
123
  old_id = node['id']
123
124
  new_id = id_mapping[old_id]
124
- region = node.get('region', 'Unknown')
125
- region_code = REGION_ENCODING.get(region, 14)
125
+ region = node.get('region', UNKNOWN_REGION)
126
+ region_code = region_encoding[region]
126
127
  G.add_node(new_id, node_feature=region_code, original_id=old_id, region=region)
127
128
 
128
129
  spatial_count = 0
@@ -142,7 +143,7 @@ def preprocess_graph_spatial(input_json, output_pkl):
142
143
 
143
144
  return G, spatial_count
144
145
 
145
- def preprocess_graph_spatiotemporal(input_json, output_pkl):
146
+ def preprocess_graph_spatiotemporal(input_json, output_pkl, region_encoding):
146
147
  with open(input_json, 'r') as f:
147
148
  data = json.load(f)
148
149
 
@@ -150,8 +151,8 @@ def preprocess_graph_spatiotemporal(input_json, output_pkl):
150
151
 
151
152
  for node in data['nodes']:
152
153
  node_id = node['id']
153
- region = node.get('region', 'Unknown')
154
- region_code = REGION_ENCODING.get(region, 14)
154
+ region = node.get('region', UNKNOWN_REGION)
155
+ region_code = region_encoding[region]
155
156
  G.add_node(node_id, node_feature=region_code)
156
157
 
157
158
  temporal_count = 0
@@ -181,17 +182,17 @@ def preprocess_graph_spatiotemporal(input_json, output_pkl):
181
182
 
182
183
  return G, temporal_count, spatial_count
183
184
 
184
- def preprocess_graph(input_json, output_pkl, mode):
185
+ def preprocess_graph(input_json, output_pkl, mode, region_encoding):
185
186
  print(f' [1/3] Preprocessing {input_json.name} (mode: {mode})...')
186
187
 
187
188
  if mode == 't':
188
- G, edge_count = preprocess_graph_temporal(input_json, output_pkl)
189
+ G, edge_count = preprocess_graph_temporal(input_json, output_pkl, region_encoding)
189
190
  print(f' Saved: {G.number_of_nodes()} nodes, {edge_count} temporal edges')
190
191
  elif mode == 's':
191
- G, edge_count = preprocess_graph_spatial(input_json, output_pkl)
192
+ G, edge_count = preprocess_graph_spatial(input_json, output_pkl, region_encoding)
192
193
  print(f' Saved: {G.number_of_nodes()} nodes, {edge_count} spatial edges')
193
194
  elif mode == 'st':
194
- G, temporal_count, spatial_count = preprocess_graph_spatiotemporal(input_json, output_pkl)
195
+ G, temporal_count, spatial_count = preprocess_graph_spatiotemporal(input_json, output_pkl, region_encoding)
195
196
  print(f' Saved: {G.number_of_nodes()} nodes, {temporal_count} temporal + {spatial_count} spatial edges')
196
197
 
197
198
  return G
@@ -224,7 +225,7 @@ def run_spminer(pkl_path, json_path, output_dir, mode, node_anchored=True):
224
225
  print(f'\n SPMiner completed successfully')
225
226
  return True
226
227
 
227
- def process_single_graph(json_file, relative_path, mode):
228
+ def process_single_graph(json_file, relative_path, mode, region_encoding):
228
229
  print(f'\n{"="*70}')
229
230
  print(f'Processing: {relative_path.stem} (mode: {mode})')
230
231
  print(f'{"="*70}')
@@ -232,7 +233,7 @@ def process_single_graph(json_file, relative_path, mode):
232
233
  try:
233
234
  pkl_path = Path(PREPROCESSED_FOLDER) / relative_path.with_name(f'{relative_path.stem}_{mode}.pkl')
234
235
  pkl_path.parent.mkdir(parents=True, exist_ok=True)
235
- _ = preprocess_graph(json_file, pkl_path, mode)
236
+ _ = preprocess_graph(json_file, pkl_path, mode, region_encoding)
236
237
 
237
238
  graph_output_dir = Path(OUTPUT_FOLDER) / relative_path.with_suffix('')
238
239
  graph_output_dir.mkdir(parents=True, exist_ok=True)
@@ -305,6 +306,11 @@ def main():
305
306
  print(f'\n Found {len(json_files)} JSON files:')
306
307
  for f in json_files:
307
308
  print(f' - {f.name}')
309
+
310
+ region_encoding = build_region_encoding(json_files)
311
+ print(f'\n Region encoding ({len(region_encoding)} regions):')
312
+ for region, code in region_encoding.items():
313
+ print(f' {code}: {region}')
308
314
 
309
315
  os.makedirs(OUTPUT_FOLDER, exist_ok=True)
310
316
  os.makedirs(PREPROCESSED_FOLDER, exist_ok=True)
@@ -318,7 +324,7 @@ def main():
318
324
  for mode in modes:
319
325
  task_num += 1
320
326
  print(f'\n[{task_num}/{total_tasks}] ', end='')
321
- process_single_graph(json_file, relative_path, mode)
327
+ process_single_graph(json_file, relative_path, mode, region_encoding)
322
328
 
323
329
  print(f'\n{"="*70}')
324
330
  print(' ALL GRAPHS PROCESSED')
File without changes