2 Commits

Author SHA1 Message Date
shahin 701879c934 Update the Main Branch from certainty 2026-05-04 15:00:17 +02:00
shahin 85aadeb996 add all .py files 2026-01-19 00:10:58 +01:00
16 changed files with 2980 additions and 5705 deletions
+25 -84
View File
@@ -1,88 +1,29 @@
# ========================= <<<<<<< HEAD
# Python # Ignore all contents of these directories
# ========================= !**/*.py
__pycache__/ /Data/
*.py[cod] /attach/
*$py.class /results/
.ipynb_checkpoints/ /enarcelona/
=======
# ========================= # 1. Broad Ignores
# Virtual environments /Data/*
# ========================= /attach/*
env/ /results/*
env*/ /enarcelona/*
venv/ >>>>>>> Certainty
.venv/
enarcelona/
# =========================
# Secrets
# =========================
.env .env
*.env __pycache__/
*.pyc
# =========================
# Patient data / sensitive data
# =========================
Data/
data/raw/
data/processed/
data/ground_truth/
reference/
# =========================
# Generated results and logs
# =========================
results/
results_edss_benchmark/
*.log
# =========================
# Large/generated file types
# =========================
*.csv *.csv
*.tsv =======
*.json /reference/
*.jsonl
*.xlsx
*.xls
*.png
*.PNG
*.jpg
*.jpeg
*.svg *.svg
*.pdf >>>>>>> Stashed changes
# 2. Ignore virtual environments COMPLETELY
# This must come BEFORE the unignore rule
env*/
# ========================= # 3. The "Unignore" rule (Whitelisting)
# Temporary / backup files # We only unignore .py files that aren't already blocked by the rules above
# ========================= !**/*.py
*.tmp
*.bak
*.orig
.DS_Store
# =========================
# Keep important code/config/docs
# =========================
!README.md
!requirements.txt
!*.py
!*.md
!*.yml
!*.yaml
!*.toml
# Keep prompt templates / schemas if safe to publish
!prompts/
!prompts/**
!attach/
!attach/*.gbnf
!attach/just_edss_text.txt
!attach/Komplett.txt
# Keep example/synthetic data only
!data/
!data/example/
!data/example/**
!Data/example/
!Data/example/**
+29
View File
@@ -0,0 +1,29 @@
<<<<<<< HEAD
# Ignore all contents of these directories
!**/*.py
/Data/
/attach/
/results/
/enarcelona/
=======
# 1. Broad Ignores
/Data/*
/attach/*
/results/*
/enarcelona/*
>>>>>>> Certainty
.env
__pycache__/
*.pyc
*.csv
=======
/reference/
*.svg
>>>>>>> Stashed changes
# 2. Ignore virtual environments COMPLETELY
# This must come BEFORE the unignore rule
env*/
# 3. The "Unignore" rule (Whitelisting)
# We only unignore .py files that aren't already blocked by the rules above
!**/*.py
-31
View File
@@ -1,31 +0,0 @@
# Project Structure
This project was reorganized into:
- `data/`
- `raw/`: original raw data, if retained locally
- `processed/`: cleaned or derived input data
- `ground_truth/`: manually annotated reference data
- `external/`: externally provided data
- `prompts/`
- EDSS instructions and prompt/schema assets
- `scripts/`
- runnable analysis and plotting scripts
- `results/`
- `benchmark_runs/`: full model benchmark runs
- `final_results/`: final selected model outputs
- `figures/`: generated figures
- `tables/`: generated tables
- `logs/`: terminal logs
- `manuscript/`
- final figures and tables for paper/thesis writing
- `archive/`
- old scripts, old results, temporary files, and unclear legacy files
Important:
The reorganization was performed after creating a full timestamped backup.
+3
View File
@@ -216,3 +216,6 @@ if __name__ == "__main__":
# %% name
eXXXXXXXX
##
+600
View File
@@ -0,0 +1,600 @@
# %% API call1
#import time
#import json
#import os
#from datetime import datetime
#import pandas as pd
#from openai import OpenAI
#from dotenv import load_dotenv
#
## Load environment variables
#load_dotenv()
#
## === CONFIGURATION ===
#OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
#OPENAI_BASE_URL = os.getenv("OPENAI_BASE_URL")
#MODEL_NAME = "GPT-OSS-120B"
#HEALTH_URL = f"{OPENAI_BASE_URL}/health" # Placeholder - actual health check would need to be implemented
#CHAT_URL = f"{OPENAI_BASE_URL}/chat/completions"
#
## File paths
#INPUT_CSV = "/home/shahin/Lab/Doktorarbeit/Barcelona/Data/MS_Briefe_400_with_unique_id_SHA3_explore_cleaned_unique.csv"
#EDSS_INSTRUCTIONS_PATH = "/home/shahin/Lab/Doktorarbeit/Barcelona/attach/Komplett.txt"
##GRAMMAR_FILE = "/home/shahin/Lab/Doktorarbeit/Barcelona/attach/just_edss_schema.gbnf"
#
## Initialize OpenAI client
#client = OpenAI(
# api_key=OPENAI_API_KEY,
# base_url=OPENAI_BASE_URL
#)
#
## Read EDSS instructions from file
#with open(EDSS_INSTRUCTIONS_PATH, 'r') as f:
# EDSS_INSTRUCTIONS = f.read().strip()
## === RUN INFERENCE 2 ===
#def run_inference(patient_text):
# prompt = f'''
# Du bist ein medizinischer Assistent, der spezialisiert darauf ist, EDSS-Scores (Expanded Disability Status Scale) aus klinischen Berichten zu extrahieren.
#### Regeln für die Ausgabe:
#1. **Reason**: Erstelle eine prägnante Zusammenfassung (max. 400 Zeichen) der Befunde auf **DEUTSCH**, die zur Einstufung führen.
#2. **klassifizierbar**:
# - Setze dies auf **true**, wenn ein EDSS-Wert identifiziert, berechnet oder basierend auf den klinischen Hinweisen plausibel geschätzt werden kann.
# - Setze dies auf **false**, NUR wenn die Daten absolut unzureichend oder so widersprüchlich sind, dass keinerlei Einstufung möglich ist.
#3. **EDSS**:
# - Dieses Feld ist **VERPFLICHTEND**, wenn "klassifizierbar" auf true steht.
# - Es muss eine Zahl zwischen 0.0 und 10.0 sein.
# - Versuche stets, den EDSS-Wert so präzise wie möglich zu bestimmen, auch wenn die Datenlage dünn ist (nutze verfügbare Informationen zu Gehstrecke und Funktionssystemen).
# - Dieses Feld **DARF NICHT ERSCHEINEN**, wenn "klassifizierbar" auf false steht.
#
#### Einschränkungen:
#- Erfinde keine Fakten, aber nutze klinische Herleitungen aus dem Bericht, um den EDSS zu bestimmen.
#- Priorisiere die Vergabe eines EDSS-Wertes gegenüber der Markierung als nicht klassifizierbar.
#- Halte dich strikt an die JSON-Struktur.
#
#EDSS-Bewertungsrichtlinien:
#{EDSS_INSTRUCTIONS}
#
#Patientenbericht:
#{patient_text}
#'''
# start_time = time.time()
#
# try:
# # Make API call using OpenAI client
# response = client.chat.completions.create(
# messages=[
# {
# "role": "system",
# "content": "You extract EDSS scores. You prioritize providing a score even if data is partial, by using clinical inference."
# },
# {
# "role": "user",
# "content": prompt
# }
# ],
# model=MODEL_NAME,
# max_tokens=2048,
# temperature=0.0,
# response_format={"type": "json_object"}
# )
#
# # Extract content from response
# content = response.choices[0].message.content
#
# # Parse the JSON response
# parsed = json.loads(content)
#
# inference_time = time.time() - start_time
#
# return {
# "success": True,
# "result": parsed,
# "inference_time_sec": inference_time
# }
#
# except Exception as e:
# print(f"Inference error: {e}")
# return {
# "success": False,
# "error": str(e),
# "inference_time_sec": -1
# }
## === BUILD PATIENT TEXT ===
#def build_patient_text(row):
# return (
# str(row["T_Zusammenfassung"]) + "\n" +
# str(row["Diagnosen"]) + "\n" +
# str(row["T_KlinBef"]) + "\n" +
# str(row["T_Befunde"]) + "\n"
# )
#
#if __name__ == "__main__":
# # Read CSV file ONLY inside main block
# df = pd.read_csv(INPUT_CSV, sep=';')
# results = []
#
# # Process each row
# for idx, row in df.iterrows():
# print(f"Processing row {idx + 1}/{len(df)}")
# try:
# patient_text = build_patient_text(row)
# result = run_inference(patient_text)
#
# # Add unique_id and MedDatum to result for tracking
# result["unique_id"] = row.get("unique_id", f"row_{idx}")
# result["MedDatum"] = row.get("MedDatum", None)
#
# results.append(result)
# print(json.dumps(result, indent=2))
# except Exception as e:
# print(f"Error processing row {idx}: {e}")
# results.append({
# "success": False,
# "error": str(e),
# "unique_id": row.get("unique_id", f"row_{idx}"),
# "MedDatum": row.get("MedDatum", None)
# })
#
# # Save results to a JSON file
# output_json = INPUT_CSV.replace(".csv", "_results_Nisch.json")
# with open(output_json, 'w') as f:
# json.dump(results, f, indent=2)
# print(f"Results saved to {output_json}")
##
# %% API call1 - Enhanced with certainty scoring
#import time
#import json
#import os
#from datetime import datetime
#import pandas as pd
#from openai import OpenAI
#from dotenv import load_dotenv
#
## Load environment variables
#load_dotenv()
#
## === CONFIGURATION ===
#OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
#OPENAI_BASE_URL = os.getenv("OPENAI_BASE_URL")
#MODEL_NAME = "GPT-OSS-120B"
#
## File paths
#INPUT_CSV = "/home/shahin/Lab/Doktorarbeit/Barcelona/Data/Test.csv"
#EDSS_INSTRUCTIONS_PATH = "/home/shahin/Lab/Doktorarbeit/Barcelona/attach/Komplett.txt"
#
## Initialize OpenAI client
#client = OpenAI(
# api_key=OPENAI_API_KEY,
# base_url=OPENAI_BASE_URL
#)
#
## Read EDSS instructions from file
#with open(EDSS_INSTRUCTIONS_PATH, 'r') as f:
# EDSS_INSTRUCTIONS = f.read().strip()
#
## === PROMPT WITH CERTAINTY REQUEST ===
#def build_prompt(patient_text):
# return f'''Du bist ein medizinischer Assistent, der spezialisiert darauf ist, EDSS-Scores (Expanded Disability Status Scale), alle Unterkategorien und die Bewertungssicherheit aus klinischen Berichten zu extrahieren.
#
#### Deine Aufgabe:
#1. Analysiere den Patientenbericht und extrahiere:
# - Den Gesamt-EDSS-Score (0.010.0)
# - Alle 8 EDSS-Unterkategorien (mit jeweils eigener Maximalpunktzahl)
#2. Schätze für jede Entscheidung die Sicherheit als Ganzzahl von 0100 % ein.
#
#### Struktur der JSON-Ausgabe (VERPFLICHTEND):
#Gib NUR gültiges JSON zurück — kein Markdown, kein Text davor/dahinter.
#
#{{
# "reason": "Kernaussage zur EDSS-Begründung (max. 400 Zeichen, auf Deutsch).",
# "klassifizierbar": true/false,
# "EDSS": null ODER Zahl zwischen 0.0 und 10.0 (nur wenn klassifizierbar=true)",
# "certainty_percent": 0 ODER Zahl zwischen 0 und 100 (Ganzzahl)",
# "subcategories": {{
# "VISUAL_OPTIC_FUNCTIONS": null ODER Zahl zwischen 0.0 und 6.0,
# "BRAINSTEM_FUNCTIONS": null ODER Zahl zwischen 0.0 und 6.0,
# "PYRAMIDAL_FUNCTIONS": null ODER Zahl zwischen 0.0 und 6.0,
# "CEREBELLAR_FUNCTIONS": null ODER Zahl zwischen 0.0 und 6.0,
# "SENSORY_FUNCTIONS": null ODER Zahl zwischen 0.0 und 6.0,
# "BOWEL_AND_BLADDER_FUNCTIONS": null ODER Zahl zwischen 0.0 und 6.0,
# "CEREBRAL_FUNCTIONS": null ODER Zahl zwischen 0.0 und 6.0,
# "AMBULATION": null ODER Zahl zwischen 0.0 und 10.0
# }}
#}}
#
#### Regeln:
#- **reason**: Kurze, prägnante Begründung (auf Deutsch, max. 400 Zeichen), warum du den EDSS-Wert und die Unterkategorien so bewertest.
#- **klassifizierbar**:
# - `true`, wenn EDSS und mindestens die wichtigsten Unterkategorien *eindeutig ableitbar* oder *plausibel inferierbar* sind.
# - `false`, **nur**, wenn keine relevanten Daten vorliegen, oder diese so widersprüchlich/inkonsistent sind, dass keine vernünftige Einschätzung möglich ist.
#- **EDSS**:
# - **VERPFLICHTEND**, wenn `klassifizierbar=true`.
# - Zahl zwischen 0.0 und 10.0 (z.B. 3.0, 5.5). Darf **nicht** erscheinen, wenn `klassifizierbar=false`.
#- **certainty_percent**:
# - **Immer present** — Ganzzahl (0100), basierend auf:
# - Klarheit und Vollständigkeit der Berichtsangaben,
# - Stichhaltigkeit der Schlussfolgerung (inkl. Inferenz),
# - Konsistenz zwischen den Unterkategorien.
#- **subcategories**:
# - **Immer present** — **alle 8 Unterkategorien** müssen enthalten sein.
# - Jeder Wert ist entweder:
# - `null` (wenn keine ausreichende Information vorliegt), **oder**
# - eine Zahl ≤ jeweiliger Obergrenze (z.B. Ambulation ≤ 10.0).
# - Wenn die Unterkategorie plausibel inferiert werden kann (auch indirekt), gib einen sinnvollen Wert ab.
# - Beispiel: Wenn „Gang mit Krückstock auf ebenem Boden bis 200 m“ steht, setze `AMBULATION: 5.5`.
#
#### EDSS-Bewertungsrichtlinien:
#{EDSS_INSTRUCTIONS}
#
#Patientenbericht:
#{patient_text}
#'''
#
## === INFERENCE FUNCTION ===
#def run_inference(patient_text):
# prompt = build_prompt(patient_text)
#
# start_time = time.time()
#
# try:
# response = client.chat.completions.create(
# messages=[
# {"role": "system", "content": "Du gibst EXKLUSIV gültiges JSON zurück — keine weiteren Erklärungen."}
# ] + [
# {"role": "user", "content": prompt}
# ],
# model=MODEL_NAME,
# max_tokens=2048,
# temperature=0.1, # Slightly higher for more natural certainty estimation (still low for reliability)
# response_format={"type": "json_object"}
# )
#
# content = response.choices[0].message.content
#
# # Parse and validate JSON
# try:
# parsed = json.loads(content)
# except json.JSONDecodeError as e:
# print(f"⚠️ JSON parsing failed: {e}")
# print("Raw response:", content[:500])
# raise ValueError("Model did not return valid JSON")
#
# # Enforce required keys
# if "certainty_percent" not in parsed:
# print("⚠️ Missing 'certainty_percent' in output! Force-adding fallback.")
# parsed["certainty_percent"] = 0 # fallback
# elif not isinstance(parsed["certainty_percent"], (int, float)):
# parsed["certainty_percent"] = int(parsed["certainty_percent"])
#
# # Clamp certainty to [0, 100]
# pct = parsed["certainty_percent"]
# parsed["certainty_percent"] =max(0, min(100, int(pct)))
#
# # Enforce EDSS rules: if not classifiable → remove EDSS
# if not parsed.get("klassifizierbar", False):
# if "EDSS" in parsed:
# del parsed["EDSS"] # per spec, must not appear if not classifiable
# else:
# if "EDSS" not in parsed:
# print("⚠️ 'klassifizierbar' is true but EDSS missing — adding fallback.")
# parsed["EDSS"] = 7.0 # last-resort fallback
#
# inference_time = time.time() - start_time
#
# return {
# "success": True,
# "result": parsed,
# "inference_time_sec": inference_time
# }
#
# except Exception as e:
# print(f"❌ Inference error: {e}")
# return {
# "success": False,
# "error": str(e),
# "inference_time_sec": -1,
# "result": None # no structured output
# }
#
## === BUILD PATIENT TEXT ===
#def build_patient_text(row):
# return (
# str(row.get("T_Zusammenfassung", "")) + "\n" +
# str(row.get("Diagnosen", "")) + "\n" +
# str(row.get("T_KlinBef", "")) + "\n" +
# str(row.get("T_Befunde", ""))
# )
#
#if __name__ == "__main__":
# # Load data
# df = pd.read_csv(INPUT_CSV, sep=';')
# results = []
#
# # Optional: limit for testing
# # df = df.head(3)
#
# print(f"Processing {len(df)} rows...")
# for idx, row in df.iterrows():
# print(f"\n— Row {idx + 1}/{len(df)} —")
# try:
# patient_text = build_patient_text(row)
# result = run_inference(patient_text)
#
# # Attach metadata
# result["unique_id"] = row.get("unique_id", f"row_{idx}")
# result["MedDatum"] = row.get("MedDatum", None)
#
# results.append(result)
#
# # Print summary
# if result["success"]:
# res = result["result"]
# edss = res.get("EDSS", "N/A") if res.get("klassifizierbar") else "N/A"
# print(f"✅ Result → EDSS={edss}, certainty={res.get('certainty_percent', 'N/A')}%")
# print(f" Reason: {res.get('reason', 'N/A')[:100]}…")
# else:
# print(f"❌ Failed: {result.get('error', 'Unknown error')[:100]}")
#
# except Exception as e:
# print(f"⚠️ Error processing row {idx}: {e}")
# results.append({
# "success": False,
# "error": str(e),
# "unique_id": row.get("unique_id", f"row_{idx}"),
# "MedDatum": row.get("MedDatum", None),
# "result": None
# })
#
# # Save results
# output_json = INPUT_CSV.replace(".csv", "_results_Nisch_certainty.json")
# with open(output_json, 'w', encoding='utf-8') as f:
# json.dump(results, f, indent=2, ensure_ascii=False)
# print(f"\n✅ Saved results to: {output_json}")
#
##
# %% API call - Multi-iteration EDSS + certainty extraction
import time
import json
import os
from datetime import datetime
import pandas as pd
from openai import OpenAI
from dotenv import load_dotenv
# Load environment variables
load_dotenv()
# === CONFIGURATION ===
OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
OPENAI_BASE_URL = os.getenv("OPENAI_BASE_URL")
MODEL_NAME = "GPT-OSS-120B"
# File paths
INPUT_CSV = "/home/shahin/Lab/Doktorarbeit/Barcelona/Data/MS_Briefe_400_with_unique_id_SHA3_explore_cleaned_unique.csv"
EDSS_INSTRUCTIONS_PATH = "/home/shahin/Lab/Doktorarbeit/Barcelona/attach/Komplett.txt"
# Iteration settings
NUM_ITERATIONS = 20
STOP_ON_FIRST_ERROR = False # Set to True for debugging
# Initialize OpenAI client
client = OpenAI(
api_key=OPENAI_API_KEY,
base_url=OPENAI_BASE_URL
)
# Read EDSS instructions from file
with open(EDSS_INSTRUCTIONS_PATH, 'r') as f:
EDSS_INSTRUCTIONS = f.read().strip()
# === PROMPT (unchanged from before) ===
def build_prompt(patient_text):
return f'''Du bist ein medizinischer Assistent, der spezialisiert darauf ist, EDSS-Scores (Expanded Disability Status Scale), alle Unterkategorien und die Bewertungssicherheit aus klinischen Berichten zu extrahieren.
### Deine Aufgabe:
1. Analysiere den Patientenbericht und extrahiere:
- Den Gesamt-EDSS-Score (0.010.0)
- Alle 8 EDSS-Unterkategorien (mit jeweils eigener Maximalpunktzahl)
2. Schätze für jede Entscheidung die Sicherheit als Ganzzahl von 0100 % ein.
### Struktur der JSON-Ausgabe (VERPFLICHTEND):
Gib NUR gültiges JSON zurück — kein Markdown, kein Text davor/dahinter.
{{
"reason": "Kernaussage zur EDSS-Begründung (max. 400 Zeichen, auf Deutsch).",
"klassifizierbar": true/false,
"EDSS": null ODER Zahl zwischen 0.0 und 10.0 (nur wenn klassifizierbar=true)",
"certainty_percent": 0 ODER Zahl zwischen 0 und 100 (Ganzzahl)",
"subcategories": {{
"VISUAL_OPTIC_FUNCTIONS": null ODER Zahl zwischen 0.0 und 6.0,
"BRAINSTEM_FUNCTIONS": null ODER Zahl zwischen 0.0 und 6.0,
"PYRAMIDAL_FUNCTIONS": null ODER Zahl zwischen 0.0 und 6.0,
"CEREBELLAR_FUNCTIONS": null ODER Zahl zwischen 0.0 und 6.0,
"SENSORY_FUNCTIONS": null ODER Zahl zwischen 0.0 und 6.0,
"BOWEL_AND_BLADDER_FUNCTIONS": null ODER Zahl zwischen 0.0 und 6.0,
"CEREBRAL_FUNCTIONS": null ODER Zahl zwischen 0.0 und 6.0,
"AMBULATION": null ODER Zahl zwischen 0.0 und 10.0
}}
}}
### Regeln:
- **reason**: Kurze, prägnante Begründung (auf Deutsch, max. 400 Zeichen), warum du den EDSS-Wert und die Unterkategorien so bewertest.
- **klassifizierbar**:
- `true`, wenn EDSS und mindestens die wichtigsten Unterkategorien *eindeutig ableitbar* oder *plausibel inferierbar* sind.
- `false`, **nur**, wenn keine relevanten Daten vorliegen, oder diese so widersprüchlich/inkonsistent sind, dass keine vernünftige Einschätzung möglich ist.
- **EDSS**:
- **VERPFLICHTEND**, wenn `klassifizierbar=true`.
- Zahl zwischen 0.0 und 10.0 (z.B. 3.0, 5.5). Darf **nicht** erscheinen, wenn `klassifizierbar=false`.
- **certainty_percent**:
- **Immer present** — Ganzzahl (0100), basierend auf:
- Klarheit und Vollständigkeit der Berichtsangaben,
- Stichhaltigkeit der Schlussfolgerung (inkl. Inferenz),
- Konsistenz zwischen den Unterkategorien.
- **subcategories**:
- **Immer present** — **alle 8 Unterkategorien** müssen enthalten sein.
- Jeder Wert ist entweder:
- `null` (wenn keine ausreichende Information vorliegt), **oder**
- eine Zahl ≤ jeweiliger Obergrenze (z.B. Ambulation ≤ 10.0).
- Wenn die Unterkategorie plausibel inferiert werden kann (auch indirekt), gib einen sinnvollen Wert ab.
- Beispiel: Wenn „Gang mit Krückstock auf ebenem Boden bis 200 m“ steht, setze `AMBULATION: 5.5`.
### EDSS-Bewertungsrichtlinien:
{EDSS_INSTRUCTIONS}
Patientenbericht:
{patient_text}
'''
# === INFERENCE FUNCTION (unchanged) ===
def run_inference(patient_text):
prompt = build_prompt(patient_text)
start_time = time.time()
try:
response = client.chat.completions.create(
messages=[
{"role": "system", "content": "Du gibst EXKLUSIV gültiges JSON zurück — keine weiteren Erklärungen."}
] + [
{"role": "user", "content": prompt}
],
model=MODEL_NAME,
max_tokens=2048,
temperature=0.1,
response_format={"type": "json_object"}
)
content = response.choices[0].message.content
# Parse and validate JSON
try:
parsed = json.loads(content)
except json.JSONDecodeError as e:
print(f"⚠️ JSON parsing failed: {e}")
print("Raw response:", content[:500])
raise ValueError("Model did not return valid JSON")
# Enforce required keys
if "certainty_percent" not in parsed:
print("⚠️ Missing 'certainty_percent' in output! Force-adding fallback.")
parsed["certainty_percent"] = 0
elif not isinstance(parsed["certainty_percent"], (int, float)):
parsed["certainty_percent"] = int(parsed["certainty_percent"])
# Clamp certainty to [0, 100]
pct = parsed["certainty_percent"]
parsed["certainty_percent"] = max(0, min(100, int(pct)))
# Enforce EDSS rules
if not parsed.get("klassifizierbar", False):
if "EDSS" in parsed:
del parsed["EDSS"]
else:
if "EDSS" not in parsed:
print("⚠️ 'klassifizierbar' is true but EDSS missing — adding fallback.")
parsed["EDSS"] = 7.0
inference_time = time.time() - start_time
return {
"success": True,
"result": parsed,
"inference_time_sec": inference_time
}
except Exception as e:
print(f"❌ Inference error: {e}")
return {
"success": False,
"error": str(e),
"inference_time_sec": -1,
"result": None
}
# === BUILD PATIENT TEXT ===
def build_patient_text(row):
return (
str(row.get("T_Zusammenfassung", "")) + "\n" +
str(row.get("Diagnosen", "")) + "\n" +
str(row.get("T_KlinBef", "")) + "\n" +
str(row.get("T_Befunde", ""))
)
# === MAIN LOOP (NEW: MULTI-ITERATION) ===
if __name__ == "__main__":
# Load data ONCE (to avoid repeated I/O overhead)
df = pd.read_csv(INPUT_CSV, sep=';')
total_rows = len(df)
print(f"Loaded {total_rows} patient records.")
for iteration in range(1, NUM_ITERATIONS + 1):
print(f"\n{'='*60}")
print(f"🔄 ITERATION {iteration}/{NUM_ITERATIONS}")
print(f"{'='*60}")
iteration_results = []
start_iter = time.time()
for idx, row in df.iterrows():
print(f"\rRow {idx+1}/{total_rows} | Iter {iteration}", end='', flush=True)
try:
patient_text = build_patient_text(row)
result = run_inference(patient_text)
# Attach metadata
if result["success"]:
res = result["result"].copy() # avoid mutation
res["iteration"] = iteration
res["unique_id"] = row.get("unique_id", f"row_{idx}")
res["MedDatum"] = row.get("MedDatum", None)
result["result"] = res
else:
result["iteration"] = iteration
result["unique_id"] = row.get("unique_id", f"row_{idx}")
result["MedDatum"] = row.get("MedDatum", None)
iteration_results.append(result)
if result["success"]:
res = result["result"]
edss = res.get("EDSS", "N/A") if res.get("klassifizierbar") else "N/A"
print(f" ✅ EDSS={edss}, cert={res.get('certainty_percent', '?')}%")
else:
print(f"{result.get('error', 'Unknown')}")
except Exception as e:
print(f"\n⚠️ Row {idx} failed: {e}")
iteration_results.append({
"success": False,
"error": str(e),
"iteration": iteration,
"unique_id": row.get("unique_id", f"row_{idx}"),
"MedDatum": row.get("MedDatum", None),
"result": None
})
if STOP_ON_FIRST_ERROR:
break
# Save per-iteration results
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
output_path = INPUT_CSV.replace(".csv", f"_results_iter_{iteration}_{timestamp}.json")
with open(output_path, 'w', encoding='utf-8') as f:
json.dump(iteration_results, f, indent=2, ensure_ascii=False)
print(f"\n✅ Iteration {iteration} complete. Saved to: {output_path}")
elapsed = time.time() - start_iter
print(f"⏱️ Iteration {iteration} took {elapsed:.1f}s ({elapsed/total_rows:.1f}s/row)")
print(f"\n🎉 All {NUM_ITERATIONS} iterations completed!")
##
View File
-384
View File
@@ -1,384 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
# ============================================================
# Organize Barcelona EDSS project safely
# - Creates a timestamped backup first
# - Creates a cleaner folder structure
# - Moves files conservatively
# - Does NOT delete anything
# ============================================================
PROJECT_ROOT="$(pwd)"
TIMESTAMP="$(date +%Y%m%d_%H%M%S)"
BACKUP_PARENT="${PROJECT_ROOT}/../Barcelona_backups"
BACKUP_DIR="${BACKUP_PARENT}/Barcelona_backup_${TIMESTAMP}"
echo "Project root: ${PROJECT_ROOT}"
echo "Backup dir: ${BACKUP_DIR}"
echo
# ------------------------------------------------------------
# Safety checks
# ------------------------------------------------------------
if [ ! -f "${PROJECT_ROOT}/README.md" ]; then
echo "WARNING: README.md not found. Are you sure you are in the project root?"
echo "Current directory: ${PROJECT_ROOT}"
read -r -p "Continue anyway? [y/N] " answer
case "$answer" in
y|Y|yes|YES) ;;
*) echo "Aborted."; exit 1 ;;
esac
fi
if [ -d "${PROJECT_ROOT}/.git" ]; then
if ! git diff --quiet || ! git diff --cached --quiet; then
echo "ERROR: Git working tree is not clean."
echo "Please commit or stash changes before organizing."
exit 1
fi
fi
echo "This script will:"
echo "1. Create a full backup."
echo "2. Create organized folders."
echo "3. Move files into data/, prompts/, scripts/, results/, archive/."
echo "4. Keep your original files in the backup."
echo
read -r -p "Proceed? [y/N] " answer
case "$answer" in
y|Y|yes|YES) ;;
*) echo "Aborted."; exit 1 ;;
esac
# ------------------------------------------------------------
# Backup
# ------------------------------------------------------------
mkdir -p "${BACKUP_PARENT}"
echo
echo "Creating backup..."
rsync -a \
--exclude "enarcelona/" \
--exclude "env/" \
--exclude ".venv/" \
--exclude "__pycache__/" \
"${PROJECT_ROOT}/" "${BACKUP_DIR}/"
echo "Backup created at:"
echo "${BACKUP_DIR}"
# ------------------------------------------------------------
# Create target structure
# ------------------------------------------------------------
echo
echo "Creating new directory structure..."
mkdir -p \
data/raw \
data/processed \
data/ground_truth \
data/external \
prompts \
scripts \
results/benchmark_runs \
results/final_results/model_outputs \
results/figures \
results/tables \
results/logs \
manuscript/figures \
manuscript/tables \
archive/old_scripts \
archive/old_results \
archive/tmp \
archive/old_data \
archive/old_project_files
# ------------------------------------------------------------
# Helper move functions
# ------------------------------------------------------------
move_if_exists() {
src="$1"
dest="$2"
if [ -e "$src" ]; then
mkdir -p "$(dirname "$dest")"
if [ -e "$dest" ]; then
echo "SKIP: destination exists: $dest"
else
echo "MOVE: $src -> $dest"
mv "$src" "$dest"
fi
fi
}
move_glob_if_exists() {
pattern="$1"
dest_dir="$2"
mkdir -p "$dest_dir"
shopt -s nullglob
files=( $pattern )
shopt -u nullglob
for f in "${files[@]}"; do
base="$(basename "$f")"
dest="${dest_dir}/${base}"
if [ -e "$dest" ]; then
echo "SKIP: destination exists: $dest"
else
echo "MOVE: $f -> $dest"
mv "$f" "$dest"
fi
done
}
# ------------------------------------------------------------
# Move prompts / attached instruction files
# ------------------------------------------------------------
echo
echo "Moving prompt and instruction files..."
move_if_exists "attach/Komplett.txt" "prompts/Komplett.txt"
move_if_exists "attach/just_edss_schema.gbnf" "prompts/just_edss_schema.gbnf"
move_if_exists "attach/just_edss_text.txt" "prompts/just_edss_text.txt"
# Move leftover attach folder if empty or archive it
if [ -d "attach" ]; then
if [ -z "$(ls -A attach)" ]; then
rmdir attach
else
move_if_exists "attach" "archive/old_project_files/attach"
fi
fi
# ------------------------------------------------------------
# Move important data files
# ------------------------------------------------------------
echo
echo "Moving data files..."
move_if_exists "Data/MS_Briefe_400_with_unique_id_SHA3_explore_cleaned.csv" \
"data/processed/MS_Briefe_400_with_unique_id_SHA3_explore_cleaned.csv"
move_if_exists "Data/MS_Briefe_400_with_unique_id_SHA3_explore_cleaned_unique.csv" \
"data/processed/MS_Briefe_400_with_unique_id_SHA3_explore_cleaned_unique.csv"
move_if_exists "Data/Join_edssandsub.tsv" \
"data/ground_truth/Join_edssandsub.tsv"
move_if_exists "Data/GT_Numbers.csv" \
"data/ground_truth/GT_Numbers.csv"
move_if_exists "Data/Annika1.csv" \
"data/ground_truth/Annika1.csv"
move_if_exists "Data/comparison.tsv" \
"data/ground_truth/comparison.tsv"
move_if_exists "Data/edss_distribution_summary.csv" \
"data/processed/edss_distribution_summary.csv"
move_if_exists "Data/empirical_confidence_table.csv" \
"data/processed/empirical_confidence_table.csv"
move_if_exists "Data/functional_system_colors.json" \
"data/processed/functional_system_colors.json"
move_if_exists "Data/Test.csv" \
"archive/tmp/Test.csv"
move_if_exists "Data/Hernan" \
"data/external/Hernan"
move_if_exists "Data/iteration" \
"archive/old_data/iteration"
# Old generated JSON/results from Data folder
move_glob_if_exists "Data/*results*.json" "archive/old_results"
move_glob_if_exists "Data/join_*.tsv" "archive/old_results"
# Move remaining Data folder if anything left
if [ -d "Data" ]; then
if [ -z "$(ls -A Data)" ]; then
rmdir Data
else
move_if_exists "Data" "archive/old_data/Data_remaining"
fi
fi
# ------------------------------------------------------------
# Move benchmark results
# ------------------------------------------------------------
echo
echo "Moving benchmark results..."
if [ -d "results_edss_benchmark" ]; then
move_glob_if_exists "results_edss_benchmark/run_*" "results/benchmark_runs"
move_if_exists "results_edss_benchmark/endresults" \
"results/final_results/model_outputs"
move_if_exists "results_edss_benchmark/confusion_matrices" \
"results/figures/confusion_matrices"
if [ -z "$(ls -A results_edss_benchmark 2>/dev/null || true)" ]; then
rmdir results_edss_benchmark
else
move_if_exists "results_edss_benchmark" \
"archive/old_results/results_edss_benchmark_remaining"
fi
fi
# ------------------------------------------------------------
# Move old/general results
# ------------------------------------------------------------
echo
echo "Moving existing results files..."
if [ -d "results" ]; then
# Figures
move_glob_if_exists "results/*.png" "results/figures"
move_glob_if_exists "results/*.PNG" "results/figures"
move_glob_if_exists "results/*.jpg" "results/figures"
move_glob_if_exists "results/*.jpeg" "results/figures"
move_glob_if_exists "results/*.svg" "results/figures"
# Tables
move_glob_if_exists "results/*.csv" "results/tables"
move_glob_if_exists "results/*.tsv" "results/tables"
move_glob_if_exists "results/*.xlsx" "results/tables"
# Subfolders that look like old results
move_if_exists "results/Jan_visual" "archive/old_results/Jan_visual"
move_if_exists "results/Lab_meeting" "archive/old_results/Lab_meeting"
move_if_exists "results/just_edss" "archive/old_results/just_edss"
fi
# Root-level result tables
move_if_exists "edss_distribution_summary.csv" \
"results/tables/edss_distribution_summary.csv"
# Logs
move_if_exists "edss_benchmark_terminal.log" \
"results/logs/edss_benchmark_terminal.log"
# ------------------------------------------------------------
# Move scripts
# ------------------------------------------------------------
echo
echo "Moving scripts..."
move_if_exists "audit.py" "scripts/audit_outputs.py"
move_if_exists "certainty.py" "scripts/analyze_certainty.py"
move_if_exists "certainty_show.py" "scripts/certainty_show.py"
move_if_exists "figure1.py" "scripts/figure1.py"
move_if_exists "show_plots.py" "scripts/show_plots.py"
move_if_exists "show_plots.py.orig" "archive/old_scripts/show_plots.py.orig"
# Apps / old entry points
move_if_exists "app.py" "archive/old_scripts/app.py"
move_if_exists "total_app.py" "archive/old_scripts/total_app.py"
# Existing project visuals folder
move_if_exists "project/visuals" "results/figures/project_visuals"
if [ -d "project" ]; then
if [ -z "$(ls -A project)" ]; then
rmdir project
else
move_if_exists "project" "archive/old_project_files/project"
fi
fi
# ------------------------------------------------------------
# Environment folder
# ------------------------------------------------------------
echo
echo "Handling virtual environment..."
if [ -d "enarcelona" ]; then
echo "Leaving virtual environment in place: enarcelona/"
echo "It should remain ignored by .gitignore."
fi
# ------------------------------------------------------------
# Create README notes
# ------------------------------------------------------------
echo
echo "Writing organization notes..."
cat > "PROJECT_STRUCTURE.md" <<'EOF'
# Project Structure
This project was reorganized into:
- `data/`
- `raw/`: original raw data, if retained locally
- `processed/`: cleaned or derived input data
- `ground_truth/`: manually annotated reference data
- `external/`: externally provided data
- `prompts/`
- EDSS instructions and prompt/schema assets
- `scripts/`
- runnable analysis and plotting scripts
- `results/`
- `benchmark_runs/`: full model benchmark runs
- `final_results/`: final selected model outputs
- `figures/`: generated figures
- `tables/`: generated tables
- `logs/`: terminal logs
- `manuscript/`
- final figures and tables for paper/thesis writing
- `archive/`
- old scripts, old results, temporary files, and unclear legacy files
Important:
The reorganization was performed after creating a full timestamped backup.
EOF
# ------------------------------------------------------------
# Final checks
# ------------------------------------------------------------
echo
echo "Organization complete."
echo
echo "Backup is here:"
echo "${BACKUP_DIR}"
echo
echo "New top-level structure:"
find . -maxdepth 2 -type d | sort
echo
if [ -d ".git" ]; then
echo "Git status:"
git status --short
fi
echo
echo "Next recommended commands:"
echo " git status"
echo " git add ."
echo " git commit -m \"Reorganize project structure\""
-481
View File
@@ -1,481 +0,0 @@
1 VISUAL OPTIC FUNCTIONS
VISUAL ACUITY
The visual acuity score is based on the line in the Snellen chart at 20 feet 5 meters
for which the patient makes no more than one error using best available correction
Alternatively best corrected near vision can be assessed but this should be noted and
consistently performed during follow up examinations Switching from near to distance
visual acuity measurements should be avoided in follow up examinations
VISUAL FIELDS
0 normal
1 signs only deficits present only on formal confrontational testing
2 moderate patient aware of deficit but incomplete hemianopsia on examination
3 marked complete homonymous hemianopsia or equivalent
SCOTOMA
0 none
1 small detectable only on formal confrontational testing
2 large spontaneously reported by patient
* DISC PALLOR
0 not present
1 present
NOTE
When determining the EDSS step the Visual FS score must be converted to a lower
score as follows
Visual FS Score 6 5 4 3 2 1
Converted Visual FS Score 4 3 3 2 2 1
FUNCTIONAL SYSTEM SCORE
0 normal
1 disc pallor and or small scotoma and or visual acuity corrected of worse eye less than 20 20 1.0 but better than 20 30 0.67
2 worse eye with maximal visual acuity corrected of 20 30 to 20 59 0.67 0.34
3 worse eye with large scotoma and or moderate decrease in fields and or maximal visual acuity corrected of 20 60 to 20 99 0.33 0.21
4 worse eye with marked decrease of fields and or maximal visual acuity corrected of 20 100 to 20 200 0.2 0.1 grade 3 plus maximal acuity of better eye of 20 60 0.33 or less
5 worse eye with maximal visual acuity corrected less than 20 200 0.1 grade 4 plus maximal acuity of better eye of 20 60 0.33 or less
6 grade 5 plus maximal acuity of better eye of 20 60 0.33 or less * = optional part of the examination
### BRAINSTEM FUNCTIONS
**DYSARTHRIA**
- **0**: None
- **1**: Signs only
- **2**: Mild: Clinically detectable, patient is aware
- **3**: Moderate: Obvious during conversation, impairs comprehension
- **4**: Marked: Incomprehensible speech
- **5**: Inability to speak
**DYSPHAGIA**
- **0**: None
- **1**: Signs only
- **2**: Mild: Difficulty with thin liquids
- **3**: Moderate: Difficulty with liquids and solid food
- **4**: Marked: Sustained difficulty, requires pureed diet
- **5**: Inability to swallow
**OTHER CRANIAL NERVE FUNCTIONS**
- **0**: Normal
- **1**: Signs only
- **2**: Mild disability: Clinically detectable deficit, patient is usually aware
- **3**: Moderate disability
- **4**: Marked disability
**EXTRAOCULAR MOVEMENTS (EOM) IMPAIRMENT**
- **0**: None
- **1**: Signs only: Subtle EOM weakness, no complaints of vision issues
- **2**: Mild: Subtle EOM weakness or obvious incomplete paralysis not noticed by patient
- **3**: Moderate: Obvious incomplete paralysis noticed by patient or complete loss in one direction
- **4**: Marked: Complete loss in more than one direction
**NYSTAGMUS**
- **0**: None
- **1**: Signs only or mild: Gaze-evoked nystagmus below moderate limits (equivalent to Brainstem FS score of 1)
- **2**: Moderate: Sustained nystagmus on horizontal/vertical gaze at 30 degrees, patient may not notice
- **3**: Severe: Nystagmus in primary position or coarse persistent nystagmus interfering with vision; complete internuclear ophthalmoplegia; oscillopsia
**TRIGEMINAL DAMAGE**
- **0**: None
- **1**: Signs only
- **2**: Mild: Clinically detectable numbness, patient is aware
- **3**: Moderate: Impaired sharp/dull discrimination in one to three branches or trigeminal neuralgia (at least one recent attack)
- **4**: Marked: Unable to discriminate between sharp/dull or complete loss of sensation in one or both nerves
**FACIAL WEAKNESS**
- **0**: None
- **1**: Signs only
- **2**: Mild: Clinically detectable weakness, patient is aware
- **3**: Moderate: Incomplete facial palsy (e.g., eye closure requires patching, drooling)
- **4**: Marked: Complete unilateral or bilateral facial palsy with lagophthalmus or difficulty with liquids
**HEARING LOSS**
- **0**: None
- **1**: Signs only: Hears finger rub less on one/both sides, lateralized Weber test but no complaints
- **2**: Mild: As in 1, aware of hearing problem
- **3**: Moderate: Does not hear finger rub on one/both sides, misses several whispered numbers
- **4**: Marked: Misses all or nearly all whispered numbers
**FUNCTIONAL SYSTEM SCORE**
- **0**: Normal
- **1**: Signs only
- **2**: Moderate nystagmus/EOM impairment/other mild disability
- **3**: Severe nystagmus/marked EOM impairment/moderate other cranial nerve disability
- **4**: Marked dysarthria/other marked disability
- **5**: Inability to swallow or speak
### PYRAMIDAL FUNCTIONS
#### REFLEXES
- **0**: Absent
- **1**: Diminished
- **2**: Normal
- **3**: Exaggerated
- **4**: Nonsustained clonus (a few beats of clonus)
- **5**: Sustained clonus
##### Cutaneous Reflexes
- **0**: Normal
- **1**: Weak
- **2**: Absent
###### Palmomental Reflex
- **0**: Absent
- **1**: Present
###### Plantar Response
- **0**: Flexor
- **1**: Neutral or equivocal
- **2**: Extensor
#### LIMB STRENGTH
The weakest muscle in each group defines the score for that muscle group. Optional functional tests (hopping on one foot and walking on heels/toes) are recommended for BMRC grades 35.
##### BMRC Rating Scale
- **0**: No muscle contraction detected
- **1**: Visible contraction without visible joint movement
- **2**: Visible movement only on the plane of gravity
- **3**: Active movement against gravity, but not against resistance
- **4**: Active movement against resistance, but not full strength
- **5**: Normal strength
#### FUNCTIONAL TESTS
##### Pronator Drift (Upper Extremities)
Pronation and downward drift:
- **0**: None
- **1**: Mild
- **2**: Evident
##### Position Test (Lower Extremities)
Ask patient to lift both legs together, with legs fully extended at the knee. Sinking:
- **0**: None
- **1**: Mild
- **2**: Evident
- **3**: Able to lift only one leg at a time (grade from the horizontal position at the hip joints in degrees)
- **4**: Unable to lift one leg at a time
##### Walking on Heels/Toes
- **0**: Normal
- **1**: Impaired
- **2**: Not possible
##### Hopping on One Foot
- **0**: Normal
- **1**: 610 times
- **2**: 15 times
- **3**: Not possible
#### LIMB SPASTICITY (AFTER RAPID FLEXION OF THE EXTREMITY)
- **0**: None
- **1**: Mild: barely increased muscle tone
- **2**: Moderate: moderately increased muscle tone that can be overcome; full range of motion is possible
- **3**: Severe: severely increased muscle tone that is extremely difficult to overcome; full range of motion is not possible
- **4**: Contracted
#### GAIT SPASTICITY
- **0**: None
- **1**: Barely perceptible
- **2**: Evident: minor interference with function
- **3**: Permanent shuffling: major interference with function
#### OVERALL MOTOR PERFORMANCE
- **0**: Normal
- **1**: Abnormal weakness (as compared to peers) in performing more demanding tasks, e.g., walking longer distances; no reduction in limb strength on formal testing
- **2**: Reduction in strength of individual muscle groups at confrontational testing
#### FUNCTIONAL SYSTEM SCORE
- **0**: Normal
- **1**: Abnormal signs without disability
- **2**: Minimal disability: patient complains of motor-fatigability or reduced performance in strenuous motor tasks (motor performance grade 1) and/or BMRC grade 4 in one or two muscle groups
- **3**: Mild to moderate paraparesis or hemiparesis: usually BMRC grade 4 in more than two muscle groups; and/or BMRC grade 3 in one or two muscle groups (movements against gravity
are possible); and/or severe monoparesis: BMRC grade 2 or less in one muscle group
- **4**: Marked paraparesis or hemiparesis: usually BMRC grade 2 in two limbs or monoplegia with BMRC grade 0 or 1 in one limb; and/or moderate tetraparesis: BMRC grade 3 in three or more limbs
- **5**: Paraplegia: BMRC grade 0 or 1 in all muscle groups of the lower limbs; and/or marked tetraparesis: BMRC grade 2 or less in three or more limbs; and/or hemiplegia
- **6**: Tetraplegia: BMRC grade 0 or 1 in all muscle groups of the upper and lower limbs
### CEREBELLAR FUNCTIONS
#### HEAD TREMOR
- **0**: none
- **1**: mild
- **2**: moderate
- **3**: severe
#### TRUNCAL ATAXIA
- **0**: none
- **1**: signs only
- **2**: mild (swaying with eyes closed)
- **3**: moderate (swaying with eyes open)
- **4**: severe (unable to sit without assistance)
#### LIMB ATAXIA (TREMOR / DYSMETRIA AND RAPID ALTERNATING MOVEMENTS)
- **0**: none
- **1**: signs only
- **2**: mild (tremor or clumsy movements easily seen, minor interference with function)
- **3**: moderate (tremor or clumsy movements interfere with function in all spheres)
- **4**: severe (most functions are very difficult)
#### TANDEM (STRAIGHT LINE) WALKING
- **0**: normal
- **1**: impaired
- **2**: not possible
#### GAIT ATAXIA
- **0**: none
- **1**: signs only
- **2**: mild (problems with balance realized by patient and/or significant other)
- **3**: moderate (abnormal balance with ordinary walking)
- **4**: severe (unable to walk more than a few steps unassisted or requires a walking aid or assistance due to ataxia)
#### ROMBERG TEST
- **0**: normal
- **1**: mild (mild instability with eyes closed)
- **2**: moderate (not stable with eyes closed)
- **3**: severe (not stable with eyes open)
#### OTHER CEREBELLAR TESTS
- **0**: normal
- **1**: mild abnormality
- **2**: moderate abnormality
- **3**: severe abnormality
**NOTE:**
- The presence of severe gait and/or truncal ataxia alone (without severe ataxia in three or four limbs) results in a Cerebellar FS score of 3.
- If weakness or sensory deficits interfere with the testing of ataxia, score the patients actual performance. Indicate the possible role of weakness by marking an "X" after the
affected subsystems and Cerebellar FS score.
#### FUNCTIONAL SYSTEM SCORE
- **0**: normal
- **1**: abnormal signs without disability
- **2**: mild ataxia and/or moderate station ataxia (Romberg) and/or tandem walking not possible
- **3**: moderate limb ataxia and/or moderate or severe gait/truncal ataxia
- **4**: severe gait/truncal ataxia and severe ataxia in three or four limbs
- **5**: unable to perform coordinated movements due to ataxia
- **X**: pyramidal weakness (BMRC grade 3 or worse in limb strength) or sensory deficits interfere with cerebellar testing
### SENSORY FUNCTIONS
#### SUPERFICIAL SENSATION (LIGHT TOUCH AND PAIN)
- **0**: normal
- **1**: signs only (slightly diminished sensation on formal testing, patient not aware)
- **2**: mild (patient aware of impaired light touch or pain but can discriminate sharp/dull)
- **3**: moderate (impaired discrimination of sharp/dull)
- **4**: marked (unable to discriminate between sharp/dull and/or unable to feel light touch)
- **5**: complete loss (anesthesia)
#### VIBRATION SENSE (AT THE MOST DISTAL JOINT)
- **0**: normal
- **1**: mild (graded tuning fork 57 of 8; detects more than 10 seconds but less than examiner)
- **2**: moderate (graded tuning fork 14 of 8; detects between 2 and 10 sec.)
- **3**: marked (complete loss of vibration sense)
#### POSITION SENSE
- **0**: normal
- **1**: mild (12 incorrect responses, only distal joints affected)
- **2**: moderate (misses many movements of fingers or toes; proximal joints affected)
- **3**: marked (no perception of movement, astasia)
* **LHERMITTES SIGN** (does not contribute to the Sensory FS score)
- **0**: negative
- **1**: positive
* **PARAESTHESIAE (TINGLING)** (does not contribute to the Sensory FS score)
- **0**: none
- **1**: present
#### FUNCTIONAL SYSTEM SCORE
- **0**: normal
- **1**: impaired superficial sensation in one or two limbs
- **2**: mild impairment in more than two limbs, no major proprioceptive deficits
- **3**: moderate impairment in more than two limbs with minor proprioceptive deficits
- **4**: severe impairment in more than two limbs with significant proprioceptive deficits
- **5**: loss of sensation in one or two limbs, significant proprioceptive deficits in most of the body below the head
- **6**: essentially no sensation below the head
### BOWEL AND BLADDER FUNCTIONS
#### URINARY HESITANCY AND RETENTION
- **0**: none
- **1**: mild (no major impact on lifestyle)
- **2**: moderate (urinary retention; frequent urinary tract infections)
- **3**: severe (requires catheterization)
- **4**: loss of function (overflow incontinence)
#### URINARY URGENCY AND INCONTINENCE
- **0**: none
- **1**: mild (no major impact on lifestyle)
- **2**: moderate (rare incontinence occurring no more than once a week; must wear pads)
- **3**: severe (frequent incontinence occurring from several times a week to more than once a day; must wear urinal or pads)
- **4**: loss of function (loss of bladder control)
#### BLADDER CATHETERIZATION
- **0**: none
- **1**: intermittent self-catheterization
- **2**: constant catheterization
#### BOWEL DYSFUNCTION
- **0**: none
- **1**: mild (no incontinence, no major impact on lifestyle, mild constipation)
- **2**: moderate (must wear pads or alter lifestyle to be near lavatory)
- **3**: severe (in need of enemas or manual measures to evacuate bowels)
- **4**: complete loss of function
#### SEXUAL DYSFUNCTION
**Male**
- **0**: none
- **1**: mild (difficulty maintaining erection during intercourse, but achieves erection and still has intercourse)
- **2**: moderate (difficulty achieving erection, decreased libido, still has intercourse and reaches orgasm)
- **3**: severe (marked decrease in libido, inability to achieve full erection, intercourse with difficulty, hypoorgasmia)
- **4**: loss of function
**Female**
- **0**: none
- **1**: mild (mild lack of lubrication, still sexually active and reaches orgasm)
- **2**: moderate (dyspareunia, hypoorgasmia, decrease in sexual activity)
- **3**: severe (marked decrease in sexual activity, anorgasmia)
- **4**: loss of function
**NOTE**
When determining the EDSS step, the Bowel and Bladder FS score must be converted to a lower score as follows:
- Bowel and Bladder FS Score: 6 → Converted Bowel and Bladder FS Score: 5
- Bowel and Bladder FS Score: 5 → Converted Bowel and Bladder FS Score: 4
- Bowel and Bladder FS Score: 4 → Converted Bowel and Bladder FS Score: 3
- Bowel and Bladder FS Score: 3 → Converted Bowel and Bladder FS Score: 3
- Bowel and Bladder FS Score: 2 → Converted Bowel and Bladder FS Score: 2
- Bowel and Bladder FS Score: 1 → Converted Bowel and Bladder FS Score: 1
Sexual dysfunction can be documented but generally does not impact the FS score due to assessment difficulties by examining physicians.
### FUNCTIONAL SYSTEM SCORE
- **0**: normal
- **1**: mild urinary hesitancy, urgency, and/or constipation
- **2**: moderate urinary hesitancy/retention and/or moderate urinary urgency/incontinence and/or moderate bowel dysfunction
- **3**: frequent urinary incontinence or intermittent self-catheterization; needs enemas or manual measures to evacuate bowels
- **4**: in need of almost constant catheterization
- **5**: loss of bladder or bowel function (external or indwelling catheter)
- **6**: loss of bowel and bladder function
### CEREBRAL FUNCTIONS
#### DEPRESSION AND EUPHORIA
- **0**: none
- **1**: present (Patient complains of depression or is considered depressed or euphoric by the investigator or significant other.)
**Note**: Depression and Euphoria are documented on the scoring sheet but are not taken into consideration for FS and EDSS calculation.
#### DECREASE IN MENTATION
- **0**: none
- **1**: signs only (not apparent to patient and/or significant other)
- **2**: mild (Patient and/or significant other report mild changes in mentation. Examples include: impaired ability to follow a rapid course of association or survey complex matters;
impaired judgment in certain demanding situations; capable of handling routine daily activities, but unable to tolerate additional stressors; intermittently symptomatic even with
normal levels of stress; reduced performance; tendency toward negligence due to obliviousness or fatigue.)
- **3**: moderate (Definite abnormalities on brief mental status testing, but still oriented to person, place, and time)
- **4**: marked (Not oriented in one or two spheres (person, place, or time); marked effect on lifestyle)
- **5**: dementia, confusion, and/or complete disorientation
#### FATIGUE
- **0**: none
- **1**: mild (Does not usually interfere with daily activities)
- **2**: moderate (Interferes but does not limit daily activities for more than 50%)
- **3**: severe (Significant limitation in daily activities (> 50% reduction))
**Note**: Because fatigue is difficult to evaluate objectively, in some studies it does not contribute to the Cerebral FS score or EDSS step. Please adhere to the studys specific
instructions.
### FUNCTIONAL SYSTEM SCORE
- **0**: normal
- **1**: signs only in decrease in mentation; mild fatigue
- **2**: mild decrease in mentation; moderate or severe fatigue
- **3**: moderate decrease in mentation
- **4**: marked decrease in mentation
- **5**: dementia
### AMBULATION
**Unrestricted Ambulation**
- The patient can walk a normal distance without assistance, comparable to healthy individuals of similar age and physical condition.
- EDSS step can range from 0 to 5.0, depending on the Functional System (FS) scores.
**Fully Ambulatory**
- At least 500 meters of ambulation without assistance, but not unrestricted.
- EDSS step can range from 2.0 to 5.0, depending on FS scores.
- The Pyramidal and/or Cerebellar FS must be ≥ 2 to reflect this restriction in ambulation.
**Ambulation < 500 Meters**
- If the walking distance is less than 500 meters, the EDSS step must be ≥ 4.5, depending on the walking ranges provided by the ambulation score and combination of FS scores.
- EDSS steps 5.5 to 8.0 are exclusively defined by the ability to ambulate and type of assistance required, or the ability to use a wheelchair.
**Assistance Needed**
- Definitions for EDSS steps 6.0 or 6.5 include both the type of assistance required when walking and the walking range.
- Assistance by another person is equivalent to bilateral assistance.
**Note:**
- The ambulation score represents both the walking range and the type of assistance required.
- This score replaces several checkboxes used previously on the scoring sheet but does not introduce new definitions.
- Use of a wheelchair can now be scored on the scoring sheet.
- Indicate the reported distance and time for the patient in the appropriate field on the scoring sheet, followed by the type of assistance and walking distance measured during assessment.
### DISTANCE AND TIME REPORTED BY PATIENT
**Maximal Unassisted Walking Distance**
- Maximal unassisted walking distance reported by the patient (in meters) without rest or assistance.
- Time required to walk the maximum distance according to the patient (in minutes).
**Assistance**
0. Without help or assistance (allowing use of an ankle-foot orthotic device, but no other assistive devices).
1. Unilateral assistance: one stick/crutch/brace.
2. Bilateral assistance: two sticks/crutches/braces or assistance by another person.
3. Wheelchair.
**Distance**
- Measure the distance the patient can walk in meters.
- **Unassisted:** Observe walking for a minimum of 500 meters and measure time needed, if possible.
- **Assisted:** Observe walking with assistive devices or help from another person for a minimum of 130 meters, if possible.
---
### AMBULATION SCORE
0. Unrestricted
1. Fully ambulatory
2. ≥ 300 meters but < 500 meters, without help or assistance (EDSS 4.5 or 5.0)
3. ≥ 200 meters but < 300 meters, without help or assistance (EDSS 5.0)
4. ≥ 100 meters but < 200 meters, without help or assistance (EDSS 5.5)
5. Walking range < 100 meters without assistance (EDSS 6.0)
6. Unilateral assistance, ≥ 50 meters (EDSS 6.0)
7. Bilateral assistance, ≥ 120 meters (EDSS 6.0)
8. Unilateral assistance, < 50 meters (EDSS 6.5)
9. Bilateral assistance, ≥ 5 meters but < 120 meters (EDSS 6.5)
10. Uses wheelchair without help; unable to walk 5 meters even with aid, essentially restricted to wheelchair; wheels self and transfers alone; up and about in wheelchair for some 12 hours a day (EDSS 7.0)
11. Uses wheelchair with help; unable to take more than a few steps; restricted to wheelchair; may need some help in transferring and wheeling self (EDSS 7.5)
12. Essentially restricted to bed or chair or perambulated in wheelchair, but out of bed most of the day; retains many self-care functions; generally has effective use of arms (EDSS 8.0)
Expanded Disability Status Scale (EDSS)
0 - Normal neurological exam (all Functional Systems [FS] grade 0)
1.0 - No disability, minimal signs in one FS (one FS grade 1)
1.5 - No disability, minimal signs in more than one FS (more than one FS grade 1)
2.0 - Minimal disability in one FS (one FS grade 2, others 0 or 1)
2.5 - Minimal disability in two FS (two FS grades 2, others 0 or 1)
3.0 - Moderate disability in one FS (one FS grade 3, others 0 or 1) though fully ambulatory;
or mild disability in three or four FS (three/four FS grades 2, others 0 or 1) though fully ambulatory
3.5 - Fully ambulatory but with moderate disability in one FS (one FS grade 3) and mild disability in one or two FS (one/two FS grade 2) and others 0 or 1;
or fully ambulatory with two FS grades 3 (others 0 or 1);
or fully ambulatory with five FS grades 2 (others 0 or 1)
4.0 - Unable to walk > 25 feet without aid
4.5 - Unable to walk > 100 feet without aid
5.0 - Relies on a walking aid; unable to walk > 300 feet without resting
5.5 - Relies on a walking aid; unable to walk > 200 feet without resting
6.0 - Unable to walk more than 50 feet with or without aid; cannot stand unaided for five minutes
6.5 - Unable to walk more than 10 feet with or without aid; cannot stand unaided for two minutes
7.0 - Unable to walk 5 meters even with aid, essentially restricted to wheelchair; wheels self and transfers alone; up and about in wheelchair some 12 hours a day
7.5 - Unable to take more than a few steps; restricted to wheelchair; may need some help in transferring and in wheeling self
8.0 - Essentially restricted to bed or chair or perambulated in wheelchair, but out of bed most of the day; retains many self-care functions; generally has effective use of arms
8.5 - Essentially restricted to bed much of the day; has some effective use of arm(s); retains some self-care functions
9.0 - Helpless bed patient; can communicate and eat
9.5 - Totally helpless bed patient; unable to communicate effectively or eat/swallow
10 - Death due to MS
-11
View File
@@ -1,11 +0,0 @@
EDSS-kv ::= "\"EDSS\"" space ":" space number
Reason ::= "\"" char{0,400} "\"" space
Reason-kv ::= "\"Reason\"" space ":" space Reason
boolean ::= ("true" | "false") space
char ::= [^"\\\x7F\x00-\x1F] | [\\] (["\\bfnrt] | "u" [0-9a-fA-F]{4})
decimal-part ::= [0-9]{1,16}
integral-part ::= [0] | [1-9] [0-9]{0,15}
nicht-klassifizierbar-kv ::= "\"nicht_klassifizierbar\"" space ":" space boolean
number ::= ("-"? integral-part) ("." decimal-part)? ([eE] [-+]? integral-part)? space
root ::= "{" space Reason-kv "," space nicht-klassifizierbar-kv ( "," space ( EDSS-kv ) )? "}" space
space ::= | " " | "\n"{1,2} [ \t]{0,20}
-25
View File
@@ -1,25 +0,0 @@
Expanded Disability Status Scale (EDSS)
0 - Normal neurological exam (all Functional Systems [FS] grade 0)
1.0 - No disability, minimal signs in one FS (one FS grade 1)
1.5 - No disability, minimal signs in more than one FS (more than one FS grade 1)
2.0 - Minimal disability in one FS (one FS grade 2, others 0 or 1)
2.5 - Minimal disability in two FS (two FS grades 2, others 0 or 1)
3.0 - Moderate disability in one FS (one FS grade 3, others 0 or 1) though fully ambulatory;
or mild disability in three or four FS (three/four FS grades 2, others 0 or 1) though fully ambulatory
3.5 - Fully ambulatory but with moderate disability in one FS (one FS grade 3) and mild disability in one or two FS (one/two FS grade 2) and others 0 or 1;
or fully ambulatory with two FS grades 3 (others 0 or 1);
or fully ambulatory with five FS grades 2 (others 0 or 1)
4.0 - Unable to walk > 25 feet without aid
4.5 - Unable to walk > 100 feet without aid
5.0 - Relies on a walking aid; unable to walk > 300 feet without resting
5.5 - Relies on a walking aid; unable to walk > 200 feet without resting
6.0 - Unable to walk more than 50 feet with or without aid; cannot stand unaided for five minutes
6.5 - Unable to walk more than 10 feet with or without aid; cannot stand unaided for two minutes
7.0 - Unable to walk 5 meters even with aid, essentially restricted to wheelchair; wheels self and transfers alone; up and about in wheelchair some 12 hours a day
7.5 - Unable to take more than a few steps; restricted to wheelchair; may need some help in transferring and in wheeling self
8.0 - Essentially restricted to bed or chair or perambulated in wheelchair, but out of bed most of the day; retains many self-care functions; generally has effective use of arms
8.5 - Essentially restricted to bed much of the day; has some effective use of arm(s); retains some self-care functions
9.0 - Helpless bed patient; can communicate and eat
9.5 - Totally helpless bed patient; unable to communicate effectively or eat/swallow
10 - Death due to MS
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+2320
View File
File diff suppressed because it is too large Load Diff