from pathlib import Path import matplotlib.pyplot as plt import pandas as pd import seaborn as sns import streamlit as st st.set_page_config(page_title="Speed Dating Analysis", layout="wide") sns.set_theme(style="whitegrid") @st.cache_data def load_data(): data_path = Path(__file__).parent / "Speed_Dating_Data.csv" df = pd.read_csv(data_path, encoding="latin1") df["gender_label"] = df["gender"].map({0: "Femme", 1: "Homme"}) df["age_gap"] = (df["age"] - df["age_o"]).abs() return df def make_barplot(data, x, y, hue=None, title="", xlabel="", ylabel="", palette="Set2", rotation=0): fig, ax = plt.subplots(figsize=(10, 5)) sns.barplot(data=data, x=x, y=y, hue=hue, palette=palette, ax=ax) ax.set_title(title) ax.set_xlabel(xlabel) ax.set_ylabel(ylabel) ax.tick_params(axis="x", rotation=rotation) if hue is None and ax.legend_ is not None: ax.legend_.remove() return fig def make_countplot(data, x, title, labels, palette): fig, ax = plt.subplots(figsize=(6, 4)) sns.countplot(data=data, x=x, hue=x, legend=False, palette=palette, ax=ax) ax.set_title(title) ax.set_xlabel("") ax.set_ylabel("Nombre") ax.set_xticks([0, 1], labels) return fig df = load_data() st.title("Speed Dating Analysis") st.write( "Cette application reprend l'analyse du notebook de facon simple pour comprendre " "ce qui semble favoriser un match lors d'un speed dating." ) st.sidebar.header("Navigation") section = st.sidebar.radio( "Aller a une section", [ "Vue d'ensemble", "Variable cible", "Preferences", "Decision individuelle", "Facteurs du match", "Facteurs complementaires", "Conclusion", ], ) if section == "Vue d'ensemble": st.header("Vue d'ensemble du dataset") col1, col2, col3 = st.columns(3) col1.metric("Nombre de lignes", f"{df.shape[0]}") col2.metric("Nombre de colonnes", f"{df.shape[1]}") col3.metric("Nombre de waves", f"{df['wave'].nunique()}") st.subheader("Apercu des donnees") st.dataframe(df.head()) st.subheader("Colonnes les plus incompletes") missing = (df.isna().mean() * 100).sort_values(ascending=False).head(15).round(1) missing_df = missing.reset_index() missing_df.columns = ["colonne", "missing_pct"] st.dataframe(missing_df, use_container_width=True) fig = make_barplot( missing_df, x="colonne", y="missing_pct", title="Top 15 des colonnes avec valeurs manquantes", xlabel="Colonne", ylabel="% de valeurs manquantes", palette="rocket", rotation=65, ) st.pyplot(fig) st.info( "Le dataset contient beaucoup de variables. Pour garder une analyse simple, " "on se concentre sur celles qui aident le plus a comprendre les decisions et les matches." ) elif section == "Variable cible": st.header("Comprendre la variable cible : match") col1, col2, col3 = st.columns(3) col1.metric("Taux de match", f"{df['match'].mean() * 100:.2f}%") col2.metric("Taux de oui", f"{df['dec'].mean() * 100:.2f}%") col3.metric("Taux de oui partenaire", f"{df['dec_o'].mean() * 100:.2f}%") left, right = st.columns(2) with left: st.pyplot(make_countplot(df, "match", "Repartition des matches", ["Non", "Oui"], "Set2")) with right: st.pyplot(make_countplot(df, "dec", "Repartition des decisions individuelles", ["Non", "Oui"], "Set1")) st.info( "Le match est rare, car il faut un accord mutuel. Dire oui seul est plus frequent que reussir a obtenir un match." ) elif section == "Preferences": st.header("Que recherchent les participants ?") preference_cols = ["attr1_1", "sinc1_1", "intel1_1", "fun1_1", "amb1_1", "shar1_1"] preferences = ( df.groupby("gender_label")[preference_cols] .mean() .round(2) .T .rename(columns={"Femme": "Femmes", "Homme": "Hommes"}) ) st.dataframe(preferences, use_container_width=True) pref_plot = preferences.reset_index().rename(columns={"index": "critere"}) pref_plot = pref_plot.melt(id_vars="critere", var_name="groupe", value_name="importance") st.pyplot( make_barplot( pref_plot, x="critere", y="importance", hue="groupe", title="Preferences declarees selon le genre", xlabel="Critere", ylabel="Importance moyenne", palette="Set2", rotation=20, ) ) st.info( "Les hommes declarent donner plus d'importance a l'attractivite. " "Les femmes declarent donner un peu plus d'importance a l'intelligence, " "a la sincerite et a l'ambition." ) elif section == "Decision individuelle": st.header("Qu'est-ce qui pousse une personne a dire oui ?") rating_cols = ["attr", "sinc", "intel", "fun", "amb", "shar", "like", "prob"] decision_means = df.groupby("dec")[rating_cols].mean().round(2) decision_means.index = ["A dit non", "A dit oui"] st.dataframe(decision_means, use_container_width=True) decision_plot = df.groupby("dec")[["attr", "sinc", "intel", "fun", "amb", "shar"]].mean().T.reset_index() decision_plot.columns = ["critere", "A dit non", "A dit oui"] decision_plot = decision_plot.melt(id_vars="critere", var_name="decision", value_name="score") st.pyplot( make_barplot( decision_plot, x="critere", y="score", hue="decision", title="Notes moyennes selon la decision individuelle", xlabel="Critere", ylabel="Note moyenne", palette="Set1", ) ) corr_dec = df[rating_cols + ["dec"]].corr(numeric_only=True)["dec"].sort_values(ascending=False) st.subheader("Correlation avec la decision") st.dataframe(corr_dec.to_frame("correlation"), use_container_width=True) st.info( "Quand une personne dit oui, toutes les notes sont plus elevees. " "L'attractivite ressort fortement, mais le fun, l'intelligence, la sincerite " "et les interets communs comptent aussi." ) elif section == "Facteurs du match": st.header("Qu'est-ce qui favorise un match ?") rating_cols = ["attr", "sinc", "intel", "fun", "amb", "shar", "like", "prob"] match_means = df.groupby("match")[rating_cols].mean().round(2) match_means.index = ["Pas de match", "Match"] st.dataframe(match_means, use_container_width=True) match_plot = df.groupby("match")[["attr", "sinc", "intel", "fun", "amb", "shar"]].mean().T.reset_index() match_plot.columns = ["critere", "Pas de match", "Match"] match_plot = match_plot.melt(id_vars="critere", var_name="statut", value_name="score") st.pyplot( make_barplot( match_plot, x="critere", y="score", hue="statut", title="Notes moyennes selon la presence d'un match", xlabel="Critere", ylabel="Note moyenne", palette="viridis", ) ) corr_match = df[rating_cols + ["match"]].corr(numeric_only=True)["match"].sort_values(ascending=False) st.subheader("Correlation avec le match") st.dataframe(corr_match.to_frame("correlation"), use_container_width=True) st.info( "Les matches apparaissent surtout quand la rencontre est bien notee sur plusieurs dimensions. " "L'appreciation globale, l'attractivite, le fun et les interets communs sont parmi les facteurs les plus visibles." ) elif section == "Facteurs complementaires": st.header("Deux facteurs complementaires") left, right = st.columns(2) with left: st.subheader("Ecart d'age") age_gap = df.groupby("match")["age_gap"].mean().round(2) age_gap.index = ["Pas de match", "Match"] st.dataframe(age_gap.to_frame("age_gap_moyen"), use_container_width=True) fig, ax = plt.subplots(figsize=(6, 4)) sns.boxplot(data=df, x="match", y="age_gap", hue="match", legend=False, palette="pastel", ax=ax) ax.set_title("Ecart d'age selon le match") ax.set_xlabel("Match") ax.set_ylabel("Ecart d'age") ax.set_xticks([0, 1], ["Non", "Oui"]) st.pyplot(fig) with right: st.subheader("Variable samerace") same_race = df.groupby("samerace", dropna=True)["match"].mean().round(3).reset_index() st.dataframe(same_race, use_container_width=True) fig = make_barplot( same_race, x="samerace", y="match", title="Taux de match selon samerace", xlabel="Same race", ylabel="Taux de match", palette="Set2", ) fig.axes[0].set_xticks([0, 1], ["Non", "Oui"]) st.pyplot(fig) st.info( "Un ecart d'age plus faible semble legerement aider. " "La variable samerace semble aussi jouer un petit role, mais plus faible que les notes donnees pendant le date." ) else: st.header("Conclusion") st.markdown( """ Les idees principales a retenir sont : 1. Le taux de match est faible car il faut deux reponses positives. 2. Les preferences declarees changent un peu selon le genre. 3. L'attractivite joue un role important dans la decision individuelle. 4. Le match depend surtout d'une bonne impression globale. 5. Le fun, les interets communs et l'appreciation generale sont tres importants. 6. L'ecart d'age et `samerace` semblent avoir un effet plus limite. """ ) st.success( "En resume, ce qui cree un match n'est pas seulement le physique. " "La qualite globale de l'interaction semble tres importante." )