tarekmasryo commited on
Commit
b44f5c3
Β·
verified Β·
1 Parent(s): b1b58c9

Upload streamlit_app.py

Browse files
Files changed (1) hide show
  1. src/streamlit_app.py +2760 -36
src/streamlit_app.py CHANGED
@@ -1,40 +1,2764 @@
1
- import altair as alt
2
  import numpy as np
3
  import pandas as pd
4
  import streamlit as st
 
 
 
 
 
 
 
 
 
 
 
 
5
 
6
- """
7
- # Welcome to Streamlit!
8
-
9
- Edit `/streamlit_app.py` to customize this app to your heart's desire :heart:.
10
- If you have any questions, checkout our [documentation](https://docs.streamlit.io) and [community
11
- forums](https://discuss.streamlit.io).
12
-
13
- In the meantime, below is an example of what you can do with just a few lines of code:
14
- """
15
-
16
- num_points = st.slider("Number of points in spiral", 1, 10000, 1100)
17
- num_turns = st.slider("Number of turns in spiral", 1, 300, 31)
18
-
19
- indices = np.linspace(0, 1, num_points)
20
- theta = 2 * np.pi * num_turns * indices
21
- radius = indices
22
-
23
- x = radius * np.cos(theta)
24
- y = radius * np.sin(theta)
25
-
26
- df = pd.DataFrame({
27
- "x": x,
28
- "y": y,
29
- "idx": indices,
30
- "rand": np.random.randn(num_points),
31
- })
32
-
33
- st.altair_chart(alt.Chart(df, height=700, width=700)
34
- .mark_point(filled=True)
35
- .encode(
36
- x=alt.X("x", axis=None),
37
- y=alt.Y("y", axis=None),
38
- color=alt.Color("idx", legend=None, scale=alt.Scale()),
39
- size=alt.Size("rand", legend=None, scale=alt.Scale(range=[1, 150])),
40
- ))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import numpy as np
2
  import pandas as pd
3
  import streamlit as st
4
+ import plotly.express as px
5
+ import plotly.graph_objects as go
6
+ from plotly.subplots import make_subplots
7
+ from sklearn.metrics import (
8
+ roc_auc_score,
9
+ average_precision_score,
10
+ brier_score_loss,
11
+ confusion_matrix,
12
+ roc_curve,
13
+ precision_recall_curve,
14
+ )
15
+ from datetime import datetime
16
 
17
+ # ========================================================
18
+ # Page Configuration
19
+ # ========================================================
20
+ st.set_page_config(
21
+ page_title="Advanced AI Health Intelligence Platform",
22
+ page_icon="🧬",
23
+ layout="wide",
24
+ initial_sidebar_state="expanded",
25
+ )
26
+
27
+ # ========================================================
28
+ # Advanced Styling with Glassmorphism & Animations
29
+ # ========================================================
30
+ st.markdown(
31
+ """
32
+ <style>
33
+ @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap');
34
+
35
+ * {
36
+ font-family: 'Inter', sans-serif;
37
+ }
38
+
39
+ .main {
40
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 50%, #f093fb 100%);
41
+ animation: gradientShift 15s ease infinite;
42
+ }
43
+
44
+ @keyframes gradientShift {
45
+ 0% { background-position: 0% 50%; }
46
+ 50% { background-position: 100% 50%; }
47
+ 100% { background-position: 0% 50%; }
48
+ }
49
+
50
+ .stApp {
51
+ background: transparent;
52
+ }
53
+
54
+ /* Glassmorphism Cards */
55
+ .glass-card {
56
+ background: rgba(255, 255, 255, 0.08);
57
+ backdrop-filter: blur(20px);
58
+ border-radius: 20px;
59
+ border: 1px solid rgba(255, 255, 255, 0.18);
60
+ padding: 25px;
61
+ box-shadow: 0 8px 32px 0 rgba(31, 38, 135, 0.37);
62
+ transition: all 0.4s cubic-bezier(0.165, 0.84, 0.44, 1);
63
+ }
64
+
65
+ .glass-card:hover {
66
+ transform: translateY(-8px);
67
+ box-shadow: 0 15px 45px 0 rgba(31, 38, 135, 0.5);
68
+ border: 1px solid rgba(255, 255, 255, 0.3);
69
+ }
70
+
71
+ /* Hero Section */
72
+ .hero-section {
73
+ background: linear-gradient(135deg, rgba(102, 126, 234, 0.15) 0%, rgba(118, 75, 162, 0.15) 100%);
74
+ backdrop-filter: blur(25px);
75
+ border-radius: 30px;
76
+ padding: 50px;
77
+ margin: 30px 0;
78
+ border: 2px solid rgba(255, 255, 255, 0.2);
79
+ box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
80
+ animation: float 6s ease-in-out infinite;
81
+ }
82
+
83
+ @keyframes float {
84
+ 0%, 100% { transform: translateY(0px); }
85
+ 50% { transform: translateY(-20px); }
86
+ }
87
+
88
+ .hero-title {
89
+ font-size: 4.5rem;
90
+ font-weight: 800;
91
+ background: linear-gradient(135deg, #ffffff 0%, #e0e7ff 100%);
92
+ -webkit-background-clip: text;
93
+ -webkit-text-fill-color: transparent;
94
+ margin: 0;
95
+ text-shadow: 0 4px 20px rgba(255, 255, 255, 0.3);
96
+ animation: pulse 3s ease-in-out infinite;
97
+ }
98
+
99
+ @keyframes pulse {
100
+ 0%, 100% { opacity: 1; }
101
+ 50% { opacity: 0.8; }
102
+ }
103
+
104
+ .hero-subtitle {
105
+ font-size: 1.4rem;
106
+ color: rgba(255, 255, 255, 0.9);
107
+ margin-top: 15px;
108
+ font-weight: 400;
109
+ }
110
+
111
+ /* Advanced Metrics Cards */
112
+ .metric-card {
113
+ background: linear-gradient(135deg, rgba(255, 255, 255, 0.12) 0%, rgba(255, 255, 255, 0.05) 100%);
114
+ backdrop-filter: blur(15px);
115
+ border-radius: 18px;
116
+ padding: 30px;
117
+ border: 1px solid rgba(255, 255, 255, 0.2);
118
+ position: relative;
119
+ overflow: hidden;
120
+ transition: all 0.3s ease;
121
+ }
122
+
123
+ .metric-card::before {
124
+ content: '';
125
+ position: absolute;
126
+ top: 0;
127
+ left: -100%;
128
+ width: 100%;
129
+ height: 100%;
130
+ background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent);
131
+ transition: left 0.5s;
132
+ }
133
+
134
+ .metric-card:hover::before {
135
+ left: 100%;
136
+ }
137
+
138
+ .metric-card:hover {
139
+ transform: scale(1.05);
140
+ border: 1px solid rgba(255, 255, 255, 0.4);
141
+ }
142
+
143
+ .metric-icon {
144
+ font-size: 3rem;
145
+ margin-bottom: 15px;
146
+ display: inline-block;
147
+ animation: bounce 2s infinite;
148
+ }
149
+
150
+ @keyframes bounce {
151
+ 0%, 100% { transform: translateY(0); }
152
+ 50% { transform: translateY(-10px); }
153
+ }
154
+
155
+ .metric-value {
156
+ font-size: 3rem;
157
+ font-weight: 800;
158
+ color: #ffffff;
159
+ margin: 10px 0;
160
+ text-shadow: 0 2px 10px rgba(0, 0, 0, 0.3);
161
+ }
162
+
163
+ .metric-label {
164
+ font-size: 1rem;
165
+ color: rgba(255, 255, 255, 0.8);
166
+ font-weight: 500;
167
+ text-transform: uppercase;
168
+ letter-spacing: 1.5px;
169
+ }
170
+
171
+ .metric-change {
172
+ font-size: 0.9rem;
173
+ color: rgba(255, 255, 255, 0.7);
174
+ margin-top: 8px;
175
+ }
176
+
177
+ /* AI Insight Cards */
178
+ .insight-card {
179
+ background: linear-gradient(135deg, rgba(16, 185, 129, 0.15) 0%, rgba(59, 130, 246, 0.15) 100%);
180
+ backdrop-filter: blur(20px);
181
+ border-radius: 16px;
182
+ padding: 25px;
183
+ margin: 15px 0;
184
+ border-left: 5px solid #10b981;
185
+ box-shadow: 0 8px 30px rgba(0, 0, 0, 0.2);
186
+ animation: slideInLeft 0.6s ease-out;
187
+ }
188
+
189
+ @keyframes slideInLeft {
190
+ from {
191
+ opacity: 0;
192
+ transform: translateX(-50px);
193
+ }
194
+ to {
195
+ opacity: 1;
196
+ transform: translateX(0);
197
+ }
198
+ }
199
+
200
+ .insight-warning {
201
+ border-left-color: #f59e0b;
202
+ background: linear-gradient(135deg, rgba(245, 158, 11, 0.15) 0%, rgba(239, 68, 68, 0.15) 100%);
203
+ }
204
+
205
+ .insight-danger {
206
+ border-left-color: #ef4444;
207
+ background: linear-gradient(135deg, rgba(239, 68, 68, 0.15) 0%, rgba(220, 38, 38, 0.15) 100%);
208
+ }
209
+
210
+ .insight-title {
211
+ font-size: 1.3rem;
212
+ font-weight: 700;
213
+ color: #ffffff;
214
+ margin-bottom: 10px;
215
+ }
216
+
217
+ .insight-text {
218
+ font-size: 1rem;
219
+ color: rgba(255, 255, 255, 0.9);
220
+ line-height: 1.6;
221
+ }
222
+
223
+ /* Section Headers */
224
+ .section-header {
225
+ background: linear-gradient(135deg, rgba(255, 255, 255, 0.1) 0%, rgba(255, 255, 255, 0.05) 100%);
226
+ backdrop-filter: blur(15px);
227
+ border-radius: 15px;
228
+ padding: 20px 30px;
229
+ margin: 30px 0 20px 0;
230
+ border: 1px solid rgba(255, 255, 255, 0.2);
231
+ display: flex;
232
+ align-items: center;
233
+ gap: 15px;
234
+ }
235
+
236
+ .section-icon {
237
+ font-size: 2rem;
238
+ animation: rotate 3s linear infinite;
239
+ }
240
+
241
+ @keyframes rotate {
242
+ from { transform: rotate(0deg); }
243
+ to { transform: rotate(360deg); }
244
+ }
245
+
246
+ .section-title {
247
+ font-size: 1.8rem;
248
+ font-weight: 700;
249
+ color: #ffffff;
250
+ margin: 0;
251
+ }
252
+
253
+ .section-subtitle {
254
+ font-size: 1rem;
255
+ color: rgba(255, 255, 255, 0.7);
256
+ margin: 5px 0 0 0;
257
+ }
258
+
259
+ /* Interactive Buttons */
260
+ .stButton > button {
261
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
262
+ color: white;
263
+ border: none;
264
+ border-radius: 12px;
265
+ padding: 15px 35px;
266
+ font-weight: 600;
267
+ font-size: 1rem;
268
+ transition: all 0.3s ease;
269
+ box-shadow: 0 5px 15px rgba(102, 126, 234, 0.4);
270
+ }
271
+
272
+ .stButton > button:hover {
273
+ transform: translateY(-3px);
274
+ box-shadow: 0 8px 25px rgba(102, 126, 234, 0.6);
275
+ }
276
+
277
+ /* Custom Tabs */
278
+ .stTabs [data-baseweb="tab-list"] {
279
+ gap: 12px;
280
+ background: rgba(255, 255, 255, 0.05);
281
+ backdrop-filter: blur(10px);
282
+ border-radius: 18px;
283
+ padding: 12px;
284
+ }
285
+
286
+ .stTabs [data-baseweb="tab"] {
287
+ background: transparent;
288
+ border-radius: 12px;
289
+ color: rgba(255, 255, 255, 0.8);
290
+ font-weight: 600;
291
+ font-size: 1.02rem;
292
+ padding: 14px 32px;
293
+ transition: all 0.3s ease;
294
+ }
295
+
296
+ .stTabs [aria-selected="true"] {
297
+ background: linear-gradient(
298
+ 135deg,
299
+ rgba(102, 126, 234, 0.3) 0%,
300
+ rgba(118, 75, 162, 0.3) 100%
301
+ );
302
+ color: #ffffff;
303
+ border: 1px solid rgba(255, 255, 255, 0.35);
304
+ }
305
+
306
+ /* Data Tables */
307
+ .stDataFrame {
308
+ background: rgba(255, 255, 255, 0.05);
309
+ backdrop-filter: blur(10px);
310
+ border-radius: 12px;
311
+ border: 1px solid rgba(255, 255, 255, 0.1);
312
+ }
313
+
314
+ /* Plotly Charts Enhancement */
315
+ .js-plotly-plot {
316
+ border-radius: 15px;
317
+ overflow: hidden;
318
+ box-shadow: 0 10px 40px rgba(0, 0, 0, 0.3);
319
+ }
320
+
321
+ /* Sidebar Styling */
322
+ [data-testid="stSidebar"] {
323
+ background: linear-gradient(180deg, rgba(102, 126, 234, 0.15) 0%, rgba(118, 75, 162, 0.15) 100%);
324
+ backdrop-filter: blur(20px);
325
+ border-right: 1px solid rgba(255, 255, 255, 0.2);
326
+ }
327
+
328
+ [data-testid="stSidebar"] .element-container {
329
+ color: white;
330
+ }
331
+
332
+ /* Loading Animation */
333
+ @keyframes shimmer {
334
+ 0% { background-position: -1000px 0; }
335
+ 100% { background-position: 1000px 0; }
336
+ }
337
+
338
+ .loading {
339
+ background: linear-gradient(90deg, rgba(255,255,255,0.1) 25%, rgba(255,255,255,0.2) 50%, rgba(255,255,255,0.1) 75%);
340
+ background-size: 1000px 100%;
341
+ animation: shimmer 2s infinite;
342
+ }
343
+
344
+ /* Status Badges */
345
+ .status-badge {
346
+ display: inline-block;
347
+ padding: 6px 15px;
348
+ border-radius: 20px;
349
+ font-weight: 600;
350
+ font-size: 0.85rem;
351
+ letter-spacing: 0.5px;
352
+ }
353
+
354
+ .status-success {
355
+ background: linear-gradient(135deg, #10b981 0%, #059669 100%);
356
+ color: white;
357
+ }
358
+
359
+ .status-warning {
360
+ background: linear-gradient(135deg, #f59e0b 0%, #d97706 100%);
361
+ color: white;
362
+ }
363
+
364
+ .status-danger {
365
+ background: linear-gradient(135deg, #ef4444 0%, #dc2626 100%);
366
+ color: white;
367
+ }
368
+
369
+ /* Tooltip Enhancement */
370
+ .tooltip-custom {
371
+ background: rgba(0, 0, 0, 0.9);
372
+ backdrop-filter: blur(10px);
373
+ border: 1px solid rgba(255, 255, 255, 0.2);
374
+ border-radius: 8px;
375
+ padding: 10px;
376
+ color: white;
377
+ }
378
+
379
+ /* Hide Streamlit Branding */
380
+ #MainMenu {visibility: hidden;}
381
+ footer {visibility: hidden;}
382
+ header {visibility: hidden;}
383
+ </style>
384
+ """,
385
+ unsafe_allow_html=True,
386
+ )
387
+
388
+ # ========================================================
389
+ # Enhanced Data Generation with More Features
390
+ # ========================================================
391
+ @st.cache_data
392
+ def generate_advanced_data(n_users: int = 5000, seed: int = 42):
393
+ rng = np.random.default_rng(seed)
394
+
395
+ # Basic demographics
396
+ user_id = np.arange(1, n_users + 1)
397
+ age = rng.integers(15, 75, size=n_users)
398
+ age_group = pd.cut(
399
+ age,
400
+ bins=[14, 22, 30, 40, 50, 60, 75],
401
+ labels=["Gen Z", "Millennials", "Young Adults", "Middle Age", "Senior", "Elderly"],
402
+ )
403
+ gender = rng.choice(["Male", "Female", "Other"], size=n_users, p=[0.48, 0.48, 0.04])
404
+ location = rng.choice(["Urban", "Suburban", "Rural"], size=n_users, p=[0.55, 0.35, 0.10])
405
+ occupation = rng.choice(
406
+ ["Student", "Professional", "Healthcare", "Tech", "Retired", "Other"],
407
+ size=n_users,
408
+ p=[0.20, 0.30, 0.15, 0.20, 0.10, 0.05],
409
+ )
410
+
411
+ # Digital behavior
412
+ screen_hours = np.clip(rng.normal(7.2, 2.5, size=n_users), 1.0, 18.0)
413
+ sleep_hours = np.clip(
414
+ 7.8 - 0.35 * (screen_hours - 7) + rng.normal(0, 0.9, size=n_users),
415
+ 3.0,
416
+ 12.0,
417
+ )
418
+
419
+ # Mental health indicators
420
+ stress = np.clip(
421
+ 5.5 + 0.65 * (screen_hours - 7) + rng.normal(0, 1.3, size=n_users),
422
+ 1,
423
+ 10,
424
+ )
425
+ anxiety = np.clip(
426
+ 5.0 + 0.55 * stress + rng.normal(0, 1.1, size=n_users),
427
+ 1,
428
+ 10,
429
+ )
430
+ depression = np.clip(
431
+ 4.5 + 0.45 * stress + 0.3 * anxiety + rng.normal(0, 1.2, size=n_users),
432
+ 1,
433
+ 10,
434
+ )
435
+ focus = np.clip(
436
+ 8.0 - 0.45 * (screen_hours - 7) - 0.35 * stress + rng.normal(0, 0.9, size=n_users),
437
+ 1,
438
+ 10,
439
+ )
440
+ wellbeing = np.clip(
441
+ 8.2 - 0.45 * stress + 0.45 * (sleep_hours - 7.5) + rng.normal(0, 0.8, size=n_users),
442
+ 1,
443
+ 10,
444
+ )
445
+ mood = np.clip(
446
+ 7.5 - 0.35 * stress + 0.25 * wellbeing + rng.normal(0, 1.0, size=n_users),
447
+ 1,
448
+ 10,
449
+ )
450
+ energy = np.clip(
451
+ 7.8 - 0.3 * stress + 0.4 * (sleep_hours - 7.5) + rng.normal(0, 0.9, size=n_users),
452
+ 1,
453
+ 10,
454
+ )
455
+
456
+ # App usage patterns
457
+ phone_unlocks = np.clip(
458
+ rng.normal(110 + 15 * (screen_hours - 7), 35, size=n_users),
459
+ 10,
460
+ 400,
461
+ ).astype(int)
462
+ notifications = np.clip(
463
+ rng.normal(95 + 12 * (screen_hours - 7), 32, size=n_users),
464
+ 5,
465
+ 350,
466
+ ).astype(int)
467
+ social_minutes = np.clip(
468
+ rng.normal(165 + 18 * (screen_hours - 7), 55, size=n_users),
469
+ 5,
470
+ 480,
471
+ ).astype(int)
472
+ gaming_minutes = np.clip(
473
+ rng.normal(70 + 10 * (screen_hours - 7), 40, size=n_users),
474
+ 0,
475
+ 360,
476
+ ).astype(int)
477
+ work_minutes = np.clip(
478
+ rng.normal(220, 80, size=n_users),
479
+ 0,
480
+ 600,
481
+ ).astype(int)
482
+
483
+ # Exercise and lifestyle
484
+ exercise_minutes = np.clip(
485
+ rng.normal(40 - 2.5 * (screen_hours - 7), 28, size=n_users),
486
+ 0,
487
+ 200,
488
+ ).astype(int)
489
+ outdoor_time = np.clip(
490
+ rng.normal(55 - 3.5 * (screen_hours - 7), 35, size=n_users),
491
+ 0,
492
+ 280,
493
+ ).astype(int)
494
+
495
+ # Health metrics
496
+ bmi = np.clip(rng.normal(24.5 + 0.3 * (screen_hours - 7), 4.5, size=n_users), 16, 45)
497
+ heart_rate = np.clip(
498
+ rng.normal(72 + 2 * stress, 10, size=n_users),
499
+ 50,
500
+ 120,
501
+ ).astype(int)
502
+ steps_daily = np.clip(
503
+ rng.normal(7500 - 300 * (screen_hours - 7), 2500, size=n_users),
504
+ 1000,
505
+ 20000,
506
+ ).astype(int)
507
+
508
+ # Social factors
509
+ social_support = np.clip(
510
+ rng.normal(7.0 - 0.2 * stress, 1.5, size=n_users),
511
+ 1,
512
+ 10,
513
+ )
514
+ loneliness = np.clip(
515
+ rng.normal(4.5 + 0.4 * stress - 0.5 * social_support, 1.8, size=n_users),
516
+ 1,
517
+ 10,
518
+ )
519
+
520
+ # Risk calculation with enhanced features
521
+ logit = (
522
+ 0.62 * (screen_hours - 7)
523
+ + 0.58 * (stress - 5.5)
524
+ + 0.52 * (anxiety - 5)
525
+ + 0.48 * (depression - 4.5)
526
+ - 0.46 * (sleep_hours - 7.5)
527
+ - 0.42 * (wellbeing - 8)
528
+ - 0.38 * (mood - 7.5)
529
+ - 0.35 * (energy - 7.8)
530
+ + 0.22 * ((phone_unlocks - 110) / 60)
531
+ + 0.18 * ((social_minutes - 165) / 70)
532
+ - 0.15 * ((exercise_minutes - 40) / 35)
533
+ - 0.12 * ((outdoor_time - 55) / 55)
534
+ + 0.2 * (loneliness - 4.5)
535
+ - 0.18 * (social_support - 7)
536
+ + rng.normal(0, 0.8, size=n_users)
537
+ )
538
+
539
+ risk_score = 1.0 / (1.0 + np.exp(-logit))
540
+ risk_score = np.clip(risk_score, 0.01, 0.99)
541
+ high_risk = (rng.random(n_users) < risk_score).astype(int)
542
+
543
+ risk_segment = pd.cut(
544
+ risk_score,
545
+ bins=[0.0, 0.30, 0.60, 1.0],
546
+ labels=["Low Risk", "Moderate Risk", "High Risk"],
547
+ )
548
+
549
+ # Engagement metrics
550
+ last_active = pd.Timestamp.now() - pd.to_timedelta(
551
+ rng.integers(0, 45, size=n_users), unit="D"
552
+ )
553
+ engagement_score = np.clip(
554
+ rng.normal(7.8 - 0.25 * (screen_hours - 7), 1.6, size=n_users),
555
+ 1,
556
+ 10,
557
+ )
558
+
559
+ # Treatment history
560
+ seeking_help = rng.choice([0, 1], size=n_users, p=[0.7, 0.3])
561
+ medication = rng.choice([0, 1], size=n_users, p=[0.8, 0.2])
562
+
563
+ df = pd.DataFrame(
564
+ {
565
+ "user_id": user_id,
566
+ "age": age,
567
+ "age_group": age_group,
568
+ "gender": gender,
569
+ "location": location,
570
+ "occupation": occupation,
571
+ "screen_hours": screen_hours.round(2),
572
+ "sleep_hours": sleep_hours.round(2),
573
+ "stress": stress.round(1),
574
+ "anxiety": anxiety.round(1),
575
+ "depression": depression.round(1),
576
+ "focus": focus.round(1),
577
+ "wellbeing": wellbeing.round(1),
578
+ "mood": mood.round(1),
579
+ "energy": energy.round(1),
580
+ "phone_unlocks": phone_unlocks,
581
+ "notifications": notifications,
582
+ "social_minutes": social_minutes,
583
+ "gaming_minutes": gaming_minutes,
584
+ "work_minutes": work_minutes,
585
+ "exercise_minutes": exercise_minutes,
586
+ "outdoor_time": outdoor_time,
587
+ "bmi": bmi.round(1),
588
+ "heart_rate": heart_rate,
589
+ "steps_daily": steps_daily,
590
+ "social_support": social_support.round(1),
591
+ "loneliness": loneliness.round(1),
592
+ "seeking_help": seeking_help,
593
+ "medication": medication,
594
+ "risk_score": risk_score,
595
+ "high_risk": high_risk,
596
+ "risk_segment": risk_segment,
597
+ "last_active": last_active,
598
+ "engagement_score": engagement_score.round(1),
599
+ }
600
+ )
601
+
602
+ # Time series data (90 days)
603
+ dates = pd.date_range(end=pd.Timestamp.now(), periods=90, freq="D")
604
+ time_series = pd.DataFrame(
605
+ {
606
+ "date": dates,
607
+ "avg_screen": 7.0
608
+ + np.sin(np.arange(90) / 7) * 1.8
609
+ + rng.normal(0, 0.4, 90),
610
+ "avg_stress": 5.8
611
+ + np.sin(np.arange(90) / 10) * 1.4
612
+ + rng.normal(0, 0.4, 90),
613
+ "avg_wellbeing": 7.8
614
+ - np.sin(np.arange(90) / 10) * 1.2
615
+ + rng.normal(0, 0.4, 90),
616
+ "high_risk_count": (
617
+ 800
618
+ + np.sin(np.arange(90) / 7) * 120
619
+ + rng.normal(0, 40, 90)
620
+ ).astype(int),
621
+ "avg_sleep": 7.5
622
+ - np.sin(np.arange(90) / 12) * 0.8
623
+ + rng.normal(0, 0.3, 90),
624
+ "engagement": 7.5
625
+ + np.sin(np.arange(90) / 8) * 1.0
626
+ + rng.normal(0, 0.3, 90),
627
+ }
628
+ )
629
+
630
+ # Hourly patterns
631
+ hours = list(range(24))
632
+ hourly = pd.DataFrame(
633
+ {
634
+ "hour": hours,
635
+ "screen_time": [
636
+ 25 if h < 6 else 55 + 20 * np.sin((h - 6) / 3.5) for h in hours
637
+ ],
638
+ "notifications": [
639
+ 12 if h < 6 else 35 + 18 * np.sin((h - 6) / 3.8) for h in hours
640
+ ],
641
+ "stress": [
642
+ 3.5 if h < 6 else 5.5 + 2.5 * np.sin((h - 10) / 3.2) for h in hours
643
+ ],
644
+ "energy": [
645
+ 4 if h < 6 else 7.5 - 1.5 * np.sin((h - 14) / 4) for h in hours
646
+ ],
647
+ }
648
+ )
649
+
650
+ return df, time_series, hourly
651
+
652
+
653
+ # ========================================================
654
+ # Helper Functions
655
+ # ========================================================
656
+ def calculate_metrics(df_metrics: pd.DataFrame, threshold: float = 0.5):
657
+ y_true = df_metrics["high_risk"].values
658
+ y_score = df_metrics["risk_score"].values
659
+ y_pred = (y_score >= threshold).astype(int)
660
+
661
+ auc = roc_auc_score(y_true, y_score)
662
+ ap = average_precision_score(y_true, y_score)
663
+ brier = brier_score_loss(y_true, y_score)
664
+ cm = confusion_matrix(y_true, y_pred)
665
+
666
+ if cm.shape == (2, 2):
667
+ tn, fp, fn, tp = cm.ravel()
668
+ else:
669
+ tn = fp = fn = tp = 0
670
+
671
+ precision = tp / (tp + fp) if (tp + fp) > 0 else 0.0
672
+ recall = tp / (tp + fn) if (tp + fn) > 0 else 0.0
673
+ f1 = (
674
+ 2 * precision * recall / (precision + recall)
675
+ if (precision + recall) > 0
676
+ else 0.0
677
+ )
678
+ specificity = tn / (tn + fp) if (tn + fp) > 0 else 0.0
679
+
680
+ return {
681
+ "auc": auc,
682
+ "ap": ap,
683
+ "brier": brier,
684
+ "precision": precision,
685
+ "recall": recall,
686
+ "f1": f1,
687
+ "specificity": specificity,
688
+ "tp": int(tp),
689
+ "fp": int(fp),
690
+ "tn": int(tn),
691
+ "fn": int(fn),
692
+ }
693
+
694
+
695
+ def generate_ai_insights(df_view: pd.DataFrame, metrics: dict):
696
+ insights = []
697
+
698
+ if len(df_view) == 0:
699
+ return insights
700
+
701
+ # Critical risk concentration
702
+ high_risk_pct = (df_view["risk_segment"] == "High Risk").mean() * 100
703
+ if high_risk_pct > 35:
704
+ insights.append(
705
+ {
706
+ "type": "danger",
707
+ "title": "🚨 CRITICAL: High Risk Alert",
708
+ "text": f"{high_risk_pct:.1f}% of monitored population in high-risk category. Systematic intervention programs are recommended.",
709
+ "priority": "critical",
710
+ }
711
+ )
712
+ elif high_risk_pct > 25:
713
+ insights.append(
714
+ {
715
+ "type": "warning",
716
+ "title": "⚠️ WARNING: Elevated Risk Levels",
717
+ "text": f"{high_risk_pct:.1f}% high-risk users detected. Targeted support strategies are advised.",
718
+ "priority": "high",
719
+ }
720
+ )
721
+
722
+ # Screen time impact analysis
723
+ high_risk_screen = df_view[df_view["risk_segment"] == "High Risk"][
724
+ "screen_hours"
725
+ ].mean()
726
+ low_risk_screen = df_view[df_view["risk_segment"] == "Low Risk"][
727
+ "screen_hours"
728
+ ].mean()
729
+ if high_risk_screen - low_risk_screen > 2.5:
730
+ insights.append(
731
+ {
732
+ "type": "info",
733
+ "title": "πŸ“± Digital Exposure Correlation",
734
+ "text": f"High-risk cohort shows {high_risk_screen - low_risk_screen:.1f}h higher daily screen time compared to low-risk users.",
735
+ "priority": "medium",
736
+ }
737
+ )
738
+
739
+ # Sleep deprivation alert
740
+ avg_sleep = df_view["sleep_hours"].mean()
741
+ if avg_sleep < 6.5:
742
+ critical_sleep = (df_view["sleep_hours"] < 6).sum()
743
+ insights.append(
744
+ {
745
+ "type": "warning",
746
+ "title": "😴 Sleep Deficit Detected",
747
+ "text": f"Population average sleep: {avg_sleep:.1f}h. {critical_sleep:,} users are below 6 hours.",
748
+ "priority": "high",
749
+ }
750
+ )
751
+
752
+ # Mental health trends
753
+ avg_stress = df_view["stress"].mean()
754
+ avg_anxiety = df_view["anxiety"].mean()
755
+ if avg_stress > 6.5 or avg_anxiety > 6.5:
756
+ insights.append(
757
+ {
758
+ "type": "warning",
759
+ "title": "🧠 Mental Health Stress Indicators",
760
+ "text": f"Elevated psychological metrics: Stress {avg_stress:.1f}/10, Anxiety {avg_anxiety:.1f}/10.",
761
+ "priority": "high",
762
+ }
763
+ )
764
+
765
+ # Model performance
766
+ if metrics["auc"] > 0.88:
767
+ insights.append(
768
+ {
769
+ "type": "success",
770
+ "title": "βœ… Model Performance: Strong Signal",
771
+ "text": f"Predictive performance on this dataset is strong (AUC: {metrics['auc']:.3f}). Risk stratification is internally consistent with the available features.",
772
+ "priority": "low",
773
+ }
774
+ )
775
+ elif metrics["auc"] < 0.75:
776
+ insights.append(
777
+ {
778
+ "type": "warning",
779
+ "title": "⚠️ Model Performance Warning",
780
+ "text": f"AUC {metrics['auc']:.3f} is below the preferred range for reliable screening. Consider recalibration or feature refinement.",
781
+ "priority": "medium",
782
+ }
783
+ )
784
+
785
+ # Social isolation patterns
786
+ avg_loneliness = df_view["loneliness"].mean()
787
+ if avg_loneliness > 6.0:
788
+ insights.append(
789
+ {
790
+ "type": "warning",
791
+ "title": "πŸ‘₯ Social Isolation Signal",
792
+ "text": f"Average loneliness score: {avg_loneliness:.1f}/10. Social support and engagement may be helpful.",
793
+ "priority": "medium",
794
+ }
795
+ )
796
+
797
+ # Physical activity deficit
798
+ avg_exercise = df_view["exercise_minutes"].mean()
799
+ if avg_exercise < 25:
800
+ sedentary_count = (df_view["exercise_minutes"] < 15).sum()
801
+ insights.append(
802
+ {
803
+ "type": "info",
804
+ "title": "πŸƒ Physical Activity Deficit",
805
+ "text": f"Average exercise: {avg_exercise:.0f} min/day. {sedentary_count:,} users show very low activity.",
806
+ "priority": "medium",
807
+ }
808
+ )
809
+
810
+ return sorted(
811
+ insights,
812
+ key=lambda x: {"critical": 0, "high": 1, "medium": 2, "low": 3}[x["priority"]],
813
+ )
814
+
815
+
816
+ # ========================================================
817
+ # Load Data
818
+ # ========================================================
819
+ df, time_series_df, hourly_df = generate_advanced_data()
820
+
821
+ # ========================================================
822
+ # Sidebar Configuration
823
+ # ========================================================
824
+ with st.sidebar:
825
+ st.markdown(
826
+ """
827
+ <div style='text-align: center; padding: 20px;'>
828
+ <h1 style='font-size: 2.5rem; margin: 0; color: white;'>🧬</h1>
829
+ <h3 style='margin: 10px 0; color: white;'>AI Health Intelligence</h3>
830
+ <p style='color: rgba(255,255,255,0.7); font-size: 0.9rem;'>Advanced analytics for digital wellbeing</p>
831
+ </div>
832
+ """,
833
+ unsafe_allow_html=True,
834
+ )
835
+
836
+ st.markdown("---")
837
+
838
+ st.markdown("### 🎯 Risk Configuration")
839
+ threshold = st.slider(
840
+ "Risk Classification Threshold",
841
+ min_value=0.1,
842
+ max_value=0.9,
843
+ value=0.5,
844
+ step=0.05,
845
+ help="Adjust sensitivity for high-risk classification",
846
+ )
847
+
848
+ st.markdown("---")
849
+ st.markdown("### πŸ” Advanced Filters")
850
+
851
+ selected_segments = st.multiselect(
852
+ "Risk Segments",
853
+ options=["Low Risk", "Moderate Risk", "High Risk"],
854
+ default=["Low Risk", "Moderate Risk", "High Risk"],
855
+ )
856
+
857
+ selected_age_groups = st.multiselect(
858
+ "Age Demographics",
859
+ options=df["age_group"].unique().tolist(),
860
+ default=df["age_group"].unique().tolist(),
861
+ )
862
+
863
+ selected_gender = st.multiselect(
864
+ "Gender",
865
+ options=["Male", "Female", "Other"],
866
+ default=["Male", "Female", "Other"],
867
+ )
868
+
869
+ selected_occupation = st.multiselect(
870
+ "Occupation Type",
871
+ options=df["occupation"].unique().tolist(),
872
+ default=df["occupation"].unique().tolist(),
873
+ )
874
+
875
+ screen_time_range = st.slider(
876
+ "Screen Time (hours/day)",
877
+ min_value=0.0,
878
+ max_value=18.0,
879
+ value=(0.0, 18.0),
880
+ step=0.5,
881
+ )
882
+
883
+ stress_range = st.slider(
884
+ "Stress Level Range",
885
+ min_value=1.0,
886
+ max_value=10.0,
887
+ value=(1.0, 10.0),
888
+ step=0.5,
889
+ )
890
+
891
+ st.markdown("---")
892
+ st.markdown("### πŸ“Š Display Preferences")
893
+
894
+ show_animations = st.checkbox("Enable Animations", value=True)
895
+ show_insights = st.checkbox("AI-Powered Insights", value=True)
896
+ show_advanced_metrics = st.checkbox("Advanced Analytics", value=True)
897
+ real_time_mode = st.checkbox("Real-Time Mode (UI only)", value=False)
898
+
899
+ st.markdown("---")
900
+ st.markdown("### πŸ“₯ Data Export")
901
+
902
+ export_format = st.selectbox("Export Format", ["CSV", "JSON"])
903
+
904
+ if st.button("πŸ“Š Export Dataset", use_container_width=True):
905
+ if export_format == "CSV":
906
+ data = df.to_csv(index=False).encode("utf-8")
907
+ st.download_button(
908
+ "⬇️ Download CSV",
909
+ data=data,
910
+ file_name=f"health_data_{datetime.now().strftime('%Y%m%d')}.csv",
911
+ mime="text/csv",
912
+ use_container_width=True,
913
+ )
914
+ elif export_format == "JSON":
915
+ data = df.to_json(orient="records", indent=2).encode("utf-8")
916
+ st.download_button(
917
+ "⬇️ Download JSON",
918
+ data=data,
919
+ file_name=f"health_data_{datetime.now().strftime('%Y%m%d')}.json",
920
+ mime="application/json",
921
+ use_container_width=True,
922
+ )
923
+
924
+
925
+
926
+ # Apply filters
927
+ mask = (
928
+ df["risk_segment"].isin(selected_segments)
929
+ & df["age_group"].isin(selected_age_groups)
930
+ & df["gender"].isin(selected_gender)
931
+ & df["occupation"].isin(selected_occupation)
932
+ & (df["screen_hours"] >= screen_time_range[0])
933
+ & (df["screen_hours"] <= screen_time_range[1])
934
+ & (df["stress"] >= stress_range[0])
935
+ & (df["stress"] <= stress_range[1])
936
+ )
937
+
938
+ df_filtered = df[mask].copy()
939
+ no_filter_data = len(df_filtered) == 0
940
+ plot_df = df_filtered.copy() if not no_filter_data else df.copy()
941
+
942
+ plot_df["flagged"] = (plot_df["risk_score"] >= threshold).astype(int)
943
+
944
+ if plot_df["high_risk"].nunique() < 2:
945
+ df_metrics = df.copy()
946
+ metrics_scope = "full dataset (filtered data insufficient)"
947
+ else:
948
+ df_metrics = plot_df.copy()
949
+ metrics_scope = "filtered subset" if not no_filter_data else "full dataset"
950
+
951
+ metrics = calculate_metrics(df_metrics, threshold)
952
+ insights = generate_ai_insights(plot_df, metrics) if show_insights else []
953
+
954
+ if no_filter_data:
955
+ st.warning("⚠️ No data matches current filters. Displaying full dataset.")
956
+
957
+ kpi_df = plot_df
958
+
959
+ # ========================================================
960
+ # Hero Section
961
+ # ========================================================
962
+ st.markdown(
963
+ """
964
+ <div class='hero-section'>
965
+ <div style='text-align: center;'>
966
+ <h1 class='hero-title'>🧬 AI Health Intelligence Platform</h1>
967
+ <p class='hero-subtitle'>
968
+ Advanced predictive analytics for digital wellbeing and mental health risk assessment
969
+ </p>
970
+ <p style='color: rgba(255,255,255,0.6); font-size: 0.95rem; margin-top: 15px;'>
971
+ Interactive monitoring β€’ Machine learning predictions β€’ Scenario-based interventions
972
+ </p>
973
+ </div>
974
+ </div>
975
+ """,
976
+ unsafe_allow_html=True,
977
+ )
978
+
979
+ # ========================================================
980
+ # Key Metrics Dashboard
981
+ # ========================================================
982
+ col1, col2, col3, col4, col5 = st.columns(5)
983
+
984
+ with col1:
985
+ total_users = len(plot_df)
986
+ st.markdown(
987
+ f"""
988
+ <div class='metric-card'>
989
+ <div class='metric-icon'>πŸ‘₯</div>
990
+ <div class='metric-value'>{total_users:,}</div>
991
+ <div class='metric-label'>Active Users</div>
992
+ <div class='metric-change'>Monitoring scope</div>
993
+ </div>
994
+ """,
995
+ unsafe_allow_html=True,
996
+ )
997
+
998
+ with col2:
999
+ high_risk_count = (plot_df["risk_segment"] == "High Risk").sum()
1000
+ high_risk_pct = (high_risk_count / len(plot_df) * 100) if len(plot_df) > 0 else 0
1001
+ st.markdown(
1002
+ f"""
1003
+ <div class='metric-card'>
1004
+ <div class='metric-icon'>🚨</div>
1005
+ <div class='metric-value'>{high_risk_count:,}</div>
1006
+ <div class='metric-label'>High Risk</div>
1007
+ <div class='metric-change'>{high_risk_pct:.1f}% of population</div>
1008
+ </div>
1009
+ """,
1010
+ unsafe_allow_html=True,
1011
+ )
1012
+
1013
+ with col3:
1014
+ st.markdown(
1015
+ f"""
1016
+ <div class='metric-card'>
1017
+ <div class='metric-icon'>🎯</div>
1018
+ <div class='metric-value'>{metrics['auc']:.3f}</div>
1019
+ <div class='metric-label'>Model AUC</div>
1020
+ <div class='metric-change'>Predictive performance</div>
1021
+ </div>
1022
+ """,
1023
+ unsafe_allow_html=True,
1024
+ )
1025
+
1026
+ with col4:
1027
+ avg_screen = kpi_df["screen_hours"].mean()
1028
+ screen_delta = avg_screen - 7.0
1029
+ st.markdown(
1030
+ f"""
1031
+ <div class='metric-card'>
1032
+ <div class='metric-icon'>πŸ“±</div>
1033
+ <div class='metric-value'>{avg_screen:.1f}h</div>
1034
+ <div class='metric-label'>Avg Screen Time</div>
1035
+ <div class='metric-change'>{"↑" if screen_delta > 0 else "↓"} {abs(screen_delta):.1f}h vs baseline</div>
1036
+ </div>
1037
+ """,
1038
+ unsafe_allow_html=True,
1039
+ )
1040
+
1041
+ with col5:
1042
+ avg_wellbeing = kpi_df["wellbeing"].mean()
1043
+ st.markdown(
1044
+ f"""
1045
+ <div class='metric-card'>
1046
+ <div class='metric-icon'>πŸ’š</div>
1047
+ <div class='metric-value'>{avg_wellbeing:.1f}</div>
1048
+ <div class='metric-label'>Wellbeing Score</div>
1049
+ <div class='metric-change'>Out of 10.0</div>
1050
+ </div>
1051
+ """,
1052
+ unsafe_allow_html=True,
1053
+ )
1054
+
1055
+ st.markdown("<br>", unsafe_allow_html=True)
1056
+
1057
+ # Secondary KPIs
1058
+ col1, col2, col3, col4 = st.columns(4)
1059
+
1060
+ with col1:
1061
+ st.metric("Precision", f"{metrics['precision']:.1%}", f"F1: {metrics['f1']:.3f}")
1062
+
1063
+ with col2:
1064
+ st.metric(
1065
+ "Recall",
1066
+ f"{metrics['recall']:.1%}",
1067
+ f"Specificity: {metrics['specificity']:.3f}",
1068
+ )
1069
+
1070
+ with col3:
1071
+ flagged_pct = plot_df["flagged"].mean()
1072
+ st.metric("Flagged Users", f"{flagged_pct:.1%}", f"At threshold {threshold:.2f}")
1073
+
1074
+ with col4:
1075
+ avg_stress = kpi_df["stress"].mean()
1076
+ st.metric(
1077
+ "Avg Stress",
1078
+ f"{avg_stress:.1f}/10",
1079
+ f"Anxiety: {kpi_df['anxiety'].mean():.1f}/10",
1080
+ )
1081
+
1082
+ # ========================================================
1083
+ # AI Insights Section
1084
+ # ========================================================
1085
+ if show_insights and insights:
1086
+ st.markdown("<br>", unsafe_allow_html=True)
1087
+ st.markdown(
1088
+ """
1089
+ <div class='section-header'>
1090
+ <div class='section-icon'>πŸ€–</div>
1091
+ <div>
1092
+ <div class='section-title'>AI-Powered Insights</div>
1093
+ <div class='section-subtitle'>Machine learning analysis and recommendations</div>
1094
+ </div>
1095
+ </div>
1096
+ """,
1097
+ unsafe_allow_html=True,
1098
+ )
1099
+
1100
+ for insight in insights[:6]:
1101
+ alert_class = ""
1102
+ if insight["type"] == "warning":
1103
+ alert_class = "insight-warning"
1104
+ elif insight["type"] == "danger":
1105
+ alert_class = "insight-danger"
1106
+
1107
+ st.markdown(
1108
+ f"""
1109
+ <div class='insight-card {alert_class}'>
1110
+ <div class='insight-title'>{insight['title']}</div>
1111
+ <div class='insight-text'>{insight['text']}</div>
1112
+ </div>
1113
+ """,
1114
+ unsafe_allow_html=True,
1115
+ )
1116
+
1117
+ # ========================================================
1118
+ # Main Tabs
1119
+ # ========================================================
1120
+ st.markdown("<br>", unsafe_allow_html=True)
1121
+
1122
+ tab1, tab2, tab3, tab4, tab5, tab6 = st.tabs(
1123
+ [
1124
+ "Executive Dashboard",
1125
+ "Risk Analytics",
1126
+ "Behavioral Insights",
1127
+ "Model Performance",
1128
+ "Scenario Simulator",
1129
+ "Clinical Reports",
1130
+ ]
1131
+ )
1132
+
1133
+ # ========================================================
1134
+ # TAB 1: Executive Dashboard
1135
+ # ========================================================
1136
+ with tab1:
1137
+ st.markdown(
1138
+ """
1139
+ <div class='section-header'>
1140
+ <div class='section-icon'>πŸ“Š</div>
1141
+ <div>
1142
+ <div class='section-title'>Population Health Overview</div>
1143
+ <div class='section-subtitle'>Comprehensive metrics and trend analysis</div>
1144
+ </div>
1145
+ </div>
1146
+ """,
1147
+ unsafe_allow_html=True,
1148
+ )
1149
+
1150
+ col1, col2 = st.columns([1.5, 1])
1151
+
1152
+ with col1:
1153
+ st.markdown("#### Risk Score Distribution & Segmentation")
1154
+ fig = px.histogram(
1155
+ plot_df,
1156
+ x="risk_score",
1157
+ color="risk_segment",
1158
+ nbins=60,
1159
+ color_discrete_map={
1160
+ "Low Risk": "#10b981",
1161
+ "Moderate Risk": "#f59e0b",
1162
+ "High Risk": "#ef4444",
1163
+ },
1164
+ labels={"risk_score": "Risk Score", "count": "User Count"},
1165
+ )
1166
+ fig.add_vline(
1167
+ x=threshold,
1168
+ line_dash="dash",
1169
+ line_color="#ffffff",
1170
+ line_width=3,
1171
+ annotation_text=f"Threshold: {threshold:.2f}",
1172
+ annotation_position="top right",
1173
+ )
1174
+ fig.update_layout(
1175
+ height=450,
1176
+ plot_bgcolor="rgba(0,0,0,0)",
1177
+ paper_bgcolor="rgba(0,0,0,0)",
1178
+ font=dict(color="#ffffff", size=12),
1179
+ margin=dict(l=40, r=20, t=20, b=40),
1180
+ )
1181
+ st.plotly_chart(fig, use_container_width=True)
1182
+
1183
+ with col2:
1184
+ st.markdown("#### Risk Segment Distribution")
1185
+ segment_counts = plot_df["risk_segment"].value_counts()
1186
+ fig = go.Figure(
1187
+ data=[
1188
+ go.Pie(
1189
+ labels=segment_counts.index,
1190
+ values=segment_counts.values,
1191
+ hole=0.6,
1192
+ marker=dict(
1193
+ colors=["#10b981", "#f59e0b", "#ef4444"],
1194
+ line=dict(color="rgba(0,0,0,0.5)", width=3),
1195
+ ),
1196
+ textfont=dict(size=15, color="#fff"),
1197
+ textposition="outside",
1198
+ )
1199
+ ]
1200
+ )
1201
+ fig.update_layout(
1202
+ height=450,
1203
+ plot_bgcolor="rgba(0,0,0,0)",
1204
+ paper_bgcolor="rgba(0,0,0,0)",
1205
+ font=dict(color="#ffffff", size=12),
1206
+ margin=dict(l=20, r=20, t=20, b=20),
1207
+ showlegend=True,
1208
+ )
1209
+ st.plotly_chart(fig, use_container_width=True)
1210
+
1211
+ # 90-Day Trends
1212
+ st.markdown("<br>", unsafe_allow_html=True)
1213
+ st.markdown("#### 90-Day Population Health Trends")
1214
+
1215
+ fig = make_subplots(
1216
+ rows=2,
1217
+ cols=3,
1218
+ subplot_titles=(
1219
+ "Screen Time Trajectory",
1220
+ "Stress Evolution",
1221
+ "Wellbeing Index",
1222
+ "High-Risk Population",
1223
+ "Sleep Patterns",
1224
+ "Engagement Metrics",
1225
+ ),
1226
+ vertical_spacing=0.12,
1227
+ horizontal_spacing=0.08,
1228
+ )
1229
+
1230
+ # Screen time
1231
+ fig.add_trace(
1232
+ go.Scatter(
1233
+ x=time_series_df["date"],
1234
+ y=time_series_df["avg_screen"],
1235
+ mode="lines",
1236
+ name="Screen Time",
1237
+ line=dict(color="#3b82f6", width=3),
1238
+ fill="tozeroy",
1239
+ fillcolor="rgba(59,130,246,0.3)",
1240
+ ),
1241
+ row=1,
1242
+ col=1,
1243
+ )
1244
+
1245
+ # Stress
1246
+ fig.add_trace(
1247
+ go.Scatter(
1248
+ x=time_series_df["date"],
1249
+ y=time_series_df["avg_stress"],
1250
+ mode="lines",
1251
+ name="Stress",
1252
+ line=dict(color="#ef4444", width=3),
1253
+ fill="tozeroy",
1254
+ fillcolor="rgba(239,68,68,0.3)",
1255
+ ),
1256
+ row=1,
1257
+ col=2,
1258
+ )
1259
+
1260
+ # Wellbeing
1261
+ fig.add_trace(
1262
+ go.Scatter(
1263
+ x=time_series_df["date"],
1264
+ y=time_series_df["avg_wellbeing"],
1265
+ mode="lines",
1266
+ name="Wellbeing",
1267
+ line=dict(color="#10b981", width=3),
1268
+ fill="tozeroy",
1269
+ fillcolor="rgba(16,185,129,0.3)",
1270
+ ),
1271
+ row=1,
1272
+ col=3,
1273
+ )
1274
+
1275
+ # High-risk count
1276
+ fig.add_trace(
1277
+ go.Bar(
1278
+ x=time_series_df["date"],
1279
+ y=time_series_df["high_risk_count"],
1280
+ name="High-Risk Users",
1281
+ marker=dict(color="#f59e0b"),
1282
+ ),
1283
+ row=2,
1284
+ col=1,
1285
+ )
1286
+
1287
+ # Sleep
1288
+ fig.add_trace(
1289
+ go.Scatter(
1290
+ x=time_series_df["date"],
1291
+ y=time_series_df["avg_sleep"],
1292
+ mode="lines+markers",
1293
+ name="Sleep",
1294
+ line=dict(color="#8b5cf6", width=3),
1295
+ fill="tozeroy",
1296
+ fillcolor="rgba(139,92,246,0.3)",
1297
+ ),
1298
+ row=2,
1299
+ col=2,
1300
+ )
1301
+
1302
+ # Engagement
1303
+ fig.add_trace(
1304
+ go.Scatter(
1305
+ x=time_series_df["date"],
1306
+ y=time_series_df["engagement"],
1307
+ mode="lines",
1308
+ name="Engagement",
1309
+ line=dict(color="#ec4899", width=3),
1310
+ fill="tozeroy",
1311
+ fillcolor="rgba(236,72,153,0.3)",
1312
+ ),
1313
+ row=2,
1314
+ col=3,
1315
+ )
1316
+
1317
+ fig.update_layout(
1318
+ height=700,
1319
+ plot_bgcolor="rgba(0,0,0,0)",
1320
+ paper_bgcolor="rgba(0,0,0,0)",
1321
+ font=dict(color="#ffffff", size=11),
1322
+ margin=dict(l=40, r=20, t=50, b=40),
1323
+ showlegend=False,
1324
+ )
1325
+
1326
+ fig.update_xaxes(showgrid=True, gridcolor="rgba(255,255,255,0.1)")
1327
+ fig.update_yaxes(showgrid=True, gridcolor="rgba(255,255,255,0.1)")
1328
+
1329
+ st.plotly_chart(fig, use_container_width=True)
1330
+
1331
+ # Demographics Analysis
1332
+ st.markdown("<br>", unsafe_allow_html=True)
1333
+ col1, col2, col3, col4 = st.columns(4)
1334
+
1335
+ with col1:
1336
+ st.markdown("#### Age Distribution (Avg Risk)")
1337
+ age_risk = plot_df.groupby("age_group")["risk_score"].mean().reset_index()
1338
+ fig = px.bar(
1339
+ age_risk,
1340
+ x="age_group",
1341
+ y="risk_score",
1342
+ color="risk_score",
1343
+ color_continuous_scale=["#10b981", "#f59e0b", "#ef4444"],
1344
+ )
1345
+ fig.update_layout(
1346
+ height=350,
1347
+ plot_bgcolor="rgba(0,0,0,0)",
1348
+ paper_bgcolor="rgba(0,0,0,0)",
1349
+ font=dict(color="#ffffff"),
1350
+ showlegend=False,
1351
+ )
1352
+ st.plotly_chart(fig, use_container_width=True)
1353
+
1354
+ with col2:
1355
+ st.markdown("#### Gender Distribution")
1356
+ gender_counts = plot_df["gender"].value_counts()
1357
+ fig = px.bar(
1358
+ x=gender_counts.index,
1359
+ y=gender_counts.values,
1360
+ color=gender_counts.index,
1361
+ color_discrete_map={
1362
+ "Male": "#3b82f6",
1363
+ "Female": "#ec4899",
1364
+ "Other": "#8b5cf6",
1365
+ },
1366
+ )
1367
+ fig.update_layout(
1368
+ height=350,
1369
+ plot_bgcolor="rgba(0,0,0,0)",
1370
+ paper_bgcolor="rgba(0,0,0,0)",
1371
+ font=dict(color="#ffffff"),
1372
+ showlegend=False,
1373
+ )
1374
+ st.plotly_chart(fig, use_container_width=True)
1375
+
1376
+ with col3:
1377
+ st.markdown("#### Location Distribution")
1378
+ location_counts = plot_df["location"].value_counts()
1379
+ fig = px.pie(
1380
+ values=location_counts.values,
1381
+ names=location_counts.index,
1382
+ color_discrete_sequence=["#3b82f6", "#8b5cf6", "#10b981"],
1383
+ hole=0.4,
1384
+ )
1385
+ fig.update_layout(
1386
+ height=350,
1387
+ plot_bgcolor="rgba(0,0,0,0)",
1388
+ paper_bgcolor="rgba(0,0,0,0)",
1389
+ font=dict(color="#ffffff"),
1390
+ )
1391
+ st.plotly_chart(fig, use_container_width=True)
1392
+
1393
+ with col4:
1394
+ st.markdown("#### Occupation (Avg Risk)")
1395
+ occ_risk = (
1396
+ plot_df.groupby("occupation")["risk_score"]
1397
+ .mean()
1398
+ .sort_values(ascending=False)
1399
+ )
1400
+ fig = px.bar(
1401
+ x=occ_risk.values,
1402
+ y=occ_risk.index,
1403
+ orientation="h",
1404
+ color=occ_risk.values,
1405
+ color_continuous_scale=["#10b981", "#f59e0b", "#ef4444"],
1406
+ )
1407
+ fig.update_layout(
1408
+ height=350,
1409
+ plot_bgcolor="rgba(0,0,0,0)",
1410
+ paper_bgcolor="rgba(0,0,0,0)",
1411
+ font=dict(color="#ffffff"),
1412
+ showlegend=False,
1413
+ )
1414
+ st.plotly_chart(fig, use_container_width=True)
1415
+
1416
+ # ========================================================
1417
+ # TAB 2: Risk Analytics
1418
+ # ========================================================
1419
+ with tab2:
1420
+ st.markdown(
1421
+ """
1422
+ <div class='section-header'>
1423
+ <div class='section-icon'>🎯</div>
1424
+ <div>
1425
+ <div class='section-title'>Advanced Risk Assessment</div>
1426
+ <div class='section-subtitle'>Deep dive into risk factors and correlations</div>
1427
+ </div>
1428
+ </div>
1429
+ """,
1430
+ unsafe_allow_html=True,
1431
+ )
1432
+
1433
+ col1, col2 = st.columns([1.3, 1])
1434
+
1435
+ with col1:
1436
+ st.markdown("#### Multi-Factor Correlation Matrix")
1437
+ corr_cols = [
1438
+ "screen_hours",
1439
+ "sleep_hours",
1440
+ "stress",
1441
+ "anxiety",
1442
+ "depression",
1443
+ "wellbeing",
1444
+ "mood",
1445
+ "energy",
1446
+ "social_support",
1447
+ "loneliness",
1448
+ "risk_score",
1449
+ ]
1450
+ corr_matrix = plot_df[corr_cols].corr()
1451
+
1452
+ fig = go.Figure(
1453
+ data=go.Heatmap(
1454
+ z=corr_matrix.values,
1455
+ x=corr_matrix.columns,
1456
+ y=corr_matrix.columns,
1457
+ colorscale=[[0, "#ef4444"], [0.5, "#f3f4f6"], [1, "#10b981"]],
1458
+ text=corr_matrix.values.round(2),
1459
+ texttemplate="%{text}",
1460
+ textfont={"size": 10},
1461
+ colorbar=dict(title="Correlation"),
1462
+ )
1463
+ )
1464
+ fig.update_layout(
1465
+ height=550,
1466
+ plot_bgcolor="rgba(0,0,0,0)",
1467
+ paper_bgcolor="rgba(0,0,0,0)",
1468
+ font=dict(color="#ffffff", size=10),
1469
+ margin=dict(l=120, r=40, t=20, b=120),
1470
+ )
1471
+ st.plotly_chart(fig, use_container_width=True)
1472
+
1473
+ with col2:
1474
+ st.markdown("#### Risk Distribution by Segment")
1475
+ fig = go.Figure()
1476
+ for segment in ["Low Risk", "Moderate Risk", "High Risk"]:
1477
+ data = plot_df[plot_df["risk_segment"] == segment]["risk_score"]
1478
+ color = {
1479
+ "Low Risk": "#10b981",
1480
+ "Moderate Risk": "#f59e0b",
1481
+ "High Risk": "#ef4444",
1482
+ }[segment]
1483
+ fig.add_trace(
1484
+ go.Box(y=data, name=segment, marker_color=color, boxmean="sd")
1485
+ )
1486
+ fig.update_layout(
1487
+ height=550,
1488
+ plot_bgcolor="rgba(0,0,0,0)",
1489
+ paper_bgcolor="rgba(0,0,0,0)",
1490
+ font=dict(color="#ffffff"),
1491
+ yaxis_title="Risk Score",
1492
+ )
1493
+ st.plotly_chart(fig, use_container_width=True)
1494
+
1495
+ # ROC and Confusion Matrix
1496
+ st.markdown("<br>", unsafe_allow_html=True)
1497
+ col1, col2 = st.columns(2)
1498
+
1499
+ with col1:
1500
+ st.markdown("#### Confusion Matrix Analysis")
1501
+ cm = confusion_matrix(
1502
+ df_metrics["high_risk"].values,
1503
+ (df_metrics["risk_score"].values >= threshold).astype(int),
1504
+ )
1505
+ if cm.shape == (2, 2):
1506
+ tn, fp, fn, tp = cm.ravel()
1507
+ else:
1508
+ tn = fp = fn = tp = 0
1509
+
1510
+ cm_display = [[tn, fp], [fn, tp]]
1511
+ fig = go.Figure(
1512
+ data=go.Heatmap(
1513
+ z=cm_display,
1514
+ x=["Predicted Negative", "Predicted Positive"],
1515
+ y=["Actual Negative", "Actual Positive"],
1516
+ text=cm_display,
1517
+ texttemplate="%{text}",
1518
+ textfont={"size": 24},
1519
+ colorscale=[[0, "#1e293b"], [1, "#3b82f6"]],
1520
+ showscale=False,
1521
+ )
1522
+ )
1523
+ fig.update_layout(
1524
+ height=450,
1525
+ plot_bgcolor="rgba(0,0,0,0)",
1526
+ paper_bgcolor="rgba(0,0,0,0)",
1527
+ font=dict(color="#ffffff", size=13),
1528
+ margin=dict(l=100, r=20, t=20, b=80),
1529
+ )
1530
+ st.plotly_chart(fig, use_container_width=True)
1531
+
1532
+ with col2:
1533
+ st.markdown("#### ROC Curve Analysis")
1534
+ fpr, tpr, _ = roc_curve(
1535
+ df_metrics["high_risk"].values, df_metrics["risk_score"].values
1536
+ )
1537
+ fig = go.Figure()
1538
+ fig.add_trace(
1539
+ go.Scatter(
1540
+ x=fpr,
1541
+ y=tpr,
1542
+ mode="lines",
1543
+ name=f"ROC (AUC = {metrics['auc']:.3f})",
1544
+ line=dict(color="#3b82f6", width=4),
1545
+ fill="tozeroy",
1546
+ fillcolor="rgba(59,130,246,0.3)",
1547
+ )
1548
+ )
1549
+ fig.add_trace(
1550
+ go.Scatter(
1551
+ x=[0, 1],
1552
+ y=[0, 1],
1553
+ mode="lines",
1554
+ name="Random Classifier",
1555
+ line=dict(color="#64748b", width=2, dash="dash"),
1556
+ )
1557
+ )
1558
+ fig.update_layout(
1559
+ height=450,
1560
+ plot_bgcolor="rgba(0,0,0,0)",
1561
+ paper_bgcolor="rgba(0,0,0,0)",
1562
+ font=dict(color="#ffffff"),
1563
+ xaxis_title="False Positive Rate",
1564
+ yaxis_title="True Positive Rate",
1565
+ )
1566
+ st.plotly_chart(fig, use_container_width=True)
1567
+
1568
+ # Feature Importance
1569
+ st.markdown("<br>", unsafe_allow_html=True)
1570
+ st.markdown("#### Risk Factor Importance (Conceptual)")
1571
+
1572
+ features = [
1573
+ "Screen Time",
1574
+ "Stress",
1575
+ "Sleep Quality",
1576
+ "Anxiety",
1577
+ "Depression",
1578
+ "Wellbeing",
1579
+ "Social Support",
1580
+ "Loneliness",
1581
+ "Phone Unlocks",
1582
+ "Social Media",
1583
+ "Exercise",
1584
+ "Outdoor Time",
1585
+ ]
1586
+ importance = [0.62, 0.58, 0.46, 0.52, 0.48, 0.42, 0.18, 0.2, 0.22, 0.18, 0.15, 0.12]
1587
+
1588
+ fig = px.bar(
1589
+ x=importance,
1590
+ y=features,
1591
+ orientation="h",
1592
+ color=importance,
1593
+ color_continuous_scale=["#64748b", "#3b82f6", "#8b5cf6", "#ec4899"],
1594
+ )
1595
+ fig.update_layout(
1596
+ height=500,
1597
+ plot_bgcolor="rgba(0,0,0,0)",
1598
+ paper_bgcolor="rgba(0,0,0,0)",
1599
+ font=dict(color="#ffffff"),
1600
+ xaxis_title="Relative Importance (Model Coefficients)",
1601
+ showlegend=False,
1602
+ )
1603
+ st.plotly_chart(fig, use_container_width=True)
1604
+
1605
+ # Risk scatter plots
1606
+ st.markdown("<br>", unsafe_allow_html=True)
1607
+ col1, col2 = st.columns(2)
1608
+
1609
+ sample = (
1610
+ plot_df.sample(n=min(2000, len(plot_df)), random_state=42)
1611
+ if len(plot_df) > 0
1612
+ else plot_df
1613
+ )
1614
+
1615
+ with col1:
1616
+ st.markdown("#### Screen Time vs Sleep")
1617
+ if len(sample) > 0:
1618
+ fig = px.scatter(
1619
+ sample,
1620
+ x="screen_hours",
1621
+ y="sleep_hours",
1622
+ color="risk_score",
1623
+ size="stress",
1624
+ color_continuous_scale=["#10b981", "#f59e0b", "#ef4444"],
1625
+ hover_data=["stress", "wellbeing", "age"],
1626
+ )
1627
+ fig.update_layout(
1628
+ height=450,
1629
+ plot_bgcolor="rgba(0,0,0,0)",
1630
+ paper_bgcolor="rgba(0,0,0,0)",
1631
+ font=dict(color="#ffffff"),
1632
+ )
1633
+ st.plotly_chart(fig, use_container_width=True)
1634
+
1635
+ with col2:
1636
+ st.markdown("#### Stress vs Wellbeing")
1637
+ if len(sample) > 0:
1638
+ fig = px.scatter(
1639
+ sample,
1640
+ x="stress",
1641
+ y="wellbeing",
1642
+ color="risk_segment",
1643
+ size="risk_score",
1644
+ color_discrete_map={
1645
+ "Low Risk": "#10b981",
1646
+ "Moderate Risk": "#f59e0b",
1647
+ "High Risk": "#ef4444",
1648
+ },
1649
+ hover_data=["screen_hours", "sleep_hours", "depression"],
1650
+ )
1651
+ fig.update_layout(
1652
+ height=450,
1653
+ plot_bgcolor="rgba(0,0,0,0)",
1654
+ paper_bgcolor="rgba(0,0,0,0)",
1655
+ font=dict(color="#ffffff"),
1656
+ )
1657
+ st.plotly_chart(fig, use_container_width=True)
1658
+
1659
+ # ========================================================
1660
+ # TAB 3: Behavioral Insights
1661
+ # ========================================================
1662
+ with tab3:
1663
+ st.markdown(
1664
+ """
1665
+ <div class='section-header'>
1666
+ <div class='section-icon'>πŸ“±</div>
1667
+ <div>
1668
+ <div class='section-title'>Digital Behavior Analysis</div>
1669
+ <div class='section-subtitle'>Usage patterns and lifestyle correlations</div>
1670
+ </div>
1671
+ </div>
1672
+ """,
1673
+ unsafe_allow_html=True,
1674
+ )
1675
+
1676
+ # 24-hour patterns
1677
+ st.markdown("#### Circadian Activity Patterns")
1678
+ fig = make_subplots(specs=[[{"secondary_y": True}]])
1679
+
1680
+ fig.add_trace(
1681
+ go.Scatter(
1682
+ x=hourly_df["hour"],
1683
+ y=hourly_df["screen_time"],
1684
+ name="Screen Time",
1685
+ mode="lines+markers",
1686
+ line=dict(color="#3b82f6", width=3),
1687
+ fill="tozeroy",
1688
+ fillcolor="rgba(59,130,246,0.3)",
1689
+ ),
1690
+ secondary_y=False,
1691
+ )
1692
+
1693
+ fig.add_trace(
1694
+ go.Scatter(
1695
+ x=hourly_df["hour"],
1696
+ y=hourly_df["notifications"],
1697
+ name="Notifications",
1698
+ mode="lines+markers",
1699
+ line=dict(color="#f59e0b", width=2),
1700
+ ),
1701
+ secondary_y=False,
1702
+ )
1703
+
1704
+ fig.add_trace(
1705
+ go.Scatter(
1706
+ x=hourly_df["hour"],
1707
+ y=hourly_df["stress"],
1708
+ name="Stress Level",
1709
+ mode="lines+markers",
1710
+ line=dict(color="#ef4444", width=2, dash="dash"),
1711
+ ),
1712
+ secondary_y=True,
1713
+ )
1714
+
1715
+ fig.add_trace(
1716
+ go.Scatter(
1717
+ x=hourly_df["hour"],
1718
+ y=hourly_df["energy"],
1719
+ name="Energy Level",
1720
+ mode="lines",
1721
+ line=dict(color="#10b981", width=2, dash="dot"),
1722
+ ),
1723
+ secondary_y=True,
1724
+ )
1725
+
1726
+ fig.update_layout(
1727
+ height=500,
1728
+ plot_bgcolor="rgba(0,0,0,0)",
1729
+ paper_bgcolor="rgba(0,0,0,0)",
1730
+ font=dict(color="#ffffff"),
1731
+ hovermode="x unified",
1732
+ )
1733
+
1734
+ fig.update_xaxes(
1735
+ title_text="Hour of Day", showgrid=True, gridcolor="rgba(255,255,255,0.1)"
1736
+ )
1737
+ fig.update_yaxes(
1738
+ title_text="Activity Level",
1739
+ secondary_y=False,
1740
+ showgrid=True,
1741
+ gridcolor="rgba(255,255,255,0.1)",
1742
+ )
1743
+ fig.update_yaxes(title_text="Psychological Metrics", secondary_y=True)
1744
+
1745
+ st.plotly_chart(fig, use_container_width=True)
1746
+
1747
+ st.markdown("<br>", unsafe_allow_html=True)
1748
+ col1, col2, col3 = st.columns(3)
1749
+
1750
+ with col1:
1751
+ st.markdown("#### App Usage Distribution")
1752
+ usage_data = pd.DataFrame(
1753
+ {
1754
+ "Category": ["Social Media", "Work/Study", "Gaming", "Entertainment", "Other"],
1755
+ "Minutes": [
1756
+ plot_df["social_minutes"].mean(),
1757
+ plot_df["work_minutes"].mean(),
1758
+ plot_df["gaming_minutes"].mean(),
1759
+ 80,
1760
+ 45,
1761
+ ],
1762
+ }
1763
+ )
1764
+ fig = px.pie(
1765
+ usage_data,
1766
+ values="Minutes",
1767
+ names="Category",
1768
+ color_discrete_sequence=[
1769
+ "#3b82f6",
1770
+ "#10b981",
1771
+ "#8b5cf6",
1772
+ "#ec4899",
1773
+ "#64748b",
1774
+ ],
1775
+ hole=0.5,
1776
+ )
1777
+ fig.update_layout(
1778
+ height=400,
1779
+ plot_bgcolor="rgba(0,0,0,0)",
1780
+ paper_bgcolor="rgba(0,0,0,0)",
1781
+ font=dict(color="#ffffff"),
1782
+ )
1783
+ st.plotly_chart(fig, use_container_width=True)
1784
+
1785
+ with col2:
1786
+ st.markdown("#### Digital Interaction Metrics")
1787
+ interaction_data = (
1788
+ plot_df.groupby("risk_segment")
1789
+ .agg({"phone_unlocks": "mean", "notifications": "mean"})
1790
+ .reset_index()
1791
+ )
1792
+
1793
+ fig = go.Figure()
1794
+ fig.add_trace(
1795
+ go.Bar(
1796
+ x=interaction_data["risk_segment"],
1797
+ y=interaction_data["phone_unlocks"],
1798
+ name="Phone Unlocks",
1799
+ marker_color="#3b82f6",
1800
+ )
1801
+ )
1802
+ fig.add_trace(
1803
+ go.Bar(
1804
+ x=interaction_data["risk_segment"],
1805
+ y=interaction_data["notifications"],
1806
+ name="Notifications",
1807
+ marker_color="#f59e0b",
1808
+ )
1809
+ )
1810
+ fig.update_layout(
1811
+ height=400,
1812
+ plot_bgcolor="rgba(0,0,0,0)",
1813
+ paper_bgcolor="rgba(0,0,0,0)",
1814
+ font=dict(color="#ffffff"),
1815
+ barmode="group",
1816
+ )
1817
+ st.plotly_chart(fig, use_container_width=True)
1818
+
1819
+ with col3:
1820
+ st.markdown("#### Physical Activity Balance")
1821
+ balance_data = pd.DataFrame(
1822
+ {
1823
+ "Activity": ["Exercise", "Outdoor", "Screen Time"],
1824
+ "Minutes": [
1825
+ plot_df["exercise_minutes"].mean(),
1826
+ plot_df["outdoor_time"].mean(),
1827
+ plot_df["screen_hours"].mean() * 60,
1828
+ ],
1829
+ }
1830
+ )
1831
+ fig = px.bar(
1832
+ balance_data,
1833
+ x="Activity",
1834
+ y="Minutes",
1835
+ color="Activity",
1836
+ color_discrete_sequence=["#10b981", "#8b5cf6", "#ef4444"],
1837
+ )
1838
+ fig.update_layout(
1839
+ height=400,
1840
+ plot_bgcolor="rgba(0,0,0,0)",
1841
+ paper_bgcolor="rgba(0,0,0,0)",
1842
+ font=dict(color="#ffffff"),
1843
+ showlegend=False,
1844
+ )
1845
+ st.plotly_chart(fig, use_container_width=True)
1846
+
1847
+ # Health metrics
1848
+ st.markdown("<br>", unsafe_allow_html=True)
1849
+ st.markdown("#### Population Health Indicators")
1850
+
1851
+ col1, col2, col3, col4 = st.columns(4)
1852
+
1853
+ with col1:
1854
+ high_screen = len(plot_df[plot_df["screen_hours"] > 10])
1855
+ st.metric(
1856
+ "Excessive Screen Use",
1857
+ f"{high_screen:,}",
1858
+ f"{(high_screen/len(plot_df)*100):.1f}% of users",
1859
+ )
1860
+
1861
+ with col2:
1862
+ sleep_deficit = len(plot_df[plot_df["sleep_hours"] < 6])
1863
+ st.metric(
1864
+ "Sleep Deficit",
1865
+ f"{sleep_deficit:,}",
1866
+ f"{(sleep_deficit/len(plot_df)*100):.1f}% critical",
1867
+ )
1868
+
1869
+ with col3:
1870
+ high_stress = len(plot_df[plot_df["stress"] > 7])
1871
+ st.metric(
1872
+ "High Stress",
1873
+ f"{high_stress:,}",
1874
+ f"{(high_stress/len(plot_df)*100):.1f}% elevated",
1875
+ )
1876
+
1877
+ with col4:
1878
+ sedentary = len(plot_df[plot_df["exercise_minutes"] < 20])
1879
+ st.metric(
1880
+ "Sedentary Lifestyle",
1881
+ f"{sedentary:,}",
1882
+ f"{(sedentary/len(plot_df)*100):.1f}% inactive",
1883
+ )
1884
+
1885
+ # ========================================================
1886
+ # TAB 4: Model Performance
1887
+ # ========================================================
1888
+ with tab4:
1889
+ st.markdown(
1890
+ """
1891
+ <div class='section-header'>
1892
+ <div class='section-icon'>πŸ”¬</div>
1893
+ <div>
1894
+ <div class='section-title'>ML Model Performance</div>
1895
+ <div class='section-subtitle'>Evaluation and diagnostics on the current cohort</div>
1896
+ </div>
1897
+ </div>
1898
+ """,
1899
+ unsafe_allow_html=True,
1900
+ )
1901
+
1902
+ # Performance metrics
1903
+ col1, col2, col3, col4, col5 = st.columns(5)
1904
+
1905
+ metrics_data = [
1906
+ ("AUC-ROC", metrics["auc"], "🎯"),
1907
+ ("Precision", metrics["precision"], "πŸ”"),
1908
+ ("Recall", metrics["recall"], "πŸ“Š"),
1909
+ ("F1 Score", metrics["f1"], "⚑"),
1910
+ ("Brier Score", metrics["brier"], "πŸ“ˆ"),
1911
+ ]
1912
+
1913
+ for i, (label, value, icon) in enumerate(metrics_data):
1914
+ with [col1, col2, col3, col4, col5][i]:
1915
+ st.markdown(
1916
+ f"""
1917
+ <div class='metric-card'>
1918
+ <div class='metric-icon'>{icon}</div>
1919
+ <div class='metric-value'>{value:.3f}</div>
1920
+ <div class='metric-label'>{label}</div>
1921
+ </div>
1922
+ """,
1923
+ unsafe_allow_html=True,
1924
+ )
1925
+
1926
+ st.caption(f"Analysis scope: {metrics_scope}")
1927
+
1928
+ st.markdown("<br>", unsafe_allow_html=True)
1929
+
1930
+ # Threshold analysis
1931
+ col1, col2 = st.columns(2)
1932
+
1933
+ with col1:
1934
+ st.markdown("#### Threshold Optimization Curve")
1935
+ thresholds = np.linspace(0.1, 0.9, 60)
1936
+ precision_scores = []
1937
+ recall_scores = []
1938
+ f1_scores = []
1939
+
1940
+ for t in thresholds:
1941
+ temp_metrics = calculate_metrics(df_metrics, t)
1942
+ precision_scores.append(temp_metrics["precision"])
1943
+ recall_scores.append(temp_metrics["recall"])
1944
+ f1_scores.append(temp_metrics["f1"])
1945
+
1946
+ fig = go.Figure()
1947
+ fig.add_trace(
1948
+ go.Scatter(
1949
+ x=thresholds,
1950
+ y=precision_scores,
1951
+ mode="lines",
1952
+ name="Precision",
1953
+ line=dict(color="#10b981", width=3),
1954
+ )
1955
+ )
1956
+ fig.add_trace(
1957
+ go.Scatter(
1958
+ x=thresholds,
1959
+ y=recall_scores,
1960
+ mode="lines",
1961
+ name="Recall",
1962
+ line=dict(color="#3b82f6", width=3),
1963
+ )
1964
+ )
1965
+ fig.add_trace(
1966
+ go.Scatter(
1967
+ x=thresholds,
1968
+ y=f1_scores,
1969
+ mode="lines",
1970
+ name="F1 Score",
1971
+ line=dict(color="#8b5cf6", width=3),
1972
+ )
1973
+ )
1974
+ fig.add_vline(
1975
+ x=threshold,
1976
+ line_dash="dash",
1977
+ line_color="#f59e0b",
1978
+ line_width=3,
1979
+ annotation_text=f"Current: {threshold:.2f}",
1980
+ )
1981
+ fig.update_layout(
1982
+ height=450,
1983
+ plot_bgcolor="rgba(0,0,0,0)",
1984
+ paper_bgcolor="rgba(0,0,0,0)",
1985
+ font=dict(color="#ffffff"),
1986
+ xaxis_title="Decision Threshold",
1987
+ yaxis_title="Score",
1988
+ )
1989
+ st.plotly_chart(fig, use_container_width=True)
1990
+
1991
+ with col2:
1992
+ st.markdown("#### Precision-Recall Curve")
1993
+ precision_vals, recall_vals, _ = precision_recall_curve(
1994
+ df_metrics["high_risk"].values, df_metrics["risk_score"].values
1995
+ )
1996
+ fig = go.Figure()
1997
+ fig.add_trace(
1998
+ go.Scatter(
1999
+ x=recall_vals,
2000
+ y=precision_vals,
2001
+ mode="lines",
2002
+ name=f"PR Curve (AP = {metrics['ap']:.3f})",
2003
+ line=dict(color="#8b5cf6", width=4),
2004
+ fill="tozeroy",
2005
+ fillcolor="rgba(139,92,246,0.3)",
2006
+ )
2007
+ )
2008
+ fig.update_layout(
2009
+ height=450,
2010
+ plot_bgcolor="rgba(0,0,0,0)",
2011
+ paper_bgcolor="rgba(0,0,0,0)",
2012
+ font=dict(color="#ffffff"),
2013
+ xaxis_title="Recall",
2014
+ yaxis_title="Precision",
2015
+ )
2016
+ st.plotly_chart(fig, use_container_width=True)
2017
+
2018
+ # Calibration
2019
+ st.markdown("<br>", unsafe_allow_html=True)
2020
+ st.markdown("#### Model Calibration Analysis")
2021
+
2022
+ df_cal = df_metrics.copy()
2023
+ df_cal["score_bin"] = pd.qcut(
2024
+ df_cal["risk_score"], q=10, duplicates="drop"
2025
+ )
2026
+ cal_stats = (
2027
+ df_cal.groupby("score_bin")
2028
+ .agg({"risk_score": "mean", "high_risk": "mean", "user_id": "count"})
2029
+ .reset_index()
2030
+ )
2031
+
2032
+ fig = go.Figure()
2033
+ fig.add_trace(
2034
+ go.Scatter(
2035
+ x=cal_stats["risk_score"],
2036
+ y=cal_stats["high_risk"],
2037
+ mode="markers+lines",
2038
+ name="Observed Rate",
2039
+ marker=dict(size=cal_stats["user_id"] / 25, color="#3b82f6"),
2040
+ line=dict(color="#3b82f6", width=3),
2041
+ )
2042
+ )
2043
+ fig.add_trace(
2044
+ go.Scatter(
2045
+ x=[0, 1],
2046
+ y=[0, 1],
2047
+ mode="lines",
2048
+ name="Perfect Calibration",
2049
+ line=dict(color="#10b981", width=2, dash="dash"),
2050
+ )
2051
+ )
2052
+ fig.update_layout(
2053
+ height=500,
2054
+ plot_bgcolor="rgba(0,0,0,0)",
2055
+ paper_bgcolor="rgba(0,0,0,0)",
2056
+ font=dict(color="#ffffff"),
2057
+ xaxis_title="Predicted Risk Score",
2058
+ yaxis_title="Observed Event Rate",
2059
+ )
2060
+ st.plotly_chart(fig, use_container_width=True)
2061
+
2062
+ # ========================================================
2063
+ # TAB 5: Scenario Simulator
2064
+ # ========================================================
2065
+ with tab5:
2066
+ st.markdown(
2067
+ """
2068
+ <div class='section-header'>
2069
+ <div class='section-icon'>πŸ§ͺ</div>
2070
+ <div>
2071
+ <div class='section-title'>Interactive Risk Simulator</div>
2072
+ <div class='section-subtitle'>Test behavioral interventions and quantify risk impact</div>
2073
+ </div>
2074
+ </div>
2075
+ """,
2076
+ unsafe_allow_html=True,
2077
+ )
2078
+
2079
+ if real_time_mode:
2080
+ st.markdown(
2081
+ """
2082
+ <div style='background: rgba(34,197,94,0.1); padding: 14px 18px; border-radius: 10px;
2083
+ border-left: 4px solid #22c55e; margin-bottom: 14px;'>
2084
+ <span style='color:#bbf7d0; font-size:0.9rem;'>
2085
+ Real-time mode enabled β€” use this simulator as a live what-if console for new profiles.
2086
+ </span>
2087
+ </div>
2088
+ """,
2089
+ unsafe_allow_html=True,
2090
+ )
2091
+
2092
+ st.markdown(
2093
+ """
2094
+ <div style='background: rgba(59,130,246,0.12); padding: 20px; border-radius: 12px;
2095
+ border-left: 4px solid #3b82f6; margin: 16px 0;'>
2096
+ <strong>How this works:</strong><br>
2097
+ 1) Configure a single user profile using the sliders below.<br>
2098
+ 2) The risk engine predicts mental health risk in real-time based on the inputs.<br>
2099
+ 3) Intervention scenarios show how small changes shift the risk curve.
2100
+ </div>
2101
+ """,
2102
+ unsafe_allow_html=True,
2103
+ )
2104
+
2105
+ # Helper: risk computation for scenarios
2106
+ def compute_risk_from_features(params: dict) -> float:
2107
+ logit_val = (
2108
+ 0.62 * (params["screen"] - 7)
2109
+ + 0.58 * (params["stress"] - 5.5)
2110
+ + 0.52 * (params["anxiety"] - 5)
2111
+ + 0.48 * (params["depression"] - 4.5)
2112
+ - 0.46 * (params["sleep"] - 7.5)
2113
+ - 0.42 * (params["wellbeing"] - 8)
2114
+ - 0.38 * (params["mood"] - 7.5)
2115
+ - 0.35 * (params["energy"] - 7.8)
2116
+ + 0.22 * ((params["unlocks"] - 110) / 60.0)
2117
+ + 0.18 * ((params["social"] - 165) / 70.0)
2118
+ - 0.15 * ((params["exercise"] - 40) / 35.0)
2119
+ - 0.12 * ((params["outdoor"] - 55) / 55.0)
2120
+ + 0.2 * (params["loneliness"] - 4.5)
2121
+ - 0.18 * (params["social_support"] - 7)
2122
+ )
2123
+ return float(np.clip(1.0 / (1.0 + np.exp(-logit_val)), 0.01, 0.99))
2124
+
2125
+ # Sliders: base scenario
2126
+ col1, col2, col3 = st.columns(3)
2127
+
2128
+ with col1:
2129
+ st.markdown("##### πŸ“± Digital Behavior")
2130
+ sim_screen = st.slider(
2131
+ "Screen Time (hours/day)", 1.0, 18.0, 7.0, 0.5, key="sim_screen"
2132
+ )
2133
+ sim_unlocks = st.slider(
2134
+ "Phone Unlocks/day", 10, 400, 110, 5, key="sim_unlocks"
2135
+ )
2136
+ sim_notifications = st.slider(
2137
+ "Notifications/day", 5, 350, 95, 5, key="sim_notif"
2138
+ )
2139
+ sim_social = st.slider(
2140
+ "Social Media (min/day)", 5, 480, 165, 10, key="sim_social"
2141
+ )
2142
+ sim_gaming = st.slider(
2143
+ "Gaming (min/day)", 0, 360, 70, 10, key="sim_gaming"
2144
+ )
2145
+
2146
+ with col2:
2147
+ st.markdown("##### 😴 Health & Wellbeing")
2148
+ sim_sleep = st.slider(
2149
+ "Sleep (hours/day)", 3.0, 12.0, 7.5, 0.5, key="sim_sleep"
2150
+ )
2151
+ sim_stress = st.slider(
2152
+ "Stress Level (1-10)", 1.0, 10.0, 5.5, 0.5, key="sim_stress"
2153
+ )
2154
+ sim_anxiety = st.slider(
2155
+ "Anxiety Level (1-10)", 1.0, 10.0, 5.0, 0.5, key="sim_anxiety"
2156
+ )
2157
+ sim_depression = st.slider(
2158
+ "Depression (1-10)", 1.0, 10.0, 4.5, 0.5, key="sim_depression"
2159
+ )
2160
+ sim_wellbeing = st.slider(
2161
+ "Wellbeing (1-10)", 1.0, 10.0, 8.0, 0.5, key="sim_wellbeing"
2162
+ )
2163
+ sim_mood = st.slider(
2164
+ "Mood (1-10)", 1.0, 10.0, 7.5, 0.5, key="sim_mood"
2165
+ )
2166
+ sim_energy = st.slider(
2167
+ "Energy (1-10)", 1.0, 10.0, 7.8, 0.5, key="sim_energy"
2168
+ )
2169
+
2170
+ with col3:
2171
+ st.markdown("##### πŸƒ Physical & Social")
2172
+ sim_exercise = st.slider(
2173
+ "Exercise (min/day)", 0, 200, 40, 5, key="sim_exercise"
2174
+ )
2175
+ sim_outdoor = st.slider(
2176
+ "Outdoor Time (min/day)", 0, 280, 55, 10, key="sim_outdoor"
2177
+ )
2178
+ sim_social_support = st.slider(
2179
+ "Social Support (1-10)", 1.0, 10.0, 7.0, 0.5, key="sim_social_support"
2180
+ )
2181
+ sim_loneliness = st.slider(
2182
+ "Loneliness (1-10)", 1.0, 10.0, 4.5, 0.5, key="sim_loneliness"
2183
+ )
2184
+
2185
+ st.markdown("<br>", unsafe_allow_html=True)
2186
+ if st.button("πŸ”„ Reset to Population Baseline", use_container_width=True):
2187
+ st.rerun()
2188
+
2189
+ # Bundle base features
2190
+ base_features = {
2191
+ "screen": sim_screen,
2192
+ "sleep": sim_sleep,
2193
+ "stress": sim_stress,
2194
+ "anxiety": sim_anxiety,
2195
+ "depression": sim_depression,
2196
+ "wellbeing": sim_wellbeing,
2197
+ "mood": sim_mood,
2198
+ "energy": sim_energy,
2199
+ "unlocks": sim_unlocks,
2200
+ "social": sim_social,
2201
+ "exercise": sim_exercise,
2202
+ "outdoor": sim_outdoor,
2203
+ "loneliness": sim_loneliness,
2204
+ "social_support": sim_social_support,
2205
+ }
2206
+
2207
+ # Base scenario risk
2208
+ scenario_risk = compute_risk_from_features(base_features)
2209
+ scenario_segment = (
2210
+ "Low Risk"
2211
+ if scenario_risk < 0.30
2212
+ else "Moderate Risk"
2213
+ if scenario_risk < 0.60
2214
+ else "High Risk"
2215
+ )
2216
+ scenario_flagged = "Yes" if scenario_risk >= threshold else "No"
2217
+ percentile = (
2218
+ (df["risk_score"] < scenario_risk).mean() * 100 if len(df) > 0 else 0.0
2219
+ )
2220
+
2221
+ # Results cards
2222
+ st.markdown("<br>", unsafe_allow_html=True)
2223
+ st.markdown("#### 🎯 Simulation Results")
2224
+
2225
+ col1, col2, col3, col4 = st.columns(4)
2226
+
2227
+ with col1:
2228
+ color_risk = (
2229
+ "#ef4444"
2230
+ if scenario_risk > 0.6
2231
+ else "#f59e0b"
2232
+ if scenario_risk > 0.3
2233
+ else "#10b981"
2234
+ )
2235
+ st.markdown(
2236
+ f"""
2237
+ <div class='metric-card'>
2238
+ <div class='metric-label'>PREDICTED RISK SCORE</div>
2239
+ <div class='metric-value' style='color: {color_risk};'>
2240
+ {scenario_risk:.3f}
2241
+ </div>
2242
+ </div>
2243
+ """,
2244
+ unsafe_allow_html=True,
2245
+ )
2246
+
2247
+ with col2:
2248
+ segment_color = {
2249
+ "Low Risk": "#10b981",
2250
+ "Moderate Risk": "#f59e0b",
2251
+ "High Risk": "#ef4444",
2252
+ }[scenario_segment]
2253
+ st.markdown(
2254
+ f"""
2255
+ <div class='metric-card'>
2256
+ <div class='metric-label'>RISK CATEGORY</div>
2257
+ <div class='metric-value' style='color: {segment_color}; font-size: 2rem;'>
2258
+ {scenario_segment}
2259
+ </div>
2260
+ </div>
2261
+ """,
2262
+ unsafe_allow_html=True,
2263
+ )
2264
+
2265
+ with col3:
2266
+ flag_color = "#ef4444" if scenario_flagged == "Yes" else "#10b981"
2267
+ st.markdown(
2268
+ f"""
2269
+ <div class='metric-card'>
2270
+ <div class='metric-label'>INTERVENTION FLAG</div>
2271
+ <div class='metric-value' style='color: {flag_color}; font-size: 2.5rem;'>
2272
+ {scenario_flagged}
2273
+ </div>
2274
+ <div class='metric-change'>At threshold {threshold:.2f}</div>
2275
+ </div>
2276
+ """,
2277
+ unsafe_allow_html=True,
2278
+ )
2279
+
2280
+ with col4:
2281
+ st.markdown(
2282
+ f"""
2283
+ <div class='metric-card'>
2284
+ <div class='metric-label'>POPULATION PERCENTILE</div>
2285
+ <div class='metric-value'>{percentile:.0f}th</div>
2286
+ <div class='metric-change'>Risk ranking vs full population</div>
2287
+ </div>
2288
+ """,
2289
+ unsafe_allow_html=True,
2290
+ )
2291
+
2292
+ # Scenario vs population + radar
2293
+ st.markdown("<br>", unsafe_allow_html=True)
2294
+ col_left, col_right = st.columns(2)
2295
+
2296
+ with col_left:
2297
+ st.markdown("#### πŸ“Š Scenario vs Population Averages")
2298
+
2299
+ comparison_data = pd.DataFrame(
2300
+ {
2301
+ "Metric": [
2302
+ "Screen Time",
2303
+ "Sleep",
2304
+ "Stress",
2305
+ "Anxiety",
2306
+ "Wellbeing",
2307
+ "Exercise (Γ—10min)",
2308
+ "Social Support",
2309
+ ],
2310
+ "Your Scenario": [
2311
+ sim_screen,
2312
+ sim_sleep,
2313
+ sim_stress,
2314
+ sim_anxiety,
2315
+ sim_wellbeing,
2316
+ sim_exercise / 10.0,
2317
+ sim_social_support,
2318
+ ],
2319
+ "Population Avg": [
2320
+ df["screen_hours"].mean(),
2321
+ df["sleep_hours"].mean(),
2322
+ df["stress"].mean(),
2323
+ df["anxiety"].mean(),
2324
+ df["wellbeing"].mean(),
2325
+ df["exercise_minutes"].mean() / 10.0,
2326
+ df["social_support"].mean(),
2327
+ ]
2328
+ if len(df) > 0
2329
+ else [0, 0, 0, 0, 0, 0, 0],
2330
+ }
2331
+ )
2332
+
2333
+ fig_comp = go.Figure()
2334
+ fig_comp.add_trace(
2335
+ go.Bar(
2336
+ x=comparison_data["Metric"],
2337
+ y=comparison_data["Your Scenario"],
2338
+ name="Your Scenario",
2339
+ marker_color="#3b82f6",
2340
+ )
2341
+ )
2342
+ fig_comp.add_trace(
2343
+ go.Bar(
2344
+ x=comparison_data["Metric"],
2345
+ y=comparison_data["Population Avg"],
2346
+ name="Population Average",
2347
+ marker_color="#64748b",
2348
+ )
2349
+ )
2350
+ fig_comp.update_layout(
2351
+ height=430,
2352
+ plot_bgcolor="rgba(0,0,0,0)",
2353
+ paper_bgcolor="rgba(0,0,0,0)",
2354
+ font=dict(color="#ffffff"),
2355
+ barmode="group",
2356
+ )
2357
+ st.plotly_chart(fig_comp, use_container_width=True)
2358
+
2359
+ with col_right:
2360
+ st.markdown("#### 🧬 Profile Radar View")
2361
+
2362
+ radar_categories = [
2363
+ "Screen Load",
2364
+ "Sleep",
2365
+ "Stress",
2366
+ "Anxiety",
2367
+ "Wellbeing",
2368
+ "Exercise",
2369
+ "Social Support",
2370
+ ]
2371
+
2372
+ radar_values = [
2373
+ min(sim_screen / 1.8, 10.0),
2374
+ min(sim_sleep * 1.2, 10.0),
2375
+ sim_stress,
2376
+ sim_anxiety,
2377
+ sim_wellbeing,
2378
+ min(sim_exercise / 12.0, 10.0),
2379
+ sim_social_support,
2380
+ ]
2381
+
2382
+ radar_categories += radar_categories[:1]
2383
+ radar_values += radar_values[:1]
2384
+
2385
+ fig_radar = go.Figure()
2386
+ fig_radar.add_trace(
2387
+ go.Scatterpolar(
2388
+ r=radar_values,
2389
+ theta=radar_categories,
2390
+ fill="toself",
2391
+ name="Scenario profile",
2392
+ line=dict(color="#3b82f6", width=3),
2393
+ fillcolor="rgba(59,130,246,0.35)",
2394
+ )
2395
+ )
2396
+ fig_radar.update_layout(
2397
+ height=430,
2398
+ plot_bgcolor="rgba(0,0,0,0)",
2399
+ paper_bgcolor="rgba(0,0,0,0)",
2400
+ font=dict(color="#ffffff"),
2401
+ polar=dict(
2402
+ radialaxis=dict(
2403
+ visible=True,
2404
+ range=[0, 10],
2405
+ gridcolor="rgba(148,163,184,0.4)",
2406
+ ),
2407
+ angularaxis=dict(gridcolor="rgba(148,163,184,0.4)"),
2408
+ ),
2409
+ showlegend=False,
2410
+ )
2411
+ st.plotly_chart(fig_radar, use_container_width=True)
2412
+
2413
+ # Quick intervention scenarios
2414
+ st.markdown("<br>", unsafe_allow_html=True)
2415
+ st.markdown("#### 🧭 Quick Intervention What-If Scenarios")
2416
+
2417
+ # Digital reset: reduce screen & social, increase exercise a bit
2418
+ digital_reset = base_features.copy()
2419
+ digital_reset["screen"] = max(1.0, digital_reset["screen"] - 2.0)
2420
+ digital_reset["social"] = max(5.0, digital_reset["social"] - 60.0)
2421
+ digital_reset["unlocks"] = max(10.0, digital_reset["unlocks"] - 40.0)
2422
+ digital_reset["exercise"] = min(200.0, digital_reset["exercise"] + 20.0)
2423
+ risk_digital = compute_risk_from_features(digital_reset)
2424
+
2425
+ # Sleep protocol: +1.5h sleep, -1 stress, -0.5 anxiety
2426
+ sleep_reset = base_features.copy()
2427
+ sleep_reset["sleep"] = min(12.0, sleep_reset["sleep"] + 1.5)
2428
+ sleep_reset["stress"] = max(1.0, sleep_reset["stress"] - 1.0)
2429
+ sleep_reset["anxiety"] = max(1.0, sleep_reset["anxiety"] - 0.5)
2430
+ risk_sleep = compute_risk_from_features(sleep_reset)
2431
+
2432
+ # Holistic plan: combine modest improvements
2433
+ holistic = base_features.copy()
2434
+ holistic["screen"] = max(1.0, holistic["screen"] - 1.5)
2435
+ holistic["sleep"] = min(12.0, holistic["sleep"] + 1.0)
2436
+ holistic["exercise"] = min(200.0, holistic["exercise"] + 25.0)
2437
+ holistic["outdoor"] = min(280.0, holistic["outdoor"] + 30.0)
2438
+ holistic["loneliness"] = max(1.0, holistic["loneliness"] - 1.0)
2439
+ holistic["social_support"] = min(10.0, holistic["social_support"] + 1.0)
2440
+ risk_holistic = compute_risk_from_features(holistic)
2441
+
2442
+ col1, col2, col3 = st.columns(3)
2443
+
2444
+ def render_intervention_card(title: str, risk_new: float):
2445
+ delta = risk_new - scenario_risk
2446
+ color = (
2447
+ "#10b981"
2448
+ if delta < 0
2449
+ else "#f59e0b"
2450
+ if abs(delta) < 0.01
2451
+ else "#ef4444"
2452
+ )
2453
+ sign = "+" if delta >= 0 else "βˆ’"
2454
+ st.markdown(
2455
+ f"""
2456
+ <div class='metric-card'>
2457
+ <div class='metric-label'>{title}</div>
2458
+ <div class='metric-value' style='font-size: 2.1rem; color: {color};'>
2459
+ {risk_new:.3f}
2460
+ </div>
2461
+ <div class='metric-change'>
2462
+ Ξ” risk: {sign}{abs(delta):.3f}
2463
+ </div>
2464
+ </div>
2465
+ """,
2466
+ unsafe_allow_html=True,
2467
+ )
2468
+
2469
+ with col1:
2470
+ render_intervention_card(
2471
+ "Digital Reset (βˆ’2h screen, βˆ’60min social)", risk_digital
2472
+ )
2473
+ with col2:
2474
+ render_intervention_card("Sleep Protocol (+1.5h sleep, ↓ stress)", risk_sleep)
2475
+ with col3:
2476
+ render_intervention_card(
2477
+ "Holistic Plan (screen, sleep, exercise, social)", risk_holistic
2478
+ )
2479
+
2480
+ # Sensitivity curve: risk vs screen time
2481
+ st.markdown("<br>", unsafe_allow_html=True)
2482
+ st.markdown("#### πŸ“ˆ Sensitivity: Risk vs Screen Time (other factors fixed)")
2483
+
2484
+ screen_min = max(1.0, sim_screen - 3.0)
2485
+ screen_max = min(18.0, sim_screen + 3.0)
2486
+ screen_grid = np.linspace(screen_min, screen_max, 40)
2487
+ risk_grid = []
2488
+
2489
+ for s_val in screen_grid:
2490
+ tmp = base_features.copy()
2491
+ tmp["screen"] = float(s_val)
2492
+ risk_grid.append(compute_risk_from_features(tmp))
2493
+
2494
+ fig_sens = go.Figure()
2495
+ fig_sens.add_trace(
2496
+ go.Scatter(
2497
+ x=screen_grid,
2498
+ y=risk_grid,
2499
+ mode="lines",
2500
+ name="Risk vs Screen Time",
2501
+ line=dict(color="#3b82f6", width=4),
2502
+ fill="tozeroy",
2503
+ fillcolor="rgba(59,130,246,0.25)",
2504
+ )
2505
+ )
2506
+ fig_sens.add_vline(
2507
+ x=sim_screen,
2508
+ line_dash="dash",
2509
+ line_color="#f59e0b",
2510
+ line_width=3,
2511
+ annotation_text=f"Current: {sim_screen:.1f}h",
2512
+ annotation_position="top right",
2513
+ )
2514
+ fig_sens.update_layout(
2515
+ height=430,
2516
+ plot_bgcolor="rgba(0,0,0,0)",
2517
+ paper_bgcolor="rgba(0,0,0,0)",
2518
+ font=dict(color="#ffffff"),
2519
+ xaxis_title="Screen Time (hours/day)",
2520
+ yaxis_title="Predicted Risk",
2521
+ )
2522
+ fig_sens.update_xaxes(showgrid=True, gridcolor="rgba(255,255,255,0.14)")
2523
+ fig_sens.update_yaxes(showgrid=True, gridcolor="rgba(255,255,255,0.14)")
2524
+ st.plotly_chart(fig_sens, use_container_width=True)
2525
+
2526
+ # Recommendations
2527
+ st.markdown("<br>", unsafe_allow_html=True)
2528
+ st.markdown("#### πŸ’‘ AI-Generated Recommendations")
2529
+
2530
+ recommendations = []
2531
+
2532
+ if sim_screen > 10:
2533
+ recommendations.append(
2534
+ (
2535
+ "danger",
2536
+ "πŸ”΄ Screen time is well above healthy limits. Introduce strict boundaries, offline blocks, and app usage limits.",
2537
+ )
2538
+ )
2539
+ elif sim_screen > 8:
2540
+ recommendations.append(
2541
+ (
2542
+ "warning",
2543
+ "🟑 High digital exposure detected. Add app time caps and at least one daily offline block of 60–90 minutes.",
2544
+ )
2545
+ )
2546
+
2547
+ if sim_sleep < 6:
2548
+ recommendations.append(
2549
+ (
2550
+ "danger",
2551
+ "πŸ”΄ Severe sleep deficit. Establish a consistent sleep schedule, limit screens before bedtime, and consider medical follow-up if problems persist.",
2552
+ )
2553
+ )
2554
+ elif sim_sleep < 7:
2555
+ recommendations.append(
2556
+ (
2557
+ "warning",
2558
+ "🟑 Sleep is below the recommended range. Aim for 30–60 additional minutes for the next two weeks and track consistency.",
2559
+ )
2560
+ )
2561
+
2562
+ if sim_stress > 7 or sim_anxiety > 7:
2563
+ recommendations.append(
2564
+ (
2565
+ "danger",
2566
+ "πŸ”΄ Elevated psychological stress and anxiety. Structured support (for example, counseling or therapy) may be beneficial.",
2567
+ )
2568
+ )
2569
+
2570
+ if sim_exercise < 20:
2571
+ recommendations.append(
2572
+ (
2573
+ "warning",
2574
+ "🟑 Physical activity is low. Target at least 30 minutes of light-to-moderate movement on most days.",
2575
+ )
2576
+ )
2577
+
2578
+ if sim_loneliness > 6:
2579
+ recommendations.append(
2580
+ (
2581
+ "warning",
2582
+ "🟑 Social isolation signals detected. Strengthening connections through community activities or regular check-ins may help.",
2583
+ )
2584
+ )
2585
+
2586
+ if (
2587
+ sim_wellbeing > 7.5
2588
+ and sim_stress < 5.5
2589
+ and sim_exercise > 30
2590
+ and sim_sleep >= 7
2591
+ ):
2592
+ recommendations.append(
2593
+ (
2594
+ "success",
2595
+ "🟒 Overall balance looks healthy. Continue tracking digital habits and maintaining the current protective lifestyle factors.",
2596
+ )
2597
+ )
2598
+
2599
+ if not recommendations:
2600
+ recommendations.append(
2601
+ (
2602
+ "success",
2603
+ "🟒 Metrics are balanced. Maintain current routines and review again after any major lifestyle changes.",
2604
+ )
2605
+ )
2606
+
2607
+ for rec_type, rec_text in recommendations:
2608
+ bg_color = {
2609
+ "danger": "rgba(239,68,68,0.15)",
2610
+ "warning": "rgba(245,158,11,0.15)",
2611
+ "success": "rgba(16,185,129,0.15)",
2612
+ }[rec_type]
2613
+ border_color = {
2614
+ "danger": "#ef4444",
2615
+ "warning": "#f59e0b",
2616
+ "success": "#10b981",
2617
+ }[rec_type]
2618
+ st.markdown(
2619
+ f"""
2620
+ <div style='background: {bg_color}; padding: 15px; border-radius: 10px;
2621
+ border-left: 4px solid {border_color}; margin: 8px 0;'>
2622
+ <p style='color: white; margin: 0; font-size: 1rem;'>{rec_text}</p>
2623
+ </div>
2624
+ """,
2625
+ unsafe_allow_html=True,
2626
+ )
2627
+
2628
+ # ========================================================
2629
+ # TAB 6: Clinical Reports
2630
+ # ========================================================
2631
+ with tab6:
2632
+ st.markdown(
2633
+ """
2634
+ <div class='section-header'>
2635
+ <div class='section-icon'>πŸ₯</div>
2636
+ <div>
2637
+ <div class='section-title'>Clinical Intelligence Reports</div>
2638
+ <div class='section-subtitle'>High-risk users and summary indicators</div>
2639
+ </div>
2640
+ </div>
2641
+ """,
2642
+ unsafe_allow_html=True,
2643
+ )
2644
+
2645
+ # Top risk users
2646
+ st.markdown("#### 🚨 Critical Risk Users - Priority List")
2647
+
2648
+ if len(plot_df) > 0:
2649
+ top_risk = plot_df.nlargest(
2650
+ 30,
2651
+ "risk_score",
2652
+ )[
2653
+ [
2654
+ "user_id",
2655
+ "age",
2656
+ "gender",
2657
+ "occupation",
2658
+ "risk_score",
2659
+ "risk_segment",
2660
+ "screen_hours",
2661
+ "sleep_hours",
2662
+ "stress",
2663
+ "anxiety",
2664
+ "depression",
2665
+ "wellbeing",
2666
+ "social_support",
2667
+ "loneliness",
2668
+ "last_active",
2669
+ ]
2670
+ ].copy()
2671
+
2672
+ top_risk["risk_score"] = top_risk["risk_score"].round(3)
2673
+ top_risk["screen_hours"] = top_risk["screen_hours"].round(1)
2674
+ top_risk["sleep_hours"] = top_risk["sleep_hours"].round(1)
2675
+ top_risk["days_inactive"] = (
2676
+ pd.Timestamp.now() - top_risk["last_active"]
2677
+ ).dt.days
2678
+
2679
+ st.dataframe(
2680
+ top_risk.drop(columns=["last_active"]),
2681
+ use_container_width=True,
2682
+ height=500,
2683
+ )
2684
+
2685
+ # Export high-risk report
2686
+ if st.button(
2687
+ "πŸ“„ Export High-Risk Report (CSV)", use_container_width=False
2688
+ ):
2689
+ csv_data = top_risk.to_csv(index=False).encode("utf-8")
2690
+ st.download_button(
2691
+ "⬇️ Download Report",
2692
+ data=csv_data,
2693
+ file_name=f"high_risk_users_{datetime.now().strftime('%Y%m%d_%H%M')}.csv",
2694
+ mime="text/csv",
2695
+ )
2696
+ else:
2697
+ st.info("No users in current filtered view.")
2698
+
2699
+ st.markdown("<br>", unsafe_allow_html=True)
2700
+
2701
+ # Summary statistics
2702
+ col1, col2, col3 = st.columns(3)
2703
+
2704
+ with col1:
2705
+ st.markdown("#### Risk Distribution Summary")
2706
+ if len(plot_df) > 0:
2707
+ risk_summary = (
2708
+ plot_df["risk_segment"]
2709
+ .value_counts()
2710
+ .rename_axis("risk_segment")
2711
+ .to_frame("count")
2712
+ )
2713
+ risk_summary["percentage"] = (
2714
+ risk_summary["count"] / len(plot_df) * 100
2715
+ ).round(1)
2716
+ st.dataframe(risk_summary, use_container_width=True)
2717
+ else:
2718
+ st.write("No data available for summary.")
2719
+
2720
+ with col2:
2721
+ st.markdown("#### Mental Health Averages")
2722
+ if len(plot_df) > 0:
2723
+ mental_health_avg = (
2724
+ plot_df[
2725
+ [
2726
+ "stress",
2727
+ "anxiety",
2728
+ "depression",
2729
+ "wellbeing",
2730
+ "mood",
2731
+ "energy",
2732
+ ]
2733
+ ]
2734
+ .mean()
2735
+ .round(2)
2736
+ .to_frame(name="Average Score")
2737
+ )
2738
+ st.dataframe(mental_health_avg, use_container_width=True)
2739
+ else:
2740
+ st.write("No data available for summary.")
2741
+
2742
+ with col3:
2743
+ st.markdown("#### Behavioral Metrics")
2744
+ if len(plot_df) > 0:
2745
+ behavior_avg = (
2746
+ plot_df[
2747
+ [
2748
+ "screen_hours",
2749
+ "phone_unlocks",
2750
+ "notifications",
2751
+ "social_minutes",
2752
+ "gaming_minutes",
2753
+ "exercise_minutes",
2754
+ "outdoor_time",
2755
+ "steps_daily",
2756
+ ]
2757
+ ]
2758
+ .mean()
2759
+ .round(2)
2760
+ .to_frame(name="Average Value")
2761
+ )
2762
+ st.dataframe(behavior_avg, use_container_width=True)
2763
+ else:
2764
+ st.write("No data available for summary.")