Results and Logs
run() gives a dictionary: one pandas.Series of features for each configuration. This page makes tables from the results, saves them, and describes the feature catalog and the processing log.
The Result of a Run
results = pipeline.run("ct.nii.gz", "lesion.nii.gz", config_names=["ct_fbs_25"])
features = results["ct_fbs_25"] # a pandas Series
print(features["mean_intensity_Q4LE"]) # the feature name, then its IBSI code
print(len(features)) # the number of features
A feature key is the feature name and its IBSI code, for example mean_intensity_Q4LE. The IBSI code identifies the feature in the IBSI reference manual. Each configuration gives all its feature names, also when a step fails (see Result Guarantees).
Tables: format_results
format_results() makes a table row (wide format) or a list of feature rows (long format) from the results of one run.
One row for each run, with one column for each feature, named {config}__{feature}:
| Parameter | Default | Description |
|---|---|---|
fmt |
"wide" |
"wide" or "long" |
meta |
None |
Columns to put first, for example the subject and the ROI |
output_type |
"dict" |
"dict" (a dict for wide, a list of dicts for long), "pandas" (a DataFrame) or "json" (a JSON text; NaN becomes null) |
config_col |
"config" |
The name of the configuration column of the long format |
The long format suits many configurations and statistics programs such as R. The wide format gives one row for each case or ROI.
Files: save_results
save_results() writes results to a file. It takes a dict, a list of dicts, a DataFrame, a list of DataFrames, a JSON text or a list of JSON texts.
from pictologics import save_results
rows = []
for case in cases:
results = pipeline.run(case["image"], case["mask"], config_names=["ct_fbs_25"])
rows.append(format_results(results, meta={"subject_id": case["subject_id"]}))
pipeline.clear_log() # the log grows with each run
save_results(rows, "study/features.csv")
- File type: the extension sets it:
.csv,.tsvor.json(no extension gives CSV). Another extension raises aValueError.file_format="tsv"sets the type for any name. - Folders: a missing folder is made.
- JSON:
NaNand infinite values becomenull, so every JSON reader accepts the file. - Columns: the rows merge by column name. When the rows have other columns (for example cases with other configurations), the table has all columns, and the cells without a value are empty.
- Many cases:
run_batchgives the table of a whole study, and writes each case to its own file while it runs.
The Feature Catalog: describe_features
describe_features() gives one row for each feature of each configuration, before you run it. Save it with your results as a data dictionary.
catalog = pipeline.describe_features()
catalog.to_csv("study/feature_catalog.csv", index=False)
texture = catalog[(catalog["family_group"] == "Texture") & (catalog["discretisation_method"] == "FBN")]
| Column | Description |
|---|---|
config, feature_key |
The configuration and the feature key of the results |
feature_name, ibsi_code |
The feature name without the code, and the IBSI code |
family, family_group |
The family (for example glcm) and its group: Intensity, Morphology or Texture |
requires_discretisation |
Whether the family needs a discretised image |
uses_morph_mask, uses_intensity_mask |
The masks that the feature reads |
source_mode, sentinel_value |
The source mode of the configuration |
feature_extraction_step_index, feature_extraction_params |
The extract_features step of the feature and its parameters |
preprocessing_sequence |
The steps before the extraction, for example 1:resample > 2:resegment > 3:discretise |
preprocessing_steps |
The steps before the extraction, as compact JSON |
is_resampled, resampling_spacing, interpolation |
The resampling |
is_discretised, discretisation_method, discretisation_param |
The discretisation |
is_filtered, filter_type, filter_params |
The filter |
is_resegmented, is_outlier_filtered, is_intensity_rounded, keeps_largest_component, is_mask_grown, is_normalised, is_mask_binarized |
Whether the steps before the extraction hold this step |
..._apply_to, ..._params |
The mask (apply_to) and the parameters of each of these steps |
- A step parameter column holds the parameters of the configuration only. A summary column such as
interpolationordiscretisation_methodalso gives the default when the step leaves out the parameter. - A step that repeats gets a JSON list in its
..._paramscell, with one entry for each time, with itsstep_index(from 1).
The Processing Log
The pipeline keeps a log entry for each configuration that run() or run_rois() runs. run_batch() writes the log of each case into its result file.
for entry in pipeline.get_log(): # a copy of the entries
if entry["status"] != "completed":
print(entry["config_name"], entry["status"], entry["error"], entry["failed_step"])
pipeline.save_log("study/log.json") # the entries, with the versions
pipeline.clear_log() # empty the log
An entry holds these fields:
| Field | Holds |
|---|---|
timestamp |
The start of the configuration (ISO 8601) |
subject_id |
The subject_id of the run |
config_name, config_hash |
The configuration, and the SHA-256 hash of its source mode, sentinel value and steps |
roi |
The ROI name (run_rois() only) |
image_source, image_options |
The image path ("InMemory" for an Image) and its load options |
mask_source |
The mask path, "InMemory", or "GeneratedFullMask" without a mask |
source_mode, sentinel_detected, sentinel_value, sentinel_auto_detected, sentinel_proportion |
The source mode and the sentinel that the run used |
config_snapshot |
The source mode, the sentinel value and the steps of the configuration |
steps_executed |
Each step that ran, with its parameters. FBS steps add min_val_effective, normalise steps add center_effective and scale_effective, and filter steps add the requested and effective boundary and parameters |
status |
"completed", "empty_roi" or "error" |
error, failed_step |
The error and its step, when there is one |
family_errors |
The feature families that failed, with their errors |
result_feature_count |
The number of features |
elapsed_seconds |
The run time of the configuration |
environment |
The versions of Python and of the packages that change feature values (NumPy, SciPy, Numba, PyWavelets, nibabel, pydicom, python-gdcm), the platform, and the thread count |
schema_version, pictologics_version |
The configuration schema and the Pictologics version |
deduplication, run_parameters, mask_repositioning_settings |
The settings of the run |
mask_roi_semantics |
"nonzero_values_are_roi_membership": each voxel that is not 0 is ROI |
save_logwrites a JSON file with the entries, the log and schema versions, the Pictologics version and the export time. A name without.jsongets it.- The log grows with each run. In a long loop, save it and call
clear_log().
What to Report
IBSI asks a study to report how it computed its features. Pictologics gives each part:
- The configurations: save them with
pipeline.save_configs("study/configs.yaml", config_names=[...]). The file loads again withRadiomicsPipeline.load_configs. See Configuration & Reproducibility. - The
config_hashof each configuration: the same configuration gives the same hash on every computer. It is in each log entry. - The versions: the
pictologics_versionand theenvironmentof the log entries. - The data dictionary:
pipeline.describe_features(), with the IBSI code of each feature. - The image processing: the steps and their parameters (in the configurations), for example the spacing and the interpolation, the resegmentation range, and the discretisation method and its bins.
- The IBSI compliance of the software: see IBSI 1 and IBSI 2. For the Gabor filter, also report the
responsevalue (see Image Filtering).