Plotting#
VIBE uses Strategies for generating plots from validation data. This allows you to control:
How datasets are combined in plots (overlaid, separate, per-component)
Which rendering backend to use (matplotlib, ROOT)
How plots are styled and formatted
Overview of Strategies#
A plotting strategy defines how plots are generated from your parquet data files. VIBE provides three built-in strategies:
PerDatasetPlottingStrategy: Generates separate plots for each dataset
OverlayDatasetsPlottingStrategy: Overlays all datasets on a single plot
OverlayDatasetsPerComponentPlottingStrategy: Creates overlay plots separately for each histogram component
The plotting design separates the plotting logic (strategy) from the rendering backend (renderer), allowing you to mix and match different combinations.
Built-in Plotting Strategies#
Per-Dataset Strategy#
This strategy generates one plot per dataset. Each histogram defined in your validation mode will produce multiple output files - one for each dataset in your configuration.
Use case: When you want to analyze each dataset individually and compare them side-by-side.
Output: For 3 datasets and 5 histograms, you get 15 separate plots.
Overlay Datasets Strategy#
This strategy overlays all datasets on the same plot. All histogram components from all datasets are shown together with distinct colors/styles.
Use case: When you want to directly compare all datasets on a single plot.
Output: For 3 datasets and 5 histograms, you get 5 plots, each containing all datasets.
Overlay Datasets Per-Component Strategy#
This strategy creates separate overlay plots for each histogram component. All datasets are overlaid, but only for a single component at a time.
Use case: When your histogram has multiple components (e.g., signal/background) and you want to compare how each component varies across datasets.
Output: For 3 datasets, 5 histograms, and 2 components per histogram, you get 10 plots.
Configuring Strategies in Your Mode#
Default Strategies#
By default, all three built-in strategies are available for any validation mode. You can control which strategies to use by overriding the plotting_strategies property:
from vibe.core.validation_mode import ValidationModeBaseClass
class MyValidationMode(ValidationModeBaseClass):
name = "MyMode"
@property
def plotting_strategies(self):
# Only use per-dataset and overlay strategies
return ["per_dataset", "overlay_datasets"]
# ... rest of your mode implementation
Available strategy names:
"per_dataset""overlay_datasets""overlay_datasets_per_component"
Using Custom Strategies#
If you’ve registered a custom plotting strategy (see below), you can include it in the list:
@property
def plotting_strategies(self):
return ["per_dataset", "my_custom_strategy"]
Renderers and Output Formats#
VIBE supports multiple renderers for different output formats:
- Matplotlib Renderer (
MatplotlibHistogramRenderer) Generates matplotlib-style plots
Default renderer for all built-in strategies
- ROOT Renderer (
ROOTHistogramRenderer) Generates ROOT-compatible plots
Uses PyROOT for rendering
Creating a Custom Plotting Strategy#
Step 1: Inherit from PlottingStrategy#
Create a new Python file in vibe/core/plotting/strategies/custom/ and define your strategy class:
# vibe/core/plotting/strategies/custom/my_custom_strategy.py
from vibe.core.plotting.strategies.plotting_strategy import PlottingStrategy
from vibe.core.plotting.renderers.renderer_config import (
RendererConfigFactory,
ConfigurableRenderer
)
from vibe.core.helper.root_helper import makeROOTCompatible
from vibe.core.helper.settings import get_setting
from vibe.core.helper.parquet_helper import read_parquet
import os
import numpy as np
class MyCustomPlottingStrategy(PlottingStrategy):
"""Custom plotting strategy with specialized behavior."""
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
Step 2: Implement the generate_plots Method#
The generate_plots method is where all the plotting logic goes. It receives:
input_file_paths: Dictionary mapping dataset keys to parquet file pathsmode: The validation mode instance containing configurationdry_run: Boolean flag for testing without actually generating plots
def generate_plots(self, input_file_paths, mode, dry_run=False):
"""Generate plots according to custom strategy.
Args:
input_file_paths (dict): Mapping of keys to parquet file paths
mode: Validation mode configuration
dry_run (bool): If True, only notify observers without rendering
"""
if self._histogram_renderer is None:
raise ValueError("Histogram renderer is not set.")
# Iterate through histograms
histograms_by_particle_list = mode.validation_class(
base_path=get_setting(key="base_path")
).get_analysis_validation_histograms_by_particle_list()
for particle_list, histograms in histograms_by_particle_list.items():
for histogram in histograms:
self._generate_single_plot(
histogram=histogram,
particle_list=particle_list,
input_file_paths=input_file_paths,
mode=mode,
dry_run=dry_run
)
Here you would implement your custom plotting logic inside the _generate_single_plot. In principle, this can be any logic you want, for example generating ratio plots, normalized overlays, etc.
Step 4: Register Your Strategy#
Register your custom strategy with the PlottingStrategiesProvider so it can be used in validation modes:
# vibe/core/plotting/strategies/plotting_strategies_provider.py
from vibe.core.plotting.strategies.custom.my_custom_strategy import (
MyCustomPlottingStrategy
)
from vibe.core.plotting.renderers.matplotlib_histogram_renderer import (
MatplotlibHistogramRenderer
)
from vibe.core.plotting.renderers.root_histogram_renderer import (
ROOTHistogramRenderer
)
# Register with matplotlib renderer
PlottingStrategiesProvider.register(
"my_custom_strategy",
MyCustomPlottingStrategy(
histogram_renderer=MatplotlibHistogramRenderer()
)
)
# Or register with ROOT renderer
PlottingStrategiesProvider.register(
"my_custom_strategy_root",
MyCustomPlottingStrategy(
histogram_renderer=ROOTHistogramRenderer()
)
)
Step 5: Use in Your Validation Mode#
Now you can use your custom strategy in any validation mode:
from vibe.core.validation_mode import ValidationModeBaseClass
class MyMode(ValidationModeBaseClass):
name = "MyMode"
@property
def plotting_strategies(self):
return ["my_custom_strategy", "per_dataset"]
# ... rest of mode implementation
Notifying Observers#
You need to remember to notify observers before generating each plot, as it is a way to tell VIBE what plots are being created. When a plot is about to be generated, the strategy notifies observers with the output key and path:
def generate_plots(self, input_file_paths, mode, dry_run=False):
# ... setup code ...
for histogram in histograms:
output_key = "MyMode_dataset1_histogram1"
output_path = "/path/to/output.pdf"
# Notify observers before generating
self.notify_observers(output_key, output_path)
# Generate the actual plot
if not dry_run:
self._histogram_renderer.render(...)