Dermatological Safety: Photoallergy (Type IV Hypersensitivity) Dashboard

DrugBank database
MolPort database
Python script number 118 to build the frequency distribution graph of the Photoallergy parameter on DrugBank molecules.
import matplotlib.pyplot as plt
import matplotlib.colors as mcolors
from matplotlib.patches import Patch
import numpy as np
from scipy.interpolate import PchipInterpolator

# 1. PASTE YOUR DATA HERE (Photoallergy)
datos_crudos = """Bin Center	% Frequency
0.0499999999999999	1.52866242038217
0.0999999999999999	5.64755838641189
0.15	9.4692144373673
0.2	8.78980891719745
0.25	9.17197452229299
0.3	9.51167728237792
0.35	7.85562632696391
0.4	7.09129511677282
0.45	6.53927813163482
0.5	7.13375796178344
0.55	6.53927813163482
0.6	7.3036093418259
0.65	4.37367303609342
0.7	3.86411889596603
0.75	2.59023354564756
0.8	1.74097664543524
0.85	0.721868365180467
0.9	0.127388535031847
0.95	0
1	0"""

# 2. AUTOMATIC PROCESSING
lineas = datos_crudos.strip().split('\n')[1:] 
bins_array = []
freq_array = []

for linea in lineas:
    b, f = linea.strip().split() 
    bins_array.append(float(b))
    freq_array.append(float(f))

bins = np.array(bins_array)
freq = np.array(freq_array)
mean_val = np.average(bins, weights=freq)

interpolator = PchipInterpolator(bins, freq)
x_fit = np.linspace(min(bins), max(bins), 500)
y_fit = interpolator(x_fit)
y_fit = np.clip(y_fit, 0, None)

def get_colors(b_array):
    return ['#008000' if b < 0.4 else '#FFD700' if b <= 0.7 else '#B22222' for b in b_array]

colors_hex = get_colors(bins)
face_colors = [mcolors.to_rgba(c, alpha=0.60) for c in colors_hex]
edge_colors = [mcolors.to_rgba(c, alpha=0.90) for c in colors_hex]

# 3. CREATION OF THE GRAPH

plt.figure(figsize=(7, 6))

plt.bar(bins, freq, width=0.04, color=face_colors, edgecolor=edge_colors, linewidth=1.5, zorder=2)

# Gaussian Curve
amplitude = 8.907
mean = 0.3876
sd = 0.2043
gauss_y = amplitude * np.exp(-((x_fit - mean)**2) / (2 * sd**2))
plt.plot(x_fit, gauss_y, color='orange', linewidth=2.5, linestyle='-', alpha=0.7, zorder=4)

# 4. LABELS AND TITLES
plt.xlabel('Photoallergy Probability (PIH)', fontsize=12)
plt.ylabel('% Frequency', fontsize=12)
plt.title('Dermatological Safety: Photoallergy (Type IV Hypersensitivity)', fontsize=14)

legend_elements = [
    Patch(facecolor=mcolors.to_rgba('#008000', 0.6), edgecolor='#008000', label='Non-Sensitizer (< 0.4)'),
    Patch(facecolor=mcolors.to_rgba('#FFD700', 0.6), edgecolor='#FFD700', label='Moderate Haptenization Risk (0.4 - 0.7)'),
    Patch(facecolor=mcolors.to_rgba('#B22222', 0.6), edgecolor='#B22222', label='High Sensitization (> 0.7)'),
    plt.Line2D([0], [0], color='orange', lw=2, linestyle='-', alpha=0.7, label=f'Fit (Mean={mean}, SD={sd})')
]
plt.legend(handles=legend_elements, loc='upper right', framealpha=0.95, fontsize=10)

plt.grid(axis='y', linestyle=':', alpha=0.7, zorder=0)
plt.xlim(0, 1.05)
plt.ylim(0, max(max(freq), max(gauss_y)) * 1.15) 
plt.tight_layout()

plt.show()