3. Graph construction from time-series sensor data

In industrial fault localization, the graph is not a given — it must be inferred from the data and the plant itself. A chemical process unit, a wind turbine drivetrain, or a power distribution feeder rarely ships with a clean adjacency matrix. Instead, we have hundreds of time-series tags sampled at 1 Hz to 10 kHz, plus a P&ID, a wiring diagram, and maintenance logs. The quality of the resulting graph determines whether a GNN can localize a fault to a specific bearing, valve, or busbar, or whether it merely reports "something is wrong somewhere."

This section covers three complementary construction strategies — correlation-based, causality-based, and physical-connectivity-based — and how to combine them into a single weighted adjacency matrix suitable for message passing.

3.1 Correlation-Based Edges: Fast but Directionless

The simplest edge criterion is statistical dependence. Given N sensor channels with T samples, compute the pairwise Pearson correlation matrix R ∈ ℝN×N. An edge exists between nodes i and j if |Rij| exceeds a threshold τ. For non-linear relationships, replace Pearson with Spearman rank correlation or mutual information.

A practical pitfall: raw correlation on non-stationary industrial signals is dominated by common trends (e.g., ambient temperature, load level). Always detrend and normalize first. A common recipe is to difference the signal, apply a rolling z-score with a 10-minute window, then compute correlation on residuals.

import numpy as np
from scipy.stats import zscore

def correlation_adjacency(X, tau=0.6, window=600):
    # X: (T, N) raw sensor matrix
    Xd = np.diff(X, axis=0)                      # detrend by differencing
    Xz = zscore(Xd, axis=0)                      # per-channel normalization
    R = np.corrcoef(Xz.T)                        # (N, N)
    A = (np.abs(R) >= tau).astype(float)
    np.fill_diagonal(A, 0)
    return A, R

Correlation edges are symmetric and undirected. They capture co-movement but not influence: a downstream pressure sensor and an upstream flow controller may correlate strongly, yet only one drives the other. For fault localization this matters, because propagation direction determines which node is the root cause.

3.2 Causality-Based Edges: Direction and Lag

To recover direction, use Granger causality or transfer entropy. Granger causality tests whether past values of Xj improve the prediction of Xi beyond Xi's own history. For each ordered pair (i, j), fit two autoregressions and compare residual variances via an F-test:

from statsmodels.tsa.stattools import grangercausalitytests

def granger_edges(X, max_lag=5, alpha=0.01):
    N = X.shape[1]
    A = np.zeros((N, N))
    for i in range(N):
        for j in range(N):
            if i == j: continue
            data = np.column_stack([X[:, i], X[:, j]])
            res = grangercausalitytests(data, maxlag=max_lag, verbose=False)
            p = min(res[l][0]['ssr_ftest'][1] for l in range(1, max_lag+1))
            if p < alpha:
                A[j, i] = 1.0   # j Granger-causes i
    return A

Note the index convention: A[j, i] = 1 means information flows from j to i. This asymmetry is exactly what we want for fault propagation. In a real compressor skid, for example, vibration at the drive-end bearing typically Granger-causes discharge temperature with a lag of 2–4 samples, while the reverse is not significant.

Watch out: Granger causality assumes stationarity and linearity. On strongly non-linear loops (e.g., a control valve near saturation), it can produce spurious edges. Cross-check with transfer entropy or with known control-loop topology before trusting the result.

3.3 Physical Connectivity: The Prior You Already Have

Piping and instrumentation diagrams, electrical single-line diagrams, and network topology files encode hard constraints. Two sensors on the same header are physically coupled; two sensors on isolated branches are not. This prior is invaluable because it is static, reliable, and sparse.

Represent physical connectivity as a binary matrix Aphys where Aphysij = 1 if a pipe, wire, or shaft directly connects the components monitored by sensors i and j. In practice, this is extracted from a tag-to-equipment mapping table and an equipment-to-equipment connection list.

3.4 Fusion: Building the Final Adjacency Matrix

Each source has different reliability and semantics. A robust approach is a weighted sum with a physical mask:

A = Aphys ⊙ (α·Acorr + β·Acause + γ·I)

where ⊙ is element-wise multiplication, I is the identity (self-loops), and α, β, γ are tunable weights. The physical mask ensures that a spurious correlation between two hydraulically isolated lines never becomes an edge. Typical starting values: α = 0.4, β = 0.5, γ = 0.1. These are example defaults; tune them on a validation set of labeled faults.

After fusion, symmetrize if the GNN uses undirected message passing, or keep the directed version for directed GNN variants. Normalize rows to sum to 1 (or use symmetric normalization D−1/2AD−1/2) to stabilize training.

Three Edge Sources → Fused Adjacency Matrix Correlation Pearson / Spearman / MI |R_ij| ≥ τ Symmetric, undirected Fast, but no direction Causality Granger / Transfer Entropy p-value < α Directed, lag-aware Captures propagation Physical Connectivity P&ID / wiring / topology Binary mask A_phys Static, reliable, sparse Hard constraint Weighted Fusion A = A_phys ⊙ (α·A_corr + β·A_cause + γ·I) Final Adjacency Matrix A Row-normalized → GNN input Example: bearing vibration → discharge temp (directed, lag 3) ; both on same shaft (physical) V1 T1 cause

3.5 A Worked Example: Compressor Skid

Consider a three-stage reciprocating compressor with 12 tags: suction pressure (P1), inter-stage pressures (P2, P3), discharge pressure (P4), four bearing vibrations (V1–V4), motor current (I1), lube-oil temperature (T1), and ambient temperature (T2). Sampling at 10 Hz for 8 hours yields 288,000 samples per channel.

Step 1 — Preprocess. Remove startup/shutdown transients, difference each channel, and z-score with a 5-minute rolling window.

Step 2 — Correlation. With τ = 0.6, we obtain edges such as V1–V2 (r = 0.82), P2–P3 (r = 0.74), and I1–P4 (r = 0.68). Ambient T2 correlates with everything weakly (r ≈ 0.3) and is excluded.

Step 3 — Causality. Granger tests with max_lag = 5 and α = 0.01 reveal directed edges: V1 → T1 (p = 0.003), V1 → P4 (p = 0.007), I1 → P4 (p = 0.001). The reverse directions are non-significant.

Step 4 — Physical mask. The P&ID shows V1 and V2 on the same crankshaft (edge kept), V3 on a separate shaft (edge removed even though correlation was 0.55), and T1 in the oil circuit connected to all bearings (edges kept).

Step 5 — Fuse and normalize. Apply α = 0.4, β = 0.5, γ = 0.1, multiply by the physical mask, then row-normalize. The resulting 12×12 matrix has 23 non-zero off-diagonal entries — sparse enough for efficient message passing.

Validation tip: Inject a known fault (e.g., loosen the V1 accelerometer mount) and verify that the GNN's attention weights concentrate on the V1 → T1 and V1 → P4 edges. If attention spreads uniformly, the graph is too dense or the weights are mis-tuned.

3.6 Practical Checklist

StageActionTypical ParameterFailure Symptom
PreprocessDetrend, z-score, remove transientsWindow = 300–600 sSpurious edges from common trends
CorrelationCompute |R|, thresholdτ = 0.5–0.7Too dense (τ too low) or disconnected (τ too high)
CausalityGranger F-test per ordered pairmax_lag = 3–10, α = 0.01Spurious edges on non-linear loops
Physical maskMap tags to equipment, build binary matrix—Missing edges if tag mapping is incomplete
FusionWeighted sum, mask, normalizeα = 0.4, β = 0.5, γ = 0.1Over-smoothing if weights too uniform

The graph you build here becomes the fixed input to every subsequent GNN layer. Getting the edge semantics right — direction, weight, and physical plausibility — is often more impactful than swapping one GNN architecture for another. In the next section, we will see how these graphs feed into spectral and spatial convolution operators.