Skip to content

Commit 0e3790f

Browse files
Enhance interactive plots, registry, and smart recommendations
Improve interactive plot panel layout and rendering, update registry with expanded module support, refine smart compare/recommend logic, and update next_steps guidance and script editor. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 8e327b5 commit 0e3790f

7 files changed

Lines changed: 350 additions & 161 deletions

File tree

src/statspai/core/next_steps.py

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -584,43 +584,56 @@ def causal_next_steps(result) -> List[Step]:
584584
# ====================================================================== #
585585

586586
def _detect_family(method: str) -> str:
587-
"""Classify a method string into a family."""
587+
"""Classify a method string into a family.
588+
589+
Order matters: more specific patterns are checked before broader ones
590+
(e.g. ``sdid`` → synth before ``did`` → did).
591+
"""
588592
m = method.lower()
589593

594+
# --- Synthetic control (check before DID: "sdid" contains "did") ---
595+
if any(k in m for k in ("synth", "scm", "sdid", "augsynth",
596+
"causal impact", "structural time")):
597+
return "synth"
598+
599+
# --- DID ---
590600
if any(k in m for k in ("did", "diff", "callaway", "sun_abraham",
591601
"stagger", "imputation", "wooldridge",
592602
"chaisemartin", "changes-in-changes",
593603
"cic", "stacked")):
594604
return "did"
595605

606+
# --- RD ---
596607
if any(k in m for k in ("rd", "discontinuity", "rdrobust", "kink",
597608
"rdit")):
598609
return "rd"
599610

600-
if any(k in m for k in ("iv ", "2sls", "instrumental", "bartik",
611+
# --- IV (match bare "iv" and "iv ..." via word boundary) ---
612+
if any(k in m for k in ("2sls", "instrumental", "bartik",
601613
"deepiv", "shift-share")):
602614
return "iv"
615+
import re
616+
if re.search(r'\biv\b', m):
617+
return "iv"
603618

619+
# --- Matching / IPW ---
604620
if any(k in m for k in ("match", "psm", "cem", "mahalanobis",
605621
"ipw", "entropy", "aipw", "tmle")):
606622
return "matching"
607623

608-
if any(k in m for k in ("synth", "scm", "sdid", "augsynth")):
609-
return "synth"
610-
624+
# --- DML ---
611625
if any(k in m for k in ("dml", "double", "debiased")):
612626
return "dml"
613627

628+
# --- HTE / Meta-learners ---
614629
if any(k in m for k in ("metalearner", "slearner", "tlearner",
615630
"xlearner", "rlearner", "drlearner",
616631
"causal forest", "bcf", "cate",
617632
"tarnet", "cfrnet", "dragonnet")):
618633
return "hte"
619634

635+
# --- Mediation ---
620636
if any(k in m for k in ("mediat",)):
621637
return "mediation"
622638

623-
if any(k in m for k in ("causal impact", "structural time")):
624-
return "synth"
625-
626639
return "generic"

src/statspai/plots/_script_editor.py

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ def _on_submit(text):
111111
old = target.get_text()
112112
target.set_text(text)
113113
from .interactive import EditRecord
114-
editor.edits.append(EditRecord(
114+
editor._record_edit(EditRecord(
115115
f'{prefix}.text', 'text', old, text,
116116
f"# annotation text changed to {text!r}",
117117
))
@@ -123,8 +123,8 @@ def _on_submit(text):
123123
textbox.on_submit(_on_submit)
124124

125125
# Generate Code button
126-
ax_codebtn = fig.add_axes([0.65, 0.04, 0.15, 0.05])
127-
btn_code = Button(ax_codebtn, 'Get Code')
126+
ax_codebtn = fig.add_axes([0.62, 0.04, 0.12, 0.05])
127+
btn_code = Button(ax_codebtn, 'Code')
128128

129129
def _on_code(event):
130130
print("\n" + "=" * 50)
@@ -134,15 +134,27 @@ def _on_code(event):
134134
btn_code.on_clicked(_on_code)
135135

136136
# Undo button
137-
ax_undobtn = fig.add_axes([0.82, 0.04, 0.1, 0.05])
137+
ax_undobtn = fig.add_axes([0.75, 0.04, 0.1, 0.05])
138138
btn_undo = Button(ax_undobtn, 'Undo')
139139

140140
def _on_undo(event):
141141
editor.undo()
142-
print("[StatsPAI] Undone last edit")
142+
print(f"[StatsPAI] Undone last edit "
143+
f"({len(editor._redo_stack)} redo available)")
143144

144145
btn_undo.on_clicked(_on_undo)
145146

147+
# Redo button
148+
ax_redobtn = fig.add_axes([0.86, 0.04, 0.1, 0.05])
149+
btn_redo = Button(ax_redobtn, 'Redo')
150+
151+
def _on_redo(event):
152+
editor.redo()
153+
print(f"[StatsPAI] Redone edit "
154+
f"({len(editor._redo_stack)} redo remaining)")
155+
156+
btn_redo.on_clicked(_on_redo)
157+
146158
# Grid toggle button
147159
ax_gridbtn = fig.add_axes([0.65, 0.10, 0.12, 0.04])
148160
btn_grid = Button(ax_gridbtn, 'Grid')
@@ -155,7 +167,7 @@ def _on_grid(event):
155167
btn_grid.on_clicked(_on_grid)
156168

157169
# Spine toggle
158-
ax_spinebtn = fig.add_axes([0.78, 0.10, 0.14, 0.04])
170+
ax_spinebtn = fig.add_axes([0.78, 0.10, 0.18, 0.04])
159171
btn_spine = Button(ax_spinebtn, 'Spines')
160172
_spine_state = {'minimal': False}
161173

@@ -193,6 +205,7 @@ def _on_close(event):
193205
'textbox': textbox,
194206
'btn_code': btn_code,
195207
'btn_undo': btn_undo,
208+
'btn_redo': btn_redo,
196209
'btn_grid': btn_grid,
197210
'btn_spine': btn_spine,
198211
}

0 commit comments

Comments
 (0)