V_de_Cramer / app.py
Eric2mangel's picture
Update app.py
174b3e4 verified
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)