← Files ClaraARCHIVED FILE
modules/set-overlap-analysis/vendor/modules/charting/setup_fig.py
25.5 KB · Oct 4, 2026 · 12:28 UTC
import copy
import datetime as dt
import logging
import math
import numpy as np
import plotly.express as px
import plotly.graph_objects as go
import polars as pl
from plotly.subplots import make_subplots
from modules.charting.chart_primitives import change_metric_if_cost_analysis
from modules.utilities.config import (
get_config_params,
get_naming_params,
)
from modules.utilities.ui_notifier import ui
from modules.utilities.utils import get_schema_and_column_names
def setup_fig_for_stacked_column_charts(
df, repeatArray, chosenDimension, paramDict, chartDict
):
"""
we need to set up the subplots fig based on the number of subplots we want to draw
"""
namingParams = get_naming_params()
configParams = get_config_params()
goldenRatio = configParams[namingParams["goldenRatio"]]
chosenChart = namingParams["chosenChart"]
plotSmallMultiplesKey = namingParams["plotSmallMultiplesOtherCharts"]
deltaName = namingParams["deltaName"]
figureName = namingParams["figureName"]
chosenChart = chartDict[chosenChart]
verticalSpacingDict = {
1: 0,
2: 0.08,
3: 0.06,
4: 0.05,
5: 0.05,
6: 0.04,
}
horizontalSpacing = 0.01
subplotTitles = None
if plotSmallMultiplesKey in chartDict and chartDict[plotSmallMultiplesKey]:
numberOfItems = len(repeatArray)
numberOfCols = 2
plotSmallMultiples = True
sharedYaxes = "all"
numberOfCols = 2
numberOfRows = int(math.ceil(numberOfItems / numberOfCols))
if numberOfItems < numberOfCols:
numberOfRows = 1
specs = []
rowHeights = []
if len(repeatArray) > 1:
subplotTitles = repeatArray
showItems = subplotTitles
else:
numberOfCols = 1
numberOfRows = 1
sharedYaxes = None
verticalSpacing = 0.05
if numberOfRows in verticalSpacingDict:
verticalSpacing = verticalSpacingDict[numberOfRows]
else:
verticalSpacing = verticalSpacingDict[len(verticalSpacingDict)]
fig = make_subplots(
rows=numberOfRows,
cols=numberOfCols,
shared_xaxes="all",
shared_yaxes=sharedYaxes,
vertical_spacing=verticalSpacing,
horizontal_spacing=horizontalSpacing,
subplot_titles=subplotTitles,
)
paramDict[figureName] = fig
return paramDict, numberOfCols, numberOfRows
def setup_fig_for_multitier_bar_charts(
repeatArray, chosenDimension, paramDict, chartDict
):
"""
we need to set up the subplots fig based on the number of subplots we want to draw
"""
namingParams = get_naming_params()
configParams = get_config_params()
goldenRatio = configParams[namingParams["goldenRatio"]]
chosenChart = namingParams["chosenChart"]
plotSmallMultiplesKey = namingParams["plotSmallMultiplesOtherCharts"]
xAxisDimension = namingParams["xAxisDimension"]
deltaName = namingParams["deltaName"]
totalName = namingParams["totalName"]
fatherAndChildDimensions = namingParams["fatherAndChildDimensions"]
showTopForEachItem = namingParams["showTopForEachItem"]
numberOfTopKey = namingParams["numberOfTop"]
chosenChart = chartDict[chosenChart]
verticalSpacingDict = {
1: 0,
2: 0.18,
3: 0.10,
4: 0.08,
5: 0.06,
6: 0.06,
}
subplotTitles = None
if plotSmallMultiplesKey in chartDict and chartDict[plotSmallMultiplesKey]:
numberOfItems = len(repeatArray)
numberOfCols = 2
numberOfRows = int(math.ceil(numberOfItems / numberOfCols))
plotSmallMultiples = True
sharedYaxes = None
if xAxisDimension in chartDict:
if (
fatherAndChildDimensions in chartDict
and chartDict[fatherAndChildDimensions]
) or chartDict[showTopForEachItem]:
sharedYaxes = None
else:
sharedYaxes = "rows"
sharedXaxes = "all"
if numberOfItems < numberOfCols:
numberOfRows = 1
titleSpace = 150
numberOfTop = chartDict["X"][numberOfTopKey]
separationSpace = 20
if chosenDimension == totalName:
singleRowSpace = 20
height = (
titleSpace
+ (singleRowSpace * numberOfRows)
+ (separationSpace * (numberOfRows - 1))
)
width = (height * goldenRatio) * numberOfCols * 2.2
verticalSpacing = 0.0
horizontalSpacing = 0.15
else:
singleRowSpace = 25
height = (
titleSpace
+ (singleRowSpace * (numberOfTop + 1) * numberOfRows)
+ (separationSpace * numberOfRows - 1)
)
width = (
(
titleSpace
+ (separationSpace * (numberOfRows - 1))
+ (singleRowSpace * (numberOfTop + 1))
)
* goldenRatio
* numberOfCols
* 1.1
)
horizontalSpacing = 0.35
if numberOfRows in verticalSpacingDict:
verticalSpacing = verticalSpacingDict[numberOfRows]
else:
verticalSpacing = 0.03
if len(repeatArray) > 1:
subplotTitles = repeatArray
costSubplotTitles = []
for element in subplotTitles:
metric = change_metric_if_cost_analysis(element, chartDict)
costSubplotTitles.append(metric)
if subplotTitles != costSubplotTitles:
subplotTitles = costSubplotTitles
showItems = subplotTitles
fig = make_subplots(
rows=numberOfRows,
cols=numberOfCols,
shared_xaxes=sharedXaxes,
shared_yaxes=sharedYaxes,
vertical_spacing=verticalSpacing,
horizontal_spacing=horizontalSpacing,
subplot_titles=subplotTitles,
)
else:
numberOfCols = 3
numberOfRows = 1
sharedYaxes = True
# sharedXaxes=True
height = None
width = None
colWidths = [0.4, 0.4, 0.2]
showItems = ["", deltaName, deltaName + "%"]
verticalSpacing = 0.05
fig = make_subplots(
rows=numberOfRows,
cols=numberOfCols,
shared_yaxes=sharedYaxes,
column_widths=colWidths,
subplot_titles=showItems,
# vertical_spacing=verticalSpacing,
# specs=specs,
# shared_xaxes=sharedXaxes,
)
return fig, height, width, numberOfCols, numberOfRows
def setup_fig_for_horizontal_waterfall_charts(
repeatArray, chosenDimension, chartDict, plotWithPins
):
"""
we need to set up the subplots fig based on the number of subplots we want to draw
"""
namingParams = get_naming_params()
configParams = get_config_params()
goldenRatio = configParams[namingParams["goldenRatio"]]
filterDates = namingParams["filterDates"]
chosenChart = namingParams["chosenChart"]
plotSmallMultiplesKey = namingParams["plotSmallMultiplesOtherCharts"]
deltaName = namingParams["deltaName"]
chosenChart = chartDict[chosenChart]
verticalSpacingDict = {
1: 0,
2: 0.08,
3: 0.06,
4: 0.05,
5: 0.05,
6: 0.04,
}
horizontalSpacing = 0.01
subplotTitles = None
if (
not plotWithPins
and plotSmallMultiplesKey in chartDict
and chartDict[plotSmallMultiplesKey]
):
numberOfItems = len(repeatArray)
numberOfCols = 2
height = 400
plotSmallMultiples = True
sharedYaxes = "all"
sharedXaxes = "all"
numberOfCols = 2
numberOfRows = int(math.ceil(numberOfItems / numberOfCols))
if numberOfItems < numberOfCols:
numberOfRows = 1
width = (height * goldenRatio) * numberOfCols
specs = []
rowHeights = []
if numberOfRows > 1:
height = height * (1 + numberOfRows / 3)
if len(repeatArray) > 1:
subplotTitles = repeatArray
showItems = subplotTitles
if numberOfRows in verticalSpacingDict:
verticalSpacing = verticalSpacingDict[numberOfRows]
else:
verticalSpacing = verticalSpacingDict[len(verticalSpacingDict)]
fig = make_subplots(
rows=numberOfRows,
cols=numberOfCols,
shared_xaxes=sharedXaxes,
shared_yaxes=sharedYaxes,
vertical_spacing=verticalSpacing,
horizontal_spacing=horizontalSpacing,
subplot_titles=subplotTitles,
)
else:
numberOfCols = 1
numberOfRows = 2
sharedYaxes = None
sharedXaxes = True
height = 500
width = (height * goldenRatio) * (numberOfCols)
specs = [[{}], [{"type": "waterfall"}]]
rowHeights = [0.2, 0.8]
showItems = [deltaName + "%", ""]
verticalSpacing = 0.05
fig = make_subplots(
rows=numberOfRows,
cols=numberOfCols,
shared_xaxes=sharedXaxes,
shared_yaxes=sharedYaxes,
vertical_spacing=verticalSpacing,
specs=specs,
row_heights=rowHeights,
subplot_titles=None,
)
return fig, height, width, numberOfCols, numberOfRows
def setup_venn_figure(fig, title, font, fontSize, plot):
raise RuntimeError("Venn charts are not available in this Plotly-only runtime.")
def setup_upset_figure(plt, title):
raise RuntimeError(
"Static upset charts are not available in this Plotly-only runtime."
)
def setup_fig_for_timeline_charts(repeatArray, chosenDimension, paramDict, chartDict):
"""
we need to set up the subplots fig based on the number of subplots we want to draw
"""
namingParams = get_naming_params()
configParams = get_config_params()
goldenRatio = configParams[namingParams["goldenRatio"]]
timelineChart = namingParams["timelineChart"]
filterDates = namingParams["filterDates"]
chosenChart = namingParams["chosenChart"]
plotSmallMultiplesKey = namingParams["plotSmallMultiplesOtherCharts"]
chosenChart = chartDict[chosenChart]
verticalSpacingDict = {
1: 0,
2: 0.08,
3: 0.06,
4: 0.05,
5: 0.05,
6: 0.04,
}
horizontalSpacing = 0.01
subplotTitles = None
if (
chosenChart in [timelineChart]
and filterDates in chartDict
and chartDict[filterDates]
):
horizontalSpacing = 0.03
if plotSmallMultiplesKey in chartDict and chartDict[plotSmallMultiplesKey]:
numberOfItems = len(repeatArray)
numberOfCols = 2
height = 400
if numberOfItems < numberOfCols:
numberOfCols = numberOfItems
plotSmallMultiples = True
sharedYaxes = "all"
numberOfRows = numberOfItems
numberOfRows = int(math.ceil(numberOfItems / numberOfCols))
width = (height * goldenRatio) * numberOfCols
if numberOfRows > 1:
height = height * (1 + numberOfRows / 3)
if len(repeatArray) > 1:
subplotTitles = repeatArray
else:
numberOfCols = 1
numberOfRows = 1
sharedYaxes = None
height = 500
width = (height * goldenRatio) * numberOfCols
if numberOfRows in verticalSpacingDict:
verticalSpacing = verticalSpacingDict[numberOfRows]
else:
verticalSpacing = verticalSpacingDict[len(verticalSpacingDict)]
fig = make_subplots(
rows=numberOfRows,
cols=numberOfCols,
shared_xaxes="all",
shared_yaxes=sharedYaxes,
vertical_spacing=verticalSpacing,
horizontal_spacing=horizontalSpacing,
subplot_titles=subplotTitles,
)
return fig, height, width, numberOfCols, numberOfRows
def setup_fig_for_multitier_column_charts(
repeatArray, chosenDimension, paramDict, chartDict, plotWithPins
):
"""
we need to set up the subplots fig based on the number of subplots we want to draw
"""
namingParams = get_naming_params()
configParams = get_config_params()
goldenRatio = configParams[namingParams["goldenRatio"]]
chosenChart = namingParams["chosenChart"]
plotSmallMultiplesKey = namingParams["plotSmallMultiplesOtherCharts"]
chosenChart = chartDict[chosenChart]
verticalSpacingDict = {
1: 0,
2: 0.08,
3: 0.06,
4: 0.05,
5: 0.05,
6: 0.04,
}
horizontalSpacing = 0.04
subplotTitles = None
if (
not plotWithPins
and plotSmallMultiplesKey in chartDict
and chartDict[plotSmallMultiplesKey]
):
numberOfItems = len(repeatArray)
numberOfCols = 2
height = 400
plotSmallMultiples = True
# Each multitier-column panel needs its own scale. Sharing axes makes
# smaller panels collapse and hides the local period labels.
sharedYaxes = None
sharedXaxes = None
numberOfCols = 2
numberOfRows = int(math.ceil(numberOfItems / numberOfCols))
if numberOfItems < numberOfCols:
numberOfRows = 1
width = (height * goldenRatio) * numberOfCols
if numberOfRows > 1:
height = height * (1 + numberOfRows / 3)
if len(repeatArray) > 1:
subplotTitles = repeatArray
showItems = subplotTitles
if numberOfRows in verticalSpacingDict:
verticalSpacing = verticalSpacingDict[numberOfRows]
else:
verticalSpacing = verticalSpacingDict[len(verticalSpacingDict)]
fig = make_subplots(
rows=numberOfRows,
cols=numberOfCols,
shared_xaxes=sharedXaxes,
shared_yaxes=sharedYaxes,
vertical_spacing=verticalSpacing,
horizontal_spacing=horizontalSpacing,
subplot_titles=subplotTitles,
)
else:
numberOfCols = 1
numberOfRows = 3
sharedYaxes = None
sharedXaxes = True
height = 500
width = (height * goldenRatio) * numberOfCols
specs = [
[{}],
[{}],
[{}],
]
rowHeights = [0.2, 0.4, 0.4]
verticalSpacing = 0.05
fig = make_subplots(
rows=numberOfRows,
cols=numberOfCols,
shared_xaxes=sharedXaxes,
shared_yaxes=sharedYaxes,
vertical_spacing=verticalSpacing,
specs=specs,
row_heights=rowHeights,
)
return fig, height, width, numberOfCols, numberOfRows
def setup_fig_for_slope_charts(repeatArray, chosenDimension, paramDict, chartDict):
"""
we need to set up the subplots fig based on the number of subplots we want to draw
"""
namingParams = get_naming_params()
configParams = get_config_params()
goldenRatio = configParams[namingParams["goldenRatio"]]
timelineChart = namingParams["timelineChart"]
filterDates = namingParams["filterDates"]
chosenChart = namingParams["chosenChart"]
plotSmallMultiplesKey = namingParams["plotSmallMultiplesOtherCharts"]
chosenChart = chartDict[chosenChart]
verticalSpacingDict = {
1: 0,
2: 0.02,
3: 0.06,
4: 0.05,
5: 0.05,
6: 0.04,
}
horizontalSpacing = 0.005
subplotTitles = None
if (
chosenChart in [timelineChart]
and filterDates in chartDict
and chartDict[filterDates]
):
horizontalSpacing = 0.001
if plotSmallMultiplesKey in chartDict and chartDict[plotSmallMultiplesKey]:
numberOfItems = len(repeatArray)
numberOfCols = numberOfItems
width = 250
if numberOfItems < numberOfCols:
numberOfCols = numberOfItems
plotSmallMultiples = True
sharedYaxes = "all"
numberOfRows = 1
height = width * ((goldenRatio * 2) - 1)
width = width * numberOfCols
if numberOfCols == 1:
width = width * 1.5
elif numberOfCols < 3:
width = width * 1.15
if len(repeatArray) > 1:
subplotTitles = repeatArray
else:
numberOfCols = 1
numberOfRows = 1
sharedYaxes = None
width = 500
height = width * goldenRatio
width = width * numberOfCols
if numberOfRows in verticalSpacingDict:
verticalSpacing = verticalSpacingDict[numberOfRows]
else:
verticalSpacing = verticalSpacingDict[len(verticalSpacingDict)]
fig = make_subplots(
rows=numberOfRows,
cols=numberOfCols,
shared_xaxes="all",
shared_yaxes=sharedYaxes,
vertical_spacing=verticalSpacing,
horizontal_spacing=horizontalSpacing,
subplot_titles=subplotTitles,
)
return fig, height, width, numberOfCols, numberOfRows
def add_by_to_syn_plot_col_labels(df: pl.LazyFrame) -> pl.LazyFrame:
"""
Prefix each column name with "by " using Polars rename mapping.
This operates lazily and avoids any pandas-style operations.
"""
namingParams = get_naming_params()
periodName = namingParams["periodName"]
# Build an old->new column name map like {"A": "by A", ...}; keep Period
# stable because downstream legacy annotation code still uses that key.
columns, _ = get_schema_and_column_names(df)
rename_map = {col: f"by {col}" for col in columns if col != periodName}
return df.rename(rename_map)
def setup_fig_for_stacked_bar_charts(
df, column, repeatArray, chosenDimension, paramDict, chartDict
):
"""
we need to set up the subplots fig based on the number of subplots we want to draw
"""
namingParams = get_naming_params()
configParams = get_config_params()
goldenRatio = configParams[namingParams["goldenRatio"]]
chosenChart = namingParams["chosenChart"]
deltaName = namingParams["deltaName"]
figureName = namingParams["figureName"]
fatherAndChildDimensions = namingParams["fatherAndChildDimensions"]
showTopForEachItem = namingParams["showTopForEachItem"]
yAxisDimension = namingParams["yAxisDimension"]
totalName = namingParams["totalName"]
numberOfRowsKey = namingParams["numberOfRows"]
numberOfColsKey = namingParams["numberOfCols"]
chosenChart = chartDict[chosenChart]
verticalSpacingDict = {
1: 0,
2: 0.08,
3: 0.06,
4: 0.05,
5: 0.05,
6: 0.04,
}
horizontalSpacing = 0.01
subplotTitles = None
if column != totalName:
numberOfItems = len(repeatArray)
numberOfCols = 2
plotSmallMultiples = True
sharedYaxes = "all"
if yAxisDimension in chartDict:
if (
fatherAndChildDimensions in chartDict
and chartDict[fatherAndChildDimensions]
) or chartDict[showTopForEachItem]:
sharedYaxes = None
else:
sharedYaxes = "rows"
numberOfCols = 2
numberOfRows = int(math.ceil(numberOfItems / numberOfCols))
if numberOfItems < numberOfCols:
numberOfRows = 1
specs = []
rowHeights = []
if len(repeatArray) > 1:
subplotTitles = repeatArray
showItems = subplotTitles
horizontalSpacing = 0.1
else:
numberOfCols = 1
numberOfRows = 1
sharedYaxes = None
verticalSpacing = 0.05
if numberOfRows in verticalSpacingDict:
verticalSpacing = verticalSpacingDict[numberOfRows]
else:
verticalSpacing = verticalSpacingDict[len(verticalSpacingDict)]
fig = make_subplots(
rows=numberOfRows,
cols=numberOfCols,
shared_xaxes="all",
shared_yaxes=sharedYaxes,
vertical_spacing=verticalSpacing,
horizontal_spacing=horizontalSpacing,
subplot_titles=subplotTitles,
)
paramDict[figureName] = fig
paramDict[numberOfRowsKey] = numberOfRows
paramDict[numberOfColsKey] = numberOfCols
return paramDict, numberOfCols, numberOfRows
def setup_fig_for_actual_vs_previous_year_charts(
repeatArray, chosenDimension, paramDict, chartDict, plotWithPins
):
"""
we need to set up the subplots fig based on the number of subplots we want to draw
"""
namingParams = get_naming_params()
configParams = get_config_params()
goldenRatio = configParams[namingParams["goldenRatio"]]
chosenChart = namingParams["chosenChart"]
plotSmallMultiplesKey = namingParams["plotSmallMultiplesOtherCharts"]
chosenChart = chartDict[chosenChart]
verticalSpacingDict = {
1: 0,
2: 0.08,
3: 0.06,
4: 0.05,
5: 0.05,
6: 0.04,
}
horizontalSpacing = 0.01
subplotTitles = None
if (
not plotWithPins
and plotSmallMultiplesKey in chartDict
and chartDict[plotSmallMultiplesKey]
):
numberOfItems = len(repeatArray)
numberOfCols = 2
height = 400
if numberOfItems < numberOfCols:
numberOfCols = numberOfItems
plotSmallMultiples = True
sharedYaxes = "all"
numberOfCols = 2
numberOfRows = numberOfItems
numberOfRows = int(math.ceil(numberOfItems / numberOfCols))
width = (height * goldenRatio) * numberOfCols
if numberOfRows > 1:
height = height * (1 + numberOfRows / 3)
if len(repeatArray) > 1:
subplotTitles = repeatArray
else:
numberOfCols = 1
numberOfRows = 1
sharedYaxes = None
height = 500
width = (height * goldenRatio) * numberOfCols
if numberOfRows in verticalSpacingDict:
verticalSpacing = verticalSpacingDict[numberOfRows]
else:
verticalSpacing = verticalSpacingDict[len(verticalSpacingDict)]
fig = make_subplots(
rows=numberOfRows,
cols=numberOfCols,
shared_xaxes="all",
shared_yaxes=sharedYaxes,
vertical_spacing=verticalSpacing,
horizontal_spacing=horizontalSpacing,
subplot_titles=subplotTitles,
)
return fig, height, width, numberOfCols, numberOfRows
def add_integrated_legends_to_trend_plot(fig, df, countRows, countCols, yArray):
fig.add_annotation(
text=yArray[0],
showarrow=False,
align="center",
yshift=0,
y=df.get_column(yArray[0]).first(),
yref="y",
x=0,
ax=0,
xshift=-30,
xref="paper",
hovertext=yArray[0],
row=countRows,
col=countCols,
)
fig.add_annotation(
text=yArray[1],
showarrow=False,
align="center",
yshift=0,
y=df.get_column(yArray[1]).first(),
yref="y",
x=0,
ax=0,
xshift=-30,
xref="paper",
hovertext=yArray[1],
row=countRows,
col=countCols,
)
return fig
def setup_fig_for_mekko_charts(
df, column, repeatArray, chosenDimension, paramDict, chartDict
):
"""
we need to set up the subplots fig based on the number of subplots we want to draw
"""
namingParams = get_naming_params()
configParams = get_config_params()
goldenRatio = configParams[namingParams["goldenRatio"]]
chosenChart = namingParams["chosenChart"]
deltaName = namingParams["deltaName"]
figureName = namingParams["figureName"]
fatherAndChildDimensions = namingParams["fatherAndChildDimensions"]
showTopForEachItem = namingParams["showTopForEachItem"]
yAxisDimension = namingParams["yAxisDimension"]
totalName = namingParams["totalName"]
numberOfRowsKey = namingParams["numberOfRows"]
numberOfColsKey = namingParams["numberOfCols"]
chosenChart = chartDict[chosenChart]
verticalSpacingDict = {
1: 0,
2: 0.06,
3: 0.05,
4: 0.04,
5: 0.04,
6: 0.03,
}
horizontalSpacing = 0.01
subplotTitles = None
sharedXaxes = None
sharedYaxes = None
if column != totalName:
numberOfItems = len(repeatArray)
numberOfCols = 2
plotSmallMultiples = True
numberOfCols = 2
numberOfRows = int(math.ceil(numberOfItems / numberOfCols))
if numberOfItems < numberOfCols:
numberOfRows = 1
specs = []
rowHeights = []
if len(repeatArray) > 1:
subplotTitles = repeatArray
showItems = subplotTitles
horizontalSpacing = 0.12
else:
numberOfCols = 1
numberOfRows = 1
sharedYaxes = None
verticalSpacing = 0.05
if numberOfRows in verticalSpacingDict:
verticalSpacing = verticalSpacingDict[numberOfRows]
else:
verticalSpacing = verticalSpacingDict[len(verticalSpacingDict)]
fig = make_subplots(
rows=numberOfRows,
cols=numberOfCols,
shared_xaxes=sharedXaxes,
shared_yaxes=sharedYaxes,
vertical_spacing=verticalSpacing,
horizontal_spacing=horizontalSpacing,
subplot_titles=subplotTitles,
)
if subplotTitles:
title_annotations = [
ann for ann in fig.layout.annotations if ann.text in subplotTitles
]
for idx, ann in enumerate(title_annotations):
axis_index = idx + 1
xaxis_key = "xaxis" if axis_index == 1 else f"xaxis{axis_index}"
xaxis = getattr(fig.layout, xaxis_key, None)
domain = getattr(xaxis, "domain", None) if xaxis else None
if domain and len(domain) == 2:
axis_ref = xaxis_key.replace("axis", "")
ann.x = 0.5
ann.xref = f"{axis_ref} domain"
ann.xanchor = "center"
ann.align = "center"
paramDict[figureName] = fig
paramDict[numberOfRowsKey] = numberOfRows
paramDict[numberOfColsKey] = numberOfCols
return paramDict, numberOfCols, numberOfRows
SHA-256: c4c72ad57df1cc5bda6b71d2cab4aaa9e4128741da4c01c655b5b68b97e99f02