data-anomaly-detector
Detect anomalies and outliers in construction data: unusual costs, schedule variances, productivity spikes. Statistical and ML-based detection methods.
Install / Use
npx skills add datadrivenconstruction/DDC_Skills_for_AI_Agents_in_Construction --skill data-anomaly-detectorInstalls into whichever agent you are using.
SKILL.md
Installable skill definition
Quality Score
Category
Development & EngineeringSupported Platforms
Our assessment of data-anomaly-detector
data-anomaly-detector scores 89/100 on our quality scale, 1789th of 4,663 Development & Engineering skills we index (top 39%).
Its SKILL.md is 19 KB long, well organised into 12 sections with 3 code examples: a thorough specification that gives an agent plenty to work with.
It has 333 GitHub stars, a meaningful sign that others use it.
Maintenance, license and trust
- The repository was last updated about 2 months ago, so data-anomaly-detector is actively maintained.
- It is released under the MIT license, a permissive license that allows use, modification and commercial use with attribution.
- Its trust signals score 100/100, with no cautions. These come from repository metadata, not a code audit — read the skill file before letting an agent act on it.
data-anomaly-detector compared with similar skills
All 4 of these similar skills score higher than data-anomaly-detector; compare them before choosing.
| Skill | Score | Stars | Updated | Format |
|---|---|---|---|---|
| data-anomaly-detector (this skill)by datadrivenconstruction | 89 | 333 | 2mo ago | SKILL.md |
| Agent-Reachby Panniantong | 100 | 106.4k | 5d ago | CLAUDE.md |
| taste-skillby Leonxlnx | 100 | 105.7k | today | CLAUDE.md |
| headroomby headroomlabs-ai | 100 | 78.3k | today | CLAUDE.md |
| ai-job-searchby ai-job-search-dev | 100 | 67.6k | 2d ago | CLAUDE.md |
Frequently asked questions
- How do I install data-anomaly-detector?
- Run
npx skills add datadrivenconstruction/DDC_Skills_for_AI_Agents_in_Construction --skill data-anomaly-detector. The install tabs above show the steps for each supported agent. - Which AI agents does data-anomaly-detector work with?
- It is written for Universal, as a SKILL.md file. Other agents that read the same format can often use it too.
- Is data-anomaly-detector safe to use?
- It is MIT-licensed and scores 100/100 on trust signals. Skills are instructions an agent will follow, so read the file before installing it and do not approve commands you do not understand.
- Is data-anomaly-detector still maintained?
- The repository was last updated about 2 months ago, so data-anomaly-detector is actively maintained.
Skill content
View source on GitHubname: "data-anomaly-detector" description: "Detect anomalies and outliers in construction data: unusual costs, schedule variances, productivity spikes. Statistical and ML-based detection methods." homepage: "https://datadrivenconstruction.io" metadata: {"openclaw": {"emoji": "✔️", "os": ["darwin", "linux", "win32"], "homepage": "https://datadrivenconstruction.io", "requires": {"bins": ["python3"]}}}
Data Anomaly Detector for Construction
Overview
Detect unusual patterns, outliers, and anomalies in construction data. Identify cost overruns, schedule delays, productivity issues, and data quality problems before they impact projects.
Business Case
Construction data often contains anomalies that indicate:
- Cost estimate errors or fraud
- Schedule logic issues
- Productivity problems
- Data entry mistakes
- Equipment or material issues
Early detection prevents costly corrections and project delays.
Technical Implementation
from dataclasses import dataclass, field
from typing import List, Dict, Any, Optional, Tuple
from enum import Enum
import pandas as pd
import numpy as np
from datetime import datetime
from scipy import stats
class AnomalyType(Enum):
OUTLIER = "outlier"
PATTERN_BREAK = "pattern_break"
MISSING_SEQUENCE = "missing_sequence"
DUPLICATE = "duplicate"
IMPOSSIBLE_VALUE = "impossible_value"
TREND_DEVIATION = "trend_deviation"
class AnomalySeverity(Enum):
CRITICAL = "critical"
HIGH = "high"
MEDIUM = "medium"
LOW = "low"
@dataclass
class Anomaly:
id: str
anomaly_type: AnomalyType
severity: AnomalySeverity
field: str
value: Any
expected_range: Optional[Tuple[float, float]] = None
description: str = ""
row_index: Optional[int] = None
detection_method: str = ""
confidence: float = 0.0
suggested_action: str = ""
@dataclass
class AnomalyReport:
source: str
detected_at: datetime
total_records: int
anomalies: List[Anomaly]
summary: Dict[str, int]
class ConstructionAnomalyDetector:
"""Detect anomalies in construction data."""
# Construction-specific thresholds
COST_THRESHOLDS = {
'concrete_per_cy': (200, 800),
'steel_per_ton': (1500, 4000),
'labor_per_hour': (25, 150),
'overhead_percentage': (5, 25),
'contingency_percentage': (3, 20),
}
SCHEDULE_THRESHOLDS = {
'max_activity_duration': 365, # days
'max_lag': 30, # days
'min_productivity': 0.1,
'max_productivity': 10.0,
}
def __init__(self):
self.anomalies: List[Anomaly] = []
self.detection_history: List[AnomalyReport] = []
def detect_cost_anomalies(self, df: pd.DataFrame, cost_column: str,
group_by: str = None) -> List[Anomaly]:
"""Detect anomalies in cost data."""
anomalies = []
# Statistical outlier detection (IQR method)
Q1 = df[cost_column].quantile(0.25)
Q3 = df[cost_column].quantile(0.75)
IQR = Q3 - Q1
lower_bound = Q1 - 1.5 * IQR
upper_bound = Q3 + 1.5 * IQR
outliers = df[(df[cost_column] < lower_bound) | (df[cost_column] > upper_bound)]
for idx, row in outliers.iterrows():
value = row[cost_column]
severity = AnomalySeverity.HIGH if abs(value - df[cost_column].median()) > 3 * IQR else AnomalySeverity.MEDIUM
anomalies.append(Anomaly(
id=f"COST-{idx}",
anomaly_type=AnomalyType.OUTLIER,
severity=severity,
field=cost_column,
value=value,
expected_range=(lower_bound, upper_bound),
description=f"Cost value {value:,.2f} outside expected range",
row_index=idx,
detection_method="IQR",
confidence=0.95,
suggested_action="Review cost estimate for errors"
))
# Negative cost check
negatives = df[df[cost_column] < 0]
for idx, row in negatives.iterrows():
anomalies.append(Anomaly(
id=f"COST-NEG-{idx}",
anomaly_type=AnomalyType.IMPOSSIBLE_VALUE,
severity=AnomalySeverity.CRITICAL,
field=cost_column,
value=row[cost_column],
expected_range=(0, None),
description="Negative cost value detected",
row_index=idx,
detection_method="Business Rule",
confidence=1.0,
suggested_action="Correct data entry error or investigate credit"
))
# Group-based anomalies (if grouped)
if group_by and group_by in df.columns:
group_stats = df.groupby(group_by)[cost_column].agg(['mean', 'std'])
for group_name, stats in group_stats.iterrows():
group_data = df[df[group_by] == group_name]
z_scores = np.abs((group_data[cost_column] - stats['mean']) / stats['std'])
for idx, z in z_scores.items():
if z > 3:
anomalies.append(Anomaly(
id=f"COST-GROUP-{idx}",
anomaly_type=AnomalyType.OUTLIER,
severity=AnomalySeverity.MEDIUM,
field=cost_column,
value=df.loc[idx, cost_column],
description=f"Unusual cost for group {group_name} (z-score: {z:.2f})",
row_index=idx,
detection_method="Z-Score by Group",
confidence=min(z / 5, 1.0)
))
return anomalies
def detect_schedule_anomalies(self, df: pd.DataFrame) -> List[Anomaly]:
"""Detect anomalies in schedule data."""
anomalies = []
# Check for required columns
required = ['start_date', 'end_date']
if not all(col in df.columns for col in required):
return anomalies
# Convert dates
df['start_date'] = pd.to_datetime(df['start_date'])
df['end_date'] = pd.to_datetime(df['end_date'])
# Calculate duration
df['duration'] = (df['end_date'] - df['start_date']).dt.days
# Negative duration (end before start)
negative_duration = df[df['duration'] < 0]
for idx, row in negative_duration.iterrows():
anomalies.append(Anomaly(
id=f"SCHED-NEG-{idx}",
anomaly_type=AnomalyType.IMPOSSIBLE_VALUE,
severity=AnomalySeverity.CRITICAL,
field="duration",
value=row['duration'],
description="End date before start date",
row_index=idx,
detection_method="Business Rule",
confidence=1.0,
suggested_action="Correct dates"
))
# Extremely long durations
long_tasks = df[df['duration'] > self.SCHEDULE_THRESHOLDS['max_activity_duration']]
for idx, row in long_tasks.iterrows():
anomalies.append(Anomaly(
id=f"SCHED-LONG-{idx}",
anomaly_type=AnomalyType.OUTLIER,
severity=AnomalySeverity.MEDIUM,
field="duration",
value=row['duration'],
expected_range=(0, self.SCHEDULE_THRESHOLDS['max_activity_duration']),
description=f"Task duration {row['duration']} days exceeds threshold",
row_index=idx,
detection_method="Threshold",
confidence=0.9,
suggested_action="Review if task should be broken down"
))
# Zero duration non-milestones
if 'is_milestone' in df.columns:
zero_duration = df[(df['duration'] == 0) & (~df['is_milestone'])]
for idx, row in zero_duration.iterrows():
anomalies.append(Anomaly(
id=f"SCHED-ZERO-{idx}",
anomaly_type=AnomalyType.IMPOSSIBLE_VALUE,
severity=AnomalySeverity.HIGH,
field="duration",
value=0,
description="Zero duration task that is not a milestone",
row_index=idx,
detection_method="Business Rule",
confidence=1.0,
suggested_action="Add duration or mark as milestone"
))
return anomalies
def detect_productivity_anomalies(self, df: pd.DataFrame,
quantity_col: str,
hours_col: str) -> List[Anomaly]:
"""Detect productivity anomalies."""
anomalies = []
# Calculate productivity
df['productivity'] = df[quantity_col] / df[hours_col].replace(0, np.nan)
# Use Modified Z-Score (more robust for skewed data)
median = df['productivity'].median()
mad = np.abs(df['productivity'] - median).median()
modified_z = 0.6745 * (df['productivity'] - median) / mad
outliers = df[np.abs(modified_z) > 3.5]
for idx, row in outliers.iterrows():
prod = row['productivity']
z = modified_z.loc[idx]
severity = AnomalySeverity.HIGH if abs(z) > 5 else AnomalySeverity.MEDIUM
direction = "high" if z > 0 else "low"
anomalies.append(Anomaly(
id=f"PROD-{idx}",
anomaly_type=AnomalyType.OUTLIER,
severity=severity,
field="productivity",
value=prod,
description=f"Unusually {direction} productivity: {prod:.2f} units/hour",
row_index=idx,
detection_method="Modified Z-Score",
confidence=min(abs(z) / 7, 1.0),
suggested_action=f"Investigate {direction} productivity cause"
))
return anomalies
def detect_time_series_anomalies(self, df: pd.DataFrame,
date_col: str,
value_col: str,
window: int = 7) -> List[Anomaly]:
"""Detect anomalies in time series data (e.g., daily costs, progress)."""
anomalies = []
df = df.sort_values(date_col).copy()
df['rolling_mean'] = df[value_col].rolling(window=window, center=True).mean()
df['rolling_std'] = df[value_col].rolling(window=window, center=True).std()
# Points outside 2 standard deviations from rolling mean
df['z_score'] = (df[value_col] - df['rolling_mean']) / df['rolling_std']
outliers = df[np.abs(df['z_score']) > 2].dropna()
for idx, row in outliers.iterrows():
anomalies.append(Anomaly(
id=f"TS-{idx}",
anomaly_type=AnomalyType.TREND_DEVIATION,
severity=AnomalySeverity.MEDIUM if abs(row['z_score']) < 3 else AnomalySeverity.HIGH,
field=value_col,
value=row[value_col],
expected_range=(
row['rolling_mean'] - 2 * row['rolling_std'],
row['rolling_mean'] + 2 * row['rolling_std']
),
description=f"Value deviates from {window}-day trend",
row_index=idx,
detection_method="Rolling Z-Score",
confidence=min(abs(row['z_score']) / 4, 1.0)
))
return anomalies
def detect_duplicate_anomalies(self, df: pd.DataFrame,
key_columns: List[str]) -> List[Anomaly]:
"""Detect duplicate records."""
anomalies = []
duplicates = df[df.duplicated(subset=key_columns, keep=False)]
if len(duplicates) > 0:
dup_groups = duplicates.groupby(key_columns).size()
Truncated for display — read the full file on GitHub.
Related Skills
Agent-Reach
106.4kGive your AI agent eyes to see the entire internet. Read & search Twitter, Reddit, YouTube, GitHub, Bilibili, XiaoHongShu — one CLI, zero API fees.
taste-skill
105.7kAgent skills that give coding agents taste: art direction, reference images and a browser check instead of generic AI pages
headroom
78.3kCompress tool outputs, logs, files, and RAG chunks before they reach the LLM. 20% fewer tokens for coding agents, 60-95% fewer tokens for JSON, same answers. Library, proxy, MCP server.
ai-job-search
67.6kThe job search that runs on your machine. AI job application framework built on Claude Code: evaluate postings, tailor CVs, write cover letters, prep interviews. Fork it and own it.
Languages
Trust signals
From repository metadata: license, adoption, age and documentation. Not a code audit — see the Safety scan above for what the skill file itself contains.
