420 lines
16 KiB
Plaintext
420 lines
16 KiB
Plaintext
{
|
||
"cells": [
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"# 🧮 Séance 2 — Représenter un graphe : matrice, liste d'adjacence, incidence\n",
|
||
"\n",
|
||
"## Objectifs pédagogiques\n",
|
||
"À la fin de cette séance, vous serez capables de :\n",
|
||
"- représenter un graphe à l'aide d'une **matrice d'adjacence** et d'une **liste d'adjacence** ;\n",
|
||
"- représenter un graphe à l'aide d'une **matrice/liste d'incidence** (sommets × arêtes) ;\n",
|
||
"- choisir la représentation la plus adaptée selon la taille et la densité d'un réseau ;\n",
|
||
"- interpréter les liens sociaux à partir de ces représentations ;\n",
|
||
"- comprendre la **symétrie**, le **degré** et les **liens indirects** dans un réseau ;\n",
|
||
"- construire et explorer ces représentations avec **Python**."
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"## Introduction\n",
|
||
"En sociologie, on cherche souvent à représenter **les relations entre individus** : qui connaît qui, qui travaille avec qui, qui échange le plus, etc.\n",
|
||
"\n",
|
||
"Un **graphe** permet de modéliser ces interactions :\n",
|
||
"- **nœuds (ou sommets)** → les individus\n",
|
||
"- **arêtes (ou liens)** → les relations entre eux\n",
|
||
"\n",
|
||
"Mais il existe plusieurs manières, plus mathématiques, de représenter ces relations : la **matrice d'adjacence**, la **liste d'adjacence**, et la **matrice/liste d'incidence**. Chacune a ses avantages selon la taille du réseau et ce qu'on veut en faire."
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"### Définition\n",
|
||
"Pour un graphe $G = (V, E)$ à *n* sommets, la **matrice d'adjacence** $A$ est une matrice carrée *n × n* où :\n",
|
||
"\n",
|
||
"$$A_{ij} = \\begin{cases}1 & \\text{si une arête relie le sommet } i \\text{ au sommet } j \\\\0 & \\text{sinon}\\end{cases}$$\n",
|
||
"\n",
|
||
"- Dans un **graphe non dirigé**, $A$ est **symétrique** : $A_{ij} = A_{ji}$\n",
|
||
"- Dans un **graphe dirigé**, $A$ peut être **asymétrique** : $A_{ij} = 1$ ne signifie pas forcément $A_{ji} = 1$"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"## Partie 1 — Représentation papier\n",
|
||
"### 1. Graphe de départ\n",
|
||
"Voici un petit réseau d'amitié entre cinq personnes :\n",
|
||
"\n",
|
||
"```\n",
|
||
"Alice — Bob — Claire\n",
|
||
" │ │\n",
|
||
" David Emma\n",
|
||
"```\n",
|
||
"\n",
|
||
"**Consigne :**\n",
|
||
"1. Listez les sommets : `Alice, Bob, Claire, David, Emma`.\n",
|
||
"2. Complétez la matrice d'adjacence correspondante :\n",
|
||
"\n",
|
||
"| | Alice | Bob | Claire | David | Emma |\n",
|
||
"|:------|:------:|:---:|:------:|:------:|:----:|\n",
|
||
"| Alice | 0 | | | | |\n",
|
||
"| Bob | | 0 | | | |\n",
|
||
"| Claire| | | 0 | | |\n",
|
||
"| David | | | | 0 | |\n",
|
||
"| Emma | | | | | 0 |\n",
|
||
"\n",
|
||
"(Remplissez avec des 1 là où il existe un lien.)"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"### 2. Questions d'analyse\n",
|
||
"1. Quel est le **degré** de chaque individu (combien de liens possède-t-il) ?\n",
|
||
"2. Quel est l'individu le plus **central** dans ce réseau ?\n",
|
||
"3. Si on ajoute un lien entre *David* et *Emma*, comment la matrice change-t-elle ?\n",
|
||
"4. Que signifie le fait que la matrice soit **symétrique** ?\n",
|
||
"5. En sociologie, que représenterait une matrice **non symétrique** ? (donnez un exemple concret)\n",
|
||
"\n",
|
||
"**Explication :**\n",
|
||
"\n",
|
||
"- Une matrice **symétrique** signifie que les relations sont **réciproques** : si Alice est amie avec Bob, Bob l'est aussi avec Alice. Cela correspond à des relations mutuelles (amitié, collaboration, parenté, etc.).\n",
|
||
"- Une matrice **non symétrique** représente des relations **non réciproques** : par exemple, *A suit B* sur Twitter, *A cite B* dans un article scientifique, ou *A supervise B* dans une organisation."
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"## Partie 2 — Exploration Python"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"metadata": {},
|
||
"execution_count": null,
|
||
"outputs": [],
|
||
"source": [
|
||
"import networkx as nx\n",
|
||
"import numpy as np\n",
|
||
"import matplotlib.pyplot as plt"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"### 1. Créer et afficher la matrice"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"metadata": {},
|
||
"execution_count": null,
|
||
"outputs": [],
|
||
"source": [
|
||
"G = nx.Graph()\n",
|
||
"G.add_edges_from([\n",
|
||
" (\"Alice\",\"Bob\"),\n",
|
||
" (\"Bob\",\"Claire\"),\n",
|
||
" (\"Alice\",\"David\"),\n",
|
||
" (\"Bob\",\"Emma\")\n",
|
||
"])\n",
|
||
"\n",
|
||
"# Obtenir la matrice d'adjacence\n",
|
||
"A = nx.to_numpy_array(G, nodelist=G.nodes())\n",
|
||
"print(list(G.nodes()))\n",
|
||
"print(A.astype(int))"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"### 2. Visualiser le graphe"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"metadata": {},
|
||
"execution_count": null,
|
||
"outputs": [],
|
||
"source": [
|
||
"plt.figure()\n",
|
||
"nx.draw(G, with_labels=True, node_color=\"lightblue\", node_size=1000)\n",
|
||
"plt.show()"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"### 3. Explorer la structure du réseau"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"metadata": {},
|
||
"execution_count": null,
|
||
"outputs": [],
|
||
"source": [
|
||
"# Degré de chaque sommet\n",
|
||
"print(\"Degré de chaque individu :\", dict(G.degree()))\n",
|
||
"\n",
|
||
"# Densité du réseau (rapport entre liens existants et liens possibles)\n",
|
||
"print(\"Densité du réseau :\", round(nx.density(G), 2))"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"### 4. Liens indirects : le carré de la matrice"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"metadata": {},
|
||
"execution_count": null,
|
||
"outputs": [],
|
||
"source": [
|
||
"A2 = np.linalg.matrix_power(A, 2)\n",
|
||
"print(\"A² =\\n\", A2.astype(int))"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"Interprétation :\n",
|
||
"- Si `A²[i][j] > 0`, cela signifie qu'il existe **un ami commun** entre les nœuds *i* et *j*.\n",
|
||
"- En sociologie : cela mesure les **liens indirects**, c'est-à-dire les \"amis d'amis\"."
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"### Pourquoi ne met-on pas les éléments eux-mêmes au carré ?\n",
|
||
"\n",
|
||
"Quand on écrit **A²**, cela signifie **A × A**, c'est-à-dire qu'on multiplie la matrice par elle-même selon les règles de la multiplication matricielle.\n",
|
||
"\n",
|
||
"On ne calcule **pas** $(A_{ij})^2$, car cela n'aurait aucun intérêt :\n",
|
||
"- $0^2 = 0$ et $1^2 = 1$, donc la matrice ne changerait pas.\n",
|
||
"- Le but du carré matriciel est de compter **les chemins de longueur 2** entre les nœuds.\n",
|
||
"\n",
|
||
"Formellement :\n",
|
||
"$$A^2_{ij} = \\sum_k A_{ik} \\times A_{kj}$$\n",
|
||
"\n",
|
||
"Cela signifie que $A^2_{ij}$ indique combien de façons il existe d'aller du nœud *i* au nœud *j* en passant par **exactement un autre nœud**.\n",
|
||
"\n",
|
||
"**Exemple :**\n",
|
||
"Si $A =$\n",
|
||
"$$\n",
|
||
"\\begin{bmatrix}\n",
|
||
"0 & 1 & 0 \\\\\n",
|
||
"1 & 0 & 1 \\\\\n",
|
||
"0 & 1 & 0\n",
|
||
"\\end{bmatrix}\n",
|
||
"$$\n",
|
||
"alors $A^2 =$\n",
|
||
"$$\n",
|
||
"\\begin{bmatrix}\n",
|
||
"1 & 0 & 1 \\\\\n",
|
||
"0 & 2 & 0 \\\\\n",
|
||
"1 & 0 & 1\n",
|
||
"\\end{bmatrix}\n",
|
||
"$$\n",
|
||
"\n",
|
||
"- $A^2_{13} = 1$ : il existe un chemin de longueur 2 entre 1 et 3 (en passant par 2).\n",
|
||
"- $A^2_{22} = 2$ : le sommet 2 a deux chemins de longueur 2 qui reviennent à lui-même.\n",
|
||
"\n",
|
||
"**Résumé :**\n",
|
||
"\n",
|
||
"| Calcul | Signification | Utilité |\n",
|
||
"|:--------|:--------------|:--------|\n",
|
||
"| $(A_{ij})^2$ | Élève chaque case au carré | Inutile (0 et 1 inchangés) |\n",
|
||
"| $A^2 = A × A$ | Produit matriciel | Donne les **liens indirects** (chemins de longueur 2) |"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"## Partie 3 — Liste d'adjacence\n",
|
||
"\n",
|
||
"La matrice d'adjacence a un défaut : pour un réseau de 500 personnes, elle contient 500 × 500 = 250 000 cases, même si chacune n'a que 10 amis en moyenne (la plupart des cases valent alors 0). Or les réseaux sociaux réels sont presque toujours **creux** (*sparse*) : chaque individu ne connaît qu'une petite fraction du groupe.\n",
|
||
"\n",
|
||
"La **liste d'adjacence** répond à ce problème : au lieu d'une grille, on associe à **chaque sommet la liste de ses voisins**. On ne stocke que les liens qui existent réellement.\n",
|
||
"\n",
|
||
"$$\\text{Alice} \\rightarrow [\\text{Bob}, \\text{David}]$$\n",
|
||
"$$\\text{Bob} \\rightarrow [\\text{Alice}, \\text{Claire}, \\text{Emma}]$$"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"### 1. Construire la liste d'adjacence"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"metadata": {},
|
||
"execution_count": null,
|
||
"outputs": [],
|
||
"source": [
|
||
"# NetworkX construit directement la liste d'adjacence à partir du graphe G\n",
|
||
"liste_adjacence = nx.to_dict_of_lists(G)\n",
|
||
"\n",
|
||
"for personne, amis in liste_adjacence.items():\n",
|
||
" print(f\"{personne} → {amis}\")"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"### 2. Comparer matrice et liste d'adjacence\n",
|
||
"\n",
|
||
"**Questions :**\n",
|
||
"1. Combien de \"cases\" contient la matrice d'adjacence de ce petit réseau à 5 personnes ? Combien de liens contient la liste d'adjacence ?\n",
|
||
"2. Pour un réseau de 10 000 personnes où chacune a en moyenne 50 ami·es, quelle représentation prendrait le moins de mémoire ?\n",
|
||
"3. Pour répondre rapidement à la question *\"Alice et Emma sont-elles amies ?\"*, quelle représentation est la plus directe ?\n",
|
||
"4. Pour répondre à *\"quels sont les ami·es de Bob ?\"*, quelle représentation est la plus directe ?\n",
|
||
"\n",
|
||
"**En résumé :** la matrice d'adjacence est pratique pour les calculs matriciels (comme $A^2$) et les réseaux **denses**. La liste d'adjacence est plus économe en mémoire et plus rapide à parcourir pour les réseaux **creux** — c'est-à-dire la grande majorité des réseaux sociaux réels."
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"## Partie 4 — Matrice et liste d'incidence\n",
|
||
"\n",
|
||
"Les représentations précédentes croisent des **sommets avec des sommets** (qui est relié à qui). La **matrice d'incidence** change de point de vue : elle croise des **sommets avec des arêtes** (quel individu est concerné par quelle relation).\n",
|
||
"\n",
|
||
"Pour un graphe à *n* sommets et *m* arêtes, la matrice d'incidence $B$ est une matrice *n × m* où :\n",
|
||
"\n",
|
||
"$$B_{ie} = \\begin{cases}1 & \\text{si le sommet } i \\text{ est une extrémité de l'arête } e \\\\0 & \\text{sinon}\\end{cases}$$\n",
|
||
"\n",
|
||
"Chaque **colonne** représente donc une relation (une arête), avec exactement deux 1 : ses deux extrémités.\n",
|
||
"\n",
|
||
"Cette idée rejoint celle du **graphe biparti** vue en Introduction (COURS.md — individus ↔ opinions, individus ↔ événements) : on peut voir la matrice d'incidence comme \"qui participe à quelle relation\", exactement comme on notait \"qui participe à quel événement\"."
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"### 1. Construire la matrice d'incidence"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"metadata": {},
|
||
"execution_count": null,
|
||
"outputs": [],
|
||
"source": [
|
||
"# Construction manuelle de la matrice d'incidence avec numpy\n",
|
||
"# (une colonne par arête, deux 1 par colonne : les deux extrémités)\n",
|
||
"liste_aretes = list(G.edges())\n",
|
||
"sommets = list(G.nodes())\n",
|
||
"\n",
|
||
"B = np.zeros((len(sommets), len(liste_aretes)), dtype=int)\n",
|
||
"for colonne, (u, v) in enumerate(liste_aretes):\n",
|
||
" B[sommets.index(u), colonne] = 1\n",
|
||
" B[sommets.index(v), colonne] = 1\n",
|
||
"\n",
|
||
"print(\"Arêtes (colonnes) :\", liste_aretes)\n",
|
||
"print(sommets)\n",
|
||
"print(B)"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"Interprétation : chaque colonne de $B$ contient exactement deux 1, ceux des deux personnes impliquées dans cette relation. Une ligne \"pleine de 0\" signifierait un individu isolé, qui n'apparaît dans aucune relation.\n",
|
||
"\n",
|
||
"### 2. Liste d'incidence\n",
|
||
"\n",
|
||
"De la même façon qu'on a allégé la matrice d'adjacence en liste d'adjacence, on peut alléger la matrice d'incidence : au lieu d'une grille sommets × arêtes, on liste simplement, pour chaque arête, ses deux extrémités. C'est en fait exactement ce que renvoie `G.edges()` :"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"metadata": {},
|
||
"execution_count": null,
|
||
"outputs": [],
|
||
"source": [
|
||
"# La liste d'incidence : pour chaque arête, ses deux extrémités\n",
|
||
"for i, (u, v) in enumerate(G.edges()):\n",
|
||
" print(f\"Arête {i} : {u} — {v}\")"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"**Application sociologique :** l'incidence est surtout utile quand la relation elle-même porte de l'information (sa date, son contexte, son type), plutôt que le simple fait qu'elle existe. Par exemple, une matrice d'incidence \"individus × réunions\" (chaque colonne = une réunion, chaque 1 = une personne présente) permet de repérer qui participe aux mêmes réunions que qui — c'est d'ailleurs la logique du graphe biparti vu en introduction, ramenée à une structure sommets × arêtes plutôt que sommets × sommets."
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"## Synthèse\n",
|
||
"| Concept mathématique | Interprétation sociologique |\n",
|
||
"|:----------------------|:----------------------------|\n",
|
||
"| 1 dans la matrice d'adjacence | lien direct entre deux individus |\n",
|
||
"| 0 dans la matrice d'adjacence | absence de lien |\n",
|
||
"| somme d'une ligne (matrice) / longueur d'une liste (liste d'adjacence) | degré (popularité / nombre de connexions) |\n",
|
||
"| symétrie | réciprocité des relations |\n",
|
||
"| $A^2$ | existence de relations indirectes |\n",
|
||
"| densité | cohésion du groupe |\n",
|
||
"| liste d'adjacence | représentation compacte, adaptée aux réseaux creux (la plupart des réseaux sociaux réels) |\n",
|
||
"| matrice/liste d'incidence | met en avant les relations elles-mêmes (utile si elles portent de l'information : date, contexte, type) |\n",
|
||
"\n",
|
||
"| Représentation | Taille | Adaptée à |\n",
|
||
"|:-----------------|:--------|:-----------|\n",
|
||
"| Matrice d'adjacence | *n × n* | petits réseaux, réseaux denses, calculs matriciels |\n",
|
||
"| Liste d'adjacence | proportionnelle au nombre de liens | grands réseaux creux (cas le plus fréquent) |\n",
|
||
"| Matrice/liste d'incidence | *n × m* (sommets × arêtes) | quand la relation elle-même porte de l'information |"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"## Questions de réflexion\n",
|
||
"1. En quoi la matrice d'adjacence permet-elle de **quantifier** un réseau social ?\n",
|
||
"2. Si une matrice est très dense, que peut-on en conclure sur le **type de groupe** étudié ?\n",
|
||
"3. Comment pourriez-vous pondérer les liens dans cette matrice pour représenter l'**intensité** des relations ?\n",
|
||
"4. Vous devez analyser le réseau de tou·tes les utilisateur·rices d'un réseau social (des millions de personnes, chacune avec quelques centaines d'ami·es en moyenne). Justifiez votre choix entre matrice et liste d'adjacence.\n",
|
||
"5. Donnez un exemple de situation sociologique où la matrice d'incidence serait plus informative que la matrice d'adjacence."
|
||
]
|
||
}
|
||
],
|
||
"metadata": {
|
||
"kernelspec": {
|
||
"display_name": "Python 3",
|
||
"language": "python",
|
||
"name": "python3"
|
||
},
|
||
"language_info": {
|
||
"name": "python",
|
||
"pygments_lexer": "ipython3"
|
||
}
|
||
},
|
||
"nbformat": 4,
|
||
"nbformat_minor": 4
|
||
}
|