Spaces:
Sleeping
Sleeping
| import streamlit as st | |
| import pandas as pd | |
| import numpy as np | |
| import seaborn as sns | |
| import matplotlib.pyplot as plt | |
| from scipy.stats import chi2_contingency | |
| # Configuration de la page | |
| st.set_page_config( | |
| page_title="Matrice V de Cramer", | |
| page_icon="📊", | |
| layout="wide" | |
| ) | |
| # Fonction de calcul du V de Cramer | |
| def cramers_v(x, y): | |
| """ | |
| Calcule le V de Cramer entre deux variables nominales (qualitatives). | |
| """ | |
| confusion_matrix = pd.crosstab(x, y) | |
| chi2 = chi2_contingency(confusion_matrix)[0] | |
| n = confusion_matrix.sum().sum() | |
| phi2 = chi2 / n | |
| r, k = confusion_matrix.shape | |
| # Correction pour les petites tailles d'échantillon | |
| phi2_corrected = max(0, phi2 - ((k-1)*(r-1))/(n-1)) | |
| r_corrected = r - ((r-1)**2)/(n-1) | |
| k_corrected = k - ((k-1)**2)/(n-1) | |
| return np.sqrt(phi2_corrected / min(k_corrected-1, r_corrected-1)) | |
| # Fonction pour calculer la matrice | |
| def calculate_cramer_matrix(df, columns): | |
| """ | |
| Calcule la matrice des V de Cramer pour les colonnes sélectionnées. | |
| """ | |
| v_cramer_matrix = pd.DataFrame(index=columns, columns=columns) | |
| for col1 in columns: | |
| for col2 in columns: | |
| if col1 == col2: | |
| v_cramer_matrix.loc[col1, col2] = 1.0 | |
| else: | |
| v_cramer = cramers_v(df[col1], df[col2]) | |
| v_cramer_matrix.loc[col1, col2] = v_cramer | |
| return v_cramer_matrix.astype(float) | |
| # Titre de l'application | |
| st.title("📊 Matrice des V de Cramer") | |
| st.markdown("Analyse de l'association entre variables catégorielles") | |
| # Sidebar pour la sélection des données | |
| st.sidebar.header("Configuration") | |
| # Choix de la source de données | |
| data_source = st.sidebar.radio( | |
| "Source des données", | |
| ["Jeu de données Seaborn", "Importer un fichier"], | |
| label_visibility="visible" | |
| ) | |
| df = None | |
| # Chargement des données | |
| if data_source == "Jeu de données Seaborn": | |
| # Liste des jeux de données disponibles | |
| seaborn_datasets = [ | |
| 'titanic', 'tips', 'penguins', 'diamonds', | |
| 'mpg', 'taxis' | |
| ] | |
| selected_dataset = st.sidebar.selectbox( | |
| "Choisir un jeu de données", | |
| seaborn_datasets | |
| ) | |
| try: | |
| df = sns.load_dataset(selected_dataset) | |
| st.sidebar.success(f"✅ Jeu '{selected_dataset}' chargé") | |
| except Exception as e: | |
| st.sidebar.error(f"Erreur : {e}") | |
| else: | |
| # Import de fichier | |
| uploaded_file = st.sidebar.file_uploader( | |
| "Importer un fichier CSV", | |
| type=['csv'] | |
| ) | |
| if uploaded_file is not None: | |
| try: | |
| # Détection automatique du séparateur avec engine='python' | |
| df = pd.read_csv(uploaded_file, sep=None, engine='python') | |
| st.sidebar.success("✅ Fichier importé") | |
| except Exception as e: | |
| st.sidebar.error(f"Erreur : {e}") | |
| # Traitement des données | |
| if df is not None: | |
| # Sélection des colonnes catégorielles | |
| categorical_cols = df.select_dtypes(include=['object', 'category', 'bool']).columns.tolist() | |
| # Ajouter les colonnes numériques avec peu de valeurs uniques | |
| numeric_cols = df.select_dtypes(include=['int64', 'float64']).columns | |
| for col in numeric_cols: | |
| if df[col].nunique() <= 10: | |
| categorical_cols.append(col) | |
| if len(categorical_cols) == 0: | |
| st.warning("⚠️ Aucune variable catégorielle détectée.") | |
| else: | |
| st.sidebar.write(f"**{len(categorical_cols)}** variables détectées") | |
| # Sélection des colonnes à analyser | |
| selected_cols = st.sidebar.multiselect( | |
| "Variables à analyser", | |
| categorical_cols, | |
| default=categorical_cols[:min(6, len(categorical_cols))] | |
| ) | |
| if len(selected_cols) < 2: | |
| st.info("ℹ️ Veuillez sélectionner au moins 2 variables.") | |
| else: | |
| # Préparation des données | |
| df_cat = df[selected_cols].copy() | |
| # Options de traitement des valeurs manquantes | |
| nan_handling = st.sidebar.radio( | |
| "Valeurs manquantes", | |
| ["Remplacer par 'Missing'", "Supprimer les lignes"] | |
| ) | |
| # Traitement des valeurs manquantes | |
| if nan_handling == "Remplacer par 'Missing'": | |
| for col in selected_cols: | |
| # Convertir d'abord en string pour éviter l'erreur avec les categories | |
| df_cat[col] = df_cat[col].astype(str) | |
| df_cat[col] = df_cat[col].replace('nan', 'Missing') | |
| df_cat[col] = df_cat[col].replace('None', 'Missing') | |
| df_cat[col] = df_cat[col].astype('category') | |
| else: | |
| df_cat = df_cat.dropna() | |
| # Convertir en category | |
| for col in selected_cols: | |
| df_cat[col] = df_cat[col].astype('category') | |
| # Calcul de la matrice | |
| if st.sidebar.button("🔄 Calculer", type="primary"): | |
| with st.spinner("Calcul en cours..."): | |
| v_cramer_matrix = calculate_cramer_matrix(df_cat, selected_cols) | |
| # Onglets pour organiser l'affichage | |
| tab1, tab2 = st.tabs(["📊 Visualisation", "📋 Données"]) | |
| with tab1: | |
| col1, col2 = st.columns([2.5, 1]) | |
| with col1: | |
| # Création de la heatmap (taille réduite) | |
| fig, ax = plt.subplots(figsize=(8, 6)) | |
| sns.heatmap( | |
| v_cramer_matrix, | |
| annot=True, | |
| cmap='coolwarm', | |
| fmt=".2f", | |
| linewidths=0.5, | |
| cbar_kws={'label': 'V de Cramer'}, | |
| ax=ax, | |
| vmin=0, | |
| vmax=1 | |
| ) | |
| plt.title('Matrice des V de Cramer', fontsize=12, pad=15) | |
| plt.tight_layout() | |
| st.pyplot(fig) | |
| with col2: | |
| st.markdown("**Interprétation :**") | |
| st.markdown(""" | |
| - **0.0-0.1** : Très faible | |
| - **0.1-0.3** : Faible | |
| - **0.3-0.5** : Modérée | |
| - **0.5-0.7** : Forte | |
| - **0.7-1.0** : Très forte | |
| """) | |
| st.markdown("**Top 5 associations :**") | |
| # Extraire les valeurs uniques (triangle supérieur) | |
| associations = [] | |
| for i, col1_name in enumerate(selected_cols): | |
| for j, col2_name in enumerate(selected_cols): | |
| if i < j: | |
| associations.append({ | |
| 'Var 1': col1_name, | |
| 'Var 2': col2_name, | |
| 'V': v_cramer_matrix.loc[col1_name, col2_name] | |
| }) | |
| if associations: | |
| top_assoc = pd.DataFrame(associations).sort_values( | |
| 'V', ascending=False | |
| ).head(5) | |
| st.dataframe(top_assoc, hide_index=True, height=210) | |
| with tab2: | |
| st.write(f"**Dimensions :** {df.shape[0]} lignes × {df.shape[1]} colonnes") | |
| st.dataframe(df.head(20), use_container_width=True) | |
| else: | |
| st.info("👈 Veuillez sélectionner ou importer un jeu de données.") | |
| # Footer | |
| st.markdown("---") | |
| st.markdown(""" | |
| <div style='text-align: center; color: gray;'> | |
| <small>Le V de Cramer mesure l'association entre deux variables catégorielles (0 = aucune association, 1 = association parfaite)</small> | |
| </div> | |
| """, unsafe_allow_html=True) |