{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# 📊 Séance 3 — Mesures statistiques d'un réseau\n", "\n", "## Objectifs pédagogiques\n", "À la fin de cette séance, vous serez capables de :\n", "- calculer et interpréter le **coefficient de clustering** d'un individu et d'un réseau ;\n", "- étudier la **distribution des degrés** d'un réseau ;\n", "- comprendre la notion de **transitivité** ;\n", "- relier ces mesures à des phénomènes sociologiques (cohésion de groupe, inégalité des connexions).\n", "\n", "## Introduction\n", "Jusqu'ici, nous avons mesuré des propriétés **individuelles** (le degré d'une personne) ou des **paires** (existe-t-il un lien entre deux personnes). Cette séance s'intéresse à des mesures qui caractérisent le réseau **dans son ensemble**, ou des structures locales à plusieurs personnes (des triangles d'amitié)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Partie 1 — Coefficient de clustering\n", "\n", "**Question sociologique :** vos ami·es sont-iels ami·es entre eux ? Un individu dont l'entourage forme un groupe très soudé (tout le monde se connaît) n'occupe pas la même position sociale qu'un individu dont l'entourage est éclaté en plusieurs groupes qui ne se connaissent pas — ce second cas est typique d'une personne qui fait le **pont** entre plusieurs cercles (déjà rencontré dans le corrigé du partiel blanc).\n", "\n", "Le **coefficient de clustering local** d'un sommet mesure cela : parmi toutes les paires possibles d'ami·es de cette personne, quelle proportion sont elles-mêmes ami·es entre elles (forment un triangle fermé) ?\n", "\n", "$$C_i = \\frac{2 \\times \\text{nombre de triangles passant par } i}{\\deg(i) \\times (\\deg(i) - 1)}$$\n", "\n", "- $C_i = 1$ : tout l'entourage de $i$ se connaît (clique fermée).\n", "- $C_i = 0$ : aucun·e ami·e de $i$ ne connaît un·e autre ami·e de $i$ ($i$ est un pur intermédiaire entre des cercles séparés)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 1. Un réseau avec un triangle d'amitié" ] }, { "cell_type": "code", "metadata": {}, "execution_count": null, "outputs": [], "source": [ "import networkx as nx\n", "import matplotlib.pyplot as plt\n", "\n", "# On reprend l'esprit du réseau des séances précédentes, en ajoutant un lien\n", "# Alice-Claire pour former un triangle d'amitié fermé (Alice, Bob, Claire)\n", "G = nx.Graph()\n", "G.add_edges_from([\n", " (\"Alice\", \"Bob\"),\n", " (\"Bob\", \"Claire\"),\n", " (\"Alice\", \"Claire\"), # ferme le triangle\n", " (\"Bob\", \"David\"),\n", " (\"David\", \"Emma\"),\n", "])\n", "\n", "plt.figure()\n", "nx.draw(G, with_labels=True, node_color=\"lightblue\", node_size=1000)\n", "plt.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 2. Calculer le coefficient de clustering\n", "\n", "NetworkX calcule directement le coefficient de clustering de chaque sommet, et la moyenne sur tout le réseau." ] }, { "cell_type": "code", "metadata": {}, "execution_count": null, "outputs": [], "source": [ "clustering_individuel = nx.clustering(G)\n", "print(\"Coefficient de clustering de chaque personne :\")\n", "for personne, c in clustering_individuel.items():\n", " print(f\" {personne} : {round(c, 2)}\")\n", "\n", "print(\"\\nCoefficient de clustering moyen du réseau :\", round(nx.average_clustering(G), 2))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Interprétation :**\n", "- **Alice** et **Claire** ont un coefficient de 1 : leur unique paire d'ami·es communs (Bob-Claire pour Alice, Alice-Bob pour Claire) est bien reliée — elles appartiennent à un petit groupe fermé.\n", "- **Bob** a un coefficient plus faible (≈ 0,33) : il a 3 ami·es, mais seule la paire Alice-Claire se connaît, pas David. Bob commence à jouer un rôle de **pont** entre le triangle Alice/Claire et la branche David-Emma.\n", "- **David** a un coefficient de 0 : ses deux ami·es (Bob et Emma) ne se connaissent pas entre eux.\n", "- **Emma**, avec un seul lien, n'a pas de paire d'ami·es à évaluer (coefficient non pertinent, NetworkX renvoie 0 par convention).\n", "\n", "**Questions :**\n", "1. Si on ajoutait le lien Claire-David, comment évoluerait le coefficient de clustering de Bob ?\n", "2. Un réseau où tout le monde a un coefficient de clustering proche de 1 ressemble-t-il plutôt à une seule grande communauté, ou à plusieurs petits groupes très soudés et peu reliés entre eux ?" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Partie 2 — Distribution des degrés\n", "\n", "**Question sociologique :** dans un réseau, est-ce que tout le monde a à peu près le même nombre de connexions, ou est-ce qu'une minorité concentre l'essentiel des liens (quelques individus très connectés, une majorité peu connectée) ? La **distribution des degrés** répond à cette question : elle montre comment les degrés se répartissent dans l'ensemble du réseau.\n", "\n", "Sur notre petit réseau à 5 personnes, une distribution n'est pas très parlante. On va donc utiliser un réseau plus grand, généré automatiquement, mais qui imite un phénomène très réel : dans beaucoup de réseaux sociaux, quelques comptes concentrent énormément de connexions (célébrités, influenceur·euses) pendant que la majorité en a très peu." ] }, { "cell_type": "code", "metadata": {}, "execution_count": null, "outputs": [], "source": [ "# Un réseau généré automatiquement de 30 \"individus\", où les nouvelles personnes\n", "# ont tendance à se connecter aux personnes déjà bien connectées (comme sur un vrai réseau social)\n", "grand_reseau = nx.barabasi_albert_graph(30, 2, seed=42)\n", "\n", "degres = [d for _, d in grand_reseau.degree()]\n", "\n", "plt.figure()\n", "plt.hist(degres, bins=range(min(degres), max(degres) + 2), edgecolor=\"black\")\n", "plt.xlabel(\"Degré (nombre de connexions)\")\n", "plt.ylabel(\"Nombre d'individus\")\n", "plt.title(\"Distribution des degrés sur un réseau de 30 personnes\")\n", "plt.show()\n", "\n", "print(\"Degré minimum :\", min(degres))\n", "print(\"Degré maximum :\", max(degres))\n", "print(\"Degré moyen :\", round(sum(degres) / len(degres), 2))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Interprétation :** l'histogramme montre que la majorité des individus ont un degré proche du minimum, tandis qu'une petite minorité concentre beaucoup plus de connexions que la moyenne. C'est le signe d'un réseau **inégalitaire** : quelques nœuds jouent un rôle disproportionné dans la structure du groupe.\n", "\n", "Nous reviendrons sur ces individus très connectés en **Séance 8 (Centralité)**, où nous verrons plusieurs façons de formaliser précisément la notion d'individu \"influent\" dans un réseau.\n", "\n", "**Questions :**\n", "1. À quoi ressemblerait l'histogramme d'un réseau où tout le monde aurait exactement le même nombre d'ami·es ?\n", "2. Donnez un exemple réel de réseau social où vous pensez observer une distribution très inégalitaire, et un exemple où elle serait plus équilibrée." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Partie 3 — Transitivité globale\n", "\n", "La **transitivité** est une autre façon de mesurer la fermeture des triangles, mais à l'échelle de **tout le réseau** plutôt que personne par personne : parmi toutes les paires d'ami·es-d'ami·es du réseau (deux personnes reliées par un intermédiaire commun), quelle proportion sont elles-mêmes ami·es ?\n", "\n", "$$T = \\frac{3 \\times \\text{nombre de triangles dans le réseau}}{\\text{nombre de \"chemins à deux étapes\" (triades ouvertes)}}$$\n", "\n", "C'est une mesure proche du clustering moyen, mais elle donne plus de poids aux personnes qui ont beaucoup d'ami·es (contrairement à la moyenne des coefficients individuels, qui traite chaque personne de façon égale)." ] }, { "cell_type": "code", "metadata": {}, "execution_count": null, "outputs": [], "source": [ "# On revient à notre petit réseau à 5 personnes\n", "print(\"Transitivité globale du petit réseau :\", round(nx.transitivity(G), 2))\n", "print(\"Coefficient de clustering moyen (rappel) :\", round(nx.average_clustering(G), 2))\n", "\n", "print(\"\\nTransitivité globale du grand réseau (30 personnes) :\", round(nx.transitivity(grand_reseau), 2))\n", "print(\"Coefficient de clustering moyen du grand réseau :\", round(nx.average_clustering(grand_reseau), 2))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Synthèse\n", "\n", "| Mesure | Ce qu'elle capture | Interprétation sociologique |\n", "|:--------|:--------------------|:------------------------------|\n", "| Coefficient de clustering (local) | Les ami·es d'un individu se connaissent-iels entre eux ? | Individu \"au cœur\" d'un groupe soudé vs individu \"pont\" entre plusieurs cercles |\n", "| Coefficient de clustering (moyen) | Moyenne des coefficients individuels | Niveau général de cohésion, en traitant chaque personne à égalité |\n", "| Distribution des degrés | Répartition des degrés dans tout le réseau | Réseau égalitaire vs réseau concentré autour de quelques individus très connectés |\n", "| Transitivité globale | Proportion de triades fermées, à l'échelle du réseau | Cohésion globale du réseau, en donnant plus de poids aux individus très connectés |\n", "\n", "## Questions de réflexion\n", "1. Un réseau avec un coefficient de clustering élevé mais une distribution des degrés très inégalitaire : que pouvez-vous en déduire sur sa structure ?\n", "2. En quoi le coefficient de clustering local est-il lié à la notion de **pont** entre communautés déjà rencontrée dans le partiel blanc ?\n", "3. Comment ces mesures pourraient-elles aider à comparer deux réseaux sociaux différents (par exemple une promotion d'étudiant·es et un réseau professionnel) ?" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "name": "python", "pygments_lexer": "ipython3" } }, "nbformat": 4, "nbformat_minor": 4 }