BusinessReport#
BusinessReport wraps any fitted diff-diff result object and produces
stakeholder-ready output:
summary()— a short paragraph block suitable for an email or Slack.full_report()— a structured multi-section markdown report.to_dict()— a stable AI-legible structured schema (single source of truth; prose renders from this dict).
By default, BusinessReport constructs an internal DiagnosticReport
to surface pre-trends, sensitivity, and other validity checks as part
of the narrative. Pass auto_diagnostics=False to skip this, or
diagnostics=<DiagnosticReport> to supply an explicit one.
Pre-computed diagnostics can be forwarded directly to the auto-
constructed DiagnosticReport via
precomputed={'parallel_trends': ...},
precomputed={'sensitivity': ...},
precomputed={'pretrends_power': ...}, or
precomputed={'bacon': ...} — same keys as
DiagnosticReport(precomputed=...). DR validates keys and rejects
estimator-incompatible entries.
Data-dependent checks (2x2 parallel trends on simple DiD,
Goodman-Bacon decomposition on staggered estimators, the EfficientDiD
Hausman PT-All vs PT-Post pretest) require the raw panel + column
names. Pass data, outcome, treatment, unit, time,
and/or first_treat to BusinessReport and they are forwarded
to the auto-constructed DiagnosticReport. Without these kwargs,
those specific checks are skipped with an explicit reason while the
rest of the report still renders.
For survey-weighted fits (any result carrying
survey_metadata) pass the original SurveyDesign via
survey_design=<design>. It is threaded through to
bacon_decompose for a fit-faithful Goodman-Bacon replay. When
survey_metadata is set but survey_design is not supplied,
Bacon is skipped with an explicit reason so the report never emits
an unweighted decomposition for a design that differs from the
estimate. The simple 2x2 parallel-trends helper has no survey-aware
variant and is skipped unconditionally on a survey-backed
DiDResults regardless of survey_design; supply
precomputed={'parallel_trends': ...} with a survey-aware
pretest to opt in.
Methodology deviations (no traffic-light gates, pre-trends verdict thresholds, power-aware phrasing, unit-translation policy, schema stability) are documented in docs/methodology/REPORTING.md.
The schema carries a top-level target_parameter block
(experimental) naming what the headline scalar represents per
estimator — simple ATT, event-study average, DID_M, DID_1,
cost-benefit delta, dose-response aggregate, factor-model-adjusted ATT,
etc. For the dCDH dynamic branch with trends_linear=True and
L_max>=2, the scalar is intentionally NaN and
aggregation is "no_scalar_headline" with
headline_attribute set to None. Agents should dispatch on
this case and inspect the headline reason field, which
distinguishes the populated-surface subcase (per-horizon table
available on linear_trends_effects) from the empty-surface
subcase (no horizons survived estimation; re-fit with a larger
L_max or with trends_linear=False). See the “Target
parameter” section of docs/methodology/REPORTING.md
for the full per-estimator dispatch table and schema shape.
Example#
from diff_diff import CallawaySantAnna, BusinessReport
cs = CallawaySantAnna(base_period="universal").fit(
df, outcome="revenue", unit="store", time="period",
first_treat="first_treat", aggregate="event_study",
)
report = BusinessReport(
cs,
outcome_label="Revenue per store",
outcome_unit="$",
business_question="Did the loyalty program lift revenue?",
treatment_label="the loyalty program",
# Optional: panel + column names so auto diagnostics can run the
# data-dependent checks (2x2 PT, Goodman-Bacon, EfficientDiD
# Hausman). Without these the auto path still runs and just
# skips those checks.
data=df,
outcome="revenue",
unit="store",
time="period",
first_treat="first_treat",
)
print(report.summary())
API#
- class diff_diff.BusinessReport[source]
Bases:
objectProduce a stakeholder-ready narrative from any diff-diff results object.
- Parameters:
results (Any) – A fitted diff-diff results object. Any of the 16 result types is accepted.
BaconDecompositionResultsis not a valid input — Bacon is a diagnostic, not an estimator; useDiagnosticReportfor that.outcome_label (str, optional) – Stakeholder-friendly outcome name (e.g.
"Revenue per user").outcome_unit (str, optional) – Unit label:
"$"/"%"/"pp"/"log_points"/"count"(recognized for formatting) or any free-form string (used verbatim without arithmetic translation).outcome_direction (str, optional) –
"higher_is_better"or"lower_is_better". Drives whether the effect is described as “lift” / “drag” rather than just “increase” / “decrease”.business_question (str, optional) – Question the analysis answers (prepended to the summary).
treatment_label (str, optional) – Stakeholder-friendly treatment name (e.g.
"the campaign").alpha (float, optional) – Significance level. Defaults to
results.alphawhen not supplied. Single knob: drives both CI level and significance phrasing.honest_did_results (HonestDiDResults or SensitivityResults, optional) – Pre-computed sensitivity result. When supplied, this is forwarded to the internal
DiagnosticReportso sensitivity is not re-computed.auto_diagnostics (bool, default True) – When
TrueanddiagnosticsisNone, auto-construct aDiagnosticReport. SetFalseto skip diagnostics entirely.diagnostics (DiagnosticReport or DiagnosticReportResults, optional) – Explicit diagnostics object. Takes precedence over
auto_diagnostics.include_appendix (bool, default True) – Whether
full_report()appends the estimator’s academicresults.summary()output under a “Technical Appendix” section.data (optional) – Raw panel + column names forwarded to the auto-constructed
DiagnosticReportso data-dependent checks (2x2 PT on simple DiD, Bacon-from-scratch, EfficientDiD Hausman pretest) can run.outcome (optional) – Raw panel + column names forwarded to the auto-constructed
DiagnosticReportso data-dependent checks (2x2 PT on simple DiD, Bacon-from-scratch, EfficientDiD Hausman pretest) can run.treatment (optional) – Raw panel + column names forwarded to the auto-constructed
DiagnosticReportso data-dependent checks (2x2 PT on simple DiD, Bacon-from-scratch, EfficientDiD Hausman pretest) can run.unit (optional) – Raw panel + column names forwarded to the auto-constructed
DiagnosticReportso data-dependent checks (2x2 PT on simple DiD, Bacon-from-scratch, EfficientDiD Hausman pretest) can run.time (optional) – Raw panel + column names forwarded to the auto-constructed
DiagnosticReportso data-dependent checks (2x2 PT on simple DiD, Bacon-from-scratch, EfficientDiD Hausman pretest) can run.first_treat (optional) – Raw panel + column names forwarded to the auto-constructed
DiagnosticReportso data-dependent checks (2x2 PT on simple DiD, Bacon-from-scratch, EfficientDiD Hausman pretest) can run.survey_design (SurveyDesign, optional) – The
SurveyDesignobject used to fit a survey-weighted estimator. Forwarded to the auto-constructedDiagnosticReportfor fit-faithful Goodman-Bacon replay. When the fit carriessurvey_metadatabutsurvey_designis not supplied, Bacon is skipped with an explicit reason rather than replaying an unweighted decomposition for a design that does not match the estimate. The simple 2x2 parallel-trends helper (utils.check_parallel_trends) has no survey-aware variant; on a survey-backedDiDResultsit is skipped unconditionally regardless ofsurvey_design. Supplyprecomputed={'parallel_trends': ...}with a survey-aware pretest to opt in. Seedocs/methodology/REPORTING.md.precomputed (dict, optional) – Pre-computed diagnostic objects forwarded to the auto- constructed
DiagnosticReport(same keys asDiagnosticReport(precomputed=...)):"parallel_trends","sensitivity","pretrends_power","bacon". DR validates keys and rejects estimator-incompatible entries (e.g., HonestDiD bounds or generic PT on SDiD / TROP).honest_did_resultsremains a shorthand forsensitivity; an explicitprecomputed['sensitivity']wins on conflict.
- __init__(results, *, outcome_label=None, outcome_unit=None, outcome_direction=None, business_question=None, treatment_label=None, alpha=None, honest_did_results=None, auto_diagnostics=True, diagnostics=None, include_appendix=True, data=None, outcome=None, treatment=None, unit=None, time=None, first_treat=None, survey_design=None, precomputed=None)[source]
- Parameters:
results (Any)
outcome_label (str | None)
outcome_unit (str | None)
outcome_direction (str | None)
business_question (str | None)
treatment_label (str | None)
alpha (float | None)
honest_did_results (Any | None)
auto_diagnostics (bool)
diagnostics (DiagnosticReport | DiagnosticReportResults | None)
include_appendix (bool)
data (Any | None)
outcome (str | None)
treatment (str | None)
unit (str | None)
time (str | None)
first_treat (str | None)
survey_design (Any | None)
- to_dict()[source]
Return the AI-legible structured schema (single source of truth).
- to_json(*, indent=2)[source]
Return
to_dict()serialized as JSON.
- class diff_diff.BusinessContext[source]
Bases:
objectFrozen bundle of business-framing metadata used when rendering prose.
Populated from
BusinessReportconstructor kwargs. Falls back to neutral labels when fields are not supplied.- outcome_label: str
- treatment_label: str
- alpha: float
- diff_diff.BUSINESS_REPORT_SCHEMA_VERSION = '2.0'#
str(object=’’) -> str str(bytes_or_buffer[, encoding[, errors]]) -> str
Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.__str__() (if defined) or repr(object). encoding defaults to sys.getdefaultencoding(). errors defaults to ‘strict’.