The Plot Node defines a data visualization within the report. It specifies the data to be used, how that data should be mapped to the figure’s axes and dimensions, and what transformations should be applied before rendering.
| Field | Type | Description | Default Value |
|---|---|---|---|
|
string |
Optional. Identifies the plot node within the report. Used for internal cross referencing |
|
|
string |
Must be set to |
|
|
string |
Optional. A descriptive caption for the figure. |
|
|
string |
The unique |
Required |
|
object |
The nested configuration object ( |
Required |
The core configuration is provided here, defining the geometry and data logic of the figure.
| Field | Type | Description | Default Value |
|---|---|---|---|
|
string |
The title displayed at the top of the figure. |
Required |
|
array of strings |
A list of one or more visualization types to generate from the same configuration (e.g., |
Required |
|
string |
Defines how the input data should be processed and normalized before plotting. |
|
|
array of objects |
A sequential list of instructions to filter and summarize the dataset before mapping to axes. |
|
|
object |
The configuration for the horizontal axis ( |
Required |
|
object |
The configuration for the vertical axis ( |
Required |
|
object |
A second independent axis used for animation (sliders) or defining a third dimension. |
|
|
object |
The parameter used to define distinct lines or groups, mapping data values to the trace color/legend. |
|
|
array of objects |
Additional parameters used for filtering, grouping, or serving as a fourth dimension in complex plots (e.g., 3D or parallel coordinates). |
|
|
object |
Allows passing raw configuration options to the underlying plotting library (Plotly) for fine-grained control (e.g., log scale). |
|
All axis fields (xaxis, yaxis, secondary_axis, color_axis, extra_axes) use the PlotAxis schema to define which data column maps to which dimension.
| Field | Type | Description | Default Value |
|---|---|---|---|
|
string |
The name of the data column to be mapped to this axis. Use dot notation ( |
Required |
|
string |
The display label for the axis in the final figure. |
Inferred from |
|
string, array, or object |
Allows explicit filtering or renaming of values within this dimension. (See Filtering section below.) |
|
The filter field within an axis allows you to select specific values from that column to include in the plot, and optionally rename them for display.
| Syntax | Action | Example |
|---|---|---|
Single string |
Filters by the value, keeps the value as the label. |
|
Array of strings |
Filters by multiple values, keeps values as labels. |
|
Map of strings |
Filters by keys, renames to values. |
|
"xaxis": {
"parameter": "System",
"filter": {
"cluster_a": "Cluster A (Production)",
"dev_box": "Development System"
}
}The transformation field defines how the data is restructured and normalized before being plotted.
| Transformation | Purpose | Output Focus |
|---|---|---|
|
The base and default strategy. Data is restructured (pivoted) using the axes dimensions but values remain absolute. |
Absolute Values |
|
Data is restructured, and each value is normalized by the total sum of its row (e.g., component percentages of total time). |
Percentage/Proportion |
|
Computes the speedup relative to the smallest value found along the X-axis dimension (often used to show parallel scaling). |
Scaling Factors |
To explain how transformation and plot types work, we can consider the following example.
records = [{"perfvalue": "elapsed_fill", "value": 28.9239, "unit": "s", "result": "pass", "system": "gaya", "partition": "public", "tasks": 1.0, "elements": 10000.0}, {"perfvalue": "elapsed_compute", "value": 1.92806, "unit": "s", "result": "pass", "system": "gaya", "partition": "public", "tasks": 1.0, "elements": 10000.0}, {"perfvalue": "elapsed_fill", "value": 63.0273, "unit": "s", "result": "pass", "system": "gaya", "partition": "public", "tasks": 1.0, "elements": 20000.0}, {"perfvalue": "elapsed_compute", "value": 3.74504, "unit": "s", "result": "pass", "system": "gaya", "partition": "public", "tasks": 1.0, "elements": 20000.0}, {"perfvalue": "elapsed_fill", "value": 92.358, "unit": "s", "result": "pass", "system": "gaya", "partition": "public", "tasks": 1.0, "elements": 30000.0}, {"perfvalue": "elapsed_compute", "value": 3.29597, "unit": "s", "result": "pass", "system": "gaya", "partition": "public", "tasks": 1.0, "elements": 30000.0}, {"perfvalue": "elapsed_fill", "value": 99.9142, "unit": "s", "result": "pass", "system": "gaya", "partition": "public", "tasks": 1.0, "elements": 40000.0}, {"perfvalue": "elapsed_compute", "value": 2.37914, "unit": "s", "result": "pass", "system": "gaya", "partition": "public", "tasks": 1.0, "elements": 40000.0}, {"perfvalue": "elapsed_fill", "value": 24.8636, "unit": "s", "result": "pass", "system": "gaya", "partition": "public", "tasks": 2.0, "elements": 10000.0}, {"perfvalue": "elapsed_compute", "value": 0.629236, "unit": "s", "result": "pass", "system": "gaya", "partition": "public", "tasks": 2.0, "elements": 10000.0}, {"perfvalue": "elapsed_fill", "value": 14.9139, "unit": "s", "result": "pass", "system": "gaya", "partition": "public", "tasks": 2.0, "elements": 20000.0}, {"perfvalue": "elapsed_compute", "value": 0.498528, "unit": "s", "result": "pass", "system": "gaya", "partition": "public", "tasks": 2.0, "elements": 20000.0}, {"perfvalue": "elapsed_fill", "value": 98.9536, "unit": "s", "result": "pass", "system": "gaya", "partition": "public", "tasks": 2.0, "elements": 30000.0}, {"perfvalue": "elapsed_compute", "value": 2.3708, "unit": "s", "result": "pass", "system": "gaya", "partition": "public", "tasks": 2.0, "elements": 30000.0}, {"perfvalue": "elapsed_fill", "value": 45.4764, "unit": "s", "result": "pass", "system": "gaya", "partition": "public", "tasks": 2.0, "elements": 40000.0}, {"perfvalue": "elapsed_compute", "value": 1.56548, "unit": "s", "result": "pass", "system": "gaya", "partition": "public", "tasks": 2.0, "elements": 40000.0}, {"perfvalue": "elapsed_fill", "value": 2.89716, "unit": "s", "result": "pass", "system": "gaya", "partition": "public", "tasks": 4.0, "elements": 10000.0}, {"perfvalue": "elapsed_compute", "value": 0.0434488, "unit": "s", "result": "pass", "system": "gaya", "partition": "public", "tasks": 4.0, "elements": 10000.0}, {"perfvalue": "elapsed_fill", "value": 36.537, "unit": "s", "result": "pass", "system": "gaya", "partition": "public", "tasks": 4.0, "elements": 20000.0}, {"perfvalue": "elapsed_compute", "value": 0.519222, "unit": "s", "result": "pass", "system": "gaya", "partition": "public", "tasks": 4.0, "elements": 20000.0}, {"perfvalue": "elapsed_fill", "value": 52.302, "unit": "s", "result": "pass", "system": "gaya", "partition": "public", "tasks": 4.0, "elements": 30000.0}, {"perfvalue": "elapsed_compute", "value": 1.23368, "unit": "s", "result": "pass", "system": "gaya", "partition": "public", "tasks": 4.0, "elements": 30000.0}, {"perfvalue": "elapsed_fill", "value": 98.397, "unit": "s", "result": "pass", "system": "gaya", "partition": "public", "tasks": 4.0, "elements": 40000.0}, {"perfvalue": "elapsed_compute", "value": 1.55742, "unit": "s", "result": "pass", "system": "gaya", "partition": "public", "tasks": 4.0, "elements": 40000.0}]import pandas as pd
master_df = pd.DataFrame.from_dict(records)master_df.head(5)We can see that this dataframe contains the parameters: - system - result - tasks - elements - value - perfvalue - unit
By having this common structure, we can make use of transformation strategies to manipulate values depending on the desired output.
Strategies will depend on the figure axis. All strategies will create a pivot dataframe that will contain the parameter specified as color_axis as columns, xaxis as first level index and secondary_axis as second level index. Values of the dataframe will always be the values of the master dataframe.
As an example, we will consider the following axis definitions:
"xaxis":{ "parameter":"tasks", "label":"Number of tasks" },
"yaxis":{ "parameter":"value", "label":"Execution time (s)" },
"secondary_axis":{ "parameter":"elements", "label":"N" },
"color_axis":{ "parameter":"perfvalue", "label":"Performance variable" }performance
This strategy should be seen as the "base" strategy. No transformation, other that a pivot, is done. For the given example, it produces the following dataframe
from feelpp.benchmarking.json_report.figures.transformationFactory import TransformationStrategyFactory
from feelpp.benchmarking.json_report.figures.schemas.plot import Plot
plot_config = Plot(**{
"title": "Absolute performance",
"plot_types": [ "stacked_bar", "grouped_bar" ],
"xaxis":{ "parameter":"tasks", "label":"Number of tasks" },
"yaxis":{ "parameter":"value", "label":"Execution time (s)"},
"color_axis":{ "parameter":"perfvalue", "label":"Performance variable" },
"secondary_axis":{"parameter":"elements", "label":"N"}
})
strategy = TransformationStrategyFactory.create(plot_config)
df = strategy.calculate(master_df)
print(df)relative_performance
The relative performance strategy computes the proportion of the time that a a color_axis variable takes with regards of the total.
plot_config = Plot(**{
"title": "Absolute performance",
"plot_types": [ "stacked_bar", "grouped_bar" ],
"transformation": "relative_performance",
"xaxis":{ "parameter":"tasks", "label":"Number of tasks" },
"yaxis":{"parameter":"value", "label":"Execution time (s)"},
"color_axis":{ "parameter":"perfvalue", "label":"Performance variable" },
"secondary_axis":{ "parameter":"elements", "label":"N"
}
})
strategy = TransformationStrategyFactory.create(plot_config)
df = strategy.calculate(master_df)
print(df)The sum along the column axis will always be equal to 100.
speedup
The speedup strategy computes the speedup of the color_axis variables. The minimum of the xaxis values is taken as the base of the speedup.
For the example, this strategy will produce the following.
plot_config = Plot(**{
"title": "Absolute performance",
"plot_types": [ "stacked_bar", "grouped_bar" ],
"transformation": "speedup",
"xaxis":{"parameter":"tasks", "label":"Number of tasks"},
"yaxis":{"parameter":"value", "label":"Execution time (s)"},
"color_axis":{ "parameter":"perfvalue", "label":"Performance variable" },
"secondary_axis":{"parameter":"elements", "label":"N"}
})
strategy = TransformationStrategyFactory.create(plot_config)
df = strategy.calculate(master_df)
print(df)The optional aggregations field allows for data reduction and filtering across the dataset before the final pivot structure is created. The order of these instructions matters.
| Aggregation Field | Type | Description |
|---|---|---|
|
string |
The column to apply the aggregation or filter to. |
|
string |
The aggregation function or filter instruction. |
| Function | Action |
|---|---|
|
Computes the arithmetic average of the column. |
|
Computes the total sum of the column. |
|
Finds the maximum or minimum value in the column. |
|
Counts the number of values or unique values. |
|
Filters the column to keep only rows where the column value matches |
"aggregations":[
{"column":"Date","agg":"max"}, // Keep only records with the latest date
{"column":"System","agg":"filter:gaya"}, // Filter to only keep the 'gaya' system
{"column":"Time","agg":"mean"} // Calculate the mean execution time
]The engine supports a diverse range of visualizations, grouped into 2D, 3D, and specialized formats. The definition of each axis depends on the plot type.
These are standard 2D plots that support the xaxis, yaxis, and often the color_axis and secondary_axis (for animation/faceting).
For examples in this section, we will define the axis as:
"xaxis":{ "parameter":"tasks", "label":"Number of tasks" },
"yaxis":{ "parameter":"value", "label":"Execution time (s)" },
"color_axis":{ "parameter":"perfvalue", "label":"Performance variable" },
"secondary_axis":{ "parameter":"elements", "label":"N" }scatter: Standard scatter plot, typically showing performance trends.
Axis definition:
-
secondary_axis: represents the figure slider
from feelpp.benchmarking.json_report.figures.figureFactory import FigureFactory
figures = FigureFactory.create(Plot(**{
"title": "Absolute performance - Scatter Plot",
"plot_types": [ "scatter" ],
"xaxis":{ "parameter":"tasks", "label":"Number of tasks" },
"yaxis":{ "parameter":"value", "label":"Execution time (s)" },
"color_axis":{ "parameter":"perfvalue", "label":"Performance variable" },
"secondary_axis":{ "parameter":"elements", "label":"N" }
}))
fig = figures[0].createFigure(master_df)
fig.show()marked_scatter: Enhanced scatter plot where the symbol/mark shape can be mapped to an additional dimension.
Axis definition:
-
secondary_axis: represents the marks
from feelpp.benchmarking.json_report.figures.figureFactory import FigureFactory
figures = FigureFactory.create(Plot(**{
"title": "Absolute performance - Marked Scatter Plot",
"plot_types": [ "marked_scatter" ],
"xaxis":{ "parameter":"tasks", "label":"Number of tasks" },
"yaxis":{ "parameter":"value", "label":"Execution time (s)" },
"color_axis":{ "parameter":"perfvalue", "label":"Performance variable" },
"secondary_axis":{ "parameter":"elements", "label":"N" }
}))
fig = figures[0].createFigure(master_df)
fig.show()stacked_bar: Displays cumulative values, useful for showing the proportion of components (e.g., relative_performance transformation).
Axis definition:
-
xaxis: represents the subplot dimension. A subplot is created for each value in the x axis. -
secondary_axis: represents the x axis of each subplot.
from feelpp.benchmarking.json_report.figures.figureFactory import FigureFactory
figures = FigureFactory.create(Plot(**{
"title": "Absolute performance - Stacked Bar Plot",
"plot_types": [ "stacked_bar" ],
"xaxis":{ "parameter":"tasks", "label":"Number of tasks" },
"yaxis":{ "parameter":"value", "label":"Execution time (s)" },
"color_axis":{ "parameter":"perfvalue", "label":"Performance variable" },
"secondary_axis":{ "parameter":"elements", "label":"N" }
}))
fig = figures[0].createFigure(master_df)
fig.show()grouped_bar: Displays values side-by-side for easy comparison.
Axis definition:
-
secondary_axis: represents the figure slider
from feelpp.benchmarking.json_report.figures.figureFactory import FigureFactory
figures = FigureFactory.create(Plot(**{
"title": "Absolute performance - Grouped Bar Plot",
"plot_types": [ "grouped_bar" ],
"xaxis":{ "parameter":"tasks", "label":"Number of tasks" },
"yaxis":{ "parameter":"value", "label":"Execution time (s)" },
"color_axis":{ "parameter":"perfvalue", "label":"Performance variable" },
"secondary_axis":{ "parameter":"elements", "label":"N" }
}))
fig = figures[0].createFigure(master_df)
fig.show()heatmap: Displays data values as colors on a 2D grid defined by the X and Color axes.
Axis definition:
-
yaxis: Is given by the color scale (I know it is counter-intuitive). -
xaxis: Represents the columns of the heatmap -
color_axis: Represents the rows of the heatmap -
secondary_axis: Represents the figure slider
from feelpp.benchmarking.json_report.figures.figureFactory import FigureFactory
figures = FigureFactory.create(Plot(**{
"title": "Absolute performance - Heatmap",
"plot_types": [ "heatmap" ],
"xaxis":{ "parameter":"tasks", "label":"Number of tasks" },
"yaxis":{ "parameter":"value", "label":"Execution time (s)" },
"color_axis":{ "parameter":"perfvalue", "label":"Performance variable" },
"secondary_axis":{ "parameter":"elements", "label":"N" }
}))
fig = figures[0].createFigure(master_df)
fig.show()table: Renders the processed data directly into a styled, tabular format (similar to the Table Node, but generated through the figure pipeline).
Axis definition: Columns are displayed in the following order, from left to right: [extra_axes] → secondary_axis → xaxis → [all values in color_axis].
Cell values correspond to yaxis.
from feelpp.benchmarking.json_report.figures.figureFactory import FigureFactory
figures = FigureFactory.create(Plot(**{
"title": "Absolute performance - Table",
"plot_types": [ "table" ],
"xaxis":{ "parameter":"tasks", "label":"Number of tasks" },
"yaxis":{ "parameter":"value", "label":"Execution time (s)" },
"color_axis":{ "parameter":"perfvalue", "label":"Performance variable" },
"secondary_axis":{ "parameter":"elements", "label":"N" }
}))
fig = figures[0].createFigure(master_df)
fig.show()sunburst: Displays hierarchical data as a multi-layered ring chart, useful for showing partitions.
Dimensions are mapped to the rings: From inner to outer: secondary_axis → xaxis→ [extra_axes] → color_axis (outer).
from feelpp.benchmarking.json_report.figures.figureFactory import FigureFactory
figures = FigureFactory.create(Plot(**{
"title": "Absolute performance - Sunburst",
"plot_types": [ "sunburst" ],
"xaxis":{ "parameter":"tasks", "label":"Number of tasks" },
"yaxis":{ "parameter":"value", "label":"Execution time (s)" },
"color_axis":{ "parameter":"perfvalue", "label":"Performance variable" },
"secondary_axis":{ "parameter":"elements", "label":"N" }
}))
fig = figures[0].createFigure(master_df)
fig.show()parallelcoordinates: Displays multidimensional data where each variable is represented by a vertical axis, and data points are drawn as colored lines connecting the axes.
Axes will be shown on the following order from left to right:
secondary_axis → xaxis, [extra_axes] → color_axis.
The yaxis will be shown in the line color.
from feelpp.benchmarking.json_report.figures.figureFactory import FigureFactory
figures = FigureFactory.create(Plot(**{
"title": "Absolute performance - Parallel Coordinates",
"plot_types": [ "parallelcoordinates" ],
"xaxis":{ "parameter":"tasks", "label":"Number of tasks" },
"yaxis":{ "parameter":"value", "label":"Execution time (s)" },
"color_axis":{ "parameter":"perfvalue", "label":"Performance variable" },
"secondary_axis":{ "parameter":"elements", "label":"N" }
}))
fig = figures[0].createFigure(master_df)
fig.show()These plots are used for visualizing three or four variables. At least three dimensions are required (xaxis, yaxis, and secondary_axis).
-
scatter3d: Displays individual data points in three dimensions. -
surface3d: Displays values as a 3D surface grid.
3D Axis |
Mapped Parameter |
X-axis |
|
Y-axis |
|
Z-axis |
|
Color/Trace |
|
scatter3d
from feelpp.benchmarking.json_report.figures.figureFactory import FigureFactory
figures = FigureFactory.create(Plot(**{
"title": "Absolute performance - Scatter 3D",
"plot_types": [ "scatter3d" ],
"xaxis":{ "parameter":"tasks", "label":"Number of tasks" },
"yaxis":{ "parameter":"value", "label":"Execution time (s)" },
"color_axis":{ "parameter":"perfvalue", "label":"Performance variable" },
"secondary_axis":{ "parameter":"elements", "label":"N" }
}))
fig = figures[0].createFigure(master_df)
fig.show()surface3d
from feelpp.benchmarking.json_report.figures.figureFactory import FigureFactory
figures = FigureFactory.create(Plot(**{
"title": "Absolute performance - Surface 3D",
"plot_types": [ "surface3d" ],
"xaxis":{ "parameter":"tasks", "label":"Number of tasks" },
"yaxis":{ "parameter":"value", "label":"Execution time (s)" },
"color_axis":{ "parameter":"perfvalue", "label":"Performance variable" },
"secondary_axis":{ "parameter":"elements", "label":"N" }
}))
fig = figures[0].createFigure(master_df)
fig.show()The layout_modifiers field allows expert users to pass raw configuration dictionaries to the underlying plotting engine (Plotly). This is useful for advanced styling not covered by the standard schema.
These options correspond to the accepted layout reference for Plotly: Plotly layout reference It accepts a nested dictionnary just as Plotly does.
For example, we could customize a figure to have have its x-axis on a logscale.
"layout_modifiers":{
"xaxis":{
"type":"log"
}
}