DavidNgoue commited on
Commit
de623e1
·
verified ·
1 Parent(s): fb37ba8

Update src/streamlit_app.py

Browse files
Files changed (1) hide show
  1. src/streamlit_app.py +572 -38
src/streamlit_app.py CHANGED
@@ -1,40 +1,574 @@
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 streamlit as st
2
+ import pandas as pd
3
+ import numpy as np
4
+ import pickle
5
+ from PIL import Image
6
+ import plotly.express as px
7
+ import plotly.graph_objects as go
8
+ from streamlit_lottie import st_lottie
9
+ import requests
10
+ from streamlit_option_menu import option_menu
11
+
12
+ # --------- UTILS ---------
13
+ @st.cache_resource
14
+ def load_model():
15
+ with open('src/best_regression_model.pkl', 'rb') as f:
16
+ data = pickle.load(f)
17
+ return data['model'], data['scaler_X'], data['scaler_y'], data.get('metrics', None)
18
+
19
+ def load_lottieurl(url: str):
20
+ r = requests.get(url)
21
+ if r.status_code != 200:
22
+ return None
23
+ return r.json()
24
+
25
+ # --------- ANIMATIONS ---------
26
+ credit_animation = load_lottieurl("https://assets2.lottiefiles.com/packages/lf20_4kx2q32n.json")
27
+ loading_animation = load_lottieurl("https://assets3.lottiefiles.com/packages/lf20_p8bfn5to.json")
28
+ about_animation = load_lottieurl("https://assets2.lottiefiles.com/packages/lf20_0yfsb3a1.json")
29
+
30
+ # --------- PAGE CONFIG ---------
31
+ st.set_page_config(
32
+ page_title="Credit Card Expenditure Predictor",
33
+ page_icon="💳",
34
+ layout="wide",
35
+ initial_sidebar_state="expanded"
36
+ )
37
+
38
+ # --------- CSS ---------
39
+ st.markdown("""
40
+ <style>
41
+ @import url('https://fonts.googleapis.com/css2?family=Poppins:wght@400;600&display=swap');
42
+ * { font-family: 'Poppins', sans-serif; }
43
+ .main { background: linear-gradient(135deg, #f5f7fa 0%, #e4e8eb 100%); }
44
+ .stButton>button {
45
+ background: linear-gradient(45deg, #4b79a1, #283e51);
46
+ color: white;
47
+ border-radius: 25px;
48
+ padding: 10px 25px;
49
+ border: none;
50
+ box-shadow: 0 4px 15px rgba(75, 121, 161, 0.3);
51
+ transition: all 0.3s ease;
52
+ font-weight: 600;
53
+ font-size: 1.1em;
54
+ }
55
+ .stButton>button:hover {
56
+ transform: translateY(-2px) scale(1.04);
57
+ box-shadow: 0 6px 20px rgba(75, 121, 161, 0.4);
58
+ background: linear-gradient(45deg, #283e51, #4b79a1);
59
+ }
60
+ .sidebar .sidebar-content {
61
+ background: linear-gradient(180deg, #ffffff 0%, #f8f9fa 100%);
62
+ box-shadow: 2px 0 10px rgba(0,0,0,0.1);
63
+ }
64
+ .stSelectbox, .stNumberInput { border-radius: 10px; }
65
+ .stProgress > div > div { background-color: #4b79a1; }
66
+ .stMarkdown { color: #2c3e50; }
67
+ .stAlert { border-radius: 10px; }
68
+ .section-card {
69
+ background: white;
70
+ padding: 2rem;
71
+ border-radius: 18px;
72
+ box-shadow: 0 8px 32px 0 rgba(76, 110, 245, 0.10);
73
+ margin-bottom: 2rem;
74
+ transition: box-shadow 0.3s;
75
+ }
76
+ .section-card:hover {
77
+ box-shadow: 0 16px 40px 0 rgba(76, 110, 245, 0.18);
78
+ }
79
+ .section-title {
80
+ color: #4b79a1;
81
+ font-size: 2.2em;
82
+ font-weight: 700;
83
+ margin-bottom: 0.5em;
84
+ text-shadow: 1px 1px 2px #e4e8eb;
85
+ }
86
+ .badge {
87
+ display: inline-block;
88
+ background: linear-gradient(90deg, #4b79a1 0%, #283e51 100%);
89
+ color: white;
90
+ border-radius: 12px;
91
+ padding: 0.3em 1em;
92
+ font-size: 1em;
93
+ font-weight: 600;
94
+ margin: 0.2em 0.3em;
95
+ box-shadow: 0 2px 8px rgba(76, 110, 245, 0.10);
96
+ }
97
+ .about-avatar {
98
+ border-radius: 50%;
99
+ border: 4px solid #4b79a1;
100
+ box-shadow: 0 4px 16px rgba(76, 110, 245, 0.15);
101
+ margin-bottom: 1em;
102
+ }
103
+ .about-contact-btn {
104
+ background: linear-gradient(90deg, #4b79a1 0%, #283e51 100%);
105
+ color: white;
106
+ border-radius: 20px;
107
+ padding: 0.5em 1.5em;
108
+ border: none;
109
+ font-weight: 600;
110
+ margin: 0.5em 0.5em 0.5em 0;
111
+ font-size: 1.1em;
112
+ box-shadow: 0 2px 8px rgba(76, 110, 245, 0.10);
113
+ transition: background 0.2s;
114
+ }
115
+ .about-contact-btn:hover {
116
+ background: linear-gradient(90deg, #283e51 0%, #4b79a1 100%);
117
+ color: #fff;
118
+ }
119
+ .card-fade {
120
+ opacity: 0;
121
+ transform: translateY(30px);
122
+ animation: fadeInUp 0.8s forwards;
123
+ animation-delay: 0.2s;
124
+ }
125
+ @keyframes fadeInUp {
126
+ to {
127
+ opacity: 1;
128
+ transform: none;
129
+ }
130
+ }
131
+ .section-sep {
132
+ border: none;
133
+ border-top: 2px solid #e4e8eb;
134
+ margin: 2.5em 0 2em 0;
135
+ width: 80%;
136
+ }
137
+ .section-title-visual {
138
+ font-size: 2em;
139
+ color: #283e51;
140
+ font-weight: 700;
141
+ margin-bottom: 0.7em;
142
+ letter-spacing: 1px;
143
+ text-shadow: 0 2px 8px #e4e8eb;
144
+ }
145
+ .visual-card {
146
+ background: linear-gradient(120deg, #fafdff 0%, #f5f7fa 100%);
147
+ border-radius: 18px;
148
+ box-shadow: 0 4px 24px 0 rgba(76, 110, 245, 0.10);
149
+ padding: 1.5em 2em 1.5em 2em;
150
+ margin-bottom: 2.5em;
151
+ transition: box-shadow 0.3s, transform 0.3s;
152
+ }
153
+ .visual-card:hover {
154
+ box-shadow: 0 12px 32px 0 rgba(76, 110, 245, 0.18);
155
+ transform: translateY(-4px) scale(1.01);
156
+ }
157
+ .metric-card {
158
+ background: linear-gradient(90deg, #4b79a1 0%, #283e51 100%);
159
+ color: white;
160
+ border-radius: 18px;
161
+ box-shadow: 0 4px 24px 0 rgba(76, 110, 245, 0.10);
162
+ padding: 2em 2em 1.5em 2em;
163
+ margin-bottom: 2.5em;
164
+ text-align: center;
165
+ position: relative;
166
+ overflow: hidden;
167
+ }
168
+ .metric-card h3 {
169
+ font-size: 1.7em;
170
+ margin-bottom: 0.7em;
171
+ color: #fff;
172
+ letter-spacing: 1px;
173
+ }
174
+ .metric-list {
175
+ list-style: none;
176
+ padding: 0;
177
+ margin: 0 auto;
178
+ font-size: 1.15em;
179
+ }
180
+ .metric-list li {
181
+ margin: 0.7em 0;
182
+ padding: 0.5em 0;
183
+ border-bottom: 1px solid #ffffff22;
184
+ }
185
+ .metric-list li:last-child {
186
+ border-bottom: none;
187
+ }
188
+ </style>
189
+ """, unsafe_allow_html=True)
190
+
191
+ # --------- SIDEBAR ---------
192
+ with st.sidebar:
193
+ st_lottie(credit_animation, height=120, key="sidebar_animation")
194
+ selected = option_menu(
195
+ menu_title="Navigation",
196
+ options=["Accueil", "Prédiction", "Analyse", "À propos"],
197
+ icons=['house', 'credit-card', 'bar-chart', 'info-circle'],
198
+ menu_icon="cast",
199
+ default_index=0,
200
+ styles={
201
+ "container": {"padding": "0!important", "background-color": "#ffffff"},
202
+ "icon": {"color": "#4b79a1", "font-size": "20px"},
203
+ "nav-link": {
204
+ "font-size": "16px",
205
+ "text-align": "left",
206
+ "margin": "0px",
207
+ "padding": "10px",
208
+ "--hover-color": "#4b79a1",
209
+ },
210
+ "nav-link-selected": {"background-color": "#4b79a1"},
211
+ }
212
+ )
213
+
214
+ # --------- ACCUEIL ---------
215
+ if selected == "Accueil":
216
+ st.markdown("""
217
+ <div style='
218
+ text-align: center;
219
+ padding: 2.5rem 1rem 2rem 1rem;
220
+ background: linear-gradient(135deg, #f8fafc 0%, #e4e8eb 100%);
221
+ border-radius: 22px;
222
+ margin-bottom: 36px;
223
+ box-shadow: 0 6px 32px 0 rgba(76, 110, 245, 0.08);
224
+ '>
225
+ <h1 class='section-title' style='font-size:2.8em; margin-bottom:0.2em;'>💳 Credit Card Expenditure Predictor</h1>
226
+ <p style='color: #2c3e50; font-size: 1.35em; font-weight: 400; margin-bottom:0.8em;'>
227
+ <i>Prédisez les dépenses annuelles de vos clients grâce à l'IA, pour une gestion financière plus intelligente et personnalisée.</i>
228
+ </p>
229
+ <hr style='border: none; border-top: 1.5px solid #e4e8eb; width: 60%; margin: 1.5em auto 1.5em auto;'/>
230
+ <p style='color: #4b79a1; font-size: 1.1em; max-width: 700px; margin: auto;'>
231
+ Cette application met la puissance du machine learning au service de la finance :
232
+ <b>analysez, prédisez et optimisez</b> les dépenses de carte de crédit de vos clients en quelques clics.<br>
233
+ <span style='color:#283e51;'>Pensée pour les professionnels, accessible à tous.</span>
234
+ </p>
235
+ </div>
236
+ """, unsafe_allow_html=True)
237
+ col1, col2 = st.columns([1.5, 1])
238
+ with col1:
239
+ st.markdown("""
240
+ <div class='section-card' style='margin-bottom:1.5em;'>
241
+ <h3 style='color:#4b79a1; font-size:1.3em;'>🎯 Mission</h3>
242
+ <p style='font-size:1.08em;'>
243
+ Offrir un outil prédictif fiable et intuitif pour anticiper les dépenses annuelles des clients,
244
+ en s'appuyant sur leurs caractéristiques financières et personnelles.
245
+ </p>
246
+ </div>
247
+ <div class='section-card'>
248
+ <h3 style='color:#4b79a1; font-size:1.2em;'>🔬 Technologies</h3>
249
+ <span class='badge'>Random Forest</span>
250
+ <span class='badge'>XGBoost</span>
251
+ <span class='badge'>SVR</span>
252
+ <span class='badge'>GridSearchCV</span>
253
+ <span class='badge'>Scikit-learn</span>
254
+ <span class='badge'>Streamlit</span>
255
+ <span class='badge'>Plotly</span>
256
+ </div>
257
+ """, unsafe_allow_html=True)
258
+ with col2:
259
+ st_lottie(credit_animation, height=220, key="main_animation")
260
+ st.markdown("""
261
+ <div style='margin-top: 18px; background: linear-gradient(135deg, #4b79a1 0%, #283e51 100%); color: white; padding: 18px; border-radius: 12px; text-align: center; box-shadow: 0 2px 10px rgba(76,110,245,0.10);'>
262
+ <h2 style='margin:0;'>+2000</h2>
263
+ <p style='margin:0;'>Clients analysés</p>
264
+ </div>
265
+ """, unsafe_allow_html=True)
266
+
267
+ # --------- PREDICTION ---------
268
+ elif selected == "Prédiction":
269
+ st.markdown("""
270
+ <div style='
271
+ background: linear-gradient(120deg, #f8fafc 0%, #e4e8eb 100%);
272
+ padding: 2.2rem 1rem 2rem 1rem;
273
+ border-radius: 22px;
274
+ color: #283e51;
275
+ margin-bottom: 2.2rem;
276
+ text-align: center;
277
+ box-shadow: 0 4px 15px rgba(0,0,0,0.07);
278
+ '>
279
+ <h1 class='section-title' style='color:#4b79a1;'>🔮 Prédiction de Dépense</h1>
280
+ <p style='font-size:1.15em;'>Remplissez le formulaire ci-dessous pour estimer la dépense annuelle d'un client</p>
281
+ </div>
282
+ """, unsafe_allow_html=True)
283
+ with st.form("prediction_form"):
284
+ st.markdown("<div class='section-card' style='background:#fafdff;'>", unsafe_allow_html=True)
285
+ col1, col2 = st.columns(2)
286
+ with col1:
287
+ st.markdown("<h4 style='color:#4b79a1; margin-bottom:0.5em;'>Informations personnelles</h4>", unsafe_allow_html=True)
288
+ age = st.number_input("Âge", min_value=18, max_value=100, value=35)
289
+ owner = st.selectbox("Propriétaire d'une maison", ["Non", "Oui"])
290
+ selfemp = st.selectbox("Travailleur indépendant", ["Non", "Oui"])
291
+ dependents = st.number_input("Nombre de personnes à charge", min_value=0, max_value=10, value=0)
292
+ with col2:
293
+ st.markdown("<h4 style='color:#4b79a1; margin-bottom:0.5em;'>Informations financières</h4>", unsafe_allow_html=True)
294
+ income = st.number_input("Revenu annuel ($)", min_value=0, max_value=500000, value=50000)
295
+ share = st.slider("Part de revenu allouée à la carte (%)", min_value=0, max_value=100, value=10)
296
+ reports = st.number_input("Nombre de rapports de crédit", min_value=0, max_value=20, value=2)
297
+ months = st.number_input("Ancienneté (mois)", min_value=0, max_value=240, value=12)
298
+ majorcards = st.number_input("Nombre de cartes principales", min_value=0, max_value=5, value=1)
299
+ active = st.number_input("Nombre de comptes actifs", min_value=0, max_value=10, value=2)
300
+ st.markdown("<hr style='margin:1.5em 0;'/>", unsafe_allow_html=True)
301
+ real_expenditure = st.number_input("Valeur réelle de la dépense (optionnel)", min_value=0, max_value=100000, value=0)
302
+ submit_button = st.form_submit_button("💡 Prédire la dépense", use_container_width=True)
303
+ st.markdown("</div>", unsafe_allow_html=True)
304
+
305
+ if submit_button:
306
+ with st.spinner("Prédiction en cours..."):
307
+ st_lottie(loading_animation, height=100, key="loading")
308
+ # Préparation des données
309
+ input_df = pd.DataFrame({
310
+ 'income': [income],
311
+ 'share': [share],
312
+ 'age': [age],
313
+ 'owner_No': [1 if owner == "Non" else 0],
314
+ 'owner_Yes': [1 if owner == "Oui" else 0],
315
+ 'selfemp_No': [1 if selfemp == "Non" else 0],
316
+ 'selfemp_Yes': [1 if selfemp == "Oui" else 0],
317
+ 'reports': [reports],
318
+ 'dependents': [dependents],
319
+ 'months': [months],
320
+ 'majorcards': [majorcards],
321
+ 'active': [active]
322
+ })
323
+ # Charger modèle et scalers
324
+ model, scaler_X, scaler_y, metrics = load_model()
325
+ # Adapter les colonnes à l'ordre attendu
326
+ X_cols = scaler_X.feature_names_in_
327
+ input_df = input_df.reindex(columns=X_cols, fill_value=0)
328
+ # Normaliser
329
+ X_scaled = scaler_X.transform(input_df)
330
+ y_pred_scaled = model.predict(X_scaled)
331
+ y_pred = scaler_y.inverse_transform(y_pred_scaled.reshape(-1, 1)).ravel()[0]
332
+ # Affichage
333
+ st.markdown("""
334
+ <div class='section-card' style='margin-top:2rem;'>
335
+ <h2 class='section-title' style='text-align:center;'>Résultat de la Prédiction</h2>
336
+ """, unsafe_allow_html=True)
337
+ st.metric("Dépense prédite ($)", f"{y_pred:,.2f}")
338
+ if real_expenditure > 0:
339
+ st.metric("Valeur réelle ($)", f"{real_expenditure:,.2f}", delta=f"{y_pred-real_expenditure:,.2f}")
340
+ fig = go.Figure()
341
+ fig.add_trace(go.Bar(
342
+ x=["Prédiction", "Réel"],
343
+ y=[y_pred, real_expenditure],
344
+ marker_color=["#4b79a1", "#283e51"]
345
+ ))
346
+ fig.update_layout(title="Comparaison Prédiction vs Réel", yaxis_title="Dépense ($)")
347
+ st.plotly_chart(fig, use_container_width=True)
348
+ st.markdown("</div>", unsafe_allow_html=True)
349
+
350
+ # --------- ANALYSE ---------
351
+ elif selected == "Analyse":
352
+ st.markdown("""
353
+ <div style='background: linear-gradient(120deg, #283e51 0%, #4b79a1 100%);
354
+ padding: 2.5rem 1rem 2rem 1rem;
355
+ border-radius: 22px;
356
+ color: white;
357
+ margin-bottom: 2.5rem;
358
+ text-align: center;
359
+ box-shadow: 0 4px 15px rgba(0,0,0,0.10);'>
360
+ <h1 class='section-title'>📊 Tableau de Bord Analytique</h1>
361
+ <p style='font-size:1.2em; color:#e4e8eb;'>Explorez la performance du modèle et les tendances clés du dataset.</p>
362
+ </div>
363
+ """, unsafe_allow_html=True)
364
+
365
+ # Charger le dataset et le modèle
366
+ df = pd.read_csv('AER_credit_card_data.csv')
367
+ X = df.drop(['expenditure', 'card'], axis=1)
368
+ y = df['expenditure']
369
+ X = pd.get_dummies(X, columns=['owner', 'selfemp'])
370
+ model, scaler_X, scaler_y, metrics = load_model()
371
+ X = X.reindex(columns=scaler_X.feature_names_in_, fill_value=0)
372
+ X_scaled = scaler_X.transform(X)
373
+ y_pred_scaled = model.predict(X_scaled)
374
+ y_pred = scaler_y.inverse_transform(y_pred_scaled.reshape(-1, 1)).ravel()
375
+ from sklearn.metrics import mean_squared_error, mean_absolute_error, r2_score
376
+ mse = mean_squared_error(y, y_pred)
377
+ rmse = np.sqrt(mse)
378
+ mae = mean_absolute_error(y, y_pred)
379
+ r2 = r2_score(y, y_pred)
380
+
381
+ # --- Bloc Performance du Modèle ---
382
+ if metrics:
383
+ # On affiche uniquement les vraies métriques du best model
384
+ st.markdown(f"""
385
+ <div class="metric-card card-fade">
386
+ <h3>✨ Performance du Meilleur Modèle</h3>
387
+ <ul class="metric-list">
388
+ <li><b>RMSE (test)</b> : {metrics.get('rmse', 'N/A'):.2f}</li>
389
+ <li><b>MAE (test)</b> : {metrics.get('mae', 'N/A'):.2f}</li>
390
+ <li><b>R² (test)</b> : {metrics.get('r2', 'N/A'):.3f}</li>
391
+ {f"<li><b>Score CV</b> : {metrics['cv_score']:.3f}</li>" if 'cv_score' in metrics else ""}
392
+ </ul>
393
+ </div>
394
+ <hr class="section-sep"/>
395
+ <div class="section-title-visual">Analyse Visuelle</div>
396
+ """, unsafe_allow_html=True)
397
+ else:
398
+ # fallback si jamais metrics n'est pas dispo
399
+ st.markdown("""
400
+ <div class="metric-card card-fade">
401
+ <h3>✨ Performance du Modèle</h3>
402
+ <p style="color:#fff;">(Métriques calculées sur tout le dataset, à titre indicatif)</p>
403
+ <ul class="metric-list">
404
+ <li><b>RMSE</b> : {:.2f}</li>
405
+ <li><b>MAE</b> : {:.2f}</li>
406
+ <li><b>R²</b> : {:.3f}</li>
407
+ </ul>
408
+ </div>
409
+ <hr class="section-sep"/>
410
+ <div class="section-title-visual">Analyse Visuelle</div>
411
+ """.format(rmse, mae, r2), unsafe_allow_html=True)
412
+
413
+ # --- 1. Scatter plot Prédiction vs Réel ---
414
+ st.markdown("""
415
+ <div class="visual-card card-fade">
416
+ <h4 style='color:#4b79a1; margin-bottom:0.3em;'>1. Prédictions vs Valeurs réelles</h4>
417
+ <p style='color:#444; margin-bottom:1.2em;'>Chaque point représente un client. Plus les points sont proches de la diagonale, plus la prédiction est précise.</p>
418
+ """, unsafe_allow_html=True)
419
+ fig1 = px.scatter(
420
+ x=y, y=y_pred,
421
+ labels={'x': 'Valeur réelle', 'y': 'Prédiction'},
422
+ color_discrete_sequence=["#4b79a1"],
423
+ title=None
424
+ )
425
+ fig1.add_shape(
426
+ type="line",
427
+ x0=y.min(), y0=y.min(),
428
+ x1=y.max(), y1=y.max(),
429
+ line=dict(color="#e74c3c", dash="dash")
430
+ )
431
+ fig1.update_layout(showlegend=False, height=350, margin=dict(l=20, r=20, t=30, b=20))
432
+ st.plotly_chart(fig1, use_container_width=True)
433
+ st.markdown("</div>", unsafe_allow_html=True)
434
+
435
+ # --- 2. Distribution des Dépenses Réelles ---
436
+ st.markdown("""
437
+ <div class="visual-card card-fade">
438
+ <h4 style='color:#4b79a1; margin-bottom:0.3em;'>2. Distribution des dépenses réelles</h4>
439
+ <p style='color:#444; margin-bottom:1.2em;'>Visualisation de la répartition des dépenses annuelles des clients.</p>
440
+ """, unsafe_allow_html=True)
441
+ fig2 = px.histogram(df, x="expenditure", nbins=40, color_discrete_sequence=["#283e51"])
442
+ fig2.update_layout(
443
+ xaxis_title="Dépense annuelle ($)",
444
+ yaxis_title="Nombre de clients",
445
+ height=300,
446
+ margin=dict(l=20, r=20, t=30, b=20)
447
+ )
448
+ st.plotly_chart(fig2, use_container_width=True)
449
+ st.markdown("</div>", unsafe_allow_html=True)
450
+
451
+ # --- 3. Importance des variables ---
452
+ st.markdown("""
453
+ <div class="visual-card card-fade">
454
+ <h4 style='color:#4b79a1; margin-bottom:0.3em;'>3. Importance des variables</h4>
455
+ <p style='color:#444; margin-bottom:1.2em;'>Les variables les plus influentes dans la prédiction selon le modèle.</p>
456
+ """, unsafe_allow_html=True)
457
+ if hasattr(model, "feature_importances_"):
458
+ importances = model.feature_importances_
459
+ features = X.columns
460
+ imp_df = pd.DataFrame({"Variable": features, "Importance": importances})
461
+ imp_df = imp_df.sort_values("Importance", ascending=True)
462
+ fig3 = px.bar(
463
+ imp_df,
464
+ x="Importance", y="Variable",
465
+ orientation="h",
466
+ color="Importance",
467
+ color_continuous_scale="blues",
468
+ height=350
469
+ )
470
+ fig3.update_layout(margin=dict(l=20, r=20, t=30, b=20), coloraxis_showscale=False)
471
+ st.plotly_chart(fig3, use_container_width=True)
472
+ else:
473
+ st.info("L'importance des variables n'est pas disponible pour ce modèle.")
474
+ st.markdown("</div>", unsafe_allow_html=True)
475
+
476
+ # --- 4. Dépense moyenne par statut de propriétaire ---
477
+ st.markdown("""
478
+ <div class="visual-card card-fade">
479
+ <h4 style='color:#4b79a1; margin-bottom:0.3em;'>4. Dépense moyenne selon le statut de propriétaire</h4>
480
+ <p style='color:#444; margin-bottom:1.2em;'>Comparaison des dépenses annuelles entre propriétaires et non-propriétaires.</p>
481
+ """, unsafe_allow_html=True)
482
+ fig4 = px.box(
483
+ df, x="owner", y="expenditure",
484
+ color="owner",
485
+ color_discrete_sequence=["#4b79a1", "#283e51"],
486
+ points="all",
487
+ height=320
488
+ )
489
+ fig4.update_layout(
490
+ xaxis_title="Statut de propriétaire",
491
+ yaxis_title="Dépense annuelle ($)",
492
+ showlegend=False,
493
+ margin=dict(l=20, r=20, t=30, b=20)
494
+ )
495
+ st.plotly_chart(fig4, use_container_width=True)
496
+ st.markdown("</div>", unsafe_allow_html=True)
497
+
498
+ # --- 5. Matrice de corrélation ---
499
+ st.markdown("""
500
+ <div class="visual-card card-fade">
501
+ <h4 style='color:#4b79a1; margin-bottom:0.3em;'>5. Corrélation entre variables</h4>
502
+ <p style='color:#444; margin-bottom:1.2em;'>Les relations linéaires entre les principales variables du dataset.</p>
503
+ """, unsafe_allow_html=True)
504
+ corr = df.select_dtypes(include=[np.number]).corr()
505
+ fig5 = px.imshow(
506
+ corr,
507
+ text_auto=True,
508
+ color_continuous_scale="blues",
509
+ aspect="auto",
510
+ height=400
511
+ )
512
+ fig5.update_layout(margin=dict(l=20, r=20, t=30, b=20))
513
+ st.plotly_chart(fig5, use_container_width=True)
514
+ st.markdown("</div>", unsafe_allow_html=True)
515
 
516
+ # --------- A PROPOS ---------
517
+ elif selected == "À propos":
518
+ st.markdown("""
519
+ <div style='background: linear-gradient(120deg, #4b79a1 0%, #283e51 100%); padding: 2rem; border-radius: 18px; color: white; margin-bottom: 2rem; text-align: center; box-shadow: 0 4px 15px rgba(0,0,0,0.1);'>
520
+ <h1 class='section-title' style='color:white;'>À propos</h1>
521
+ <p>Découvrez le créateur, le projet et les technologies utilisées</p>
522
+ </div>
523
+ """, unsafe_allow_html=True)
524
+ col1, col2 = st.columns([1, 2])
525
+ with col1:
526
+ st_lottie(about_animation, height=220, key="about_animation")
527
+ st.image(
528
+ "https://avatars.githubusercontent.com/u/TheBeyonder237",
529
+ width=180,
530
+ caption="Ngoue David",
531
+ output_format="auto",
532
+ use_column_width=False,
533
+ channels="RGB"
534
+ )
535
+ st.markdown("""
536
+ <div style='text-align:center; margin-top:1em;'>
537
+ <button class='about-contact-btn' onclick="window.open('mailto:ngouedavidrogeryannick@gmail.com')">📧 Email</button>
538
+ <button class='about-contact-btn' onclick="window.open('https://github.com/TheBeyonder237')">🌐 GitHub</button>
539
+ </div>
540
+ """, unsafe_allow_html=True)
541
+ with col2:
542
+ st.markdown("""
543
+ <div class='section-card'>
544
+ <h2 class='section-title'>Qui suis-je ?</h2>
545
+ <p>
546
+ Je suis un passionné de l'intelligence artificielle et de la donnée.<br>
547
+ Actuellement en Master 2 en IA et Big Data, je travaille sur des solutions innovantes dans le domaine de l'Intelligence Artificielle appliquée à la finance et à la santé.
548
+ </p>
549
+ <h3 style='color:#4b79a1;'>Compétences</h3>
550
+ <span class='badge'>Python</span>
551
+ <span class='badge'>Machine Learning</span>
552
+ <span class='badge'>Deep Learning</span>
553
+ <span class='badge'>NLP</span>
554
+ <span class='badge'>Data Science</span>
555
+ <span class='badge'>Cloud Computing</span>
556
+ <span class='badge'>Streamlit</span>
557
+ <span class='badge'>Scikit-learn</span>
558
+ <span class='badge'>XGBoost</span>
559
+ <span class='badge'>Pandas</span>
560
+ <span class='badge'>Plotly</span>
561
+ <span class='badge'>SQL</span>
562
+ <h3 style='color:#4b79a1; margin-top:1.5em;'>Projets Récents</h3>
563
+ <ul>
564
+ <li><b>💳 Credit Card Expenditure Predictor</b> : Application de prédiction de dépenses de carte de crédit.</li>
565
+ <li><b>🫀 HeartGuard AI</b> : Prédiction de risques cardiaques par IA.</li>
566
+ <li><b>🔊 Multi-IA</b> : Plateforme multi-modèles pour la génération de texte, synthèse vocale et traduction.</li>
567
+ </ul>
568
+ </div>
569
+ """, unsafe_allow_html=True)
570
+ st.markdown("""
571
+ <div style='text-align: center; color: #666; padding: 20px;'>
572
+ Développé avec ❤️ par Ngoue David
573
+ </div>
574
+ """, unsafe_allow_html=True)