psihum / multi_source_v22.py
Hlt58's picture
Create multi_source_v22.py
cf5b3a6 verified
Raw
History Blame Contribute Delete
10.9 kB
import io
import os
from datetime import datetime, timezone
import numpy as np
import pandas as pd
import requests
try:
from huggingface_hub import HfApi
except Exception:
HfApi = None
SOURCE_CONFIG = [
{
"name": "IA Fluide / state principal",
"url": "https://huggingface.co/datasets/Hlt58/iafluide-state/resolve/main/state.json",
"type": "state",
},
{
"name": "IA Fluide / copie test",
"url": "https://huggingface.co/datasets/Hlt58/iafluide-state/resolve/main/state.json",
"type": "state",
},
{
"name": "OWID proxy (converti)",
"url": "external",
"type": "owid",
},
]
DATASET_REPO_ID = "Hlt58/iafluide-state"
CONFLICT_HISTORY_URL = "https://huggingface.co/datasets/Hlt58/iafluide-state/resolve/main/conflict_history.csv"
def load_json(url):
try:
r = requests.get(url, timeout=8)
r.raise_for_status()
return r.json()
except Exception:
return None
def compute_age_minutes(utc_str):
try:
t = datetime.fromisoformat(utc_str.replace("Z", "+00:00"))
now = datetime.now(timezone.utc)
return (now - t).total_seconds() / 60.0
except Exception:
return None
def freshness_score(age_minutes, max_age_minutes, stale_penalty=1.0):
if age_minutes is None:
return 0.0
if age_minutes <= 0:
return 1.0
score = np.exp(-stale_penalty * age_minutes / max_age_minutes)
return float(np.clip(score, 0.0, 1.0))
def normalize_confidence_label(value):
if not isinstance(value, str):
return 0.5
v = value.strip().lower()
mapping = {
"élevée": 0.9,
"haute": 0.9,
"moyenne": 0.6,
"faible": 0.3,
"faible à moyenne": 0.45,
"moyenne à élevée": 0.75,
}
return mapping.get(v, 0.5)
def convert_owid_to_state():
return {
"utc": datetime.now(timezone.utc).isoformat(),
"observed": {
"Climat & systèmes planétaires": 0.30,
"Ressources vitales (eau, sols, énergie réelle)": 0.28,
"Santé humaine & biologie cognitive": 0.25,
"Éducation & transmission du savoir": 0.23,
"Systèmes économiques déconnectés du réel": 0.32,
},
"confidence": {
"Climat & systèmes planétaires": "élevée",
"Ressources vitales (eau, sols, énergie réelle)": "moyenne",
"Santé humaine & biologie cognitive": "moyenne",
"Éducation & transmission du savoir": "moyenne",
"Systèmes économiques déconnectés du réel": "faible",
},
}
def load_all_sources(max_age_minutes=1440, stale_penalty=1.0):
loaded = []
for cfg in SOURCE_CONFIG:
if cfg.get("type") == "state":
data = load_json(cfg["url"])
elif cfg.get("type") == "owid":
data = convert_owid_to_state()
else:
data = None
if not data:
continue
observed = data.get("observed", {})
confidence = data.get("confidence", {})
utc = data.get("utc", None)
if not isinstance(observed, dict):
continue
values = {}
for k, v in observed.items():
try:
values[k] = float(v)
except Exception:
continue
if not values:
continue
conf_values = {}
if isinstance(confidence, dict):
for k, v in confidence.items():
conf_values[k] = normalize_confidence_label(v)
age = compute_age_minutes(utc) if utc else None
fresh = freshness_score(age, max_age_minutes, stale_penalty=stale_penalty)
is_valid = bool(age is not None and age <= max_age_minutes)
if not is_valid:
continue
loaded.append({
"name": cfg["name"],
"url": cfg["url"],
"utc": utc,
"age_minutes": age,
"freshness_score": fresh,
"is_valid": is_valid,
"observed": values,
"confidence": conf_values,
"raw": data,
})
return loaded
def compute_source_status_table(sources):
rows = []
for src in sources:
rows.append({
"Nom": src["name"],
"URL": src["url"],
"UTC": src.get("utc", "unknown"),
"Âge (min)": src.get("age_minutes"),
"Fraîcheur": src.get("freshness_score"),
"Valide": src.get("is_valid"),
"Nombre de domaines": len(src["observed"]),
})
return pd.DataFrame(rows)
def collect_domains(sources):
domains = set()
for src in sources:
domains.update(src["observed"].keys())
return sorted(domains)
def build_source_comparison_table(sources):
domains = collect_domains(sources)
rows = []
for domain in domains:
row = {"Domaine": domain}
for src in sources:
row[src["name"]] = src["observed"].get(domain, None)
rows.append(row)
return pd.DataFrame(rows)
def compute_domain_conflict_table(sources):
domains = collect_domains(sources)
rows = []
for domain in domains:
vals = []
src_names = []
for src in sources:
if domain in src["observed"]:
vals.append(src["observed"][domain])
src_names.append(src["name"])
if len(vals) == 0:
continue
vals_arr = np.array(vals, dtype=float)
dispersion = float(np.std(vals_arr)) if len(vals_arr) > 1 else 0.0
vmin = float(np.min(vals_arr))
vmax = float(np.max(vals_arr))
conflict_score = float(vmax - vmin)
rows.append({
"Domaine": domain,
"Sources présentes": len(vals),
"Minimum": vmin,
"Maximum": vmax,
"Dispersion inter-sources": dispersion,
"Score de conflit": conflict_score,
"Sources": ", ".join(src_names),
})
return pd.DataFrame(rows).sort_values("Domaine")
def consolidate_sources(sources, tau0=0.15):
domains = collect_domains(sources)
consolidated_values = {}
confidence_by_domain = {}
dispersion_by_domain = {}
conflict_by_domain = {}
for domain in domains:
vals = []
weights = []
for src in sources:
if domain in src["observed"]:
val = src["observed"][domain]
base_conf = src["confidence"].get(domain, 0.5)
fresh = src.get("freshness_score", 1.0)
vals.append(val)
weights.append(base_conf * fresh)
if len(vals) == 0:
continue
vals_arr = np.array(vals, dtype=float)
w_arr = np.array(weights, dtype=float)
if np.sum(w_arr) <= 1e-9:
w_arr = np.ones_like(vals_arr)
consolidated_val = float(np.average(vals_arr, weights=w_arr))
dispersion = float(np.std(vals_arr)) if len(vals_arr) > 1 else 0.0
conflict = float(np.max(vals_arr) - np.min(vals_arr)) if len(vals_arr) > 1 else 0.0
confidence = (
float(np.mean(w_arr))
* float(np.exp(-dispersion / 0.10))
* float(np.exp(-conflict / 0.15))
)
consolidated_values[domain] = consolidated_val
confidence_by_domain[domain] = confidence
dispersion_by_domain[domain] = dispersion
conflict_by_domain[domain] = conflict
x = np.array(list(consolidated_values.values()), dtype=float)
mu = float(np.mean(x))
delta = float(np.std(x))
C = float(1 / (1 + delta / tau0))
R = float(mu * (1 - C))
global_confidence = float(np.mean(list(confidence_by_domain.values()))) if confidence_by_domain else 0.0
return {
"values": consolidated_values,
"confidence_by_domain": confidence_by_domain,
"dispersion_by_domain": dispersion_by_domain,
"conflict_by_domain": conflict_by_domain,
"mu": mu,
"delta": delta,
"C": C,
"R": R,
"global_confidence": global_confidence,
}
def load_conflict_history_df():
try:
r = requests.get(CONFLICT_HISTORY_URL, timeout=8)
if r.status_code == 200 and r.text.strip():
return pd.read_csv(io.StringIO(r.text))
except Exception:
pass
return pd.DataFrame(
columns=[
"timestamp",
"domain",
"conflict_score",
"dispersion",
"min_value",
"max_value",
"sources_present",
]
)
def append_conflict_snapshot_to_dataset(df_conflicts):
token = os.getenv("HF_TOKEN")
if not token:
return False, "HF_TOKEN manquant"
if HfApi is None:
return False, "huggingface_hub indisponible"
hist = load_conflict_history_df()
timestamp = datetime.now(timezone.utc).isoformat()
rows = []
for _, row in df_conflicts.iterrows():
rows.append({
"timestamp": timestamp,
"domain": row["Domaine"],
"conflict_score": row["Score de conflit"],
"dispersion": row["Dispersion inter-sources"],
"min_value": row["Minimum"],
"max_value": row["Maximum"],
"sources_present": row["Sources présentes"],
})
new_df = pd.DataFrame(rows)
hist = pd.concat([hist, new_df], ignore_index=True)
csv_bytes = hist.to_csv(index=False).encode("utf-8")
api = HfApi(token=token)
api.upload_file(
path_or_fileobj=csv_bytes,
path_in_repo="conflict_history.csv",
repo_id=DATASET_REPO_ID,
repo_type="dataset",
commit_message="Update conflict_history.csv from Space",
)
return True, "conflict_history.csv mis à jour"
def compute_slope(series):
if len(series) < 2:
return 0.0
x = np.arange(len(series))
y = np.array(series, dtype=float)
return float(np.polyfit(x, y, 1)[0])
def classify_trend(slope, threshold=0.001):
if slope > threshold:
return "hausse"
elif slope < -threshold:
return "baisse"
return "stable"
def build_conflict_trend_table(hist_df):
if len(hist_df) == 0:
return pd.DataFrame(columns=["domain", "conflit_actuel", "pente", "tendance", "n_points"])
rows = []
for domain, sub in hist_df.groupby("domain"):
sub = sub.sort_values("timestamp")
series = sub["conflict_score"].astype(float).tolist()
slope = compute_slope(series)
trend = classify_trend(slope)
current = float(series[-1]) if len(series) else 0.0
rows.append({
"domain": domain,
"conflit_actuel": current,
"pente": slope,
"tendance": trend,
"n_points": len(series),
})
return pd.DataFrame(rows).sort_values("domain")