HugBot commited on
Commit
74d361e
Β·
verified Β·
1 Parent(s): 74c42bf

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +168 -0
app.py ADDED
@@ -0,0 +1,168 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # -*- coding: utf-8 -*-
2
+ """Untitled0.ipynb
3
+
4
+ Automatically generated by Colab.
5
+
6
+ Original file is located at
7
+ https://colab.research.google.com/drive/1fAC7aJH5phqKIh-7Eevi1MSy6Zzvi6Eo
8
+ """
9
+
10
+ import numpy as np
11
+ import matplotlib.pyplot as plt
12
+ import gradio as gr
13
+
14
+ # ── style ──────────────────────────────────────────────────────────────────
15
+ BG = '#f8fafc'
16
+ PANEL = '#ffffff'
17
+ GRID = '#e2e8f0'
18
+ ACCENT = '#2563eb'
19
+ C_INC = '#d97706'
20
+ C_ZS = '#7c3aed'
21
+ C_RR = '#059669'
22
+ C_TEXT = '#1e293b'
23
+ C_DIM = '#64748b'
24
+ C_BORDER= '#cbd5e1'
25
+
26
+ plt.rcParams.update({
27
+ 'figure.facecolor': BG, 'axes.facecolor': PANEL,
28
+ 'axes.edgecolor': C_BORDER, 'axes.labelcolor': C_DIM,
29
+ 'xtick.color': C_DIM, 'ytick.color': C_DIM,
30
+ 'grid.color': GRID, 'grid.linewidth': 0.8,
31
+ 'text.color': C_TEXT, 'font.family': 'monospace',
32
+ })
33
+
34
+ EP_RANGE = np.linspace(0, 15, 600)
35
+
36
+ def calc_paf(episodes, incidence, zscore, rr):
37
+ return 1 - 1 / np.exp(episodes * incidence * zscore * rr)
38
+
39
+ # ── plot function ──────────────────────────────────────────────────────────
40
+ def draw(ep, inc, zs, rr):
41
+ paf_curve = calc_paf(EP_RANGE, inc, zs, rr) * 100
42
+ cur_paf = calc_paf(ep, inc, zs, rr)
43
+ exponent = ep * inc * zs * rr
44
+
45
+ fig, axes = plt.subplots(1, 2, figsize=(15, 6),
46
+ gridspec_kw={'width_ratios': [3, 1]})
47
+ fig.patch.set_facecolor(BG)
48
+
49
+ # ── left: curve ───────────────────────────────────────────────────────
50
+ ax = axes[0]
51
+ ax.set_facecolor(PANEL)
52
+ for spine in ax.spines.values():
53
+ spine.set_color(C_BORDER)
54
+
55
+ # shadow + main curve
56
+ ax.plot(EP_RANGE, paf_curve, color=ACCENT, linewidth=5, alpha=0.15)
57
+ ax.plot(EP_RANGE, paf_curve, color=ACCENT, linewidth=2.2)
58
+
59
+ # crosshairs
60
+ ax.axvline(ep, color=ACCENT, linestyle='--', linewidth=1.2, alpha=0.6)
61
+ ax.axhline(cur_paf*100, color=ACCENT, linestyle='--', linewidth=1.2, alpha=0.3)
62
+
63
+ # dot at current point
64
+ ax.scatter([ep], [cur_paf*100], color=ACCENT, s=80, zorder=5,
65
+ edgecolors='white', linewidths=1.5)
66
+
67
+ # shaded area under curve up to current episode
68
+ mask = EP_RANGE <= ep
69
+ ax.fill_between(EP_RANGE[mask], paf_curve[mask], color=ACCENT, alpha=0.08)
70
+
71
+ ax.set_xlim(0, 15)
72
+ ax.set_ylim(0, 102)
73
+ ax.set_xlabel('Episodes', fontsize=11, labelpad=8)
74
+ ax.set_ylabel('PAF (%)', fontsize=11, labelpad=8)
75
+ ax.set_title('Population Attributable Fraction',
76
+ color=C_TEXT, fontsize=13, pad=12, fontweight='normal')
77
+ ax.grid(True, linewidth=0.8)
78
+
79
+ # annotation
80
+ ax.annotate(
81
+ f' {cur_paf*100:.1f}%',
82
+ xy=(ep, cur_paf*100),
83
+ xytext=(min(ep + 1, 13), cur_paf*100 + 4),
84
+ color=ACCENT, fontsize=15, fontweight='bold',
85
+ arrowprops=dict(arrowstyle='->', color=ACCENT, lw=1.2)
86
+ )
87
+
88
+ # formula watermark
89
+ ax.text(0.98, 0.05,
90
+ 'PAF = 1 βˆ’ exp(βˆ’ep Γ— inc Γ— Ξ”z Γ— RR)',
91
+ transform=ax.transAxes, fontsize=11,
92
+ color=C_DIM, ha='right', va='bottom')
93
+
94
+ # ── right: breakdown panel ────────────────────────────────────────────
95
+ ax2 = axes[1]
96
+ ax2.set_facecolor('#f1f5f9')
97
+ for spine in ax2.spines.values():
98
+ spine.set_color(C_BORDER)
99
+ ax2.set_xticks([]); ax2.set_yticks([])
100
+
101
+ rows = [
102
+ ('episodes', f'{ep:.2f}', ACCENT),
103
+ ('incidence', f'{inc:.3f}', C_INC),
104
+ ('Ξ”z / ep.', f'{zs:.3f}', C_ZS),
105
+ ('RR', f'{rr:.3f}', C_RR),
106
+ ('─' * 10, '─' * 6, C_BORDER),
107
+ ('exponent', f'{exponent:.4f}', '#475569'),
108
+ ('exp(βˆ’x)', f'{1/np.exp(exponent):.4f}', '#475569'),
109
+ ('─' * 10, '─' * 6, C_BORDER),
110
+ ('PAF', f'{cur_paf*100:.2f}%', ACCENT),
111
+ ]
112
+
113
+ ax2.text(0.5, 0.97, 'BREAKDOWN', transform=ax2.transAxes,
114
+ ha='center', fontsize=13, color=ACCENT, fontweight='bold')
115
+
116
+ y_start = 0.88
117
+ for i, (k, v, c) in enumerate(rows):
118
+ y = y_start - i * 0.095
119
+ ax2.text(0.08, y, k, transform=ax2.transAxes,
120
+ fontsize=11, color=C_DIM, va='center')
121
+ ax2.text(0.92, y, v, transform=ax2.transAxes,
122
+ fontsize=11, color=c, va='center', ha='right', fontweight='bold')
123
+
124
+ ax2.set_title('Parameters', color=C_TEXT, fontsize=13, pad=8)
125
+
126
+ plt.tight_layout(pad=1.5)
127
+ return fig
128
+
129
+ # ── Gradio UI ───────────────────────────���─────────────────────────────────
130
+ with gr.Blocks(theme=gr.themes.Soft(), title="PAF Visualizer") as demo:
131
+
132
+ gr.HTML("""
133
+ <div style="background:#eff6ff;border:1px solid #bfdbfe;border-radius:10px;
134
+ padding:12px 20px;margin-bottom:12px;font-family:monospace;">
135
+ <span style="color:#1e293b;font-size:20px;font-weight:600;">
136
+ Population Attributable Fraction Visualizer
137
+ </span><br>
138
+ <span style="color:#64748b;font-size:13px;">
139
+ PAF = 1 βˆ’ exp(βˆ’episodes Γ— incidence Γ— Ξ”z Γ— RR)
140
+ </span>
141
+ </div>""")
142
+
143
+ with gr.Row():
144
+ with gr.Column():
145
+ ep = gr.Slider(0, 15, value=4.2, step=0.1, label="Episodes")
146
+ inc = gr.Slider(0.01, 1, value=0.3, step=0.01, label="Pathogen Incidence")
147
+ zs = gr.Slider(0.01, 2, value=0.2, step=0.01, label="Ξ”z per Episode")
148
+ rr = gr.Slider(1.01, 5, value=1.5, step=0.05, label="Relative Risk (RR)")
149
+
150
+ reset_btn = gr.Button("Reset Defaults", variant="secondary")
151
+
152
+ plot = gr.Plot(label="")
153
+
154
+ # live update on any slider change
155
+ for slider in [ep, inc, zs, rr]:
156
+ slider.change(fn=draw, inputs=[ep, inc, zs, rr], outputs=plot)
157
+
158
+ # reset button
159
+ def reset():
160
+ return 5.0, 0.3, 1.2, 2.5
161
+
162
+ reset_btn.click(fn=reset, outputs=[ep, inc, zs, rr])
163
+
164
+ # draw on load
165
+ demo.load(fn=draw, inputs=[ep, inc, zs, rr], outputs=plot)
166
+
167
+ demo.launch()
168
+