bae705aa97
- Add NFC ePassport roadmap (ICAO 9303, eIDAS) - Add TensorFlow.js edge face detection (BlazeFace) - Add structured audit logger (GDPR-compliant) - Risk scoring support Part of KYC Apple Native UX v1.1.0
352 lines
13 KiB
Python
352 lines
13 KiB
Python
"""
|
|
Causal Engine
|
|
Understands cause-and-effect relationships, not just correlations
|
|
"""
|
|
|
|
from typing import Dict, List, Optional, Tuple
|
|
from dataclasses import dataclass
|
|
from datetime import datetime
|
|
|
|
|
|
@dataclass
|
|
class CausalRelationship:
|
|
"""A causal relationship between signals"""
|
|
cause: str
|
|
effect: str
|
|
strength: float # 0-1
|
|
mechanism: str # Explanation of how cause leads to effect
|
|
evidence: List[Dict]
|
|
confidence: float
|
|
|
|
def to_dict(self) -> Dict:
|
|
return {
|
|
"cause": self.cause,
|
|
"effect": self.effect,
|
|
"strength": round(self.strength, 2),
|
|
"mechanism": self.mechanism,
|
|
"evidence_count": len(self.evidence),
|
|
"confidence": round(self.confidence, 2)
|
|
}
|
|
|
|
|
|
class CausalEngine:
|
|
"""Identifies causal relationships in urban systems"""
|
|
|
|
def __init__(self):
|
|
self.causal_graph = self._build_causal_graph()
|
|
|
|
def _build_causal_graph(self) -> Dict:
|
|
"""Build knowledge graph of causal relationships"""
|
|
return {
|
|
# Lighting → Safety
|
|
"lighting": {
|
|
"safety_index": {
|
|
"strength": 0.71,
|
|
"mechanism": "Better lighting increases visibility, deters criminal activity, and improves pedestrian confidence",
|
|
"evidence": [
|
|
{"study": "Chicago Alley Lighting", "effect": "7% crime reduction"},
|
|
{"study": "NYC Street Lighting", "effect": "36% reduction in outdoor nighttime index crimes"}
|
|
]
|
|
},
|
|
"night_activity": {
|
|
"strength": 0.65,
|
|
"mechanism": "Improved lighting extends usable hours for commercial and social activities",
|
|
"evidence": []
|
|
}
|
|
},
|
|
|
|
# Trees → Temperature
|
|
"tree_coverage": {
|
|
"heat_stress": {
|
|
"strength": 0.82,
|
|
"mechanism": "Trees provide shade and evapotranspiration, reducing surface and air temperature",
|
|
"evidence": [
|
|
{"study": "Urban Heat Island", "effect": "2-8°C cooling effect"},
|
|
{"study": "Barcelona Tree Strategy", "effect": "4°C reduction in pedestrian areas"}
|
|
]
|
|
},
|
|
"walkability": {
|
|
"strength": 0.58,
|
|
"mechanism": "Shaded walkways encourage walking and extend comfortable walking hours",
|
|
"evidence": []
|
|
},
|
|
"property_value": {
|
|
"strength": 0.45,
|
|
"mechanism": "Tree-lined streets are associated with higher property values and desirability",
|
|
"evidence": [
|
|
{"study": "Portland Trees", "effect": "$1.35B total property value increase"}
|
|
]
|
|
}
|
|
},
|
|
|
|
# Pedestrian Flow → Retail
|
|
"pedestrian_flow": {
|
|
"retail_revenue": {
|
|
"strength": 0.78,
|
|
"mechanism": "More pedestrians = more potential customers = higher sales",
|
|
"evidence": [
|
|
{"study": "High Street Retail", "effect": "1% footfall increase = 1.3% sales increase"}
|
|
]
|
|
},
|
|
"safety_index": {
|
|
"strength": 0.52,
|
|
"mechanism": "Eyes on the street effect - more people watching increases natural surveillance",
|
|
"evidence": [
|
|
{"study": "Jane Jacobs", "effect": "Natural surveillance reduces crime"}
|
|
]
|
|
}
|
|
},
|
|
|
|
# Maintenance → Everything
|
|
"maintenance_quality": {
|
|
"property_value": {
|
|
"strength": 0.63,
|
|
"mechanism": "Well-maintained areas signal investment and care, attracting residents and businesses",
|
|
"evidence": []
|
|
},
|
|
"safety_index": {
|
|
"strength": 0.55,
|
|
"mechanism": "Broken windows theory - visible neglect signals low enforcement and invites more disorder",
|
|
"evidence": [
|
|
{"study": "Broken Windows", "effect": "Maintenance prevents escalation of disorder"}
|
|
]
|
|
},
|
|
"tourism": {
|
|
"strength": 0.48,
|
|
"mechanism": "Tourists avoid areas that appear neglected or unsafe",
|
|
"evidence": []
|
|
}
|
|
},
|
|
|
|
# Public Transit → Accessibility
|
|
"transit_access": {
|
|
"property_value": {
|
|
"strength": 0.67,
|
|
"mechanism": "Transit access reduces commute costs and increases location desirability",
|
|
"evidence": [
|
|
{"study": "TOD Value", "effect": "10-20% property value premium near transit"}
|
|
]
|
|
},
|
|
"walkability": {
|
|
"strength": 0.44,
|
|
"mechanism": "Transit hubs create walkable destinations and mixed-use development",
|
|
"evidence": []
|
|
}
|
|
}
|
|
}
|
|
|
|
def explain_causality(self, cause: str, effect: str) -> Optional[Dict]:
|
|
"""
|
|
Explain why A causes B
|
|
|
|
Returns:
|
|
Causal explanation or None if no relationship known
|
|
"""
|
|
if cause in self.causal_graph and effect in self.causal_graph[cause]:
|
|
relationship = self.causal_graph[cause][effect]
|
|
|
|
return {
|
|
"cause": cause,
|
|
"effect": effect,
|
|
"strength": relationship["strength"],
|
|
"mechanism": relationship["mechanism"],
|
|
"evidence": relationship["evidence"],
|
|
"interpretation": self._interpret_strength(relationship["strength"])
|
|
}
|
|
|
|
return None
|
|
|
|
def find_causes(self, effect: str) -> List[Dict]:
|
|
"""Find all known causes of an effect"""
|
|
causes = []
|
|
|
|
for cause, effects in self.causal_graph.items():
|
|
if effect in effects:
|
|
relationship = effects[effect]
|
|
causes.append({
|
|
"cause": cause,
|
|
"strength": relationship["strength"],
|
|
"mechanism": relationship["mechanism"][:100] + "..."
|
|
})
|
|
|
|
# Sort by strength
|
|
causes.sort(key=lambda x: x["strength"], reverse=True)
|
|
|
|
return causes
|
|
|
|
def find_effects(self, cause: str) -> List[Dict]:
|
|
"""Find all known effects of a cause"""
|
|
if cause not in self.causal_graph:
|
|
return []
|
|
|
|
effects = []
|
|
for effect, relationship in self.causal_graph[cause].items():
|
|
effects.append({
|
|
"effect": effect,
|
|
"strength": relationship["strength"],
|
|
"mechanism": relationship["mechanism"][:100] + "..."
|
|
})
|
|
|
|
# Sort by strength
|
|
effects.sort(key=lambda x: x["strength"], reverse=True)
|
|
|
|
return effects
|
|
|
|
def calculate_attribution(
|
|
self,
|
|
effect: str,
|
|
signal_values: Dict[str, float]
|
|
) -> Dict:
|
|
"""
|
|
Calculate how much each cause contributes to an effect
|
|
|
|
Example:
|
|
"Safety Index = 45. What causes this?"
|
|
→ Lighting: 31% of variation
|
|
→ Maintenance: 22% of variation
|
|
→ Pedestrian flow: 18% of variation
|
|
"""
|
|
causes = self.find_causes(effect)
|
|
|
|
if not causes:
|
|
return {
|
|
"effect": effect,
|
|
"status": "unknown",
|
|
"message": f"No causal model for {effect}"
|
|
}
|
|
|
|
# Calculate attribution
|
|
attributions = []
|
|
total_strength = sum(c["strength"] for c in causes)
|
|
|
|
for cause in causes:
|
|
cause_name = cause["cause"]
|
|
current_value = signal_values.get(cause_name, 50)
|
|
|
|
# Attribution = strength * (deviation from optimal)
|
|
deviation = abs(50 - current_value) / 50 # 0 = optimal, 1 = worst
|
|
attribution = cause["strength"] / total_strength * (1 - deviation)
|
|
|
|
attributions.append({
|
|
"cause": cause_name,
|
|
"attribution_percent": round(attribution * 100, 1),
|
|
"current_value": current_value,
|
|
"potential_improvement": round(deviation * 100, 1),
|
|
"mechanism": cause["mechanism"]
|
|
})
|
|
|
|
# Sort by attribution
|
|
attributions.sort(key=lambda x: x["attribution_percent"], reverse=True)
|
|
|
|
return {
|
|
"effect": effect,
|
|
"current_value": signal_values.get(effect, 50),
|
|
"total_attribution": round(sum(a["attribution_percent"] for a in attributions), 1),
|
|
"attributions": attributions,
|
|
"top_driver": attributions[0]["cause"] if attributions else None
|
|
}
|
|
|
|
def recommend_interventions(
|
|
self,
|
|
target: str,
|
|
current_value: float,
|
|
target_value: float
|
|
) -> List[Dict]:
|
|
"""
|
|
Recommend interventions to achieve a target
|
|
|
|
Example:
|
|
"Improve safety from 45 to 70"
|
|
→ Install lighting (expected improvement: +15)
|
|
→ Increase maintenance (expected improvement: +8)
|
|
→ Activate pedestrian flow (expected improvement: +5)
|
|
"""
|
|
causes = self.find_causes(target)
|
|
|
|
interventions = []
|
|
for cause in causes:
|
|
cause_name = cause["cause"]
|
|
strength = cause["strength"]
|
|
|
|
# Expected improvement
|
|
gap = target_value - current_value
|
|
expected_improvement = gap * strength
|
|
|
|
interventions.append({
|
|
"intervention": f"Improve {cause_name}",
|
|
"target_cause": cause_name,
|
|
"expected_improvement": round(expected_improvement, 1),
|
|
"confidence": round(strength, 2),
|
|
"mechanism": cause["mechanism"],
|
|
"priority": "high" if expected_improvement > gap * 0.3 else "medium"
|
|
})
|
|
|
|
# Sort by expected improvement
|
|
interventions.sort(key=lambda x: x["expected_improvement"], reverse=True)
|
|
|
|
return interventions
|
|
|
|
def _interpret_strength(self, strength: float) -> str:
|
|
"""Interpret causal strength"""
|
|
if strength >= 0.7:
|
|
return "Strong causal relationship"
|
|
elif strength >= 0.5:
|
|
return "Moderate causal relationship"
|
|
elif strength >= 0.3:
|
|
return "Weak causal relationship"
|
|
else:
|
|
return "Very weak causal relationship"
|
|
|
|
|
|
# Example usage
|
|
def example_causal_analysis():
|
|
"""Example: Causal analysis"""
|
|
engine = CausalEngine()
|
|
|
|
# Explain causality
|
|
print("=== Causal Explanation ===")
|
|
explanation = engine.explain_causality("lighting", "safety_index")
|
|
if explanation:
|
|
print(f"{explanation['cause']} → {explanation['effect']}")
|
|
print(f"Strength: {explanation['strength']} ({explanation['interpretation']})")
|
|
print(f"Mechanism: {explanation['mechanism']}")
|
|
print("Evidence:")
|
|
for ev in explanation['evidence']:
|
|
print(f" - {ev['study']}: {ev['effect']}")
|
|
|
|
# Find causes of safety
|
|
print("\n=== Causes of Safety Index ===")
|
|
causes = engine.find_causes("safety_index")
|
|
for cause in causes:
|
|
print(f" {cause['cause']}: {cause['strength']}")
|
|
|
|
# Attribution analysis
|
|
print("\n=== Attribution Analysis ===")
|
|
signal_values = {
|
|
"lighting": 35,
|
|
"maintenance_quality": 40,
|
|
"pedestrian_flow": 60,
|
|
"safety_index": 45
|
|
}
|
|
|
|
attribution = engine.calculate_attribution("safety_index", signal_values)
|
|
print(f"Safety Index = {attribution['current_value']}")
|
|
print("Attributions:")
|
|
for attr in attribution['attributions']:
|
|
print(f" {attr['cause']}: {attr['attribution_percent']}%")
|
|
print(f" Current: {attr['current_value']}, Potential: {attr['potential_improvement']}%")
|
|
|
|
# Recommend interventions
|
|
print("\n=== Recommended Interventions ===")
|
|
interventions = engine.recommend_interventions("safety_index", 45, 70)
|
|
for intervention in interventions:
|
|
print(f" {intervention['intervention']}")
|
|
print(f" Expected improvement: +{intervention['expected_improvement']}")
|
|
print(f" Priority: {intervention['priority']}")
|
|
|
|
return engine
|
|
|
|
|
|
if __name__ == '__main__':
|
|
example_causal_analysis()
|