f9columnar 0.2.0__tar.gz → 0.2.2__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 (36) hide show
  1. {f9columnar-0.2.0 → f9columnar-0.2.2}/PKG-INFO +2 -1
  2. {f9columnar-0.2.0 → f9columnar-0.2.2}/f9columnar/hdf5_dataloader.py +88 -33
  3. {f9columnar-0.2.0 → f9columnar-0.2.2}/f9columnar/root_dataloader.py +116 -143
  4. {f9columnar-0.2.0 → f9columnar-0.2.2}/pyproject.toml +2 -1
  5. {f9columnar-0.2.0 → f9columnar-0.2.2}/README.md +0 -0
  6. {f9columnar-0.2.0 → f9columnar-0.2.2}/f9columnar/__init__.py +0 -0
  7. {f9columnar-0.2.0 → f9columnar-0.2.2}/f9columnar/analysis/__init__.py +0 -0
  8. {f9columnar-0.2.0 → f9columnar-0.2.2}/f9columnar/analysis/triggers.py +0 -0
  9. {f9columnar-0.2.0 → f9columnar-0.2.2}/f9columnar/arrays.py +0 -0
  10. {f9columnar-0.2.0 → f9columnar-0.2.2}/f9columnar/data/PMGxsecDB_mc16.txt +0 -0
  11. {f9columnar-0.2.0 → f9columnar-0.2.2}/f9columnar/data/PMGxsecDB_mc21.txt +0 -0
  12. {f9columnar-0.2.0 → f9columnar-0.2.2}/f9columnar/data/campaigns.json +0 -0
  13. {f9columnar-0.2.0 → f9columnar-0.2.2}/f9columnar/data/luminosity.json +0 -0
  14. {f9columnar-0.2.0 → f9columnar-0.2.2}/f9columnar/data/periods.json +0 -0
  15. {f9columnar-0.2.0 → f9columnar-0.2.2}/f9columnar/data/truth_classification.json +0 -0
  16. {f9columnar-0.2.0 → f9columnar-0.2.2}/f9columnar/dataset_builder.py +0 -0
  17. {f9columnar-0.2.0 → f9columnar-0.2.2}/f9columnar/hdf5_writer.py +0 -0
  18. {f9columnar-0.2.0 → f9columnar-0.2.2}/f9columnar/histograms.py +0 -0
  19. {f9columnar-0.2.0 → f9columnar-0.2.2}/f9columnar/processors.py +0 -0
  20. {f9columnar-0.2.0 → f9columnar-0.2.2}/f9columnar/processors_collection.py +0 -0
  21. {f9columnar-0.2.0 → f9columnar-0.2.2}/f9columnar/run.py +0 -0
  22. {f9columnar-0.2.0 → f9columnar-0.2.2}/f9columnar/submit/__init__.py +0 -0
  23. {f9columnar-0.2.0 → f9columnar-0.2.2}/f9columnar/submit/act_driver.py +0 -0
  24. {f9columnar-0.2.0 → f9columnar-0.2.2}/f9columnar/submit/act_handler.py +0 -0
  25. {f9columnar-0.2.0 → f9columnar-0.2.2}/f9columnar/submit/act_merger.py +0 -0
  26. {f9columnar-0.2.0 → f9columnar-0.2.2}/f9columnar/submit/act_run.py +0 -0
  27. {f9columnar-0.2.0 → f9columnar-0.2.2}/f9columnar/submit/act_run.sh +0 -0
  28. {f9columnar-0.2.0 → f9columnar-0.2.2}/f9columnar/utils/__init__.py +0 -0
  29. {f9columnar-0.2.0 → f9columnar-0.2.2}/f9columnar/utils/ak_helpers.py +0 -0
  30. {f9columnar-0.2.0 → f9columnar-0.2.2}/f9columnar/utils/config_utils.py +0 -0
  31. {f9columnar-0.2.0 → f9columnar-0.2.2}/f9columnar/utils/helpers.py +0 -0
  32. {f9columnar-0.2.0 → f9columnar-0.2.2}/f9columnar/utils/loggers.py +0 -0
  33. {f9columnar-0.2.0 → f9columnar-0.2.2}/f9columnar/utils/regex_helpers.py +0 -0
  34. {f9columnar-0.2.0 → f9columnar-0.2.2}/f9columnar/utils/rucio_db.py +0 -0
  35. {f9columnar-0.2.0 → f9columnar-0.2.2}/f9columnar/utils/rucio_utils.py +0 -0
  36. {f9columnar-0.2.0 → f9columnar-0.2.2}/f9columnar/utils/xsec_db.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: f9columnar
3
- Version: 0.2.0
3
+ Version: 0.2.2
4
4
  Summary: Columnar analysis utils.
5
5
  Home-page: https://gitlab.cern.ch/ijs-f9-ljubljana/f9columnar
6
6
  License: MIT
@@ -24,6 +24,7 @@ Requires-Dist: numba (==0.60.0)
24
24
  Requires-Dist: numpy (==2.0.2)
25
25
  Requires-Dist: pandas (==2.2.2)
26
26
  Requires-Dist: plothist (>=1.2.6,<2.0.0)
27
+ Requires-Dist: psutil (>=6.1.0,<7.0.0)
27
28
  Requires-Dist: pydot (==3.0.1)
28
29
  Requires-Dist: pygraphviz (==1.13)
29
30
  Requires-Dist: requests (>=2.28.1,<3.0.0)
@@ -3,7 +3,6 @@ from __future__ import annotations
3
3
  import copy
4
4
  import json
5
5
  import logging
6
- import multiprocessing
7
6
  import os
8
7
  from collections.abc import Callable
9
8
  from itertools import product
@@ -11,8 +10,10 @@ from typing import Any
11
10
 
12
11
  import h5py
13
12
  import numpy as np
13
+ import pandas as pd
14
14
  import torch
15
15
  from numpy.typing import ArrayLike
16
+ from torch import multiprocessing
16
17
  from torch.utils.data import DataLoader, IterableDataset
17
18
 
18
19
  from f9columnar.processors import Processor, ProcessorsGraph
@@ -95,7 +96,7 @@ class Hdf5Iterator:
95
96
  return arrays, reports
96
97
 
97
98
 
98
- class Hdf5IteratorMaker:
99
+ class Hdf5IteratorDfMaker:
99
100
  def __init__(
100
101
  self,
101
102
  name: str,
@@ -115,6 +116,7 @@ class Hdf5IteratorMaker:
115
116
  self.holdout_range = holdout_range
116
117
 
117
118
  self.total_num_entries = shape[0]
119
+ self.all_num_entries_dct: dict[str, int] = {}
118
120
 
119
121
  def _log_info(self) -> None:
120
122
  all_hdf5_files = list(self.hdf5_files_metadata.keys())
@@ -143,15 +145,15 @@ class Hdf5IteratorMaker:
143
145
  holdout_range=self.holdout_range,
144
146
  )
145
147
 
146
- def make(self) -> list[list[Hdf5Iterator]]:
148
+ def _split(self) -> list[dict[str, list[int]]]:
147
149
  self._log_info()
148
150
 
149
151
  # how many entries each worker will process
150
152
  splits = [self.total_num_entries // self.num_workers] * self.num_workers
151
153
  splits[-1] += self.total_num_entries % self.num_workers
152
154
 
153
- all_num_entries_dct = {file: metadata["shape"][0] for file, metadata in self.hdf5_files_metadata.items()}
154
- num_entries_dct = copy.deepcopy(all_num_entries_dct)
155
+ self.all_num_entries_dct = {file: metadata["shape"][0] for file, metadata in self.hdf5_files_metadata.items()}
156
+ num_entries_dct = copy.deepcopy(self.all_num_entries_dct)
155
157
 
156
158
  # keep track of the start and stop entries for each root file
157
159
  hdf5_files_start_dct: dict[str, int] = {file: 0 for file in self.hdf5_files_metadata.keys()}
@@ -171,7 +173,7 @@ class Hdf5IteratorMaker:
171
173
  total += num_entries
172
174
 
173
175
  if total <= split:
174
- result[i][hdf5_file] = [start_entry, all_num_entries_dct[hdf5_file]]
176
+ result[i][hdf5_file] = [start_entry, self.all_num_entries_dct[hdf5_file]]
175
177
  done.append(hdf5_file)
176
178
 
177
179
  if total == split:
@@ -186,39 +188,74 @@ class Hdf5IteratorMaker:
186
188
  num_entries_dct[hdf5_file] -= delta
187
189
  break
188
190
 
189
- worker_hdf5_iterators: list[list] = [[] for _ in range(self.num_workers)]
190
- check_total = 0
191
+ return result
191
192
 
192
- for i, result_dct in enumerate(result):
193
- for hdf5_file, start_end in result_dct.items():
194
- entry_start, entry_end = start_end
195
- check_total += entry_end - entry_start
193
+ def make(self) -> pd.DataFrame:
194
+ split_result = self._split()
196
195
 
197
- iterator = self._make_iterator(hdf5_file, entry_start, entry_end)
198
- worker_hdf5_iterators[i].append(iterator)
196
+ worker_df: dict[str, list] = {
197
+ "worker_id": [],
198
+ "file": [],
199
+ "start": [],
200
+ "stop": [],
201
+ "chunk_size": [],
202
+ "holdout_range": [],
203
+ "shuffle": [],
204
+ }
205
+
206
+ check_total = 0
207
+ for i, result_dct in enumerate(split_result):
208
+ for hdf5_file, start_stop in result_dct.items():
209
+ entry_start, entry_stop = start_stop
210
+ check_total += entry_stop - entry_start
211
+
212
+ worker_df["worker_id"].append(i)
213
+ worker_df["file"].append(hdf5_file)
214
+ worker_df["start"].append(entry_start)
215
+ worker_df["stop"].append(entry_stop)
216
+ worker_df["chunk_size"].append(self.chunk_size)
217
+ worker_df["holdout_range"].append(self.holdout_range)
218
+ worker_df["shuffle"].append(self.shuffle)
199
219
 
200
220
  if check_total != self.total_num_entries:
201
221
  raise ValueError("Total number of entries does not match.")
202
222
 
203
- return worker_hdf5_iterators
223
+ return pd.DataFrame(worker_df)
204
224
 
205
225
 
206
226
  class Hdf5LoaderIterator:
207
227
  def __init__(
208
228
  self,
209
229
  name: str,
210
- hdf5_iterators: list[Hdf5Iterator],
230
+ iterators_df: pd.DataFrame,
211
231
  worker_id: int,
212
232
  processors: list[Callable[[ArrayLike, dict], tuple[ArrayLike, dict]]] | ProcessorsGraph | None,
213
233
  hdf5_files_desc_dct: dict[str, dict[str, Any]] | None = None,
214
234
  ) -> None:
215
235
  self.name = name
216
- self.hdf5_iterators = hdf5_iterators
236
+ self.iterators_df = iterators_df
217
237
  self.worker_id = worker_id
218
238
  self.processors = processors
219
239
  self.hdf5_files_desc_dct = hdf5_files_desc_dct
220
240
 
221
- self.current_iterator_idx = 0
241
+ self.current_df_idx, self.current_iterator_idx = 0, 0
242
+
243
+ def _make_hdf5_iterator(self, df: pd.Series) -> Hdf5Iterator:
244
+ iterator = Hdf5Iterator(
245
+ df["file"],
246
+ chunk_size=df["chunk_size"],
247
+ start_entry=df["start"],
248
+ stop_entry=df["stop"],
249
+ shuffle=df["shuffle"],
250
+ holdout_range=df["holdout_range"],
251
+ )
252
+
253
+ return iterator
254
+
255
+ def _iterate_df(self) -> None:
256
+ df = self.iterators_df.iloc[self.current_iterator_idx]
257
+ self.iterator = self._make_hdf5_iterator(df)
258
+ self.current_df_idx += 1
222
259
 
223
260
  def _run_processors(self, arrays: ArrayLike, reports: dict) -> tuple[ArrayLike, dict] | dict[str, Processor]:
224
261
  if self.processors is None:
@@ -247,18 +284,22 @@ class Hdf5LoaderIterator:
247
284
 
248
285
  def __next__(self) -> tuple[ArrayLike, dict] | dict[str, Processor]:
249
286
  try:
250
- current_iterator = self.hdf5_iterators[self.current_iterator_idx]
251
- arrays, reports = next(current_iterator)
287
+ if self.current_df_idx == self.current_iterator_idx:
288
+ self._iterate_df()
289
+
290
+ arrays, reports = next(self.iterator)
252
291
 
253
292
  except StopIteration:
254
- current_iterator.close()
293
+ self.iterator.close()
255
294
  self.current_iterator_idx += 1
256
295
 
257
- if self.current_iterator_idx == len(self.hdf5_iterators):
296
+ if self.current_iterator_idx == len(self.iterators_df):
258
297
  raise StopIteration
259
- else:
260
- current_iterator = self.hdf5_iterators[self.current_iterator_idx]
261
- arrays, reports = next(current_iterator)
298
+
299
+ if self.current_df_idx == self.current_iterator_idx:
300
+ self._iterate_df()
301
+
302
+ arrays, reports = next(self.iterator)
262
303
 
263
304
  reports = self._make_report(reports)
264
305
 
@@ -308,7 +349,7 @@ class Hdf5IterableDataset(IterableDataset):
308
349
 
309
350
  self.shape = self._get_total_shape()
310
351
 
311
- self.worker_hdf5_iterators = self._get_iterators()
352
+ self.worker_iterators_df = self._get_df_iterators()
312
353
 
313
354
  def _setup(self) -> None:
314
355
  for file, dataset_name in product(self.files, self.dataset_names):
@@ -384,9 +425,15 @@ class Hdf5IterableDataset(IterableDataset):
384
425
 
385
426
  return sum(shapes_0), shapes_1[0]
386
427
 
387
- def _get_iterators(self) -> list[list[Hdf5Iterator]]:
388
- return Hdf5IteratorMaker(
389
- self.name, self.metadata, self.num_workers, self.shape, self.chunk_size, self.shuffle, self.holdout_range
428
+ def _get_df_iterators(self) -> pd.DataFrame:
429
+ return Hdf5IteratorDfMaker(
430
+ self.name,
431
+ self.metadata,
432
+ self.num_workers,
433
+ self.shape,
434
+ self.chunk_size,
435
+ self.shuffle,
436
+ self.holdout_range,
390
437
  ).make()
391
438
 
392
439
  def __iter__(self) -> Hdf5LoaderIterator:
@@ -397,17 +444,21 @@ class Hdf5IterableDataset(IterableDataset):
397
444
  else:
398
445
  worker_id = worker_info.id
399
446
 
400
- hdf5_iterators = self.worker_hdf5_iterators[worker_id]
447
+ iterators_df = self.worker_iterators_df[self.worker_iterators_df["worker_id"] == worker_id].copy()
401
448
 
402
449
  return Hdf5LoaderIterator(
403
450
  self.name,
404
- hdf5_iterators,
451
+ iterators_df,
405
452
  worker_id,
406
453
  self.processors,
407
454
  self.hdf5_files_desc_dct,
408
455
  )
409
456
 
410
457
 
458
+ def default_collate_fn(batch: list[Any]) -> list[Any]:
459
+ return batch
460
+
461
+
411
462
  def get_hdf5_dataloader(
412
463
  name: str,
413
464
  files: str | list[str],
@@ -421,6 +472,10 @@ def get_hdf5_dataloader(
421
472
  processors: list[Callable[[ArrayLike, dict], tuple[ArrayLike, dict]]] | ProcessorsGraph | None = None,
422
473
  dataloader_kwargs: dict[str, Any] | None = None,
423
474
  ) -> tuple[DataLoader, int]:
475
+
476
+ if multiprocessing.get_start_method() == "fork" and num_workers > 0:
477
+ logging.debug("Using 'fork' start method. Consider using 'spawn' or 'forkserver'.")
478
+
424
479
  if num_workers == -1:
425
480
  num_workers = multiprocessing.cpu_count()
426
481
 
@@ -441,7 +496,7 @@ def get_hdf5_dataloader(
441
496
  name,
442
497
  files,
443
498
  dataset_names,
444
- num_workers,
499
+ num_workers=num_workers if num_workers > 0 else 1,
445
500
  chunk_size=chunk_size,
446
501
  use_piles=use_piles,
447
502
  shuffle=shuffle,
@@ -454,7 +509,7 @@ def get_hdf5_dataloader(
454
509
  hdf5_dataset,
455
510
  batch_size=None,
456
511
  num_workers=num_workers,
457
- collate_fn=lambda batch: batch,
512
+ collate_fn=default_collate_fn,
458
513
  **dataloader_kwargs,
459
514
  )
460
515
 
@@ -2,19 +2,21 @@ from __future__ import annotations
2
2
 
3
3
  import copy
4
4
  import logging
5
- import multiprocessing
6
5
  import os
7
6
  from collections.abc import Callable, Iterator
8
7
  from dataclasses import dataclass, field
9
8
  from typing import Any
10
9
 
11
10
  import awkward as ak
11
+ import pandas as pd
12
12
  import torch
13
13
  import uproot
14
+ from torch import multiprocessing
14
15
  from torch.utils.data import DataLoader, IterableDataset
15
16
 
16
17
  from f9columnar.processors import Processor, ProcessorsGraph
17
18
  from f9columnar.utils.helpers import get_file_size
19
+ from f9columnar.utils.loggers import get_progress
18
20
 
19
21
 
20
22
  @dataclass
@@ -45,6 +47,10 @@ class RootFile:
45
47
  self.num_entries = self.tree.num_entries
46
48
  return self
47
49
 
50
+ def close(self) -> RootFile:
51
+ self.tree.close()
52
+ return self
53
+
48
54
 
49
55
  @dataclass
50
56
  class RootFiles:
@@ -60,6 +66,10 @@ class RootFiles:
60
66
  total_num_entries: int = 0
61
67
 
62
68
  def __post_init__(self) -> None:
69
+ progress = get_progress()
70
+ progress.start()
71
+ bar = progress.add_task("Preparing ROOT files", total=len(self.file_names))
72
+
63
73
  for i, file_name in enumerate(self.file_names):
64
74
  if type(self.key) is list:
65
75
  root_file = RootFile(file_name, self.key[i])
@@ -68,89 +78,32 @@ class RootFiles:
68
78
  else:
69
79
  raise ValueError(f"Key {self.key} is not a valid type.")
70
80
 
81
+ progress.update(bar, advance=1)
82
+
71
83
  root_file = root_file.open()
72
84
 
73
85
  self.files_dct[file_name] = root_file
74
-
75
86
  self.file_size_dct[file_name] = root_file.file_size
76
- self.num_entries_dct[file_name] = root_file.num_entries
77
-
78
- self.total_file_size += root_file.file_size
79
- self.total_num_entries += root_file.num_entries
80
-
81
- def __getitem__(self, file_name: str) -> RootFile:
82
- return self.files_dct[file_name]
83
87
 
88
+ num_entries = root_file.num_entries
84
89
 
85
- class UprootIterator:
86
- def __init__(
87
- self,
88
- root_file: RootFile,
89
- step_size: int,
90
- filter_name: Callable[[str], bool] | None = None,
91
- entry_start: int | None = None,
92
- entry_stop: int | None = None,
93
- **kwargs: Any,
94
- ) -> None:
95
- if type(step_size) is not int:
96
- raise ValueError(f"Step size {step_size} is not an integer.")
90
+ root_file.close()
97
91
 
98
- self.root_file = root_file
99
- self.step_size = step_size
100
- self.filter_name = filter_name
101
- self.kwargs = kwargs
92
+ if num_entries == 0:
93
+ continue
102
94
 
103
- num_entries = self.root_file.num_entries
95
+ self.num_entries_dct[file_name] = num_entries
104
96
 
105
- # assign start and stop entries
106
- if entry_start is None:
107
- self.entry_start = 0
108
- else:
109
- self.entry_start = entry_start
110
-
111
- if entry_stop is None:
112
- self.entry_stop = num_entries
113
- else:
114
- self.entry_stop = entry_stop
115
-
116
- # handle cases where step size is larger than the number of entries
117
- if self.entry_start is not None and self.entry_stop is not None:
118
- delta_entry = self.entry_stop - self.entry_start
119
-
120
- if self.step_size > delta_entry:
121
- self.step_size = delta_entry
122
- else:
123
- if self.step_size > num_entries:
124
- self.step_size = num_entries
125
-
126
- self.tree: uproot.TTree = None
127
- self.returned_iterator = False
128
-
129
- def get_iterator(self) -> Iterator[tuple[ak.Array, Any]]:
130
- if self.root_file.tree is None:
131
- self.tree = uproot.open(self.root_file.file_name)
132
- else:
133
- self.tree = self.root_file.tree
134
-
135
- uproot_iterator = self.tree.iterate(
136
- library="ak",
137
- report=True,
138
- step_size=self.step_size,
139
- filter_name=self.filter_name,
140
- entry_start=self.entry_start,
141
- entry_stop=self.entry_stop,
142
- **self.kwargs,
143
- )
144
-
145
- self.returned_iterator = True
97
+ self.total_file_size += root_file.file_size
98
+ self.total_num_entries += num_entries
146
99
 
147
- return uproot_iterator
100
+ progress.stop()
148
101
 
149
- def close(self) -> None:
150
- self.tree.close()
102
+ def __getitem__(self, file_name: str) -> RootFile:
103
+ return self.files_dct[file_name]
151
104
 
152
105
 
153
- class UprootIteratorMaker:
106
+ class UprootIteratorsDfMaker:
154
107
  def __init__(
155
108
  self,
156
109
  name: str,
@@ -158,17 +111,14 @@ class UprootIteratorMaker:
158
111
  key: str | list[str],
159
112
  step_size: int,
160
113
  num_workers: int,
161
- filter_name: Callable[[str], bool] | None = None,
162
- **kwargs: Any,
163
114
  ) -> None:
164
115
  self.name = name
165
116
  self.files, self.key = files, key
166
117
  self.num_workers = num_workers
167
118
  self.step_size = step_size
168
- self.filter_name = filter_name
169
- self.kwargs = kwargs
170
119
 
171
120
  self.total_num_entries = 0
121
+ self.all_num_entries_dct: dict[str, int] = {}
172
122
 
173
123
  def _log_info(self, total_files_size: float, total_num_entries: int) -> None:
174
124
  info_str = "\n" + 15 * "=" + " info " + 15 * "="
@@ -180,17 +130,7 @@ class UprootIteratorMaker:
180
130
 
181
131
  logging.info(info_str)
182
132
 
183
- def _make_iterator(self, root_file: RootFile, entry_start: int, entry_stop: int) -> UprootIterator:
184
- return UprootIterator(
185
- root_file,
186
- step_size=self.step_size,
187
- filter_name=self.filter_name,
188
- entry_start=entry_start,
189
- entry_stop=entry_stop,
190
- **self.kwargs,
191
- )
192
-
193
- def make(self) -> list[list[UprootIterator]]:
133
+ def _split(self) -> list[dict[str, list[int]]]:
194
134
  root_files = RootFiles(self.files, self.key)
195
135
 
196
136
  self._log_info(root_files.total_file_size, root_files.total_num_entries)
@@ -202,13 +142,13 @@ class UprootIteratorMaker:
202
142
  splits = [total_num_entries // self.num_workers] * self.num_workers
203
143
  splits[-1] += total_num_entries % self.num_workers
204
144
 
205
- all_num_entries_dct = root_files.num_entries_dct
145
+ self.all_num_entries_dct = root_files.num_entries_dct
206
146
  num_entries_dct = copy.deepcopy(root_files.num_entries_dct)
207
147
 
208
148
  # keep track of the start and stop entries for each root file
209
149
  root_files_start_dct: dict[str, int] = {root_file: 0 for root_file in self.files}
210
150
 
211
- result: list[dict[str, list[int]]] = [{} for _ in range(len(splits))]
151
+ split_result: list[dict[str, list[int]]] = [{} for _ in range(len(splits))]
212
152
 
213
153
  done = []
214
154
  for i, split in enumerate(splits):
@@ -218,12 +158,12 @@ class UprootIteratorMaker:
218
158
  if root_file in done:
219
159
  continue
220
160
 
221
- start_entry = root_files_start_dct[root_file]
161
+ entry_start = root_files_start_dct[root_file]
222
162
 
223
163
  total += num_entries
224
164
 
225
165
  if total <= split:
226
- result[i][root_file] = [start_entry, all_num_entries_dct[root_file]]
166
+ split_result[i][root_file] = [entry_start, self.all_num_entries_dct[root_file]]
227
167
  done.append(root_file)
228
168
 
229
169
  if total == split:
@@ -233,45 +173,84 @@ class UprootIteratorMaker:
233
173
 
234
174
  if total > split:
235
175
  delta = num_entries - (total - split)
236
- result[i][root_file] = [start_entry, start_entry + delta]
176
+ split_result[i][root_file] = [entry_start, entry_start + delta]
237
177
  root_files_start_dct[root_file] += delta
238
178
  num_entries_dct[root_file] -= delta
239
179
  break
240
180
 
241
- worker_uproot_iterators: list[list] = [[] for _ in range(self.num_workers)]
242
- check_total = 0
181
+ return split_result
243
182
 
244
- for i, result_dct in enumerate(result):
245
- for root_file, start_end in result_dct.items():
246
- entry_start, entry_end = start_end
247
- check_total += entry_end - entry_start
183
+ def make(self) -> pd.DataFrame:
184
+ split_result = self._split()
248
185
 
249
- iterator = self._make_iterator(root_files[root_file], entry_start, entry_end)
250
- worker_uproot_iterators[i].append(iterator)
186
+ worker_df: dict[str, list] = {"worker_id": [], "file": [], "start": [], "stop": [], "step_size": []}
251
187
 
252
- if check_total != total_num_entries:
188
+ check_total = 0
189
+ for i, result_dct in enumerate(split_result):
190
+ for root_file, start_stop in result_dct.items():
191
+ entry_start, entry_stop = start_stop
192
+ check_total += entry_stop - entry_start
193
+
194
+ delta_entry = entry_stop - entry_start
195
+ num_entries = self.all_num_entries_dct[root_file]
196
+
197
+ if self.step_size > delta_entry:
198
+ step_size = delta_entry
199
+ elif self.step_size > num_entries:
200
+ step_size = num_entries
201
+ else:
202
+ step_size = self.step_size
203
+
204
+ worker_df["worker_id"].append(i)
205
+ worker_df["file"].append(f"{root_file}:{self.key}")
206
+ worker_df["start"].append(entry_start)
207
+ worker_df["stop"].append(entry_stop)
208
+ worker_df["step_size"].append(step_size)
209
+
210
+ if check_total != self.total_num_entries:
253
211
  raise ValueError("Total number of entries does not match.")
254
212
 
255
- return worker_uproot_iterators
213
+ return pd.DataFrame(worker_df)
256
214
 
257
215
 
258
216
  class RootLoaderIterator:
259
217
  def __init__(
260
218
  self,
261
219
  name: str,
262
- uproot_iterators: list[UprootIterator],
263
- worker_id: int,
220
+ iterators_df: pd.DataFrame,
264
221
  processors: list[Callable[[ak.Array, dict], tuple[ak.Array, dict]]] | ProcessorsGraph | None = None,
222
+ filter_name: Callable[[str], bool] | None = None,
265
223
  root_files_desc_dct: dict[str, dict[str, Any]] | None = None,
266
224
  ) -> None:
267
225
  self.name = name
268
- self.uproot_iterators = uproot_iterators
269
- self.worker_id = worker_id
226
+ self.iterators_df = iterators_df
270
227
  self.processors = processors
228
+ self.filter_name = filter_name
271
229
  self.root_files_desc_dct = root_files_desc_dct
272
230
 
273
- self.current_iterator: Iterator[tuple[ak.Array, Any]]
274
- self.current_iterator_idx = 0
231
+ self.iterator: Iterator
232
+ self.tree: uproot.TTree
233
+
234
+ self.current_df_idx, self.current_iterator_idx = 0, 0
235
+
236
+ def _make_uproot_iterator(self, df: pd.Series) -> tuple[Iterator, uproot.TTree]:
237
+ tree = uproot.open(df["file"])
238
+
239
+ iterator = tree.iterate(
240
+ library="ak",
241
+ report=True,
242
+ step_size=df["step_size"],
243
+ filter_name=self.filter_name,
244
+ entry_start=df["start"],
245
+ entry_stop=df["stop"],
246
+ )
247
+
248
+ return iterator, tree
249
+
250
+ def _iterate_df(self) -> None:
251
+ df = self.iterators_df.iloc[self.current_iterator_idx]
252
+ self.iterator, self.tree = self._make_uproot_iterator(df)
253
+ self.current_df_idx += 1
275
254
 
276
255
  def _run_processors(self, arrays: ak.Array, reports: dict) -> tuple[ak.Array, dict] | dict[str, Processor]:
277
256
  if self.processors is None:
@@ -281,8 +260,7 @@ class RootLoaderIterator:
281
260
  arrays, reports = proc(arrays, reports)
282
261
  return arrays, reports
283
262
  elif type(self.processors) is ProcessorsGraph:
284
- processors = self.processors.fit(arrays, reports)
285
- return processors
263
+ return self.processors.fit(arrays, reports)
286
264
  else:
287
265
  raise ValueError(f"Processors {self.processors} is not a valid type.")
288
266
 
@@ -293,7 +271,7 @@ class RootLoaderIterator:
293
271
 
294
272
  reports = {
295
273
  "name": self.name,
296
- "worker_id": self.worker_id,
274
+ "worker_id": self.iterators_df.iloc[self.current_iterator_idx]["worker_id"],
297
275
  "file_path": file_path,
298
276
  "file": file_name,
299
277
  "start": start,
@@ -310,23 +288,22 @@ class RootLoaderIterator:
310
288
 
311
289
  def __next__(self) -> tuple[ak.Array, dict] | dict[str, Processor]:
312
290
  try:
313
- uproot_iterator = self.uproot_iterators[self.current_iterator_idx]
291
+ if self.current_df_idx == self.current_iterator_idx:
292
+ self._iterate_df()
314
293
 
315
- if uproot_iterator.returned_iterator is False:
316
- self.current_iterator = uproot_iterator.get_iterator()
317
-
318
- arrays, reports = next(self.current_iterator)
294
+ arrays, reports = next(self.iterator)
319
295
 
320
296
  except StopIteration:
321
- uproot_iterator.close()
297
+ self.tree.close()
322
298
  self.current_iterator_idx += 1
323
299
 
324
- if self.current_iterator_idx == len(self.uproot_iterators):
300
+ if self.current_iterator_idx == len(self.iterators_df):
325
301
  raise StopIteration
326
- else:
327
- uproot_iterator = self.uproot_iterators[self.current_iterator_idx]
328
- self.current_iterator = uproot_iterator.get_iterator()
329
- arrays, reports = next(self.current_iterator)
302
+
303
+ if self.current_df_idx == self.current_iterator_idx:
304
+ self._iterate_df()
305
+
306
+ arrays, reports = next(self.iterator)
330
307
 
331
308
  reports = self._make_report(reports)
332
309
 
@@ -339,13 +316,15 @@ class RootIterableDataset(IterableDataset):
339
316
  def __init__(
340
317
  self,
341
318
  name: str,
342
- worker_uproot_iterators: list[list[UprootIterator]],
319
+ worker_iterators_df: pd.DataFrame,
343
320
  processors: list[Callable[[ak.Array, dict], tuple[ak.Array, dict]]] | ProcessorsGraph | None = None,
321
+ filter_name: Callable[[str], bool] | None = None,
344
322
  root_files_desc_dct: dict[str, dict[str, Any]] | None = None,
345
323
  ) -> None:
346
324
  self.name = name
347
- self.worker_uproot_iterators = worker_uproot_iterators
325
+ self.worker_iterators_df = worker_iterators_df
348
326
  self.processors = processors
327
+ self.filter_name = filter_name
349
328
  self.root_files_desc_dct = root_files_desc_dct
350
329
 
351
330
  def __iter__(self) -> RootLoaderIterator:
@@ -356,13 +335,13 @@ class RootIterableDataset(IterableDataset):
356
335
  else:
357
336
  worker_id = worker_info.id
358
337
 
359
- uproot_iterators = self.worker_uproot_iterators[worker_id]
338
+ iterators_df = self.worker_iterators_df[self.worker_iterators_df["worker_id"] == worker_id].copy()
360
339
 
361
340
  return RootLoaderIterator(
362
341
  self.name,
363
- uproot_iterators,
364
- worker_id,
342
+ iterators_df,
365
343
  processors=self.processors,
344
+ filter_name=self.filter_name,
366
345
  root_files_desc_dct=self.root_files_desc_dct,
367
346
  )
368
347
 
@@ -376,46 +355,40 @@ def get_root_dataloader(
376
355
  processors: list[Callable[[ak.Array, dict], tuple[ak.Array, dict]]] | ProcessorsGraph | None = None,
377
356
  filter_name: Callable[[str], bool] | None = None,
378
357
  root_files_desc_dct: dict[str, dict[str, Any]] | None = None,
379
- uproot_kwargs: dict[str, Any] | None = None,
380
358
  dataloader_kwargs: dict[str, Any] | None = None,
381
359
  ) -> tuple[DataLoader, int]:
382
360
 
361
+ if multiprocessing.get_start_method() == "fork" and num_workers > 0:
362
+ logging.debug("Using 'fork' start method. Consider using 'spawn' or 'forkserver'.")
363
+
383
364
  if num_workers == -1:
384
365
  num_workers = multiprocessing.cpu_count()
385
366
 
386
- if uproot_kwargs is None:
387
- uproot_kwargs = {}
388
-
389
367
  if dataloader_kwargs is None:
390
368
  dataloader_kwargs = {}
391
369
 
392
370
  logging.info("Making ROOT dataloader!")
393
371
 
394
- uproot_iterator_maker = UprootIteratorMaker(
395
- name,
396
- files,
397
- key,
398
- step_size,
399
- num_workers,
400
- filter_name=filter_name,
401
- **uproot_kwargs,
402
- )
403
- worker_uproot_iterators = uproot_iterator_maker.make()
372
+ df_maker = UprootIteratorsDfMaker(name, files, key, step_size, num_workers if num_workers > 0 else 1)
373
+ worker_iteratorts_df = df_maker.make()
404
374
 
405
- total_num_entries = uproot_iterator_maker.total_num_entries
375
+ total_num_entries = df_maker.total_num_entries
406
376
 
407
377
  root_iterable_dataset = RootIterableDataset(
408
378
  name,
409
- worker_uproot_iterators,
379
+ worker_iteratorts_df,
410
380
  processors=processors,
381
+ filter_name=filter_name,
411
382
  root_files_desc_dct=root_files_desc_dct,
412
383
  )
413
384
 
385
+ prefetch_factor = dataloader_kwargs.get("prefetch_factor", None)
386
+
414
387
  root_dataloader = DataLoader(
415
388
  root_iterable_dataset,
416
389
  batch_size=None,
417
390
  num_workers=num_workers,
418
- prefetch_factor=None,
391
+ prefetch_factor=prefetch_factor,
419
392
  **dataloader_kwargs,
420
393
  )
421
394
 
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "f9columnar"
3
- version = "0.2.0"
3
+ version = "0.2.2"
4
4
  description = "Columnar analysis utils."
5
5
  authors = ["Jan Gavranovic <jan.gavranovic@cern.ch>"]
6
6
  readme = "README.md"
@@ -28,6 +28,7 @@ requests = "^2.28.1"
28
28
  tqdm = "^4.65.0"
29
29
  networkx = "3.3"
30
30
  rich = "^13.7.0"
31
+ psutil = "^6.1.0"
31
32
  mplhep = "^0.3.50"
32
33
  uproot = "5.3.10"
33
34
  awkward = "2.6.7"
File without changes
File without changes