13. Implementation details¶
This section describes some of the assumptions and technical details of
junifer, for developers. See the docstrings of the mentioned functions and
classes for more details.
13.1. Elements¶
The keys of the elements of a
PatternDataGrabber(PatternDataGrabber.get_element_keys()) are the replacements used by the patterns (including the nested ones) of the data types to grab, in the order ofreplacements.PatternDataGrabber.get_elements()finds the elements of each pattern in the files (translating the Unix-like path expansion directives of the patterns to a regular expression) and joins the elements of the patterns on their common keys (_join_elements), so an element is available if it has the files of all the data types, regardless of their order.The values of the replacements to grab (
PatternDataGrabber.get_replacement_values()) are given byreplacements(as a dictionary) or by the fields linked to the replacements in_REPLACEMENT_FIELDS, but not both. They filter the elements found in the files, and grabbing an element with other values raises an error (_check_element, called byBaseDataGrabber.__getitem__).BaseDataGrabber.select_elements()selects the elements to run: the selectors must have the same number of values. If they have a value for each element key, they are the elements (the dataset is not searched), otherwise they select the elements of the dataset that have their values.MultipleDataGrabberjoins the elements of its DataGrabbers on their common keys, and grabs an element with each DataGrabber using its keys. Each data type is grabbed by one DataGrabber, except for the nested data types grabbed by a DataGrabber without the main file of the data type, which replace the ones of the other DataGrabber.
13.2. Metadata¶
The meta of each data type in the data object is
built by the steps of the pipeline. Each step adds an entry with its class and
parameters (UpdateMetaMixin.update_meta) and its dependencies:
Key |
Added by |
Content |
In the MD5 |
|---|---|---|---|
|
DataGrabber |
The class and parameters of the DataGrabber, e.g., |
Yes, except |
|
DataGrabber |
The element being processed (see below). |
No |
|
DataGrabber |
The keys of the element that the data depends on (see below). |
Yes |
|
DataReader |
The class and parameters of the DataReader. |
Yes |
|
Preprocessors |
The list of the classes and parameters of the preprocessors that processed the data, in order. |
Yes |
|
Marker |
The data type, e.g., |
Yes |
|
Marker |
The class and parameters of the marker, with |
Yes |
|
All the steps |
The names of the dependencies of the steps (their |
Yes (the versions) |
|
Storage ( |
The name of the feature: the data type and the marker name, e.g.,
|
Yes |
The DataGrabber adds two keys about the element to the meta of each data
type (BaseDataGrabber.__getitem__):
element: the element being processed, with all its keys. It is the same for all the data types of the element._element_keys: the keys of the element that the data of the data type depends on (BaseDataGrabber.get_type_element_keys()). For aPatternDataGrabber, the replacements in the patterns of the data type.
For example, with BOLD data for each subject and task, and VBM_GM data
for each subject, the element (sub-01, rest) gives:
{'BOLD': {'meta': {'_element_keys': ['subject', 'task'],
'element': {'subject': 'sub-01', 'task': 'rest'},
...},
...},
'VBM_GM': {'meta': {'_element_keys': ['subject'],
'element': {'subject': 'sub-01', 'task': 'rest'},
...},
...}}
When storing the output of a marker, process_meta (in junifer.storage.utils):
Removes
elementfrom the metadata and returns the element of the data, with only the keys in_element_keys(all the keys ofelementif it is not set).Computes the MD5 hash of the metadata, which identifies the feature. It includes
_element_keysand the versions of the dependencies of the steps and ofjunifer, but notelement: the data of all the elements of a feature have the same hash, and the features computed with different versions ofjunifer(or of the dependencies) have different hashes. This is by design, for reproducibility: the differences between features with the same hash can only come from the data, not from different versions of the algorithms. A feature computed again with another version is a new feature, with the same name, so it must be read by its MD5.
13.3. Storage¶
The data is stored for the element of the data (the element with only the keys in
_element_keys), so the data that does not depend on a key (e.g., theVBM_GMdata for the tasks of a subject) is stored once.BaseFeatureStorage.store()passes the element being processed to the methods storing the metadata and the data (store_metadata,store_vector, etc.) asprocessed_element, besides the element of the data (element). The storages that write a file for each element (withsingle_output=False) name it after the element being processed, e.g.,element_sub-01_rest_out.hdf5, so that the processes running different elements in parallel never write to the same file. The storages without files (e.g., databases) can ignore it, but their methods must accept it.The data already stored for an element is not stored again (the storages skip the existing elements,
SQLiteFeatureStoragewithupsert="ignore"by default).collectonly collects the files of single elements (one element for each feature, as written for each element withsingle_output=False), and raises an error otherwise. The data of an element can be in the files of several elements being processed (e.g., theVBM_GMdata ofsub-01in the files of each of its tasks), and it is only collected once.HDF5FeatureStorage.collect()works in two passes: the first one reads the metadata and the elements of each file, to know which files to collect for each feature (one for each element), and the second one collects the data of each feature from these files, in chunks. Each file is read once in the first pass, and once for each of its features in the second one.