Source code for energytrackr.plot.builtin_plot_objects.change_events
"""ChangeEventMarkers - Draws regression/improvement markers and optional level circles."""from__future__importannotationsfromdataclassesimportdataclassfromtypingimportAnyfrombokeh.modelsimportBoxAnnotationfrombokeh.plottingimportfigurefromenergytrackr.plot.core.contextimportContextfromenergytrackr.plot.core.interfacesimportConfigurable,PlotObjfromenergytrackr.utils.loggerimportlogger
[docs]@dataclass(frozen=True)classChangeEventMarkersConfig:"""Configuration for ChangeEventMarkers."""show_levels:bool=Trueradius_base:float=6
[docs]classChangeEventMarkers(PlotObj,Configurable[ChangeEventMarkersConfig]):"""Draws regression/improvement markers and optional level circles."""def__init__(self,**params:dict[str,Any])->None:"""Initialize the ChangeEventMarkers object. Args: **params: Configuration parameters for ChangeEventMarkers. """super().__init__(ChangeEventMarkersConfig,**params)
[docs]defadd(self,ctx:Context,fig:figure)->None:"""Adds visual markers for change events to the plot in the given context. This method retrieves change events from the context's artefacts and adds box annotations to the plot to indicate regressions (in red) and improvements (in green) at the corresponding indices. If `self.show_levels` is True, it also adds circles at the median values for each event, colored and sized according to their level. Side Effects: Modifies the plot in `ctx.fig` by adding box annotations and, optionally, level circles. Logs information and debug messages about the process. Args: ctx (Context): The plotting context containing artefacts, statistics, and the figure. fig (figure): The Bokeh figure to which the change event markers will be added. """ifnot(events:=ctx.artefacts.get("change_events",[])):logger.info("ChangeEventMarkers: no events; skipping")return# Regression / improvement boxesforeinevents:ifnote.level:continuecolor="red"ife.direction=="increase"else"green"fig.add_layout(BoxAnnotation(left=e.index-0.4,right=e.index+0.4,fill_color=color,fill_alpha=0.3,line_alpha=0,level="annotation",),)# Level circlesifself.config.show_levels:color_map={1:"gray",2:"blue",3:"orange",4:"purple",5:"red"}forlvl,colorincolor_map.items():ifnot(xs:=[e.indexforeineventsife.level==lvl]):continueys=[ctx.stats["medians"][i]foriinxs]fig.circle(x=xs,y=ys,radius=lvl*0.3,color=color,alpha=0.4,legend_label=f"Level {lvl}",)logger.debug("ChangeEventMarkers added: %d events",len(events))