Sidoineko commited on
Commit
f8c1bb4
·
verified ·
1 Parent(s): 6fa6c46

Update src/dashboard_app.py

Browse files
Files changed (1) hide show
  1. src/dashboard_app.py +14 -151
src/dashboard_app.py CHANGED
@@ -1,17 +1,3 @@
1
- # --- IMPORTANT POUR HUGGING FACE SPACES ---
2
- # Assurez-vous que votre fichier requirements.txt (à la RACINE du dépôt) contient AU MOINS :
3
- # streamlit
4
- # pandas
5
- # plotly
6
- # Jinja2
7
- # python-dotenv
8
- # google-generativeai
9
- # scikit-learn
10
- # scipy
11
- # numpy
12
- # openpyxl
13
- # -----------------------------------------
14
-
15
  import os
16
  import streamlit as st
17
  import pandas as pd
@@ -29,7 +15,7 @@ from sklearn.model_selection import train_test_split
29
  from sklearn.metrics import mean_squared_error, r2_score
30
  import scipy.stats as stats
31
  import numpy as np
32
- from sklearn.preprocessing import StandardScaler # Importer StandardScaler
33
 
34
  # --- Configuration de la Page Streamlit ---
35
  st.set_page_config(layout="wide", page_title="Suite d'Analyse Interactive", page_icon="📊")
@@ -47,27 +33,6 @@ else:
47
  # Sinon, supposer que le script est à la racine ou dans un autre dossier
48
  app_root_dir = script_dir # Ou ajuster si nécessaire
49
 
50
- # Chemin vers le fichier d'exemple (supposé être à la racine)
51
- SAMPLE_EXCEL_FILE = os.path.join(app_root_dir, "sample_excel.xlsx")
52
-
53
- # Charger le template HTML depuis la racine déterminée
54
- TEMPLATE_FILE = "report_template.html"
55
- template = None
56
- try:
57
- env = Environment(loader=FileSystemLoader(app_root_dir))
58
- template = env.get_template(TEMPLATE_FILE)
59
- except TemplateNotFound:
60
- st.error(f"Erreur chargement template : '{TEMPLATE_FILE}' NON TROUVÉ dans '{app_root_dir}'. Vérifiez qu'il est bien à la racine du dépôt. Export HTML indisponible.")
61
- except Exception as e:
62
- st.error(f"Erreur chargement template '{TEMPLATE_FILE}' depuis '{app_root_dir}': {e}. Export HTML indisponible.")
63
-
64
- # Affichage état API Key
65
- if not api_key:
66
- st.sidebar.warning("⚠️ Clé API Google Gemini manquante. Chat AI désactivé.", icon="🔑")
67
- else:
68
- # st.sidebar.success("✔️ Clé API Google Gemini trouvée.") # Optionnel
69
- pass
70
-
71
  # --- Fonctions Utilitaires ---
72
  def generate_html_report(data, num_submissions, columns, tables_html="", charts_html=""):
73
  if template is None: return "Erreur: Template HTML manquant."
@@ -122,18 +87,7 @@ def load_data(source_type, source_value, header_param, sep=None):
122
  source_info_text = ""
123
 
124
  try:
125
- if source_type == "upload" and source_value is not None:
126
- file = source_value
127
- source_info_text = f"Fichier chargé : {file.name}"
128
- data_id = f"upload_{file.name}_{file.size}"
129
- if file.name.endswith('.csv'):
130
- data = pd.read_csv(file, header=header_param)
131
- elif file.name.endswith('.xlsx'):
132
- data = pd.read_excel(file, header=header_param, engine='openpyxl')
133
- else:
134
- error_message = "Format de fichier uploadé non supporté (CSV ou XLSX requis)."
135
-
136
- elif source_type == "url" and source_value:
137
  url = source_value
138
  source_info_text = f"URL chargée : {url}"
139
  data_id = f"url_{hash(url)}"
@@ -151,17 +105,6 @@ def load_data(source_type, source_value, header_param, sep=None):
151
  if not sep: sep = '\t' # Défaut tabulation pour collage Excel
152
  data = pd.read_csv(io.StringIO(pasted_str), sep=sep, header=header_param)
153
 
154
- elif source_type == "sample" and source_value:
155
- file_path = source_value
156
- source_info_text = f"Exemple : {os.path.basename(file_path)}"
157
- data_id = f"sample_{os.path.basename(file_path)}"
158
- if os.path.exists(file_path) and file_path.endswith('.xlsx'):
159
- data = pd.read_excel(file_path, header=header_param, engine='openpyxl')
160
- elif os.path.exists(file_path) and file_path.endswith('.csv'):
161
- data = pd.read_csv(file_path, header=header_param)
162
- else:
163
- error_message = f"Fichier exemple non trouvé ou format incorrect: {file_path}"
164
-
165
  # --- Vérification post-chargement ---
166
  if data is not None and error_message is None:
167
  if data.empty:
@@ -196,7 +139,7 @@ st.markdown(
196
  """
197
  <div style='background-color: #f0f2f6; border-left: 5px solid #1f77b4; padding: 15px; border-radius: 5px; margin-bottom: 20px;'>
198
  <p style='font-size: 1.1em; color: #000000;'>Bienvenue dans la <strong>Suite d'Analyse de Données Interactive</strong>.</p>
199
- <p style='color: #000000;'>Chargez vos données (CSV/Excel) de <strong>multiples façons (fichier, URL, collage, exemple)</strong>. Explorez interactivement vos données, générez des visualisations, des statistiques descriptives, effectuez des analyses avancées et exportez vos résultats. Utilisez le chat IA pour obtenir de l'aide.</p>
200
  </div>
201
  """, unsafe_allow_html=True
202
  )
@@ -213,7 +156,7 @@ if 'html_report_filename' not in st.session_state: st.session_state.html_report_
213
  if 'data_source_info' not in st.session_state: st.session_state.data_source_info = "Aucune donnée chargée"
214
  if "gemini_chat_history" not in st.session_state: st.session_state.gemini_chat_history = []
215
  # Ajouter un état pour le choix de la méthode de chargement
216
- if 'load_method' not in st.session_state: st.session_state.load_method = "Uploader un fichier"
217
 
218
  # --- Création des Onglets ---
219
  app_tab, manual_tab, chat_tab = st.tabs(["📊 Application Principale", "📘 Manuel d'Utilisation", "💬 Chat IA (Gemini)"])
@@ -231,7 +174,7 @@ with app_tab:
231
  st.subheader("1. Charger les Données")
232
 
233
  # Choix de la méthode
234
- load_options = ["Uploader un fichier", "Charger depuis URL", "Coller depuis presse-papiers", "Utiliser le fichier d'exemple"]
235
  st.session_state.load_method = st.radio(
236
  "Choisissez une méthode de chargement :",
237
  options=load_options,
@@ -248,21 +191,8 @@ with app_tab:
248
  )
249
  header_param_common = 0 if use_header_common else None
250
 
251
- # --- Méthode 1: Upload ---
252
- if st.session_state.load_method == "Uploader un fichier":
253
- uploaded_file = st.file_uploader(
254
- "Déposez votre fichier CSV ou Excel ici :",
255
- type=["csv", "xlsx"], key="file_uploader_widget",
256
- accept_multiple_files=False # S'assurer qu'un seul fichier est traité
257
- )
258
- if uploaded_file is not None:
259
- current_upload_id = f"upload_{uploaded_file.name}_{uploaded_file.size}"
260
- # Charger seulement si l'ID ou l'option header a changé
261
- if st.session_state.data_loaded_id != current_upload_id or st.session_state.last_header_preference != use_header_common:
262
- load_data("upload", uploaded_file, header_param_common)
263
-
264
- # --- Méthode 2: URL ---
265
- elif st.session_state.load_method == "Charger depuis URL":
266
  data_url = st.text_input("Collez l'URL d'un fichier CSV ou Excel public :", key="data_url_input")
267
  if st.button("Charger depuis URL", key="load_from_url_button"):
268
  if data_url:
@@ -270,7 +200,7 @@ with app_tab:
270
  else:
271
  st.warning("Veuillez entrer une URL.")
272
 
273
- # --- Méthode 3: Coller ---
274
  elif st.session_state.load_method == "Coller depuis presse-papiers":
275
  pasted_data = st.text_area(
276
  "Collez vos données ici (format type CSV/Tab) :",
@@ -460,9 +390,6 @@ with app_tab:
460
  try: base_name = os.path.basename(source_info_for_file.split(":")[-1].strip().split("?")[0]) # Essayer de nettoyer URL
461
  except: base_name = "url_data"
462
  export_filename_base = f"export_{os.path.splitext(base_name)[0]}"
463
- elif "Exemple :" in source_info_for_file:
464
- base_name = source_info_for_file.split(":")[-1].strip()
465
- export_filename_base = f"export_{base_name}"
466
  elif "Données collées" in source_info_for_file:
467
  export_filename_base = "export_donnees_collees"
468
  else:
@@ -470,7 +397,7 @@ with app_tab:
470
  # Nettoyer le nom
471
  export_filename_base = "".join(c if c.isalnum() or c in ('_', '-') else '_' for c in export_filename_base).strip('_')
472
 
473
- col_export1, col_export2, col_export3 = st.columns(3)
474
  with col_export1: # CSV
475
  try:
476
  csv_data = df_to_export.to_csv(index=False).encode('utf-8')
@@ -483,60 +410,6 @@ with app_tab:
483
  st.download_button("Exporter Excel", excel_buffer.getvalue(), f"{export_filename_base}.xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", key=f"dl_excel_{export_key_suffix}")
484
  except Exception as e:
485
  st.error(f"Erreur Export Excel: {e}"); st.warning("Vérifiez 'openpyxl' dans reqs.", icon="💡")
486
- with col_export3: # HTML Report
487
- if template:
488
- if st.button("Préparer Rapport HTML", key=f"prep_html_{export_key_suffix}"):
489
- with st.spinner("Génération rapport..."):
490
- try:
491
- data_for_report = st.session_state.dataframe_to_export
492
- if data_for_report is not None:
493
- num_submissions_report = len(data_for_report)
494
- columns_for_report = data_for_report.columns.tolist()
495
- tables_html_list, charts_html_list = [], []
496
- # --- Génération contenu HTML ---
497
- for analysis in st.session_state.get('analyses', []):
498
- result = analysis.get('result'); analysis_type = analysis.get('type')
499
- params = analysis.get('executed_params', analysis.get('params', {}))
500
- analysis_id_rep = analysis.get('id', -1) + 1
501
- if result is not None:
502
- title = f"Analyse {analysis_id_rep}: {analysis_type.replace('_', ' ').title()}"
503
- param_details_list = []
504
- for k,v in params.items():
505
- if v is not None and v != [] and v != 'None' and k not in ['type']:
506
- v_repr = f"[{v[0]}, ..., {v[-1]}] ({len(v)})" if isinstance(v, list) and len(v) > 3 else str(v)
507
- k_simple = k.replace('_graph','').replace('desc','').replace('_columns','').replace('column','').replace('_',' ').strip().title()
508
- param_details_list.append(f"{k_simple} = {v_repr}")
509
- param_details = "; ".join(param_details_list)
510
- full_title = f"{title} <small>({param_details})</small>" if param_details else title
511
- try: # Conversion résultat en HTML
512
- if analysis_type in ['aggregated_table', 'descriptive_stats'] and isinstance(result, pd.DataFrame):
513
- df_html = result.T if analysis_type == 'descriptive_stats' else result
514
- # Appliquer formatage pour le rapport aussi
515
- table_html = df_html.to_html(index=(analysis_type == 'descriptive_stats'), classes='table table-striped table-hover table-sm', border=0, float_format='{:,.2f}'.format)
516
- tables_html_list.append(f"<h3>{full_title}</h3>{table_html}")
517
- elif analysis_type == 'graph' and isinstance(result, go.Figure):
518
- chart_html = result.to_html(full_html=False, include_plotlyjs='cdn')
519
- charts_html_list.append(f"<h3>{full_title}</h3>{chart_html}")
520
- except Exception as e_render: st.warning(f"Erreur rendu résultat Analyse {analysis_id_rep}: {e_render}")
521
- # --- Fin génération contenu ---
522
- tables_html = "\n<hr/>\n".join(tables_html_list) if tables_html_list else "<p>Aucun tableau généré.</p>"
523
- charts_html = "\n<hr/>\n".join(charts_html_list) if charts_html_list else "<p>Aucun graphique généré.</p>"
524
-
525
- html_content = generate_html_report(data_for_report, num_submissions_report, columns_for_report, tables_html, charts_html)
526
- if "Erreur:" not in html_content:
527
- st.session_state.html_report_content = html_content.encode('utf-8')
528
- st.session_state.html_report_filename = f"rapport_{export_filename_base}.html"
529
- st.success("Rapport prêt.")
530
- else: st.error("Échec génération contenu HTML.")
531
- else: st.error("Pas de données pour le rapport.")
532
- except Exception as e_report: st.error(f"Erreur préparation rapport: {e_report}")
533
-
534
- if st.session_state.get('html_report_content'):
535
- st.download_button("Télécharger Rapport HTML", st.session_state.html_report_content, st.session_state.html_report_filename, "text/html", key=f"dl_html_{export_key_suffix}", on_click=lambda: st.session_state.update(html_report_content=None))
536
- else:
537
- st.info("Export HTML indisponible (template non chargé).")
538
- else:
539
- st.info("Chargez des données pour activer l'exportation.")
540
 
541
  # --- Zone Principale de l'Application ---
542
  st.header("📊 Aperçu et Analyse des Données")
@@ -848,7 +721,7 @@ with app_tab:
848
  selected_value = st.selectbox("Valeurs (Taille):", options_values, index=get_safe_index(options_values, analysis['params'].get('value_column')), key=f"graph_value_{analysis_id}")
849
  st.session_state.analyses[i]['params']['value_column'] = selected_value
850
 
851
- # --- Options d'Agrégation ---
852
  with st.expander("Options d'agrégation (avant graphique)", expanded=aggregation_enabled_graph):
853
  if not conf_categorical_columns: st.caption("Nécessite cols Catégorielles.")
854
  else:
@@ -1069,7 +942,7 @@ with app_tab:
1069
  groups = data[group_col_t].dropna().unique()
1070
  if len(groups) != 2: st.error(f"'{group_col_t}' n'a pas 2 groupes après NAs.")
1071
  else:
1072
- g1 = get_valid_data(data[data[group_col_t] == groups[0]], numeric_var_t); g2 = get_valid_data(data[data[group_col_t] == groups[1]], numeric_var_t)
1073
  if len(g1) < 3 or len(g2) < 3: st.error(f"Pas assez données valides (<3/groupe) pour '{numeric_var_t}'.")
1074
  else:
1075
  t_stat, p_value = stats.ttest_ind(g1, g2, equal_var=False, nan_policy='omit')
@@ -1096,7 +969,7 @@ with app_tab:
1096
  if group_col_a and anova_numeric_var:
1097
  try:
1098
  groups_values = data[group_col_a].dropna().unique()
1099
- groups_data = [get_valid_data(data[data[group_col_a] == v], anova_numeric_var) for v in groups_values]
1100
  groups_data_f = [g for g in groups_data if len(g) >= 3]
1101
  n_groups = len(groups_data_f)
1102
  if n_groups < 2: st.error(f"Pas assez de groupes (min 2) avec données valides (min 3) pour '{anova_numeric_var}'.")
@@ -1319,11 +1192,9 @@ with manual_tab:
1319
 
1320
  ---
1321
  ### 1. Chargement des Données (Barre Latérale ⚙️)
1322
- - **Choisir une méthode** : Sélectionnez l'une des options proposées (Uploader, URL, Coller, Exemple).
1323
- - **Uploader un Fichier** : Cliquez sur "Déposez votre fichier..." ou glissez votre fichier CSV ou Excel.
1324
- - **Charger depuis URL** : Collez l'URL directe d'un fichier CSV ou Excel public et cliquez sur "Charger depuis URL".
1325
  - **Coller depuis presse-papiers**: Copiez des données depuis un tableur (Excel, Sheets), collez-les dans la zone de texte, vérifiez le séparateur (Tabulation par défaut) et cliquez sur "Charger Données Collées".
1326
- - **Utiliser le fichier d'exemple**: Cliquez sur "Charger l'exemple" pour utiliser le fichier `sample_excel.xlsx` fourni.
1327
  - **Utiliser l'en-tête** : Cochez/décochez la case **avant** de cliquer sur le bouton de chargement correspondant à votre méthode pour indiquer si la première ligne contient les noms de colonnes.
1328
 
1329
  ---
@@ -1332,7 +1203,6 @@ with manual_tab:
1332
  - **Renommer Colonnes** : Sélectionnez une colonne, entrez un nouveau nom et cliquez sur "Appliquer Renommage".
1333
  - **Exporter** :
1334
  - **CSV/Excel** : Téléchargez les données actuellement chargées.
1335
- - **Rapport HTML** : Cliquez sur "Préparer Rapport HTML" pour générer un fichier HTML contenant un aperçu des données et les **résultats des analyses déjà exécutées** (tableaux/graphiques de la section "Analyses Configurées"). Cliquez ensuite sur "Télécharger Rapport HTML".
1336
 
1337
  ---
1338
  ### 3. Analyses (Zone Principale 📊)
@@ -1342,12 +1212,6 @@ with manual_tab:
1342
  - **Supprimer une Analyse** : Cliquez sur l'icône poubelle 🗑️ du bloc.
1343
  - **Analyses Avancées** : Cochez la case "Afficher les analyses avancées", sélectionnez un test/modèle, configurez-le et cliquez sur "Effectuer...".
1344
 
1345
- ---
1346
- ### 4. Chat IA (Onglet 💬)
1347
- (Nécessite une clé API Google Gemini configurée)
1348
- - Posez des questions sur l'analyse de données ou demandez des suggestions basées sur les colonnes détectées.
1349
- - **Rappel** : L'IA ne voit pas les valeurs de vos données.
1350
-
1351
  ---
1352
  ### 💡 Conseils & Dépannage
1353
  - **Types de Colonnes** : Vérifiez les types détectés dans la section "Afficher détails colonnes". Corrigez vos données sources si nécessaire (ex: formats de date, nombres avec texte). Une mauvaise détection peut limiter les options d'analyse.
@@ -1361,7 +1225,6 @@ with manual_tab:
1361
  📞 Contact : +229 96911346
1362
  🔗 [Profil LinkedIn](https://www.linkedin.com/in/sidoineko) | 📂 [Portfolio](https://huggingface.co/spaces/Sidoineko/portfolio)
1363
  """)
1364
-
1365
  # ==============================================================================
1366
  # ONGLET CHAT IA
1367
  # ==============================================================================
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import os
2
  import streamlit as st
3
  import pandas as pd
 
15
  from sklearn.metrics import mean_squared_error, r2_score
16
  import scipy.stats as stats
17
  import numpy as np
18
+ from sklearn.preprocessing import StandardScaler
19
 
20
  # --- Configuration de la Page Streamlit ---
21
  st.set_page_config(layout="wide", page_title="Suite d'Analyse Interactive", page_icon="📊")
 
33
  # Sinon, supposer que le script est à la racine ou dans un autre dossier
34
  app_root_dir = script_dir # Ou ajuster si nécessaire
35
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
36
  # --- Fonctions Utilitaires ---
37
  def generate_html_report(data, num_submissions, columns, tables_html="", charts_html=""):
38
  if template is None: return "Erreur: Template HTML manquant."
 
87
  source_info_text = ""
88
 
89
  try:
90
+ if source_type == "url" and source_value:
 
 
 
 
 
 
 
 
 
 
 
91
  url = source_value
92
  source_info_text = f"URL chargée : {url}"
93
  data_id = f"url_{hash(url)}"
 
105
  if not sep: sep = '\t' # Défaut tabulation pour collage Excel
106
  data = pd.read_csv(io.StringIO(pasted_str), sep=sep, header=header_param)
107
 
 
 
 
 
 
 
 
 
 
 
 
108
  # --- Vérification post-chargement ---
109
  if data is not None and error_message is None:
110
  if data.empty:
 
139
  """
140
  <div style='background-color: #f0f2f6; border-left: 5px solid #1f77b4; padding: 15px; border-radius: 5px; margin-bottom: 20px;'>
141
  <p style='font-size: 1.1em; color: #000000;'>Bienvenue dans la <strong>Suite d'Analyse de Données Interactive</strong>.</p>
142
+ <p style='color: #000000;'>Veuillez lire le manuel d'utilisation avant de commencer.</p>
143
  </div>
144
  """, unsafe_allow_html=True
145
  )
 
156
  if 'data_source_info' not in st.session_state: st.session_state.data_source_info = "Aucune donnée chargée"
157
  if "gemini_chat_history" not in st.session_state: st.session_state.gemini_chat_history = []
158
  # Ajouter un état pour le choix de la méthode de chargement
159
+ if 'load_method' not in st.session_state: st.session_state.load_method = "Charger depuis URL"
160
 
161
  # --- Création des Onglets ---
162
  app_tab, manual_tab, chat_tab = st.tabs(["📊 Application Principale", "📘 Manuel d'Utilisation", "💬 Chat IA (Gemini)"])
 
174
  st.subheader("1. Charger les Données")
175
 
176
  # Choix de la méthode
177
+ load_options = ["Charger depuis URL", "Coller depuis presse-papiers"]
178
  st.session_state.load_method = st.radio(
179
  "Choisissez une méthode de chargement :",
180
  options=load_options,
 
191
  )
192
  header_param_common = 0 if use_header_common else None
193
 
194
+ # --- Méthode 1: URL ---
195
+ if st.session_state.load_method == "Charger depuis URL":
 
 
 
 
 
 
 
 
 
 
 
 
 
196
  data_url = st.text_input("Collez l'URL d'un fichier CSV ou Excel public :", key="data_url_input")
197
  if st.button("Charger depuis URL", key="load_from_url_button"):
198
  if data_url:
 
200
  else:
201
  st.warning("Veuillez entrer une URL.")
202
 
203
+ # --- Méthode 2: Coller ---
204
  elif st.session_state.load_method == "Coller depuis presse-papiers":
205
  pasted_data = st.text_area(
206
  "Collez vos données ici (format type CSV/Tab) :",
 
390
  try: base_name = os.path.basename(source_info_for_file.split(":")[-1].strip().split("?")[0]) # Essayer de nettoyer URL
391
  except: base_name = "url_data"
392
  export_filename_base = f"export_{os.path.splitext(base_name)[0]}"
 
 
 
393
  elif "Données collées" in source_info_for_file:
394
  export_filename_base = "export_donnees_collees"
395
  else:
 
397
  # Nettoyer le nom
398
  export_filename_base = "".join(c if c.isalnum() or c in ('_', '-') else '_' for c in export_filename_base).strip('_')
399
 
400
+ col_export1, col_export2 = st.columns(2)
401
  with col_export1: # CSV
402
  try:
403
  csv_data = df_to_export.to_csv(index=False).encode('utf-8')
 
410
  st.download_button("Exporter Excel", excel_buffer.getvalue(), f"{export_filename_base}.xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", key=f"dl_excel_{export_key_suffix}")
411
  except Exception as e:
412
  st.error(f"Erreur Export Excel: {e}"); st.warning("Vérifiez 'openpyxl' dans reqs.", icon="💡")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
413
 
414
  # --- Zone Principale de l'Application ---
415
  st.header("📊 Aperçu et Analyse des Données")
 
721
  selected_value = st.selectbox("Valeurs (Taille):", options_values, index=get_safe_index(options_values, analysis['params'].get('value_column')), key=f"graph_value_{analysis_id}")
722
  st.session_state.analyses[i]['params']['value_column'] = selected_value
723
 
724
+ # --- Options d'agrégation ---
725
  with st.expander("Options d'agrégation (avant graphique)", expanded=aggregation_enabled_graph):
726
  if not conf_categorical_columns: st.caption("Nécessite cols Catégorielles.")
727
  else:
 
942
  groups = data[group_col_t].dropna().unique()
943
  if len(groups) != 2: st.error(f"'{group_col_t}' n'a pas 2 groupes après NAs.")
944
  else:
945
+ g1 = get_valid_data(data, numeric_var_t); g2 = get_valid_data(data, numeric_var_t)
946
  if len(g1) < 3 or len(g2) < 3: st.error(f"Pas assez données valides (<3/groupe) pour '{numeric_var_t}'.")
947
  else:
948
  t_stat, p_value = stats.ttest_ind(g1, g2, equal_var=False, nan_policy='omit')
 
969
  if group_col_a and anova_numeric_var:
970
  try:
971
  groups_values = data[group_col_a].dropna().unique()
972
+ groups_data = [get_valid_data(data, anova_numeric_var) for v in groups_values]
973
  groups_data_f = [g for g in groups_data if len(g) >= 3]
974
  n_groups = len(groups_data_f)
975
  if n_groups < 2: st.error(f"Pas assez de groupes (min 2) avec données valides (min 3) pour '{anova_numeric_var}'.")
 
1192
 
1193
  ---
1194
  ### 1. Chargement des Données (Barre Latérale ⚙️)
1195
+ - **Choisir une méthode** : Sélectionnez l'une des options proposées (URL, Coller).
1196
+ - **URL** : Collez l'URL directe d'un fichier CSV ou Excel public et cliquez sur "Charger depuis URL".
 
1197
  - **Coller depuis presse-papiers**: Copiez des données depuis un tableur (Excel, Sheets), collez-les dans la zone de texte, vérifiez le séparateur (Tabulation par défaut) et cliquez sur "Charger Données Collées".
 
1198
  - **Utiliser l'en-tête** : Cochez/décochez la case **avant** de cliquer sur le bouton de chargement correspondant à votre méthode pour indiquer si la première ligne contient les noms de colonnes.
1199
 
1200
  ---
 
1203
  - **Renommer Colonnes** : Sélectionnez une colonne, entrez un nouveau nom et cliquez sur "Appliquer Renommage".
1204
  - **Exporter** :
1205
  - **CSV/Excel** : Téléchargez les données actuellement chargées.
 
1206
 
1207
  ---
1208
  ### 3. Analyses (Zone Principale 📊)
 
1212
  - **Supprimer une Analyse** : Cliquez sur l'icône poubelle 🗑️ du bloc.
1213
  - **Analyses Avancées** : Cochez la case "Afficher les analyses avancées", sélectionnez un test/modèle, configurez-le et cliquez sur "Effectuer...".
1214
 
 
 
 
 
 
 
1215
  ---
1216
  ### 💡 Conseils & Dépannage
1217
  - **Types de Colonnes** : Vérifiez les types détectés dans la section "Afficher détails colonnes". Corrigez vos données sources si nécessaire (ex: formats de date, nombres avec texte). Une mauvaise détection peut limiter les options d'analyse.
 
1225
  📞 Contact : +229 96911346
1226
  🔗 [Profil LinkedIn](https://www.linkedin.com/in/sidoineko) | 📂 [Portfolio](https://huggingface.co/spaces/Sidoineko/portfolio)
1227
  """)
 
1228
  # ==============================================================================
1229
  # ONGLET CHAT IA
1230
  # ==============================================================================