eczemanage / easi_calculator.py
RezinWiz's picture
Upload 15 files
ac5551c verified
import numpy as np
from typing import Dict, Tuple, List
# EASI component categorization
EASI_CATEGORIES = {
'erythema': {
'name': 'Erythema (Redness)',
'conditions': [
'Post-Inflammatory hyperpigmentation',
'Erythema ab igne', 'Erythema annulare centrifugum',
'Erythema elevatum diutinum', 'Erythema gyratum repens',
'Erythema multiforme', 'Erythema nodosum',
'Flagellate erythema', 'Annular erythema',
'Drug Rash', 'Allergic Contact Dermatitis',
'Irritant Contact Dermatitis', 'Contact dermatitis',
'Acute dermatitis', 'Chronic dermatitis',
'Acute and chronic dermatitis',
'Sunburn', 'Photodermatitis', 'Phytophotodermatitis',
'Rosacea', 'Seborrheic Dermatitis', 'Stasis Dermatitis',
'Perioral Dermatitis',
'Burn erythema of abdominal wall',
'Burn erythema of back of hand',
'Burn erythema of lower leg',
'Cellulitis', 'Infection of skin', 'Viral Exanthem',
'Infected eczema', 'Crusted eczematous dermatitis',
'Inflammatory dermatosis',
'Vasculitis of the skin', 'Leukocytoclastic Vasculitis',
'Cutaneous lupus',
'CD - Contact dermatitis',
'Acute dermatitis, NOS',
'Herpes Simplex',
'Hypersensitivity',
'Impetigo',
'Pigmented purpuric eruption',
'Pityriasis rosea',
'Tinea',
'Tinea Versicolor'
]
},
'induration': {
'name': 'Induration/Papulation (Swelling/Bumps)',
'conditions': [
'Prurigo nodularis', 'Urticaria', 'Granuloma annulare', 'Morphea',
'Scleroderma', 'Lichen Simplex Chronicus',
'Lichen planus', 'lichenoid eruption',
'Lichen nitidus', 'Lichen spinulosus', 'Lichen striatus',
'Keratosis pilaris', 'Molluscum Contagiosum',
'Verruca vulgaris', 'Folliculitis', 'Acne',
'Hidradenitis', 'Nodular vasculitis', 'Sweet syndrome',
'Necrobiosis lipoidica', 'Basal Cell Carcinoma',
'SCC', 'SCCIS', 'SK', 'ISK',
'Cutaneous T Cell Lymphoma', 'Skin cancer',
'Adnexal neoplasm', 'Insect Bite', 'Milia',
'Miliaria', 'Xanthoma', 'Psoriasis',
'Lichen planus/lichenoid eruption'
]
},
'excoriation': {
'name': 'Excoriation (Scratching Damage)',
'conditions': [
'Inflicted skin lesions',
'Scabies', 'Abrasion', 'Abrasion of wrist',
'Superficial wound of body region', 'Scrape',
'Animal bite - wound', 'Pruritic dermatitis',
'Prurigo', 'Atopic dermatitis',
'Scab'
]
},
'lichenification': {
'name': 'Lichenification (Skin Thickening)',
'conditions': [
'Lichenified eczematous dermatitis',
'Acanthosis nigricans', 'Hyperkeratosis of skin',
'HK - Hyperkeratosis', 'Keratoderma',
'Ichthyosis', 'Ichthyosiform dermatosis',
'Chronic eczema', 'Psoriasis',
'Xerosis'
]
}
}
def probability_to_score(prob: float) -> int:
"""Convert probability to EASI score (0-3)"""
if prob < 0.171:
return 0
elif prob < 0.238:
return 1
elif prob < 0.421:
return 2
elif prob < 0.614:
return 3
else:
return 3
def calculate_easi_scores(predictions: Dict) -> Tuple[Dict, int]:
"""
Calculate EASI component scores based on condition probabilities
Args:
predictions: Dictionary containing prediction results
Returns:
Tuple of (easi_results dict, total_easi_score int)
"""
easi_results = {}
all_condition_probs = predictions['all_condition_probabilities']
for component, category_info in EASI_CATEGORIES.items():
# Find all conditions in this category
category_conditions = []
for condition_name, probability in all_condition_probs.items():
# Skip "Eczema" as it should not be included
if condition_name.lower() == 'eczema':
continue
# Check if condition is in category
if condition_name in category_info['conditions']:
individual_score = probability_to_score(probability)
if individual_score > 0:
category_conditions.append({
'condition': condition_name,
'probability': probability,
'individual_score': individual_score
})
# Sort by probability
category_conditions.sort(key=lambda x: x['probability'], reverse=True)
# Calculate component score (sum, capped at 3)
component_score = sum(c['individual_score'] for c in category_conditions)
component_score = min(component_score, 3)
easi_results[component] = {
'name': category_info['name'],
'score': component_score,
'contributing_conditions': category_conditions
}
# Calculate total EASI score
total_easi = sum(result['score'] for result in easi_results.values())
return easi_results, total_easi
def format_easi_response(easi_results: Dict, total_easi: int) -> Dict:
"""
Format EASI results for API response
Args:
easi_results: EASI calculation results
total_easi: Total EASI score
Returns:
Formatted dictionary for JSON response
"""
return {
'total_score': total_easi,
'components': {
'erythema': easi_results['erythema']['score'],
'induration': easi_results['induration']['score'],
'excoriation': easi_results['excoriation']['score'],
'lichenification': easi_results['lichenification']['score']
},
'severity': get_severity_level(total_easi),
'component_details': {
component: {
'name': data['name'],
'score': data['score'],
'contributing_conditions': [
{
'condition': c['condition'],
'probability': round(c['probability'], 4),
'contribution': c['individual_score']
}
for c in data['contributing_conditions']
]
}
for component, data in easi_results.items()
}
}
def get_severity_level(total_easi: int) -> str:
"""Get severity level description from EASI score"""
if total_easi == 0:
return "No significant EASI features detected"
elif total_easi <= 3:
return "Mild EASI severity"
elif total_easi <= 6:
return "Moderate EASI severity"
elif total_easi <= 9:
return "Severe EASI severity"
else:
return "Very Severe EASI severity"