statspai.postestimation¶
postestimation ¶
Post-estimation tools for StatsPAI.
Provides: - margins(): Average Marginal Effects (AME), Marginal Effects at the Mean (MEM) - marginsplot(): Visualize marginal effects - test(): Wald / F test for linear restrictions (beta1 = beta2, joint significance) - lincom(): Linear combinations of coefficients with inference - nlcom(): Nonlinear combinations by the delta method - predictions() / comparisons() / slopes() (+ avg_ versions) / hypotheses(): marginaleffects-style quantities with delta-method inference
Equivalent to Stata's margins, test, lincom commands.
postestimation_contract ¶
postestimation_contract(result: Any, *, data: Optional[DataFrame] = None, include_diagnostics: bool = True) -> Dict[str, Any]
Return the post-estimation actions supported by result.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
result
|
object
|
Fitted StatsPAI result, estimator, or compatible object. |
required |
data
|
DataFrame
|
Analysis frame available for data-dependent actions. When
supplied, the contract marks |
None
|
include_diagnostics
|
bool
|
Include scalar diagnostics from |
True
|
Returns:
| Type | Description |
|---|---|
dict
|
Machine-readable contract with |
Examples:
>>> import numpy as np
>>> import pandas as pd
>>> import statspai as sp
>>> rng = np.random.default_rng(42)
>>> df = pd.DataFrame({
... "x1": rng.normal(size=200),
... "x2": rng.normal(size=200),
... })
>>> df["y"] = (1.0 + 0.5 * df["x1"] - 0.3 * df["x2"]
... + rng.normal(size=200))
>>> res = sp.regress("y ~ x1 + x2", data=df)
>>> con = sp.postestimation_contract(res, data=df)
>>> con["result_type"]
'EconometricResults'
>>> "margins" in con["available"] # data supplied -> ready
True
>>> con["has_data"]
True
lincom ¶
Estimate a linear combination of coefficients with inference.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
result
|
EconometricResults or CausalResult
|
Fitted model. After |
required |
expression
|
str
|
Linear combination (Stata |
required |
alpha
|
float
|
Significance level. |
0.05
|
Returns:
| Type | Description |
|---|---|
dict
|
|
Examples:
>>> import statspai as sp
>>> import numpy as np, pandas as pd
>>> rng = np.random.default_rng(0)
>>> n = 200
>>> x1, x2 = rng.normal(size=n), rng.normal(size=n)
>>> y = 1.0 + 0.5 * x1 + 0.5 * x2 + rng.normal(size=n)
>>> df = pd.DataFrame({"y": y, "x1": x1, "x2": x2})
>>> result = sp.regress("y ~ x1 + x2", data=df)
>>> out = sp.lincom(result, "x1 + x2") # beta1 + beta2
>>> sorted(out)
['ci', 'df', 'distribution', 'estimate', 'expression',
'pvalue', 'se', 'statistic', 'z']
>>> diff = sp.lincom(result, "x1 - x2") # beta1 - beta2
>>> bool(diff["se"] > 0)
True
test ¶
Wald test for linear restrictions on coefficients.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
result
|
EconometricResults or CausalResult
|
Fitted model with |
required |
hypothesis
|
str or list of str
|
Hypothesis specification (Stata |
required |
Returns:
| Type | Description |
|---|---|
dict
|
|
Raises:
| Type | Description |
|---|---|
MethodIncompatibility
|
Unknown coefficient names, restrictions without coefficients, inconsistent restrictions, or a multi-coefficient restriction on a result that carries no covariance matrix. |
Examples:
>>> import statspai as sp
>>> import numpy as np, pandas as pd
>>> rng = np.random.default_rng(0)
>>> n = 200
>>> x1, x2, x3 = rng.normal(size=n), rng.normal(size=n), rng.normal(size=n)
>>> y = 1.0 + 0.5 * x1 + 0.5 * x2 - 0.3 * x3 + rng.normal(size=n)
>>> df = pd.DataFrame({"y": y, "x1": x1, "x2": x2, "x3": x3})
>>> result = sp.regress("y ~ x1 + x2 + x3", data=df)
>>> out = sp.test(result, "x1 = x2") # beta1 = beta2?
>>> sorted(out)
['chi2', 'df', 'distribution', 'hypothesis', 'pvalue', 'statistic']
>>> joint = sp.test(result, "x1 = x2 = 0") # joint: beta1 = beta2 = 0?
>>> bool(joint["pvalue"] < 0.05)
True
>>> restr = sp.test(result, "x1 + x2 = 1") # beta1 + beta2 = 1?
avg_comparisons ¶
avg_comparisons(result: Any, variables: Any = None, newdata: Optional[DataFrame] = None, *, by: Optional[Union[str, Sequence[str]]] = None, comparison: str = 'difference', type: str = 'response', wts: Any = None, df: Any = None, conf_level: float = 0.95, vcov: bool = True) -> DataFrame
Average contrasts (marginaleffects::avg_comparisons).
The average over rows of the unit-level contrasts of :func:comparisons
(comparison='ratioavg' gives the ratio of average predictions
instead). For a binary treatment on the response scale this is the
average treatment effect implied by the model (the g-formula).
Parameters are those of :func:comparisons.
Examples:
>>> import numpy as np, pandas as pd
>>> import statspai as sp
>>> rng = np.random.default_rng(3)
>>> d = pd.DataFrame({"x": rng.normal(size=400), "d": rng.integers(0, 2, 400)})
>>> d["y"] = (rng.uniform(size=400) < 1 / (1 + np.exp(-(d.x + d.d)))).astype(int)
>>> fit = sp.logit("y ~ d + x", data=d)
>>> ate = sp.avg_comparisons(fit, "d")
>>> bool(0 < ate.estimate.iloc[0] < 1)
True
avg_predictions ¶
avg_predictions(result: Any, newdata: Optional[DataFrame] = None, *, by: Optional[Union[str, Sequence[str]]] = None, type: str = 'response', wts: Any = None, df: Any = None, conf_level: float = 0.95, vcov: bool = True) -> DataFrame
Average prediction, overall or by= group (marginaleffects::avg_predictions).
Parameters are those of :func:predictions.
Examples:
>>> import numpy as np, pandas as pd
>>> import statspai as sp
>>> rng = np.random.default_rng(1)
>>> d = pd.DataFrame({"x": rng.normal(size=200), "g": rng.integers(0, 3, 200)})
>>> d["y"] = 1 + d.x + d.g + rng.normal(size=200)
>>> fit = sp.regress("y ~ x + C(g)", data=d)
>>> sp.avg_predictions(fit, by="g").shape[0]
3
avg_slopes ¶
avg_slopes(result: Any, variables: Any = None, newdata: Optional[DataFrame] = None, *, by: Optional[Union[str, Sequence[str]]] = None, slope: str = 'dydx', type: str = 'response', wts: Any = None, df: Any = None, conf_level: float = 0.95, eps: float = 1e-05, vcov: bool = True) -> DataFrame
Average partial derivatives (marginaleffects::avg_slopes).
The average marginal effect; with wts=True and df='model' it is
Stata's margins, dydx() after a weighted fit.
Parameters are those of :func:slopes.
Examples:
>>> import numpy as np, pandas as pd
>>> import statspai as sp
>>> rng = np.random.default_rng(5)
>>> d = pd.DataFrame({"x": rng.normal(size=300)})
>>> d["y"] = (rng.uniform(size=300) < 1 / (1 + np.exp(-d.x))).astype(int)
>>> fit = sp.logit("y ~ x", data=d)
>>> ame = sp.avg_slopes(fit, "x")
>>> bool(0 < ame.estimate.iloc[0] < fit.params["x"])
True
comparisons ¶
comparisons(result: Any, variables: Any = None, newdata: Optional[DataFrame] = None, *, by: Optional[Union[str, Sequence[str]]] = None, comparison: str = 'difference', type: str = 'response', wts: Any = None, df: Any = None, conf_level: float = 0.95, vcov: bool = True) -> DataFrame
Unit-level contrasts of predictions (marginaleffects::comparisons).
For each variable, predictions at a "high" and a "low" value of it, every other column held at the row's own value, contrasted per row:
- a factor (
C(g), strings, categories): each level against the first; - a numeric 0/1 variable: 1 against 0;
- another numeric variable: an increase of 1 from the row's value, or as
given in
variables=: a number (that increase),"sd"/"2sd"(mean +/- sd/2, mean +/- sd),"iqr"(third against first quartile),"minmax"(maximum against minimum) or a pair(low, high)of fixed values.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
result
|
fitted result
|
As in :func: |
required |
variables
|
(str, list or dict)
|
Variables to contrast (default: every model variable); a dict maps a numeric variable to its change. |
None
|
newdata
|
DataFrame
|
Rows to evaluate (default: the estimation sample). |
None
|
by
|
str or list of str
|
Average within groups. |
None
|
comparison
|
str
|
'difference', 'ratio', 'lnratio' (per row); 'ratioavg',
'lnratioavg' (ratio of the average predictions). When the contrasts
are averaged ( |
'difference'
|
type
|
str
|
As in :func: |
'response'
|
wts
|
str
|
As in :func: |
'response'
|
df
|
str
|
As in :func: |
'response'
|
conf_level
|
str
|
As in :func: |
'response'
|
vcov
|
str
|
As in :func: |
'response'
|
Returns:
| Type | Description |
|---|---|
DataFrame
|
|
Examples:
>>> import numpy as np, pandas as pd
>>> import statspai as sp
>>> rng = np.random.default_rng(2)
>>> d = pd.DataFrame({"x": rng.normal(size=300), "d": rng.integers(0, 2, 300)})
>>> d["y"] = 1 + 2 * d.d + d.x + rng.normal(size=300)
>>> fit = sp.regress("y ~ d + x", data=d)
>>> sp.comparisons(fit, "d").shape[0] == len(d)
True
hypotheses ¶
hypotheses(result: Any, hypothesis: Union[str, Sequence[str], None] = None, *, df: Any = None, conf_level: float = 0.95, joint: Union[bool, Sequence[str]] = False, joint_test: str = 'f') -> DataFrame
Delta-method test of a function of estimates (marginaleffects::hypotheses).
result is a fitted model (the estimates are its coefficients) or a
table returned by :func:predictions, :func:comparisons or
:func:slopes (the estimates are its rows, with their joint covariance
from the table's Jacobian). b1, b2 ... refer to the estimates by
position; a model's coefficients can also be named. "lhs = rhs" tests
lhs - rhs = 0; an expression without = is tested against 0.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
result
|
fitted result or marginaleffects-style table
|
|
required |
hypothesis
|
str or list of str
|
E.g. |
None
|
df
|
(None, 'model' or float)
|
Reference distribution of the one-at-a-time tests (None: normal). |
None
|
conf_level
|
float
|
|
0.95
|
joint
|
bool or list of str
|
Joint Wald test instead of one row per hypothesis.
|
False
|
joint_test
|
('f', 'chisq')
|
|
"f"
|
Returns:
| Type | Description |
|---|---|
DataFrame
|
One row per hypothesis: |
Examples:
>>> import numpy as np, pandas as pd
>>> import statspai as sp
>>> rng = np.random.default_rng(6)
>>> d = pd.DataFrame({"x1": rng.normal(size=300), "x2": rng.normal(size=300)})
>>> d["y"] = 1 + d.x1 + d.x2 + rng.normal(size=300)
>>> fit = sp.regress("y ~ x1 + x2", data=d)
>>> h = sp.hypotheses(fit, "b2 = b3")
>>> bool(h.p_value.iloc[0] > 0.01)
True
>>> sp.hypotheses(fit, ["x1 = 1", "x2 = 1"]).shape[0]
2
>>> j = sp.hypotheses(fit, ["x1 = 1", "x2 = 1"], joint=True)
>>> list(j.columns[:3])
['hypothesis', 'statistic', 'p_value']
predictions ¶
predictions(result: Any, newdata: Optional[DataFrame] = None, *, by: Optional[Union[str, Sequence[str]]] = None, type: str = 'response', wts: Any = None, df: Any = None, conf_level: float = 0.95, vcov: bool = True) -> DataFrame
Unit-level predictions with delta-method standard errors.
marginaleffects::predictions: one row per row of newdata (default:
the estimation sample), with the data's columns and estimate,
std_error, statistic, p_value, conf_low, conf_high.
With by= the predictions are averaged within groups (what
:func:avg_predictions does).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
result
|
fitted result
|
From |
required |
newdata
|
DataFrame
|
Rows to predict for. Default: the estimation sample of the fitted data. |
None
|
by
|
str or list of str
|
Average within groups of these columns. |
None
|
type
|
('response', 'link')
|
Prediction scale. |
'response'
|
wts
|
(None, True, str or array)
|
Weights for averages: None (unweighted, the marginaleffects default), True (the fit's weight column), a column name or an array. |
None
|
df
|
(None, 'model' or float)
|
Reference distribution: None is normal (marginaleffects), 'model'
the fit's own (t after |
None
|
conf_level
|
float
|
|
0.95
|
vcov
|
bool
|
False returns the estimates alone (no standard errors), as
marginaleffects' |
True
|
Returns:
| Type | Description |
|---|---|
DataFrame
|
|
Examples:
>>> import numpy as np, pandas as pd
>>> import statspai as sp
>>> rng = np.random.default_rng(0)
>>> d = pd.DataFrame({"x": rng.normal(size=300), "g": rng.integers(0, 2, 300)})
>>> d["y"] = (rng.uniform(size=300) < 1 / (1 + np.exp(-d.x))).astype(int)
>>> fit = sp.logit("y ~ x + g", data=d)
>>> p = sp.predictions(fit)
>>> bool(((p.estimate > 0) & (p.estimate < 1)).all())
True
slopes ¶
slopes(result: Any, variables: Any = None, newdata: Optional[DataFrame] = None, *, by: Optional[Union[str, Sequence[str]]] = None, slope: str = 'dydx', type: str = 'response', wts: Any = None, df: Any = None, conf_level: float = 0.95, eps: float = 1e-05, vcov: bool = True) -> DataFrame
Unit-level partial derivatives (marginaleffects::slopes).
dY/dX of the prediction with respect to each numeric variable,
differentiating through interactions and formula transformations;
slope='eyex' / 'eydx' / 'dyex' give elasticities and
semi-elasticities. Factor and 0/1 variables are reported as contrasts,
as marginaleffects does.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
result
|
Any
|
As in :func: |
required |
variables
|
Any
|
As in :func: |
required |
newdata
|
Any
|
As in :func: |
required |
by
|
Any
|
As in :func: |
required |
type
|
Any
|
As in :func: |
required |
wts
|
Any
|
As in :func: |
required |
df
|
Any
|
As in :func: |
required |
conf_level
|
Any
|
As in :func: |
required |
vcov
|
Any
|
As in :func: |
required |
slope
|
('dydx', 'eyex', 'eydx', 'dyex')
|
|
'dydx'
|
eps
|
float
|
Relative step of the central difference used on transformed terms. |
1e-5
|
Returns:
| Type | Description |
|---|---|
DataFrame
|
|
Examples:
>>> import numpy as np, pandas as pd
>>> import statspai as sp
>>> rng = np.random.default_rng(4)
>>> d = pd.DataFrame({"x": rng.normal(size=300)})
>>> d["y"] = 1 + 2 * d.x + 0.5 * d.x**2 + rng.normal(size=300)
>>> fit = sp.regress("y ~ x + I(x**2)", data=d)
>>> s = sp.slopes(fit, "x")
>>> bool(np.allclose(s.estimate, fit.params.iloc[1] + 2 * fit.params.iloc[2] * d.x))
True
contrast ¶
contrast(result: Any, data: DataFrame, variable: str, method: str = 'r', reference: Any = None, alpha: float = 0.05, effect: str = 'difference', subset: Any = None) -> DataFrame
Compute contrasts of predictive margins across levels of a variable.
Equivalent to Stata's margins <var>, contrast(ar) / contrast(r).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
result
|
EconometricResults
|
Fitted model result. |
required |
data
|
DataFrame
|
Estimation data. |
required |
variable
|
str
|
Categorical variable whose levels are contrasted. |
required |
method
|
str
|
Contrast type:
|
'r'
|
reference
|
scalar
|
Reference level when |
None
|
alpha
|
float
|
Significance level. |
0.05
|
effect
|
(difference, ratio, odds_ratio)
|
Scale of the contrast between two predictive margins |
'difference'
|
subset
|
str or boolean array-like
|
Average over a subpopulation of |
None
|
Returns:
| Type | Description |
|---|---|
DataFrame
|
Columns: |
Examples:
>>> import numpy as np
>>> import pandas as pd
>>> import statspai as sp
>>> rng = np.random.default_rng(1)
>>> n = 300
>>> group = rng.integers(0, 3, size=n).astype(float)
>>> df = pd.DataFrame({
... "group": group,
... "experience": rng.normal(10, 3, size=n),
... })
>>> df["wage"] = (10 + 2.0 * df["group"]
... + 0.3 * df["experience"] + rng.normal(size=n))
>>> result = sp.regress("wage ~ group + experience", data=df)
>>> c = sp.contrast(result, data=df, variable="group",
... method="r", reference=0)
>>> c["contrast_label"].tolist()
['1.0 vs 0', '2.0 vs 0']
event_study_table ¶
event_study_table(result: Any, *, regex: Optional[str] = None, label_fmt: str = 't={t}', include_reference: bool = False) -> _MarginsResult
Adapter that turns an event-study fit into a regtable input.
Two extraction paths:
-
CausalResult fast path — when
result.model_infocarries an"event_study"DataFrame (the canonical shape produced by :func:sp.event_study), read itsrelative_time/estimate/se/ci_lower/ci_upper/pvaluecolumns directly. Reference period (estimate==0andse==0) is dropped by default (setinclude_reference=Trueto keep it visible). -
Regex path — when
regexis provided, scanresult.params.indexfor coefficients matching the pattern and use the first capture group as the relative time.
Rows are sorted by relative time (so the rendered table reads
t=-3, t=-2, …, t=+3 regardless of the underlying coefficient
name encoding).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
result
|
CausalResult or EconometricResults
|
Fitted model. The CausalResult fast path is used automatically
when |
required |
regex
|
str
|
Pattern with one capture group that matches the relative time
in coefficient names. Required when the CausalResult fast path
is not applicable. Examples: |
None
|
label_fmt
|
str
|
Format string for the row label of each event-time bin. The
|
``"t={t}"``
|
include_reference
|
bool
|
Whether to render the reference period row (typically
|
False
|
Returns:
| Type | Description |
|---|---|
_MarginsResult
|
Duck-typed result accepted directly by :func: |
Examples:
>>> import numpy as np
>>> import pandas as pd
>>> import statspai as sp
>>> rng = np.random.default_rng(7)
>>> rows = []
>>> for u in range(60):
... tt = rng.choice([3, 4, 5, 100]) # 100 marks never-treated
... for t in range(8):
... post = 1.0 if (tt < 100 and t >= tt) else 0.0
... y = 0.3 * u / 60 + 0.1 * t + 1.5 * post + rng.normal(scale=0.5)
... rows.append({"unit": u, "time": t, "treat_time": tt, "y": y})
>>> panel = pd.DataFrame(rows)
>>> r = sp.event_study(panel, y="y", treat_time="treat_time",
... time="time", unit="unit", window=(-3, 3))
>>> tbl = sp.event_study_table(r)
>>> _ = sp.regtable(tbl, title="Event study", output="text")
margins_at ¶
margins_at(result: Any, data: DataFrame, at: Dict[str, Any], alpha: float = 0.05, contrast: Optional[str] = None, subset: Any = None, predict: Optional[str] = None, category: Any = None, vcov: bool = True) -> DataFrame
Compute predictive margins at specific covariate values.
Equivalent to Stata's margins, at(experience=(1 5 10 15 20)).
For each combination of at values, every observation has the at
variables set to those values while all other covariates stay at their
observed levels. The prediction -- on the model's default scale: the
linear prediction after regress, Pr(y=1) after logit /
probit, the expected count after poisson -- is averaged over the
estimation sample (weighted when the fit was) to give the predictive
margin at that point, with delta-method SEs and t(df) / z inference as
the fit's own.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
result
|
EconometricResults
|
Fitted model result (must have |
required |
data
|
DataFrame
|
Data to compute margins on. |
required |
at
|
dict
|
Mapping of variable names to lists/arrays of values. If multiple variables are given, the Cartesian product of all value lists is used. Example:: produces 6 grid points. Every key must be a model variable. |
required |
alpha
|
float
|
Significance level for confidence intervals. |
0.05
|
contrast
|
(None, first, adjacent)
|
Return differences between grid points instead of the margins:
each point against the first one ( |
None
|
subset
|
str or boolean array-like
|
Average over a subpopulation of |
None
|
predict
|
optional
|
The prediction after a limited-dependent-variable model and the
outcome categories to report; see :func: |
None
|
category
|
optional
|
The prediction after a limited-dependent-variable model and the
outcome categories to report; see :func: |
None
|
vcov
|
bool
|
|
True
|
Returns:
| Type | Description |
|---|---|
DataFrame
|
One row per grid point with columns for each at variable,
plus |
Examples:
>>> import numpy as np
>>> import pandas as pd
>>> import statspai as sp
>>> rng = np.random.default_rng(42)
>>> df = pd.DataFrame({
... "experience": rng.integers(0, 20, size=200).astype(float),
... "female": rng.integers(0, 2, size=200).astype(float),
... })
>>> df["wage"] = (10.0 + 0.5 * df["experience"]
... - 1.0 * df["female"] + rng.normal(size=200))
>>> result = sp.regress(
... "wage ~ experience + female + experience:female", data=df
... )
>>> m = sp.margins_at(result, data=df, at={"experience": [1, 5, 10, 15, 20]})
>>> m.shape[0]
5
>>> m.columns.tolist()
['experience', 'margin', 'se', 'ci_lower', 'ci_upper']
margins_at_plot ¶
margins_at_plot(margins_at_df: DataFrame, x: Optional[str] = None, by: Optional[str] = None, ax: Any = None, figsize: Tuple[float, float] = (8, 5), title: Optional[str] = None, xlabel: Optional[str] = None, ylabel: str = 'Predicted Value', palette: Optional[List[str]] = None) -> Tuple[Any, Any]
Plot predictive margins from margins_at() with confidence bands.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
margins_at_df
|
DataFrame
|
Output from |
required |
x
|
str
|
Variable to place on the x-axis. If None, inferred as the at-variable with the most unique values. |
None
|
by
|
str
|
Variable to produce separate lines for (legend grouping). |
None
|
ax
|
matplotlib Axes
|
|
None
|
figsize
|
tuple
|
|
(8, 5)
|
title
|
str
|
|
None
|
xlabel
|
str
|
|
None
|
ylabel
|
str
|
|
'Predicted Value'
|
palette
|
list of str
|
Colours for each |
None
|
Returns:
| Type | Description |
|---|---|
(fig, ax)
|
|
Examples:
>>> import numpy as np
>>> import pandas as pd
>>> import statspai as sp
>>> rng = np.random.default_rng(42)
>>> df = pd.DataFrame({
... "experience": rng.integers(0, 20, size=200).astype(float),
... "female": rng.integers(0, 2, size=200).astype(float),
... })
>>> df["wage"] = (10.0 + 0.5 * df["experience"]
... - 1.0 * df["female"] + rng.normal(size=200))
>>> res = sp.regress(
... "wage ~ experience + female + experience:female", data=df
... )
>>> m = sp.margins_at(
... res, data=df, at={"experience": [1, 5, 10, 15, 20]}
... )
>>> fig, ax = sp.margins_at_plot(m, x="experience")
>>> fig.savefig("margins_at.png")
margins_table ¶
margins_table(result: Any, data: Optional[DataFrame] = None, variables: Optional[List[str]] = None, at: Optional[Dict[str, Any]] = None, method: str = 'ame', eps: float = 1e-05, alpha: float = 0.05) -> _MarginsResult
Marginal-effects result that pipes straight into sp.regtable.
Thin adapter over :func:margins: computes the marginal effects
(same kwargs), then wraps the resulting DataFrame in a
:class:_MarginsResult exposing params / std_errors /
tvalues / pvalues / conf_int_* so that
sp.regtable(margins_table(model)) produces a publication-quality
marginal-effects table — closing the "estimator → margins table"
gap that previously required users to hand-build add_rows.
Mirrors the R workflow modelsummary(avg_slopes(model)).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
result
|
EconometricResults
|
Fitted model — same input |
required |
data
|
Optional[DataFrame]
|
Forwarded verbatim to :func: |
None
|
variables
|
Optional[DataFrame]
|
Forwarded verbatim to :func: |
None
|
at
|
Optional[DataFrame]
|
Forwarded verbatim to :func: |
None
|
method
|
Optional[DataFrame]
|
Forwarded verbatim to :func: |
None
|
eps
|
Optional[DataFrame]
|
Forwarded verbatim to :func: |
None
|
alpha
|
Optional[DataFrame]
|
Forwarded verbatim to :func: |
None
|
Returns:
| Type | Description |
|---|---|
_MarginsResult
|
A duck-typed result object usable directly as a positional
argument to :func: |
Examples:
>>> import numpy as np
>>> import pandas as pd
>>> import statspai as sp
>>> rng = np.random.default_rng(0)
>>> n = 300
>>> x = rng.normal(size=n)
>>> z = rng.normal(size=n)
>>> p = 1 / (1 + np.exp(-(0.5 * x - 0.4 * z)))
>>> df = pd.DataFrame({
... "y": (rng.uniform(size=n) < p).astype(int),
... "x": x,
... "z": z,
... })
>>> m = sp.logit("y ~ x + z", data=df)
>>> mt = sp.margins_table(m)
>>> _ = sp.regtable(mt, output="text")
>>> sp.regtable(mt, output="latex", filename="margins.tex")
marginsplot ¶
marginsplot(margins_df: DataFrame, ax: Any = None, figsize: Tuple[float, float] = (8, 5), color: str = '#2C3E50', title: Optional[str] = None) -> Tuple[Any, Any]
Plot marginal effects with confidence intervals.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
margins_df
|
DataFrame
|
Output from |
required |
ax
|
matplotlib Axes
|
|
None
|
figsize
|
tuple
|
|
(8, 5)
|
color
|
str
|
|
'#2C3E50'
|
title
|
str
|
|
None
|
Returns:
| Type | Description |
|---|---|
(fig, ax)
|
|
Examples:
pwcompare ¶
pwcompare(result: Any, data: DataFrame, variable: str, adjust: str = 'none', alpha: float = 0.05) -> DataFrame
Pairwise comparisons of predictive margins across all levels.
Equivalent to Stata's pwcompare <var>.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
result
|
EconometricResults
|
Fitted model result. |
required |
data
|
DataFrame
|
Estimation data. |
required |
variable
|
str
|
Categorical variable whose levels are compared pairwise. |
required |
adjust
|
str
|
P-value adjustment method:
|
'none'
|
alpha
|
float
|
Significance level for (adjusted) confidence intervals. |
0.05
|
Returns:
| Type | Description |
|---|---|
DataFrame
|
Columns: |
Examples:
>>> import numpy as np
>>> import pandas as pd
>>> import statspai as sp
>>> rng = np.random.default_rng(1)
>>> n = 300
>>> group = rng.integers(0, 3, size=n).astype(float)
>>> df = pd.DataFrame({
... "group": group,
... "experience": rng.normal(10, 3, size=n),
... })
>>> df["wage"] = (10 + 2.0 * df["group"]
... + 0.3 * df["experience"] + rng.normal(size=n))
>>> result = sp.regress("wage ~ group + experience", data=df)
>>> pw = sp.pwcompare(result, data=df, variable="group",
... adjust="bonferroni")
>>> bool((pw["pvalue_adj"] >= pw["pvalue"]).all())
True