mixle.inference.event_study module

Confirmed-exposure influence measurement: a hierarchical within-subject event study.

Many SUBJECTS are each observed BEFORE and AFTER a known event time – an exposure with a confirmed timestamp (on social data, a retweet: the act proves the content was seen, and dates it). We estimate whether, and how much, the event shifts each subject’s generative activity, then pool those shifts into a population effect with calibrated uncertainty.

A treated / control split turns the pooled shift into a difference-in-differences: the effect is (treated shift) - (control shift), so anything that moves everyone at the event time (a concurrent external shock) cancels, and only the differential – the influence attributable to the treatment – survives. The natural control is exposed non-actors: subjects the same content reached who did not act.

Two stages, exact/closed-form where the family permits:

  1. per-subject effect – from the activity family’s sufficient statistics on the pre and post windows: a Gaussian mean-shift (gaussian_effect) or a Poisson log-rate shift for event counts (poisson_lograte_effect), each with its sampling variance.

  2. hierarchical pooling – a random-effects (DerSimonian-Laird) meta-analysis over the per-subject effects: a precision-weighted population mean plus between-subject heterogeneity tau^2, computed per group, with the DiD contrast and its propagated variance and an empirical-Bayes shrinkage of each subject’s effect toward its group.

Identification, stated rather than assumed away. Within-subject differencing removes every TIME-INVARIANT subject trait – the homophily / selection-into-ties confound (Shalizi & Thomas 2011) is exactly such a trait, so unit differencing annihilates it. The treated-vs-control contrast removes shocks common to both groups. The residual threat is time-VARYING selection into the event (whatever made the subject act then); it is mitigated – not eliminated – by a matched exposed-non-actor control, and tipping_drift() reports how large an unmeasured differential drift would have to be to explain the effect away (a transparent sensitivity bound, not a guarantee).

gaussian_effect(pre, post)[source]

Per-subject mean shift and its (Welch) sampling variance from pre/post activity samples.

Parameters:
Return type:

tuple[float, float]

poisson_lograte_effect(k_pre, t_pre, k_post, t_post)[source]

Per-subject log activity-rate shift log(rate_post) - log(rate_pre) for event counts over windows.

k_* are event counts, t_* the window durations (or exposures). Uses a Haldane 0.5 correction so zero-count windows are finite; variance is the delta-method log-rate variance 1/k_post + 1/k_pre.

Parameters:
Return type:

tuple[float, float]

class EventStudyResult(effect, se, z, p_value, ci, treated_mean, treated_se, control_mean, control_se, tau2_treated, n_treated, n_control, shrunk_treated)[source]

Bases: object

Pooled influence estimate. effect is the DiD ATT (treated minus control) when a control exists.

Parameters:
effect: float
se: float
z: float
p_value: float
ci: tuple[float, float]
treated_mean: float
treated_se: float
control_mean: float | None
control_se: float | None
tau2_treated: float
n_treated: int
n_control: int
shrunk_treated: ndarray
hierarchical_event_study(treated_effects, treated_vars, control_effects=None, control_vars=None, *, alpha=0.05)[source]

Pool per-subject effects into a population influence estimate (DiD if a control group is given).

*_effects / *_vars are the per-subject shifts and their variances from stage 1. With a control group the reported effect is treated_mean - control_mean – the difference-in-differences ATT.

Parameters:
Return type:

EventStudyResult

tipping_drift(result)[source]

Sensitivity bound: the unmeasured differential drift that would explain the effect away.

Within-subject DiD is unbiased only if, absent treatment, treated and control would have drifted equally. This returns the differential drift delta (in effect units) that nullifies the estimate (= effect) and the value that pushes the 95% CI through zero – so a reader can judge whether a confound that large is plausible. Larger = more robust.

Parameters:

result (EventStudyResult)

Return type:

dict