Anomaly Detection with scomp-link


The Wheel of Time meets Data Science — March 2026

Sensing the Pattern: Anomaly Detection with scomp-link

"The Wheel weaves as the Wheel wills — but we must detect the outliers before the Dark One breaks free."

March 2026 · Giacomo Saccaggi


Prologue: The Wheel, The Pattern, and the Anomalies Within

In Robert Jordan's The Wheel of Time, the Pattern is the great tapestry woven by the Wheel from the threads of human lives. Each thread follows a predictable path — the normal distribution of existence. But some threads are different.

Ta'veren — like Rand al'Thor, Mat Cauthon, and Perrin Aybara — are statistical anomalies so powerful they bend probability itself around them. Coins always land on edge. Armies appear at impossible moments. The Pattern warps in their vicinity.

And then there are the Bubbles of Evil — malicious corruptions seeping from the Dark One's prison, distorting reality in dangerous ways. These are the fraudulent transactions, the sensor failures, the corrupted data that threaten to unravel your model's integrity.

As an Aes Sedai of Data, your task is clear: weave the One Power — in the form of Python algorithms — to sense these disturbances in the Pattern and isolate them before they spread.


Part I: Tabular Anomaly Detection — The AnomalyDetector

The Four Weaves of Detection

No single weave of the One Power can detect all forms of corruption. The AnomalyDetector from scomp-link combines four complementary methods into a consensus system — like the five flows of the Power (Earth, Air, Fire, Water, Spirit) combining into a single devastating weave:

MethodApproach (The Weave)Best For
Isolation ForestRandomly isolates points via recursive partitioning — anomalies are isolated fasterGlobal outliers, high-dimensional data
Local Outlier FactorMeasures local density deviation against neighborsLocal anomalies in dense regions
TabNet AutoencoderLearns feature reconstruction with attention — high error = anomalyComplex feature interactions
Transformer AutoencoderSelf-attention across features as tokens — captures inter-feature relationshipsNon-linear feature dependencies

Python: Channeling the One Power

from scomp_link.models.anomaly_detector import AnomalyDetector
import pandas as pd
import numpy as np

# --- The Pattern: our dataset of transactions ---
np.random.seed(42)
n = 1000
the_pattern = pd.DataFrame({
    'amount': np.random.lognormal(4, 0.5, n),
    'hour': np.random.normal(14, 4, n).clip(0, 23),
    'velocity': np.random.exponential(2, n),
    'device_risk': np.random.choice([0, 1, 2], n, p=[0.7, 0.2, 0.1])
})

# --- Inject Bubbles of Evil (fraudulent transactions) ---
bubbles_of_evil = np.random.choice(n, 50, replace=False)
the_pattern.loc[bubbles_of_evil, 'amount'] = np.random.uniform(5000, 20000, 50)
the_pattern.loc[bubbles_of_evil, 'hour'] = np.random.uniform(1, 5, 50)
the_pattern.loc[bubbles_of_evil, 'velocity'] = np.random.uniform(15, 30, 50)

# --- The Weave: consensus-based detection ---
detector = AnomalyDetector(
    contamination=0.05,
    methods=['iforest', 'lof', 'tabnet', 'transformer'],
    consensus_threshold=2,
    verbose=True
)

results = detector.fit_predict(
    the_pattern, 
    features=['amount', 'hour', 'velocity', 'device_risk']
)

# --- Reading the Pattern ---
print(results['comparison'])
#        method  n_anomalies   pct
# 0     iforest           48  4.8
# 1         lof           52  5.2
# 2      tabnet           45  4.5
# 3 transformer           47  4.7
# 4 consensus(≥2)         35  3.5

anomalies = results['data'][results['data']['is_anomaly']]
print(f"Ta'veren detected: {len(anomalies)} threads bent the Pattern")
# Output:
# [AnomalyDetector] Running Isolation Forest...
# [AnomalyDetector] Running Local Outlier Factor...
# [AnomalyDetector] Running TabNet Autoencoder...
# [AnomalyDetector] Running Transformer Autoencoder...
# [AnomalyDetector] Computing consensus (threshold=2)...
#
#        method  n_anomalies   pct
# 0     iforest           48  4.8
# 1         lof           52  5.2
# 2      tabnet           45  4.5
# 3 transformer           47  4.7
# 4 consensus(≥2)         35  3.5
#
# Ta'veren detected: 35 threads bent the Pattern

Results: The Pattern Revealed


Part II: Time Series Anomaly Detection — The TimeSeriesAnomalyDetector

Sensing Bubbles of Evil Through Time

In the Pattern, Bubbles of Evil manifest as temporal distortions — moments where reality itself fractures. The TimeSeriesAnomalyDetector trains on a clean historical baseline (the Age of Legends, if you will) and then detects corruptions in new data.

Python: The Foretelling Weave

from scomp_link.models.ts_anomaly_detector import TimeSeriesAnomalyDetector
import numpy as np

# --- The Age of Legends: clean baseline signal ---
np.random.seed(42)
t_train = np.arange(1000)
clean_pattern = 50 + 0.01*t_train + 5*np.sin(2*np.pi*t_train/50) + np.random.normal(0, 1, 1000)

# --- The Third Age: new data with Bubbles of Evil ---
t_test = np.arange(500)
corrupted_age = 60 + 0.01*t_test + 5*np.sin(2*np.pi*t_test/50) + np.random.normal(0, 1, 500)

# Inject bubbles of evil (anomalous spikes)
bubble_indices = [120, 121, 250, 251, 252, 380, 381]
corrupted_age[bubble_indices] += np.random.uniform(12, 20, len(bubble_indices))

# --- The Weave of Foretelling ---
detector = TimeSeriesAnomalyDetector(
    methods=['autoencoder', 'moving_avg', 'moving_median', 'arima'],
    time_steps=50,
    window_size=20,
    n_sigma=3.0,
    ae_epochs=30,
    threshold_percentile=95.0,
    verbose=True
)

# Train on the clean Pattern
detector.fit(clean_pattern)

# Detect Bubbles of Evil in the Third Age
results = detector.detect(corrupted_age)

# The Foretelling speaks
print(f"Bubbles of Evil detected: {results['anomalies'].sum()} / {len(corrupted_age)} points")
for method, flags in results['methods'].items():
    print(f"  {method}: {flags.sum()} anomalies sensed")

# Consensus: how many Aes Sedai agree on each disturbance
print(f"\nMax consensus score: {results['consensus_score'].max()}")
# Output:
# [TimeSeriesAnomalyDetector] Training Conv1D Autoencoder...
# [TimeSeriesAnomalyDetector] Fitting ARIMA(5,1,0)...
# [TimeSeriesAnomalyDetector] Detecting with autoencoder...
# [TimeSeriesAnomalyDetector] Detecting with moving_avg...
# [TimeSeriesAnomalyDetector] Detecting with moving_median...
# [TimeSeriesAnomalyDetector] Detecting with arima...
#
# Bubbles of Evil detected: 7 / 500 points
#   autoencoder: 9 anomalies sensed
#   moving_avg: 7 anomalies sensed
#   moving_median: 8 anomalies sensed
#   arima: 6 anomalies sensed
#
# Max consensus score: 4

Results: The Foretelling


Choosing Your Weave: Tabular vs Time Series

AnomalyDetectorTimeSeriesAnomalyDetector
Data typeTabular (rows × features)Univariate time series
TrainingSame dataset (unsupervised)Separate clean baseline
Key parameterconsensus_thresholdn_sigma, time_steps
OutputBoolean column + comparison tableBoolean array + consensus score
Dependenciespytorch-tabnet, torchtensorflow, statsmodels

Epilogue: The Wheel Weaves

"The Wheel of Time turns, and Ages come and pass, leaving memories that become legend. Legend fades to myth, and even myth is long forgotten when the Age that gave it birth comes again."

In data science, as in the Wheel, the Pattern repeats — but anomalies are the key to understanding when something has truly changed. Whether you face Ta'veren (legitimate outliers that reveal new opportunities) or Bubbles of Evil (fraud, failures, corruption), the scomp-link consensus approach ensures you detect them with confidence.

Install the package and begin channeling:

pip install scomp-link

May the Light illuminate your anomalies.