root_cause_correlation
root_cause_correlation ¤
Event-driven root-cause ranking: which signal led the events we detected?
:class:~ts_shape.events.correlation.anomaly_correlation.AnomalyCorrelationEvents
works signal-to-signal: it z-scores every signal, finds anomalies, and asks
which signal's anomalies tend to come first. That is undirected discovery, and
it can only rank signals whose own anomalies were detectable.
This detector runs the directed version. It takes the events another detector already produced -- an SPC violation, a downtime interval, a scrap booking -- and asks, for each candidate signal, whether that signal moves before those events, and by how much lead time. Only non-negative lags are tested: a cause precedes its effect, and searching negative lags would happily "explain" a stoppage with the temperature drop it caused.
Correlation is computed against a binary onset series on a resampled grid. Scanning many lags and keeping the strongest is a multiple comparison, so the p-value of the winning lag is Bonferroni-corrected by the number of lags tested: without that, noise scanned over 30 lags looks "significant" about as often as not, and every candidate would be promoted to root cause.
RootCauseCorrelationEvents ¤
RootCauseCorrelationEvents(
dataframe: DataFrame,
*,
event_uuid: str = "corr:root_cause",
value_column: str = "value_double",
time_column: str = "systime",
uuid_column: str = "uuid"
)
Bases: Base
Correlation: rank candidate causes for a set of detected events.
trigger_events is any detector's output — the onset timestamp is read
from systime (point events) or start (interval/summary events)::
stops = LongDowntimeEvents(df, ...).detect_long_downtime(min_gap="10min")
rc = RootCauseCorrelationEvents(df)
rc.rank_causes(stops, ["temp:spindle", "press:hydraulic", "flow:coolant"])
Methods: - rank_causes: Ranked causal candidates with lag and p-value. - top_cause: Single-row verdict naming the best-supported candidate. - lag_profile: Correlation-vs-lag curve for one candidate signal.
Initialize the root-cause ranker.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
dataframe
|
DataFrame
|
Timeseries frame holding the candidate signals. |
required |
event_uuid
|
str
|
UUID stamped on emitted rows. |
'corr:root_cause'
|
value_column
|
str
|
Column holding the numeric readings. |
'value_double'
|
time_column
|
str
|
Timestamp column. |
'systime'
|
uuid_column
|
str
|
Column identifying the signal. |
'uuid'
|
rank_causes ¤
rank_causes(
trigger_events: DataFrame,
candidate_uuids: list[str],
*,
window: str = "1min",
max_lag: int = 30,
p_threshold: float = 0.05,
trigger_uuid: str | None = None,
trigger_event_type: str | None = None,
only_significant: bool = False
) -> pd.DataFrame
Rank candidate signals by how strongly they lead the trigger events.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
trigger_events
|
DataFrame
|
Event frame whose onsets are being explained. |
required |
candidate_uuids
|
list[str]
|
Signals to test as possible causes. |
required |
window
|
str
|
Grid resolution (offset string); also the unit of |
'1min'
|
max_lag
|
int
|
Largest lead tested, in |
30
|
p_threshold
|
float
|
Bonferroni-corrected p-value at or below which a
candidate is marked |
0.05
|
trigger_uuid
|
str | None
|
Identifier of the triggering signal, for the audit
trail. Defaults to the |
None
|
trigger_event_type
|
str | None
|
Label of the triggering event. Defaults to the
|
None
|
only_significant
|
bool
|
Drop candidates above |
False
|
Returns:
| Type | Description |
|---|---|
DataFrame
|
Static-shape DataFrame, ranked by absolute correlation: |
DataFrame
|
|
DataFrame
|
|
DataFrame
|
|
DataFrame
|
(Bonferroni over the lag scan), |
DataFrame
|
|
top_cause ¤
top_cause(
trigger_events: DataFrame,
candidate_uuids: list[str],
*,
window: str = "1min",
max_lag: int = 30,
p_threshold: float = 0.05,
trigger_uuid: str | None = None,
trigger_event_type: str | None = None
) -> pd.DataFrame
Name the best-supported cause, or report that none qualifies.
Only significant candidates are eligible: when no candidate clears
p_threshold the row still comes back — with top_cause_uuid set
to None and the tested counts filled in — so a search that found
nothing is visible rather than silently empty.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
trigger_events
|
DataFrame
|
Event frame whose onsets are being explained. |
required |
candidate_uuids
|
list[str]
|
Signals to test as possible causes. |
required |
window
|
str
|
Grid resolution (offset string); also the unit of |
'1min'
|
max_lag
|
int
|
Largest lead tested, in |
30
|
p_threshold
|
float
|
Significance level for eligibility. |
0.05
|
trigger_uuid
|
str | None
|
See :meth: |
None
|
trigger_event_type
|
str | None
|
See :meth: |
None
|
Returns:
| Type | Description |
|---|---|
DataFrame
|
Static-shape DataFrame with a single row: |
DataFrame
|
|
DataFrame
|
|
DataFrame
|
|
DataFrame
|
|
lag_profile ¤
lag_profile(
trigger_events: DataFrame,
candidate_uuid: str,
*,
window: str = "1min",
max_lag: int = 30
) -> pd.DataFrame
Correlation against the trigger onsets at every tested lag.
The curve is the diagnostic behind :meth:rank_causes: a genuine
cause peaks at one lead time and falls away, while a spurious match
tends to wander without a clear peak. p_value here is the raw
per-lag value — the whole point of the profile is to see every lag, so
no multiple-comparison correction is applied; use
:meth:rank_causes for the corrected verdict.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
trigger_events
|
DataFrame
|
Event frame whose onsets are being explained. |
required |
candidate_uuid
|
str
|
The signal to profile. |
required |
window
|
str
|
Grid resolution (offset string); also the unit of |
'1min'
|
max_lag
|
int
|
Largest lead tested, in |
30
|
Returns:
| Type | Description |
|---|---|
DataFrame
|
Static-shape DataFrame, one row per lag: |
DataFrame
|
|
DataFrame
|
|