You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
33 lines
2.9 KiB
33 lines
2.9 KiB
"""Minimal matplotlib plots for exploratory Phase 8 review."""
|
|
from __future__ import annotations
|
|
import logging
|
|
from pathlib import Path
|
|
from .config_loader import AnalysisSettings
|
|
LOGGER=logging.getLogger(__name__)
|
|
|
|
class AnalysisPlots:
|
|
"""Generate five independent PNG files with an optional Agg backend."""
|
|
def __init__(self,config:AnalysisSettings)->None:self.config=config;self.directory=Path(config.output_directory)/"plots"
|
|
def generate(self,df,result)->list[Path]:
|
|
if not self.config.generate_plots:return []
|
|
try:
|
|
import matplotlib;matplotlib.use("Agg",force=True);import matplotlib.pyplot as plt
|
|
except ImportError as exc:raise RuntimeError("Phase 8 plots require matplotlib") from exc
|
|
self.directory.mkdir(parents=True,exist_ok=True);paths=[]
|
|
if df.empty: LOGGER.warning("Analysis plot input is empty; placeholder plots will be generated")
|
|
def save(name,title,x,y,ylabel):
|
|
fig,ax=plt.subplots();ax.bar(x,y);ax.set_title(title);ax.set_ylabel(ylabel);fig.tight_layout();path=self.directory/name;fig.savefig(path);plt.close(fig);paths.append(path)
|
|
summaries=result.condition_summaries;save("response_rate_by_condition.png","Response Rate",[x.condition for x in summaries],[x.response_rate or 0 for x in summaries],"Rate")
|
|
for metric,name,title in (("reaction_time_sec","reaction_time_by_condition.png","Reaction Time"),("max_yaw_delta_toward_signage","max_yaw_delta_by_condition.png","Max Yaw Delta")):
|
|
groups=[df[df.prompt_condition==c][metric].dropna().tolist() for c in ("prompt","control")];fig,ax=plt.subplots()
|
|
# Matplotlib 3.9 renamed ``labels`` to ``tick_labels`` and newer
|
|
# releases no longer accept the old keyword. Use the new name
|
|
# first while retaining compatibility with older supported builds.
|
|
try:
|
|
ax.boxplot(groups,tick_labels=["prompt","control"])
|
|
except TypeError:
|
|
ax.boxplot(groups,labels=["prompt","control"])
|
|
ax.set_title(title);fig.tight_layout();path=self.directory/name;fig.savefig(path);plt.close(fig);paths.append(path)
|
|
levels=["none","subtle","weak","medium","strong","not_evaluable"];p=df[df.prompt_condition=="prompt"].turn_level.fillna("not_evaluable");c=df[df.prompt_condition=="control"].turn_level.fillna("not_evaluable");fig,ax=plt.subplots();x=range(len(levels));ax.bar([i-.2 for i in x],[(p==v).sum() for v in levels],.4,label="prompt");ax.bar([i+.2 for i in x],[(c==v).sum() for v in levels],.4,label="control");ax.set_xticks(list(x),levels,rotation=30);ax.legend();fig.tight_layout();path=self.directory/"turn_level_distribution.png";fig.savefig(path);plt.close(fig);paths.append(path)
|
|
exclusions=df[df.excluded==True].exclusion_reason.fillna("unknown").value_counts();save("exclusion_reason_counts.png","Exclusion Reasons",list(exclusions.index) or ["none"],list(exclusions.values) or [0],"Count")
|
|
return paths
|
|
|