Refonte du cours en 12 séances alignées sur le programme officiel
This commit is contained in:
259
Dijkstra/Annexe_Dijkstra_Dictionnaire.ipynb
Executable file
259
Dijkstra/Annexe_Dijkstra_Dictionnaire.ipynb
Executable file
File diff suppressed because one or more lines are too long
318
Dijkstra/Annexe_Dijkstra_Dictionnaire_Corrige.ipynb
Executable file
318
Dijkstra/Annexe_Dijkstra_Dictionnaire_Corrige.ipynb
Executable file
File diff suppressed because one or more lines are too long
128
Dijkstra/Annexe_Dijkstra_Preuve_Complexite.ipynb
Normal file
128
Dijkstra/Annexe_Dijkstra_Preuve_Complexite.ipynb
Normal file
File diff suppressed because one or more lines are too long
174
Dijkstra/Annexe_Dijkstra_Preuve_Complexite_Corrige.ipynb
Normal file
174
Dijkstra/Annexe_Dijkstra_Preuve_Complexite_Corrige.ipynb
Normal file
File diff suppressed because one or more lines are too long
420
Dijkstra/Seance_6_Dijkstra_1_RecapForceBrute.ipynb
Executable file
420
Dijkstra/Seance_6_Dijkstra_1_RecapForceBrute.ipynb
Executable file
File diff suppressed because one or more lines are too long
724
Dijkstra/Seance_6_Dijkstra_1_RecapForceBrute_Corrige.ipynb
Executable file
724
Dijkstra/Seance_6_Dijkstra_1_RecapForceBrute_Corrige.ipynb
Executable file
File diff suppressed because one or more lines are too long
134
Dijkstra/Seance_6_Dijkstra_1_test.py
Executable file
134
Dijkstra/Seance_6_Dijkstra_1_test.py
Executable file
@@ -0,0 +1,134 @@
|
||||
#!/usr/bin/env python
|
||||
#-*-coding:UTF-8-*-
|
||||
|
||||
#************************************************************************
|
||||
# Transcription du graphe sous forme de liste de listes
|
||||
#************************************************************************
|
||||
|
||||
graphe = [
|
||||
[0 ,4 ,2 ,99,99,99,99,99],
|
||||
[4 ,0 ,6 ,99,5 ,99,99,99],
|
||||
[2 ,6 ,0 ,3 ,99,99,99,5 ],
|
||||
[99,99,3 ,0 ,99,3 ,4 ,1 ],
|
||||
[99,5 ,99,99,0 ,2 ,99,99],
|
||||
[99,99,99,3 ,2 ,0 ,7 ,99],
|
||||
[99,99,99,4 ,99,7 ,0 ,10],
|
||||
[99,99,5 ,1 ,99,99,10,0 ]
|
||||
]
|
||||
|
||||
def distance(Noeud_A, Noeud_B) -> int:
|
||||
#************************************************************************
|
||||
# Donne la distance entre deux noeuds adjacents Noeud_A et Noeud_B
|
||||
# Entrée : Le noeud de départ
|
||||
# Sortie : la liste des noeuds adjacents
|
||||
#************************************************************************
|
||||
# on vérifie que les valeurs de départ et arrivée sont bien des entiers
|
||||
assert type(Noeud_A) == int, "la valeur départ n'est pas un entier"
|
||||
assert type(Noeud_B) == int, "la valeur départ n'est pas un entier"
|
||||
assert graphe[Noeud_A][Noeud_B] != 99, "les noeuds ne sont pas adjacent"
|
||||
|
||||
# On retourne la distance entre les deux noeuds
|
||||
return(graphe[Noeud_A][Noeud_B])
|
||||
|
||||
|
||||
def determine_adjacents(noeud) -> list:
|
||||
#************************************************************************
|
||||
# A partir d'un noeud en entrée, la fonction donne la liste des noeuds adjacents
|
||||
# Entrée : Le noeud de départ
|
||||
# Sortie : la liste des noeuds adjacents
|
||||
#************************************************************************
|
||||
# On sélectionne la ligne contenant les noeuds adjacent par rapport a notre point de départ
|
||||
ligne = graphe[noeud]
|
||||
|
||||
# on détermine le nombre de noeuds adjacents pour ce point
|
||||
nb_adjacent = len(ligne)-ligne.count(99)-ligne.count(0)
|
||||
|
||||
# Puis on crée la variable parcours sous forme de liste de listes
|
||||
liste_adjacents = [[] for i in range(0,nb_adjacent)]
|
||||
|
||||
a = 0
|
||||
for i in range(0,len(ligne)):
|
||||
if (ligne[i] != 0 and ligne[i] != 99):
|
||||
liste_adjacents[a].append(noeud)
|
||||
liste_adjacents[a].append(i)
|
||||
a += 1
|
||||
|
||||
# On retourne la liste des noeuds adjacents
|
||||
return(liste_adjacents)
|
||||
|
||||
def recursive2(liste) -> list:
|
||||
#************************************************************************
|
||||
# La fonction récursive établit la liste exhaustive des chemins possibles
|
||||
# Entrée : La liste de chemins déja établie
|
||||
# Sortie : la liste de chemins jusqu'au noeuds suivants
|
||||
#************************************************************************
|
||||
|
||||
sommets_adjacents = []
|
||||
# On détermine le nombre d'arcs adjacents aux noeuds
|
||||
for i in range(0,len(liste)): # on boucle dans notre liste existante
|
||||
for element in determine_adjacents(liste[i][-1]): # pour chaque sommet dans la liste des adjacents
|
||||
if element not in sommets_adjacents: # si le sommets n'est pas encore dans notre liste
|
||||
sommets_adjacents.append(element) # alors on le rajoute
|
||||
|
||||
# On identifie les chemins qu'on doit créer, en ignorant les chemins:
|
||||
# - où on revient sur un noeud déja visité
|
||||
# - déja existant dans la liste
|
||||
# Puis on renseigne les chemins possibles
|
||||
|
||||
entrees_a_supprimer = [] # On va garder en mémoire les entrées a supprimer
|
||||
for i in range(0,len(liste)) : # on boucle dans notre liste existante
|
||||
if liste[i][-1] != arrivée: # on ignore les chemins menant déja au point d'arrivée
|
||||
for j in range(0,len(sommets_adjacents)): # on boucle dans la liste qu'on vient d'obtenir
|
||||
if (sommets_adjacents[j][0] == liste[i][-1] and
|
||||
sommets_adjacents[j][-1] != liste[i][0] and
|
||||
sommets_adjacents[j][-1] not in liste[i]
|
||||
):
|
||||
liste.append(liste[i] + sommets_adjacents[j][1:len(sommets_adjacents)])
|
||||
entrees_a_supprimer.append(i) # On va garder en mémoire les entrées a supprimer
|
||||
|
||||
for i in range(0,len(entrees_a_supprimer)): # avant de retourner notre résultat on supprime les entrées superflus
|
||||
del liste[entrees_a_supprimer[i]-i]
|
||||
return(liste)
|
||||
|
||||
|
||||
#********************************************************************************
|
||||
# Début du programme principal
|
||||
#********************************************************************************
|
||||
|
||||
# on définit un point de départ et d'arrivée
|
||||
départ = 0
|
||||
arrivée = 6
|
||||
|
||||
|
||||
# On détermine le nombre d'arcs partant de notre sommet départ
|
||||
liste_arcs_départs = determine_adjacents(départ)
|
||||
|
||||
|
||||
# Construction de la liste de chemins possibles
|
||||
for i in range(0,len(graphe)):
|
||||
liste_arcs_départs = recursive2(liste_arcs_départs)
|
||||
|
||||
# On imprime le nombre de chemins et les chemins possibles
|
||||
print("Il y a",len(liste_arcs_départs),"chemins possibles:",liste_arcs_départs)
|
||||
|
||||
# On identifie le chemin le plus court
|
||||
this_distance = 0
|
||||
court_distance = 9999
|
||||
court_chemin =[]
|
||||
|
||||
for element in liste_arcs_départs:
|
||||
this_distance = 0
|
||||
for i in range(0,len(element)-1):
|
||||
this_distance += distance(element[i],element[i+1])
|
||||
if this_distance < court_distance:
|
||||
court_distance = this_distance
|
||||
court_chemin = element
|
||||
|
||||
# Ecrivez votre réponse à la question 6.1 ici
|
||||
print("Le chemin le plus court est:", court_chemin)
|
||||
print("La distance a parcourir est:", court_distance)
|
||||
|
||||
# Déclaration des variables en fonction des itérations (1, 3 et 5)
|
||||
chemins_it1 = [[0, 1], [0, 2]]
|
||||
chemins_it3 = [[0, 1, 2, 3], [0, 1, 2, 7], [0, 1, 4, 5], [0, 2, 1, 4], [0, 2, 3, 5], [0, 2, 3, 6], [0, 2, 3, 7], [0, 2, 7, 3], [0, 2, 7, 6]]
|
||||
chemins_it5 = [[0, 2, 3, 6], [0, 2, 7, 6], [0, 1, 2, 3, 6], [0, 1, 2, 7, 6], [0, 1, 4, 5, 6], [0, 2, 3, 5, 6], [0, 2, 3, 7, 6], [0, 2, 7, 3, 6], [0, 1, 2, 3, 5, 4], [0, 1, 2, 3, 5, 6], [0, 1, 2, 3, 7, 6], [0, 1, 2, 7, 3, 5], [0, 1, 2, 7, 3, 6], [0, 1, 4, 5, 3, 2], [0, 1, 4, 5, 3, 6], [0, 1, 4, 5, 3, 7], [0, 2, 1, 4, 5, 3], [0, 2, 1, 4, 5, 6], [0, 2, 3, 5, 4, 1], [0, 2, 7, 3, 5, 4], [0, 2, 7, 3, 5, 6]]
|
||||
134
Dijkstra/Seance_6_Dijkstra_2_Intuition.ipynb
Normal file
134
Dijkstra/Seance_6_Dijkstra_2_Intuition.ipynb
Normal file
File diff suppressed because one or more lines are too long
167
Dijkstra/Seance_6_Dijkstra_2_Intuition_Corrige.ipynb
Normal file
167
Dijkstra/Seance_6_Dijkstra_2_Intuition_Corrige.ipynb
Normal file
File diff suppressed because one or more lines are too long
210
Dijkstra/Seance_7_Dijkstra_Implementation.ipynb
Normal file
210
Dijkstra/Seance_7_Dijkstra_Implementation.ipynb
Normal file
@@ -0,0 +1,210 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"source": [
|
||||
"# Séance 7 — Implémentation de l'algorithme de Dijkstra\n",
|
||||
"VERSION ELEVE\n",
|
||||
"\n",
|
||||
"On reprend ici l'intuition et le pseudo-code vus en Séance 6 pour les traduire en Python, sur le même graphe (circuits courts en Bretagne)."
|
||||
],
|
||||
"metadata": {}
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 5- Implémentation de l'algorithme de Dijkstra\n",
|
||||
" ### Travail à faire 6: Les données à traiter\n",
|
||||
"Le graphe sera sous forme d'une liste de liste, comme dans la 1ère partie \"méthode par force brute\". \n",
|
||||
" 1- Reprenez cette liste et insérez la ci-dessous. \n",
|
||||
" 2- La fonction 'Dijkstra' est documentée (en rouge), complétez les trois assertions permettant de s'assurer de l'intégrité des données à traiter. Puis tester ces insertions avec 'dijkstra(\"A\",0)', puis 'dijkstra(Graphe,\"A\")', puis 'dijkstra(Graphe,8)' et enfin 'dijkstra(Graphe,0) qui est la bonne écriture d'appel pour cette fonction.\n",
|
||||
" 3- Expliquer pourquoi '0' est saisi pour le paramètre 's-debut'?"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# la structure de données pour le Graphe est la liste de liste de la 1ère partie: \n",
|
||||
"Graphe = "
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def dijkstra(Graphe, s_debut):\n",
|
||||
" \"\"\" fonction: calculer les plus courts chemins à partir d'un sommet de départ vers chacun des autres sommets\n",
|
||||
" paramètres :\n",
|
||||
" 'Graphe', un graphe sous forme d'une liste de liste,\n",
|
||||
" 's_debut' un sommet de départ.\n",
|
||||
" renvoie:\n",
|
||||
" 'E_calcules', liste des sommets rangés dans l'ordre d'exploration,\n",
|
||||
" 'poids', liste des poids de chaque sommet rangés dans l'ordre d'exploration,\n",
|
||||
" 'predecesseurs', liste des sommets prédécesseur de chaque sommet rangés dans l'ordre d'exploration,\n",
|
||||
" \"\"\"\n",
|
||||
" assert type(Graphe) ... , \"Graphe doit être de type liste\"\n",
|
||||
" assert type(s_debut) ... , \" s_debut doit être un entier \"\n",
|
||||
" assert s_debut in [ ... ], \"s_debut doit être dans la plage d'indice de Graphe\""
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Travail à faire 7: Initialisation de l'algorithme\n",
|
||||
" En reprenant les éléments de l'algorithme fournis au Taf2 et au Taf4, compléter les deux lignes manquantes de la partie \"Algo: initialisation\" du script, ci-dessous."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def dijkstra(Graphe, s_debut):\n",
|
||||
" #Algo: Initialisation\n",
|
||||
" infini=float(\"inf\") # définition d'une valeur infinie\n",
|
||||
" predecesseurs = [-1 for sommet in range(len(Graphe))] # initialisation des prédecesseurs à non parcouru (-1)\n",
|
||||
" predecesseurs[s_debut] = 0 # sauf le sommet de départ qui est le prédécesseur de lui-même\n",
|
||||
" ... # initialisation des poids à l'infini\n",
|
||||
" ... # sauf le sommet de départ de poids nul\n",
|
||||
" E_sommets = [i for i in range(len(Graphe))] # initialisation de l'ensemble des sommets du graphe\n",
|
||||
" E_calcules = [] # création de la liste des calculés, vide au départ"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Travail à faire 8: Partie \"Mise à jour, poids et prédécesseur du plus proche voisin\" de l'algorithme\n",
|
||||
" En reprenant les éléments de l'algorithme fournis au Taf2 et au Taf4, complétez l'implémentation de la structure conditionnelle de la partie \"Algo: Mise à jour, poids et prédécesseur du plus proche voisin\" du script de la boucle principale, ci-dessous. \n",
|
||||
" Pour cela, répondez au QCM4 ci-dessous, puis placez la structure de code choisie dans le script de la boucle principale situé plus bas."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"$QCM4:$ Parmi les quatres extraits de script ci-dessous, un seul convient:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Réponse1\n",
|
||||
"if poids[s_voisin] > poids[s_mini] + Graphe[s_mini][s_voisin] and Graphe[s_mini][s_voisin] = 0:\n",
|
||||
" poids[s_voisin] = poids[s_mini] - Graphe[s_mini][s_voisin] \n",
|
||||
" predecesseurs[s_voisin] = s_mini "
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Réponse2\n",
|
||||
"if poids[s_voisin] < poids[s_mini] + Graphe[s_mini][s_voisin] and Graphe[s_mini][s_voisin] != 0:\n",
|
||||
" poids[s_voisin] = poids[s_mini] + Graphe[s_mini][s_voisin] \n",
|
||||
" predecesseurs[s_mini] = s_voisin "
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Réponse3\n",
|
||||
"if poids[s_voisin] > poids[s_mini] + Graphe[s_mini][s_voisin] and Graphe[s_mini][s_voisin] != 0:\n",
|
||||
" poids[s_voisin] = poids[s_mini] + Graphe[s_mini][s_voisin] \n",
|
||||
" predecesseurs[s_voisin] = s_mini "
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Réponse4\n",
|
||||
"if poids[s_voisin] > poids[s_mini] + Graphe[s_mini][s_voisin] or Graphe[s_mini][s_voisin] != 0:\n",
|
||||
" poids[s_mini] = poids[s_voisin] + Graphe[s_mini][s_voisin] \n",
|
||||
" predecesseurs[s_voisin] = s_mini "
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": "#Algo: boucle principale\n while E_sommets: # Tant que tous les sommets ne sont pas définitivement calculés:\n\n # Algo: recherche d'un sommet de distance minimale\n poids_local = infini #\n s_mini = -1 # \n for s in E_sommets: # \n if poids[s] < poids_local: # \n poids_local = poids[s] # \n s_mini = s # \n #----------> Algo: fin de recherche d'un sommet de distance minimale\n\n E_sommets.remove(s_mini) # retrait du dernier sommet calculé à l'ensemble des sommets non définitivement calculés\n E_calcules.append(s_mini) # ajout du dernier sommet calculé à la liste des sommets définitivement calculés\n for s_voisin in E_sommets: # Pour chaque sommet voisin du dernier sommet calculé :\n\"\"\" Partie du script à compléter ci-dessous à partir du choix effectué au QCM4\"\"\"\n #Algo: mise à jour poids et prédécesseur du plus proche voisin\n ... # si le chemin st plus court et il y a existance d'un arc:\n ... # mise à jour du poids du plus proche voisin\n ... # mise à jour du prédécesseur du plus proche voisin\n #----------> fin de mise à jour du poids et du prédécesseur du plus proche voisin\n#---------->fin de boucle principale"
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Travail à faire 9: Partie \"Recherche d’un sommet de distance minimale \" de l'algorithme\n",
|
||||
" En reprenant les éléments de l'algorithme fournis au Taf2 et au Taf4, commenter chaque ligne du script proposé pour la partie \"Algo: Recherche d’un sommet de distance minimale\" de façon à bien expliquer la solution implémentée pour obtenir le sommet \"actuel\" de poids minimal ."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": "#Algo: boucle principale\n while E_sommets: # Tant que tous les sommets ne sont pas définitivement calculés:\n\n # Algo: recherche d'un sommet de distance minimale\n poids_local = infini #\n s_mini = -1 # \n for s in E_sommets: # \n if poids[s] < poids_local: # \n poids_local = poids[s] # \n s_mini = s # \n #----------> Algo: fin de recherche d'un sommet de distance minimale\n\n E_sommets.remove(s_mini) # retrait du dernier sommet calculé à l'ensemble des sommets non définitivement calculés\n E_calcules.append(s_mini) # ajout du dernier sommet calculé à la liste des sommets définitivement calculés\n for s_voisin in E_sommets: # Pour chaque sommet voisin du dernier sommet calculé :\n\n #Algo: mise à jour poids et prédécesseur du plus proche voisin\n if ... and Graphe[s_mini][s_voisin] !=0: # si le chemin st plus court et il y a existance d'un arc:\n ... # mise à jour du poids du plus proche voisin\n ... # mise à jour du prédécesseur du plus proche voisin\n #----------> fin de mise à jour du poids et du prédécesseur du plus proche voisin\n#---------->fin de boucle principale"
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Travail à faire 10: la fonction Dijkstra complète\n",
|
||||
" Reconstituer ci dessous le script complet de la fonction 'dijkstra' et en effectuer le test. Si le test n'est pas probant, recherchez d'éventuelles erreurs flagrantes, et, si nécessaire appelez l'enseignant pour débloquer la situation. "
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def dijkstra(Graphe, s_debut):\n",
|
||||
" \n",
|
||||
"\n",
|
||||
"# le sommet s_debut choisi ici est \"A\", d'indice 0 dans E_sommets\n",
|
||||
"dijkstra(Graphe,0)== ([0, 2, 1, 3, 7, 5, 4, 6], [0, 4, 2, 5, 9, 8, 9, 6], [0, 0, 0, 2, 1, 3, 3, 3])"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.7.6"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 4
|
||||
}
|
||||
685
Dijkstra/Seance_7_Dijkstra_Implementation_Corrige.ipynb
Normal file
685
Dijkstra/Seance_7_Dijkstra_Implementation_Corrige.ipynb
Normal file
@@ -0,0 +1,685 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"source": [
|
||||
"# Séance 7 — Implémentation de l'algorithme de Dijkstra\n",
|
||||
"VERSION corrigée\n",
|
||||
"\n",
|
||||
"On reprend ici l'intuition et le pseudo-code vus en Séance 6 pour les traduire en Python, sur le même graphe (circuits courts en Bretagne)."
|
||||
],
|
||||
"metadata": {}
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 5- Implémentation de l'algorithme de Dijkstra\n",
|
||||
" ### Travail à faire 6: Les données à traiter\n",
|
||||
"Le graphe sera sous forme d'une liste de liste, comme dans la 1ère partie \"méthode par force brute\". \n",
|
||||
" 1- Reprenez cette liste et insérez la ci-dessous. \n",
|
||||
" 2- La fonction 'Dijkstra' est documentée (en rouge), complétez les trois assertions permettant de s'assurer de l'intégrité des données à traiter. Puis tester ces insertions avec 'dijkstra(\"A\",0)', puis 'dijkstra(Graphe,\"A\")', puis 'dijkstra(Graphe,8)' et enfin 'dijkstra(Graphe,0) qui est la bonne écriture d'appel pour cette fonction.\n",
|
||||
" 3- Expliquer pourquoi '0' est saisi pour le paramètre 's-debut'?"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# la structure de données pour le Graphe est la liste de liste de la 1ère partie: \n",
|
||||
"Graphe = "
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def dijkstra(Graphe, s_debut):\n",
|
||||
" \"\"\" fonction: calculer les plus courts chemins à partir d'un sommet de départ vers chacun des autres sommets\n",
|
||||
" paramètres :\n",
|
||||
" 'Graphe', un graphe sous forme d'une liste de liste,\n",
|
||||
" 's_debut' un sommet de départ.\n",
|
||||
" renvoie:\n",
|
||||
" 'E_calcules', liste des sommets rangés dans l'ordre d'exploration,\n",
|
||||
" 'poids', liste des poids de chaque sommet rangés dans l'ordre d'exploration,\n",
|
||||
" 'predecesseurs', liste des sommets prédécesseur de chaque sommet rangés dans l'ordre d'exploration,\n",
|
||||
" \"\"\"\n",
|
||||
" assert type(Graphe) ... , \"Graphe doit être de type liste\"\n",
|
||||
" assert type(s_debut) ... , \" s_debut doit être un entier \"\n",
|
||||
" assert s_debut in [ ... ], \"s_debut doit être dans la plage d'indice de Graphe\""
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"**Réponse Taf6-1 :** il suffit de recopier la lsite de liste de la 1ère partie de l'évaluation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 8,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# la structure de données pour le Graphe est la liste de liste de la 1ère partie: \n",
|
||||
"Graphe = [[0, 4, 2, 0, 0, 0, 0, 0 ],\n",
|
||||
" [4, 0, 6, 0, 5, 0, 0, 0 ],\n",
|
||||
" [2, 6, 0, 3, 0, 0, 0, 5 ],\n",
|
||||
" [0, 0, 3, 0, 0, 3, 4, 1 ],\n",
|
||||
" [0, 5, 0, 0, 0, 2, 0, 0 ],\n",
|
||||
" [0, 0, 0, 3, 2, 0, 7, 0 ],\n",
|
||||
" [0, 0, 0, 4, 0, 7, 0, 10],\n",
|
||||
" [0, 0, 5, 1, 0, 0, 10, 0]]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"**Réponse Taf6-2 :** \n",
|
||||
" Les trois premieres vérifications mettent en évidence le retour de chacune des trois assertions ( 'Graphe' n'est pas de type 'list'; 's_debut' n'est pas de type 'int'; 's_debut' n'est pas un entier entre 0 et 7, puisque 'Graphe' est une liste de 8 éléments). La quatrième vérification ne crée pas d'AssertionError."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 3,
|
||||
"metadata": {
|
||||
"scrolled": true
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"ename": "AssertionError",
|
||||
"evalue": "Graphe doit être de type liste",
|
||||
"output_type": "error",
|
||||
"traceback": [
|
||||
"\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
|
||||
"\u001b[1;31mAssertionError\u001b[0m Traceback (most recent call last)",
|
||||
"\u001b[1;32m<ipython-input-3-d14470863b64>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m\u001b[0m\n\u001b[0;32m 13\u001b[0m \u001b[1;32massert\u001b[0m \u001b[0ms_debut\u001b[0m \u001b[1;32min\u001b[0m \u001b[1;33m[\u001b[0m\u001b[0mi\u001b[0m \u001b[1;32mfor\u001b[0m \u001b[0mi\u001b[0m \u001b[1;32min\u001b[0m \u001b[0mrange\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mlen\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mGraphe\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m]\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;34m\"s_debut doit être dans la plage d'indice de Graphe\"\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 14\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m---> 15\u001b[1;33m \u001b[0mdijkstra\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m\"A\"\u001b[0m\u001b[1;33m,\u001b[0m\u001b[1;36m0\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m",
|
||||
"\u001b[1;32m<ipython-input-3-d14470863b64>\u001b[0m in \u001b[0;36mdijkstra\u001b[1;34m(Graphe, s_debut)\u001b[0m\n\u001b[0;32m 9\u001b[0m \u001b[1;34m'predecesseurs'\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mliste\u001b[0m \u001b[0mdes\u001b[0m \u001b[0msommets\u001b[0m \u001b[0mprédécesseur\u001b[0m \u001b[0mde\u001b[0m \u001b[0mchaque\u001b[0m \u001b[0msommet\u001b[0m \u001b[0mrangés\u001b[0m \u001b[0mdans\u001b[0m \u001b[0ml\u001b[0m\u001b[1;34m'ordre d'\u001b[0m\u001b[0mexploration\u001b[0m\u001b[1;33m,\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 10\u001b[0m \"\"\"\n\u001b[1;32m---> 11\u001b[1;33m \u001b[1;32massert\u001b[0m \u001b[0mtype\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mGraphe\u001b[0m\u001b[1;33m)\u001b[0m \u001b[1;33m==\u001b[0m \u001b[0mlist\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;34m\"Graphe doit être de type liste\"\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 12\u001b[0m \u001b[1;32massert\u001b[0m \u001b[0mtype\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0ms_debut\u001b[0m\u001b[1;33m)\u001b[0m \u001b[1;33m==\u001b[0m \u001b[0mint\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;34m\" s_debut doit être un entier \"\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 13\u001b[0m \u001b[1;32massert\u001b[0m \u001b[0ms_debut\u001b[0m \u001b[1;32min\u001b[0m \u001b[1;33m[\u001b[0m\u001b[0mi\u001b[0m \u001b[1;32mfor\u001b[0m \u001b[0mi\u001b[0m \u001b[1;32min\u001b[0m \u001b[0mrange\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mlen\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mGraphe\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m]\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;34m\"s_debut doit être dans la plage d'indice de Graphe\"\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
|
||||
"\u001b[1;31mAssertionError\u001b[0m: Graphe doit être de type liste"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"def dijkstra(Graphe, s_debut):\n",
|
||||
" \"\"\" fonction: calculer les plus courts chemins à partir d'un sommet de départ vers chacun des autres sommets\n",
|
||||
" paramètres :\n",
|
||||
" 'Graphe', un graphe sous forme d'une liste de liste,\n",
|
||||
" 's_debut' un sommet de départ.\n",
|
||||
" renvoie:\n",
|
||||
" 'E_calcules', liste des sommets rangés dans l'ordre d'obtention des plus courts chemins,\n",
|
||||
" 'poids', liste des poids de chaque sommet rangés dans l'ordre d'obtention des plus courts chemins,\n",
|
||||
" 'predecesseurs', liste des sommets \"prédécesseur de chaque sommet\" rangés dans l'ordre d'obtention des plus courts chemins,\n",
|
||||
" \"\"\"\n",
|
||||
" assert type(Graphe) == list, \"Graphe doit être de type liste\"\n",
|
||||
" assert type(s_debut) == int, \" s_debut doit être un entier \"\n",
|
||||
" assert s_debut in [i for i in range(len(Graphe))], \"s_debut doit être dans la plage d'indice de Graphe\"\n",
|
||||
" \n",
|
||||
"dijkstra(\"A\",0)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 4,
|
||||
"metadata": {
|
||||
"scrolled": true
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"ename": "AssertionError",
|
||||
"evalue": " s_debut doit être un entier ",
|
||||
"output_type": "error",
|
||||
"traceback": [
|
||||
"\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
|
||||
"\u001b[1;31mAssertionError\u001b[0m Traceback (most recent call last)",
|
||||
"\u001b[1;32m<ipython-input-4-4268aa1c9000>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m\u001b[0m\n\u001b[0;32m 13\u001b[0m \u001b[1;32massert\u001b[0m \u001b[0ms_debut\u001b[0m \u001b[1;32min\u001b[0m \u001b[1;33m[\u001b[0m\u001b[0mi\u001b[0m \u001b[1;32mfor\u001b[0m \u001b[0mi\u001b[0m \u001b[1;32min\u001b[0m \u001b[0mrange\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mlen\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mGraphe\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m]\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;34m\"s_debut doit être dans la plage d'indice de Graphe\"\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 14\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m---> 15\u001b[1;33m \u001b[0mdijkstra\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mGraphe\u001b[0m\u001b[1;33m,\u001b[0m\u001b[1;34m\"A\"\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m",
|
||||
"\u001b[1;32m<ipython-input-4-4268aa1c9000>\u001b[0m in \u001b[0;36mdijkstra\u001b[1;34m(Graphe, s_debut)\u001b[0m\n\u001b[0;32m 10\u001b[0m \"\"\"\n\u001b[0;32m 11\u001b[0m \u001b[1;32massert\u001b[0m \u001b[0mtype\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mGraphe\u001b[0m\u001b[1;33m)\u001b[0m \u001b[1;33m==\u001b[0m \u001b[0mlist\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;34m\"Graphe doit être de type liste\"\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m---> 12\u001b[1;33m \u001b[1;32massert\u001b[0m \u001b[0mtype\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0ms_debut\u001b[0m\u001b[1;33m)\u001b[0m \u001b[1;33m==\u001b[0m \u001b[0mint\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;34m\" s_debut doit être un entier \"\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 13\u001b[0m \u001b[1;32massert\u001b[0m \u001b[0ms_debut\u001b[0m \u001b[1;32min\u001b[0m \u001b[1;33m[\u001b[0m\u001b[0mi\u001b[0m \u001b[1;32mfor\u001b[0m \u001b[0mi\u001b[0m \u001b[1;32min\u001b[0m \u001b[0mrange\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mlen\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mGraphe\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m]\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;34m\"s_debut doit être dans la plage d'indice de Graphe\"\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 14\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n",
|
||||
"\u001b[1;31mAssertionError\u001b[0m: s_debut doit être un entier "
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"def dijkstra(Graphe, s_debut):\n",
|
||||
" \"\"\" fonction: calculer les plus courts chemins à partir d'un sommet de départ vers chacun des autres sommets\n",
|
||||
" paramètres :\n",
|
||||
" 'Graphe', un graphe sous forme d'une liste de liste,\n",
|
||||
" 's_debut' un sommet de départ.\n",
|
||||
" renvoie:\n",
|
||||
" 'E_calcules', liste des sommets rangés dans l'ordre d'exploration,\n",
|
||||
" 'poids', liste des poids de chaque sommet rangés dans l'ordre d'exploration,\n",
|
||||
" 'predecesseurs', liste des sommets prédécesseur de chaque sommet rangés dans l'ordre d'exploration,\n",
|
||||
" \"\"\"\n",
|
||||
" assert type(Graphe) == list, \"Graphe doit être de type liste\"\n",
|
||||
" assert type(s_debut) == int, \" s_debut doit être un entier \"\n",
|
||||
" assert s_debut in [i for i in range(len(Graphe))], \"s_debut doit être dans la plage d'indice de Graphe\"\n",
|
||||
" \n",
|
||||
"dijkstra(Graphe,\"A\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 6,
|
||||
"metadata": {
|
||||
"scrolled": true
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"ename": "AssertionError",
|
||||
"evalue": "s_debut doit être dans la plage d'indice de Graphe",
|
||||
"output_type": "error",
|
||||
"traceback": [
|
||||
"\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
|
||||
"\u001b[1;31mAssertionError\u001b[0m Traceback (most recent call last)",
|
||||
"\u001b[1;32m<ipython-input-6-c0b62074927f>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m\u001b[0m\n\u001b[0;32m 13\u001b[0m \u001b[1;32massert\u001b[0m \u001b[0ms_debut\u001b[0m \u001b[1;32min\u001b[0m \u001b[1;33m[\u001b[0m\u001b[0mi\u001b[0m \u001b[1;32mfor\u001b[0m \u001b[0mi\u001b[0m \u001b[1;32min\u001b[0m \u001b[0mrange\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mlen\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mGraphe\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m]\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;34m\"s_debut doit être dans la plage d'indice de Graphe\"\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 14\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m---> 15\u001b[1;33m \u001b[0mdijkstra\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mGraphe\u001b[0m\u001b[1;33m,\u001b[0m\u001b[1;36m8\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m",
|
||||
"\u001b[1;32m<ipython-input-6-c0b62074927f>\u001b[0m in \u001b[0;36mdijkstra\u001b[1;34m(Graphe, s_debut)\u001b[0m\n\u001b[0;32m 11\u001b[0m \u001b[1;32massert\u001b[0m \u001b[0mtype\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mGraphe\u001b[0m\u001b[1;33m)\u001b[0m \u001b[1;33m==\u001b[0m \u001b[0mlist\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;34m\"Graphe doit être de type liste\"\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 12\u001b[0m \u001b[1;32massert\u001b[0m \u001b[0mtype\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0ms_debut\u001b[0m\u001b[1;33m)\u001b[0m \u001b[1;33m==\u001b[0m \u001b[0mint\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;34m\" s_debut doit être un entier \"\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m---> 13\u001b[1;33m \u001b[1;32massert\u001b[0m \u001b[0ms_debut\u001b[0m \u001b[1;32min\u001b[0m \u001b[1;33m[\u001b[0m\u001b[0mi\u001b[0m \u001b[1;32mfor\u001b[0m \u001b[0mi\u001b[0m \u001b[1;32min\u001b[0m \u001b[0mrange\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mlen\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mGraphe\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m]\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;34m\"s_debut doit être dans la plage d'indice de Graphe\"\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 14\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 15\u001b[0m \u001b[0mdijkstra\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mGraphe\u001b[0m\u001b[1;33m,\u001b[0m\u001b[1;36m8\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
|
||||
"\u001b[1;31mAssertionError\u001b[0m: s_debut doit être dans la plage d'indice de Graphe"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"def dijkstra(Graphe, s_debut):\n",
|
||||
" \"\"\" fonction: calculer les plus courts chemins à partir d'un sommet de départ vers chacun des autres sommets\n",
|
||||
" paramètres :\n",
|
||||
" 'Graphe', un graphe sous forme d'une liste de liste,\n",
|
||||
" 's_debut' un sommet de départ.\n",
|
||||
" renvoie:\n",
|
||||
" 'E_calcules', liste des sommets rangés dans l'ordre d'exploration,\n",
|
||||
" 'poids', liste des poids de chaque sommet rangés dans l'ordre d'exploration,\n",
|
||||
" 'predecesseurs', liste des sommets prédécesseur de chaque sommet rangés dans l'ordre d'exploration,\n",
|
||||
" \"\"\"\n",
|
||||
" assert type(Graphe) == list, \"Graphe doit être de type liste\"\n",
|
||||
" assert type(s_debut) == int, \" s_debut doit être un entier \"\n",
|
||||
" assert s_debut in [i for i in range(len(Graphe))], \"s_debut doit être dans la plage d'indice de Graphe\"\n",
|
||||
" \n",
|
||||
"dijkstra(Graphe,8)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 7,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def dijkstra(Graphe, s_debut):\n",
|
||||
" \"\"\" fonction: calculer les plus courts chemins à partir d'un sommet de départ vers chacun des autres sommets\n",
|
||||
" paramètres :\n",
|
||||
" 'Graphe', un graphe sous forme d'une liste de liste,\n",
|
||||
" 's_debut' un sommet de départ.\n",
|
||||
" renvoie:\n",
|
||||
" 'E_calcules', liste des sommets rangés dans l'ordre d'exploration,\n",
|
||||
" 'poids', liste des poids de chaque sommet rangés dans l'ordre d'exploration,\n",
|
||||
" 'predecesseurs', liste des sommets prédécesseur de chaque sommet rangés dans l'ordre d'exploration,\n",
|
||||
" \"\"\"\n",
|
||||
" assert type(Graphe) == list, \"Graphe doit être de type liste\"\n",
|
||||
" assert type(s_debut) == int, \" s_debut doit être un entier \"\n",
|
||||
" assert s_debut in [i for i in range(len(Graphe))], \"s_debut doit être dans la plage d'indice de Graphe\"\n",
|
||||
" \n",
|
||||
"dijkstra(Graphe,0)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"**Réponse Taf6-3 :** \n",
|
||||
" La quatrième vérification n'ayant pas créé d'AssertionError, elle valide à priori l'appel de la fonction 'dijkstra' avec les bons paramètres. La valeur '0' correspond au premier sommet du graphe dans la structure de liste de liste qui est la liste [0, 4, 2, 0, 0, 0, 0, 0 ] indiquant bien que le sommet A a un arc de distance 4 avec B et un arc avec C de distance 2."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Travail à faire 7: Initialisation de l'algorithme\n",
|
||||
" En reprenant les éléments de l'algorithme fournis au Taf2 et au Taf4, compléter les deux lignes manquantes de la partie \"Algo: initialisation\" du script, ci-dessous."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def dijkstra(Graphe, s_debut):\n",
|
||||
" #Algo: Initialisation\n",
|
||||
" infini=float(\"inf\") # définition d'une valeur infinie\n",
|
||||
" predecesseurs = [-1 for sommet in range(len(Graphe))] # initialisation des prédecesseurs à non parcouru (-1)\n",
|
||||
" predecesseurs[s_debut] = 0 # sauf le sommet de départ qui est le prédécesseur de lui-même\n",
|
||||
" ... # initialisation des poids à l'infini\n",
|
||||
" ... # sauf le sommet de départ de poids nul\n",
|
||||
" E_sommets = [i for i in range(len(Graphe))] # initialisation de l'ensemble des sommets du graphe\n",
|
||||
" E_calcules = [] # création de la liste des calculés, vide au départ"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"**Réponse Taf7 :** \n",
|
||||
" Les deux lignes manquantes sont de la même forme que les deux précédentes en remplaçant la variable 'predecesseurs' par la variable 'poids' de type liste, et la valeur '-1' par 'infini' ."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def dijkstra(Graphe, s_debut):\n",
|
||||
"#Algo: Initialisation\n",
|
||||
" infini=float(\"inf\") # définition d'une valeur infinie\n",
|
||||
" predecesseurs = [-1 for sommet in range(len(Graphe))] # initialisation des prédecesseurs à non parcouru (-1)\n",
|
||||
" predecesseurs[s_debut] = 0 # sauf le sommet de départ qui est le prédécesseur de lui-même\n",
|
||||
" poids = [infini for sommet in range(len(Graphe))] # initialisation des poids à l'infini\n",
|
||||
" poids[s_debut] = 0 # sauf le sommet de départ de poids nul\n",
|
||||
" E_sommets = [i for i in range(len(Graphe))] # initialisation de l'ensemble des sommets du graphe\n",
|
||||
" E_calcules = [] # création de la liste des calculés, vide au départ"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Travail à faire 8: Partie \"Mise à jour, poids et prédécesseur du plus proche voisin\" de l'algorithme\n",
|
||||
" En reprenant les éléments de l'algorithme fournis au Taf2 et au Taf4, complétez l'implémentation de la structure conditionnelle de la partie \"Algo: Mise à jour, poids et prédécesseur du plus proche voisin\" du script de la boucle principale, ci-dessous. \n",
|
||||
" Pour cela, répondez au QCM4 ci-dessous, puis placez la structure de code choisie dans le script de la boucle principale situé plus bas."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"$QCM4:$ Parmi les quatres extraits de script ci-dessous, un seul convient:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Réponse1\n",
|
||||
"if poids[s_voisin] > poids[s_mini] + Graphe[s_mini][s_voisin] and Graphe[s_mini][s_voisin] = 0:\n",
|
||||
" poids[s_voisin] = poids[s_mini] - Graphe[s_mini][s_voisin] \n",
|
||||
" predecesseurs[s_voisin] = s_mini "
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Réponse2\n",
|
||||
"if poids[s_voisin] < poids[s_mini] + Graphe[s_mini][s_voisin] and Graphe[s_mini][s_voisin] != 0:\n",
|
||||
" poids[s_voisin] = poids[s_mini] + Graphe[s_mini][s_voisin] \n",
|
||||
" predecesseurs[s_mini] = s_voisin "
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Réponse3\n",
|
||||
"if poids[s_voisin] > poids[s_mini] + Graphe[s_mini][s_voisin] and Graphe[s_mini][s_voisin] != 0:\n",
|
||||
" poids[s_voisin] = poids[s_mini] + Graphe[s_mini][s_voisin] \n",
|
||||
" predecesseurs[s_voisin] = s_mini "
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Réponse4\n",
|
||||
"if poids[s_voisin] > poids[s_mini] + Graphe[s_mini][s_voisin] or Graphe[s_mini][s_voisin] != 0:\n",
|
||||
" poids[s_mini] = poids[s_voisin] + Graphe[s_mini][s_voisin] \n",
|
||||
" predecesseurs[s_voisin] = s_mini "
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"#Algo: boucle principale\n",
|
||||
" while E_sommets: # Tant que tous les sommets ne sont pas définitivement calculés:\n",
|
||||
"\n",
|
||||
" # Algo: recherche d'un sommet de distance minimale\n",
|
||||
" poids_local = infini #\n",
|
||||
" s_mini = -1 # \n",
|
||||
" for s in E_sommets: # \n",
|
||||
" if poids[s] < poids_local: # \n",
|
||||
" poids_local = poids[s] # \n",
|
||||
" s_mini = s # \n",
|
||||
" #----------> Algo: fin de recherche d'un sommet de distance minimale\n",
|
||||
"\n",
|
||||
" E_sommets.remove(s_mini) # retrait du dernier sommet calculé à l'ensemble des sommets non définitivement calculés\n",
|
||||
" E_calcules.append(s_mini) # ajout du dernier sommet calculé à la liste des sommets définitivement calculés\n",
|
||||
" for s_voisin in E_sommets: # Pour chaque sommet voisin du dernier sommet calculé :\n",
|
||||
"\"\"\" Partie du script à compléter ci-dessous à partir du choix effectué au QCM4\"\"\"\n",
|
||||
" #Algo: mise à jour poids et prédécesseur du plus proche voisin\n",
|
||||
" ... # si le chemin st plus court et il y a existance d'un arc:\n",
|
||||
" ... # mise à jour du poids du plus proche voisin\n",
|
||||
" ... # mise à jour du prédécesseur du plus proche voisin\n",
|
||||
" #----------> fin de mise à jour du poids et du prédécesseur du plus proche voisin\n",
|
||||
"#---------->fin de boucle principale"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"**Réponse Taf8 :** \n",
|
||||
" La bonne réponse au QCM4 est la 3. D'ou le script complété ci-dessous."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"#Algo: boucle principale\n",
|
||||
" while E_sommets: # Tant que tous les sommets ne sont pas définitivement calculés:\n",
|
||||
"\n",
|
||||
" # Algo: recherche d'un sommet de distance minimale\n",
|
||||
" poids_local = infini #\n",
|
||||
" s_mini = -1 # \n",
|
||||
" for s in E_sommets: # \n",
|
||||
" if poids[s] < poids_local: # \n",
|
||||
" poids_local = poids[s] # \n",
|
||||
" s_mini = s # \n",
|
||||
" #----------> Algo: fin de recherche d'un sommet de distance minimale\n",
|
||||
"\n",
|
||||
" E_sommets.remove(s_mini) # retrait du dernier sommet calculé à l'ensemble des sommets non définitivement calculés\n",
|
||||
" E_calcules.append(s_mini) # ajout du dernier sommet calculé à la liste des sommets définitivement calculés\n",
|
||||
" for s_voisin in E_sommets: # Pour chaque sommet voisin du dernier sommet calculé :\n",
|
||||
"\"\"\" Partie du script complété ci-dessous à partir de la réponse 3 du QCM4\"\"\"\n",
|
||||
" #Algo: mise à jour poids et prédécesseur du plus proche voisin\n",
|
||||
" if poids[s_voisin] > poids[s_mini] + Graphe[s_mini][s_voisin] and Graphe[s_mini][s_voisin] !=0: # si le chemin est plus court et il y a existance d'un arc:\n",
|
||||
" poids[s_voisin] = poids[s_mini] + Graphe[s_mini][s_voisin] # mise à jour du poids du plus proche voisin\n",
|
||||
" predecesseurs[s_voisin] = s_mini # mise à jour du prédécesseur du plus proche voisin\n",
|
||||
" #----------> fin de mise à jour du poids et du prédécesseur du plus proche voisin\n",
|
||||
"#---------->fin de boucle principale"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Travail à faire 9: Partie \"Recherche d’un sommet de distance minimale \" de l'algorithme\n",
|
||||
" En reprenant les éléments de l'algorithme fournis au Taf2 et au Taf4, commenter chaque ligne du script proposé pour la partie \"Algo: Recherche d’un sommet de distance minimale\" de façon à bien expliquer la solution implémentée pour obtenir le sommet \"actuel\" de poids minimal ."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"#Algo: boucle principale\n",
|
||||
" while E_sommets: # Tant que tous les sommets ne sont pas définitivement calculés:\n",
|
||||
"\n",
|
||||
" # Algo: recherche d'un sommet de distance minimale\n",
|
||||
" poids_local = infini #\n",
|
||||
" s_mini = -1 # \n",
|
||||
" for s in E_sommets: # \n",
|
||||
" if poids[s] < poids_local: # \n",
|
||||
" poids_local = poids[s] # \n",
|
||||
" s_mini = s # \n",
|
||||
" #----------> Algo: fin de recherche d'un sommet de distance minimale\n",
|
||||
"\n",
|
||||
" E_sommets.remove(s_mini) # retrait du dernier sommet calculé à l'ensemble des sommets non définitivement calculés\n",
|
||||
" E_calcules.append(s_mini) # ajout du dernier sommet calculé à la liste des sommets définitivement calculés\n",
|
||||
" for s_voisin in E_sommets: # Pour chaque sommet voisin du dernier sommet calculé :\n",
|
||||
"\n",
|
||||
" #Algo: mise à jour poids et prédécesseur du plus proche voisin\n",
|
||||
" if ... and Graphe[s_mini][s_voisin] !=0: # si le chemin st plus court et il y a existance d'un arc:\n",
|
||||
" ... # mise à jour du poids du plus proche voisin\n",
|
||||
" ... # mise à jour du prédécesseur du plus proche voisin\n",
|
||||
" #----------> fin de mise à jour du poids et du prédécesseur du plus proche voisin\n",
|
||||
"#---------->fin de boucle principale"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"**Réponse Taf9 :** \n",
|
||||
" Les commentaires doivent mettre en évidence le rôle et l'initialisation des variables locales à cette partie du script, les conditions de la boucle fort et du test conditionnel ."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"#Algo: boucle principale\n",
|
||||
" while E_sommets: # Tant que tous les sommets ne sont pas définitivement calculés:\n",
|
||||
"\n",
|
||||
" # Algo: recherche d'un sommet de distance minimale\n",
|
||||
" poids_local = infini # initialisation du poids en cours de calcul à une valeur infinie\n",
|
||||
" s_mini = -1 # initialisation du sommet de poids minimal à 'non défini'\n",
|
||||
" for s in E_sommets: # Pour chaque sommet non définitivement calculés:\n",
|
||||
" if poids[s] < poids_local: # Si le poids du sommet actuel est inférieur au poids local:\n",
|
||||
" poids_local = poids[s] # le poids local est remplacé par la valeur (inférieure) du sommet actuel\n",
|
||||
" s_mini = s # le sommet de poids minimal est le sommet actuel\n",
|
||||
" #----------> Algo: fin de recherche d'un sommet de distance minimale\n",
|
||||
"\n",
|
||||
" E_sommets.remove(s_mini) # retrait du dernier sommet calculé à l'ensemble des sommets non définitivement calculés\n",
|
||||
" E_calcules.append(s_mini) # ajout du dernier sommet calculé à la liste des sommets définitivement calculés\n",
|
||||
" for s_voisin in E_sommets: # Pour chaque sommet voisin du dernier sommet calculé :\n",
|
||||
"\n",
|
||||
" #Algo: mise à jour poids et prédécesseur du plus proche voisin\n",
|
||||
" if poids[s_voisin] > poids[s_mini] + Graphe[s_mini][s_voisin] and Graphe[s_mini][s_voisin] !=0: # si le chemin est plus court et il y a existance d'un arc:\n",
|
||||
" poids[s_voisin] = poids[s_mini] + Graphe[s_mini][s_voisin] # mise à jour du poids du plus proche voisin\n",
|
||||
" predecesseurs[s_voisin] = s_mini # mise à jour du prédécesseur du plus proche voisin\n",
|
||||
" #----------> fin de mise à jour du poids et du prédécesseur du plus proche voisin\n",
|
||||
"#---------->fin de boucle principale"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Travail à faire 10: la fonction Dijkstra complète\n",
|
||||
" Reconstituer ci dessous le script complet de la fonction 'dijkstra' et en effectuer le test. Si le test n'est pas probant, recherchez d'éventuelles erreurs flagrantes, et, si nécessaire appelez l'enseignant pour débloquer la situation. "
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def dijkstra(Graphe, s_debut):\n",
|
||||
" \n",
|
||||
"\n",
|
||||
"# le sommet s_debut choisi ici est \"A\", d'indice 0 dans E_sommets\n",
|
||||
"dijkstra(Graphe,0)== ([0, 2, 1, 3, 7, 5, 4, 6], [0, 4, 2, 5, 9, 8, 9, 6], [0, 0, 0, 2, 1, 3, 3, 3])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"**Réponse Taf10 :** \n",
|
||||
" La présence de la documentation de la fonction et des assertions est un plus dans la reconstitution de la fonction.\n",
|
||||
"L'accompagnement au bon fonctionnement du programme final se fera au prix de malus suivant le niveau d'aide apportée et le type d'erreur corrigé."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 12,
|
||||
"metadata": {
|
||||
"scrolled": false
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"True"
|
||||
]
|
||||
},
|
||||
"execution_count": 12,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"def dijkstra(Graphe, s_debut):\n",
|
||||
" \"\"\" fonction: calculer les plus courts chemins à partir d'un sommet de départ vers chacun des autres sommets\n",
|
||||
" paramètres :\n",
|
||||
" 'Graphe', un graphe sous forme d'une liste de liste,\n",
|
||||
" 's_debut' un sommet de départ.\n",
|
||||
" renvoie:\n",
|
||||
" 'E_calcules', liste des sommets rangés dans l'ordre d'exploration,\n",
|
||||
" 'poids', liste des poids de chaque sommet rangés dans l'ordre d'exploration,\n",
|
||||
" 'predecesseurs', liste des sommets prédécesseur de chaque sommet rangés dans l'ordre d'exploration,\n",
|
||||
" \"\"\"\n",
|
||||
" assert type(Graphe) == list, \"Graphe doit être de type liste\"\n",
|
||||
" assert type(s_debut) == int, \" s_debut doit être un entier \"\n",
|
||||
" assert s_debut in [i for i in range(len(Graphe))], \"s_debut doit être dans la plage d'indice de Graphe\"\n",
|
||||
" \n",
|
||||
" #Algo: Initialisation\n",
|
||||
" infini=float(\"inf\") # définition d'une valeur infinie\n",
|
||||
" predecesseurs = [-1 for sommet in range(len(Graphe))] # initialisation des prédecesseurs à non parcouru (-1)\n",
|
||||
" predecesseurs[s_debut] = 0 # sauf le sommet de départ qui est le prédécesseur de lui-même\n",
|
||||
" poids = [infini for sommet in range(len(Graphe))] # initialisation des poids à l'infini\n",
|
||||
" poids[s_debut] = 0 # sauf le sommet de départ de poids nul\n",
|
||||
" E_sommets = [i for i in range(len(Graphe))] # initialisation de l'ensemble des sommets du graphe\n",
|
||||
" E_calcules = [] # création de la liste des calculés, vide au départ\n",
|
||||
" \n",
|
||||
" #Algo: boucle principale\n",
|
||||
" while E_sommets: # Tant que tous les sommets ne sont pas définitivement calculés:\n",
|
||||
"\n",
|
||||
" # Algo: recherche d'un sommet de distance minimale\n",
|
||||
" poids_local = infini # initialisation du poids en cours de calcul à une valeur infinie\n",
|
||||
" s_mini = -1 # initialisation du sommet de poids minimal à 'non défini'\n",
|
||||
" for s in E_sommets: # Pour chaque sommet non définitivement calculés:\n",
|
||||
" if poids[s] < poids_local: # Si le poids du sommet actuel est inférieur au poids local:\n",
|
||||
" poids_local = poids[s] # le poids local est remplacé par la valeur (inférieure) du sommet actuel\n",
|
||||
" s_mini = s # le sommet de poids minimal est le sommet actuel\n",
|
||||
" #----------> Algo: fin de recherche d'un sommet de distance minimale\n",
|
||||
"\n",
|
||||
" E_sommets.remove(s_mini) # retrait du dernier sommet calculé à l'ensemble des sommets non définitivement calculés\n",
|
||||
" E_calcules.append(s_mini) # ajout du dernier sommet calculé à la liste des sommets définitivement calculés\n",
|
||||
" for s_voisin in E_sommets: # Pour chaque sommet voisin du dernier sommet calculé :\n",
|
||||
"\n",
|
||||
" #Algo: mise à jour poids et prédécesseur du plus proche voisin\n",
|
||||
" if poids[s_voisin] > poids[s_mini] + Graphe[s_mini][s_voisin] and Graphe[s_mini][s_voisin] !=0: # si le chemin est plus court et il y a existance d'un arc:\n",
|
||||
" poids[s_voisin] = poids[s_mini] + Graphe[s_mini][s_voisin] # mise à jour du poids du plus proche voisin\n",
|
||||
" predecesseurs[s_voisin] = s_mini # mise à jour du prédécesseur du plus proche voisin\n",
|
||||
" #----------> fin de mise à jour du poids et du prédécesseur du plus proche voisin\n",
|
||||
" #---------->fin de boucle principale\n",
|
||||
" return E_calcules, poids, predecesseurs\n",
|
||||
" \n",
|
||||
" # le sommet s_debut choisi ici est \"A\", d'indice 0 dans E_sommets\n",
|
||||
"dijkstra(Graphe,0)== ([0, 2, 1, 3, 7, 5, 4, 6], [0, 4, 2, 5, 9, 8, 9, 6], [0, 0, 0, 2, 1, 3, 3, 3])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Optionnel, Travail à faire 11: Complexité expérimentale\n",
|
||||
" En utilisant le script complet de la fonction 'dijkstra', effectuer la mesure de temps d'éxécution dans plusieurs situations pour évaluer expérimentalement le coût de cet algorithme. "
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": "from random import randint\n\ndef dijkstra(Graphe, s_debut):\n \"\"\" fonction: calculer les plus courts chemins à partir d'un sommet de départ vers chacun des autres sommets\n paramètres :\n 'Graphe', un graphe sous forme d'une liste de liste,\n 's_debut' un sommet de départ.\n renvoie:\n 'E_calcules', liste des sommets rangés dans l'ordre d'exploration,\n 'poids', liste des poids de chaque sommet rangés dans l'ordre d'exploration,\n 'predecesseurs', liste des sommets prédécesseur de chaque sommet rangés dans l'ordre d'exploration,\n \"\"\"\n assert type(Graphe) == list, \"Graphe doit être de type liste\"\n assert type(s_debut) == int, \" s_debut doit être un entier \"\n assert s_debut in [i for i in range(len(Graphe))], \"s_debut doit être dans la plage d'indice de Graphe\"\n\n#Algo: Initialisation\n infini=float(\"inf\") # définition d'une valeur infinie\n predecesseurs = [-1 for sommet in range(len(Graphe))] # initialisation des prédecesseurs à non parcouru (-1)\n predecesseurs[s_debut] = 0 # sauf le sommet de départ qui est le prédécesseur de lui-même\n poids = [infini for sommet in range(len(Graphe))] # initialisation des poids à l'infini\n poids[s_debut] = 0 # sauf le sommet de départ de poids nul\n E_sommets = [i for i in range(len(Graphe))] # initialisation de l'ensemble des sommets du graphe\n E_calcules = [] # création de la liste des calculés, vide au départ\n#---------->fin d'initialisation\n#Algo: boucle principale\n while E_sommets: # Tant que tous les sommets ne sont pas définitivement calculés:\n\n # Algo: recherche d'un sommet de distance minimale\n poids_local = infini # initialisation du poids en cours de calcul à une valeur infinie\n s_mini = -1 # initialisation du sommet de poids minimal à 'non défini'\n for s in E_sommets: # Pour chaque sommet non définitivement calculés:\n if poids[s] < poids_local: # Si le poids du sommet actuel est inférieur au poids local:\n poids_local = poids[s] # le poids local est remplacé par la valeur (inférieure) du sommet actuel\n s_mini = s # le sommet de poids minimal est le sommet actuel\n\n #----------> Algo: fin de recherche d'un sommet de distance minimale\n\n E_sommets.remove(s_mini) # retrait du dernier sommet calculé à l'ensemble des sommets non définitivement calculés\n E_calcules.append(s_mini) # ajout du dernier sommet calculé à la liste des sommets définitivement calculés\n for s_voisin in E_sommets: # Pour chaque sommet voisin du dernier sommet calculé :\n\n #Algo: mise à jour poids et prédécesseur du plus proche voisin\n if poids[s_voisin] > poids[s_mini] + Graphe[s_mini][s_voisin] and Graphe[s_mini][s_voisin] !=0: # si le chemin est plus court et il y a existance d'un arc:\n poids[s_voisin] = poids[s_mini] + Graphe[s_mini][s_voisin] # mise à jour du poids du plus proche voisin\n predecesseurs[s_voisin] = s_mini # mise à jour du prédécesseur du plus proche voisin\n #----------> fin de mise à jour du poids et du prédécesseur du plus proche voisin\n#---------->fin de boucle principale\n\n return E_calcules, poids, predecesseurs\n\ndef matrice(i,j): # fonction qui génère une liste de 'i' liste de 'j' éléments \n # avec des valeurs entières aléatoires entre 0 et 20\n return [[randint(0,20) for q in range(0,j)] for p in range(0,i)]\n\nfor n_elt in range(6,20,1): # test de durée d'éxécution de la fonction 'dijkstra' pour des graphes de 6 à 20 sommets\n M = matrice(n_elt, n_elt)\n %timeit dijkstra(M,0)"
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Constatation et interrogation:\n",
|
||||
"\n",
|
||||
"En doublant le nombre $n$ de sommets du graphe, on constate approximativement un doublement du temps d'éxécution $T$ de cette implémentation de l'algorithme de Dijkstra. Pour les étudiantes ayant fait l'annexe optionnelle sur la complexité théorique : ceci n'est pas en accord avec le calcul théorique qui y est fait. Cherchez l'erreur (?)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 20,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
" 100 graphes de 6 sommets pour une durée de 0.015600100000000339 secondes.\n",
|
||||
" 100 graphes de 7 sommets pour une durée de 0.015600100000000339 secondes.\n",
|
||||
" 100 graphes de 8 sommets pour une durée de 0.031200200000000677 secondes.\n",
|
||||
" 100 graphes de 9 sommets pour une durée de 0.046800300000001016 secondes.\n",
|
||||
" 100 graphes de 10 sommets pour une durée de 0.031200200000000677 secondes.\n",
|
||||
" 100 graphes de 11 sommets pour une durée de 0.031200200000000677 secondes.\n",
|
||||
" 100 graphes de 12 sommets pour une durée de 0.046800300000001016 secondes.\n",
|
||||
" 100 graphes de 13 sommets pour une durée de 0.062400400000001355 secondes.\n",
|
||||
" 100 graphes de 14 sommets pour une durée de 0.046800300000001016 secondes.\n",
|
||||
" 100 graphes de 15 sommets pour une durée de 0.062400400000001355 secondes.\n",
|
||||
" 100 graphes de 16 sommets pour une durée de 0.0780005000000017 secondes.\n",
|
||||
" 100 graphes de 17 sommets pour une durée de 0.09360060000000203 secondes.\n",
|
||||
" 100 graphes de 18 sommets pour une durée de 0.09360060000000203 secondes.\n",
|
||||
" 100 graphes de 19 sommets pour une durée de 0.10920070000000237 secondes.\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"def matrice(i,j):\n",
|
||||
" return [[randint(0,20) for q in range(0,j)] for p in range(0,i)]\n",
|
||||
"\n",
|
||||
"#import timeit\n",
|
||||
"import time\n",
|
||||
"for n_elt in range(6,20,1):\n",
|
||||
" t1 = time.process_time()\n",
|
||||
" for i in range(100):\n",
|
||||
" M = matrice(n_elt, n_elt)\n",
|
||||
" dijkstra(M, 0)\n",
|
||||
" t2 = time.process_time()\n",
|
||||
" print(\" 100 graphes de \", n_elt,\" sommets pour une durée de \", t2-t1, \" secondes.\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.7.6"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 4
|
||||
}
|
||||
59
Exercices_1.md
Normal file
59
Exercices_1.md
Normal file
@@ -0,0 +1,59 @@
|
||||
# Exercices sur les Algorithmes des Graphes
|
||||
|
||||
Ces exercices sont destinés aux étudiants en sociologie quantitative pour découvrir les bases des algorithmes des graphes et leur application dans l'analyse des réseaux sociaux.
|
||||
|
||||
## Exercice 1 : Représentation d’un réseau social avec des graphes
|
||||
|
||||
**Objectif** : Initier les étudiants à la modélisation de réseaux à l’aide de graphes.
|
||||
|
||||
### Énoncé
|
||||
Imaginez un groupe de personnes et leurs relations d’amitié, où chaque personne est représentée par un nœud, et chaque relation d'amitié par une arête (lien) entre deux nœuds. Construisez un graphe représentant les relations suivantes :
|
||||
- Alice est amie avec Bob et Carla.
|
||||
- Bob est ami avec Alice, Carla et Dan.
|
||||
- Carla est amie avec Alice, Bob et Eve.
|
||||
- Dan est ami avec Bob et Eve.
|
||||
- Eve est amie avec Carla et Dan.
|
||||
|
||||
1. Représentez ce réseau sous forme de graphe en dessinant les nœuds et les arêtes.
|
||||
2. Utilisez une **matrice d’adjacence** pour représenter ce réseau (matrice où chaque ligne et colonne représente une personne et où les cases contiennent 1 si deux personnes sont amies, 0 sinon).
|
||||
3. Utilisez une **liste d’adjacence** pour représenter le même réseau (liste où chaque personne est associée à une liste de ses amis).
|
||||
|
||||
**Question** : Quelle représentation (matrice d’adjacence ou liste d’adjacence) serait, selon vous, la plus adaptée pour analyser des réseaux sociaux de grande taille ? Expliquez pourquoi.
|
||||
|
||||
---
|
||||
|
||||
## Exercice 2 : Recherche de chemins dans un graphe
|
||||
|
||||
**Objectif** : Introduire la recherche de chemins dans un graphe et sa pertinence dans la sociologie quantitative.
|
||||
|
||||
### Énoncé
|
||||
Dans le réseau social précédent, vous devez trouver le plus court chemin entre **Alice** et **Eve**, c’est-à-dire le nombre minimum d’étapes (arêtes) pour aller de l’une à l’autre.
|
||||
|
||||
1. Tracez le graphe du réseau.
|
||||
2. Utilisez une **recherche en largeur (BFS)** pour trouver le chemin le plus court entre **Alice** et **Eve**.
|
||||
3. Énumérez le chemin que vous avez trouvé et précisez le nombre d’étapes nécessaires.
|
||||
|
||||
**Question** : En sociologie, pourquoi serait-il pertinent de savoir qu'un individu peut rejoindre un autre en un nombre minimum d'étapes dans un réseau social ?
|
||||
|
||||
---
|
||||
|
||||
## Exercice 3 : Identification de communautés dans un réseau
|
||||
|
||||
**Objectif** : Faire réfléchir les étudiants à la détection de communautés dans un réseau.
|
||||
|
||||
### Énoncé
|
||||
Dans le réseau social présenté dans le premier exercice, imaginez que les relations ne sont pas toutes égales en force : certaines amitiés sont plus fortes que d’autres, ce qui peut influencer la formation de groupes ou de communautés. Une manière simple de détecter les communautés est de voir si certaines personnes ont plus d’amis en commun entre elles qu’avec les autres.
|
||||
|
||||
1. Identifiez les groupes de personnes qui ont plus de connexions entre elles qu'avec les autres (essayez d’isoler les sous-groupes formés de relations plus fortes).
|
||||
2. Proposez une méthode pour diviser le graphe en communautés et expliquez pourquoi certaines personnes sont plus susceptibles de former une communauté.
|
||||
|
||||
**Question** : En quoi cette notion de communauté peut-elle être utile pour analyser des phénomènes sociaux dans les études quantitatives ?
|
||||
|
||||
---
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
102
Exercices_2.md
Normal file
102
Exercices_2.md
Normal file
@@ -0,0 +1,102 @@
|
||||
## Exercices Socio et algorithmes des graphes
|
||||
|
||||
|
||||
|
||||
### Exercice 1 : Questions de cours
|
||||
|
||||
1.1 Qu'est-ce qu'un graphe non orienté ? Donnez un exemple d'application pratique.
|
||||
|
||||
1.2 Quelle est la différence entre une matrice d'adjacence et une liste d'adjacence ?
|
||||
|
||||
1.3 Dans quel cas serait-il préférable d'utiliser une liste d'adjacence au lieu d'une matrice d'adjacence ?
|
||||
|
||||
---
|
||||
|
||||
### Exercice 2. Matrices et Listes d'Adjacence
|
||||
|
||||
Considérez le graphe suivant :
|
||||
|
||||
- A est connecté à B et C
|
||||
- B est connecté à A, C et D
|
||||
- C est connecté à A, B et E
|
||||
- D est connecté à B et E
|
||||
- E est connecté à C et D
|
||||
|
||||
### 2.1 Matrice d'adjacence
|
||||
|
||||
Complétez la matrice d'adjacence pour ce graphe.
|
||||
|
||||
| | A | B | C | D | E |
|
||||
| ---- | ---- | ---- | ---- | ---- | ---- |
|
||||
| A | 0 | 1 | 1 | 0 | 0 |
|
||||
| B | 1 | 0 | 1 | 1 | 0 |
|
||||
| C | 1 | 1 | 0 | 0 | 1 |
|
||||
| D | 0 | 1 | 0 | 0 | 1 |
|
||||
| E | 0 | 0 | 1 | 1 | 0 |
|
||||
|
||||
### 2.2 Liste d'adjacence
|
||||
|
||||
Écrivez la liste d'adjacence correspondante.
|
||||
|
||||
---
|
||||
|
||||
### Exercice 3 : Degré des sommets et centralité
|
||||
**Objectif** : Comprendre l’importance des sommets dans un réseau social.
|
||||
|
||||
1. **Degré d’un sommet** :
|
||||
- Calculez le degré de chaque sommet dans le graphe donné (A, B, C, D, E).
|
||||
- Quel sommet a le plus haut degré ? Que peut-on en déduire dans un contexte sociologique ?
|
||||
|
||||
2. **Centralité de degré** :
|
||||
- Expliquez pourquoi un sommet ayant un haut degré peut être considéré comme central dans un réseau social.
|
||||
- Donnez un exemple d’application (ex. influence sur les réseaux sociaux, diffusion d’informations).
|
||||
|
||||
---
|
||||
|
||||
### Exercice 4 : Graphes pondérés et applications
|
||||
**Objectif** : Introduire la notion de poids sur les arêtes pour modéliser des relations plus complexes.
|
||||
|
||||
1. **Ajout de poids** :
|
||||
Imaginez que les relations d’amitié entre les individus du graphe ont des intensités différentes. Attribuez un poids à chaque arête pour refléter cette intensité :
|
||||
- A ↔ B : 2
|
||||
- A ↔ C : 3
|
||||
- B ↔ C : 1
|
||||
- B ↔ D : 4
|
||||
- C ↔ E : 2
|
||||
- D ↔ E : 1
|
||||
|
||||
2. **Distance minimale** :
|
||||
- Trouvez le chemin de coût minimal (somme des poids) entre le sommet A et le sommet E en utilisant l’algorithme de Dijkstra.
|
||||
- Expliquez l’intérêt de l’algorithme dans des contextes réels (ex. optimisation des trajets).
|
||||
|
||||
---
|
||||
|
||||
### Exercice 5 : Représentation alternative des graphes
|
||||
**Objectif** : Explorer une autre représentation des graphes : les graphes orientés.
|
||||
|
||||
1. **Transformation en graphe orienté** :
|
||||
- Transformez le graphe initial (non orienté) en un graphe orienté en choisissant une direction pour chaque arête.
|
||||
- Justifiez les directions choisies dans un contexte de réseaux sociaux (ex. influence d’une personne sur une autre).
|
||||
|
||||
2. **Conséquences sur les chemins** :
|
||||
- Quels chemins sont encore possibles entre A et E après l'orientation des arêtes ?
|
||||
- Expliquez en quoi les graphes orientés peuvent être utiles pour modéliser des hiérarchies ou des flux d’informations.
|
||||
|
||||
---
|
||||
|
||||
### Exercice 6 : Graphes bipartis
|
||||
**Objectif** : Introduire la notion de graphes bipartis et leur utilité.
|
||||
|
||||
1. **Construction d’un graphe biparti** :
|
||||
Imaginez que le graphe initial représente des personnes (A, B, C, D, E) et des événements auxquels elles participent (E1, E2).
|
||||
Voici les participations :
|
||||
- A participe à E1 et E2.
|
||||
- B participe à E1.
|
||||
- C participe à E2.
|
||||
- D participe à E2.
|
||||
- E participe à E1 et E2.
|
||||
|
||||
Représentez ce graphe biparti sous forme de matrice d’adjacence.
|
||||
|
||||
2. **Application sociologique** :
|
||||
Expliquez en quoi les graphes bipartis peuvent être utilisés pour analyser des réseaux sociaux ou des collaborations (ex. analyse de co-participation à des projets).
|
||||
75
README.md
75
README.md
@@ -21,40 +21,27 @@ Comprendre et utiliser les graphes pour analyser les **réseaux sociaux** et les
|
||||
| **Jupyter Notebook** | `pip install notebook` puis `jupyter notebook` |
|
||||
| **Google Colab** | [colab.research.google.com](https://colab.research.google.com) |
|
||||
|
||||
## Programme
|
||||
## Programme — 12 séances (24h)
|
||||
|
||||
### 1. Introduction contextuelle
|
||||
Aligné sur le programme officiel de la maquette (BCC5, EC "Algorithme des graphes"). Une séance tampon (11) est explicitement réservée pour absorber le retard selon le niveau réel de la promo plutôt que de préparer un second parcours en parallèle, notamment s'il y a du retard sur Python.
|
||||
|
||||
Les graphes en sciences sociales : modélisation des relations, réseaux sociaux, exemples Facebook/Twitter.
|
||||
| # | Séance | Statut |
|
||||
|---|--------|--------|
|
||||
| 1 | Définitions, typologie des graphes, prise en main de NetworkX | Fait |
|
||||
| 2 | Représentations : matrice et liste d'adjacence, matrice/liste d'incidence | Fait |
|
||||
| 3 | Mesures statistiques avancées (clustering, distribution des degrés...) | Fait |
|
||||
| 4 | Parcours BFS/DFS | Fait |
|
||||
| 5 | Composantes connexes + diffusion d'information (BFS) | Fait |
|
||||
| 6-7 | Graphes pondérés et algorithme de Dijkstra (allégé) | Fait |
|
||||
| 8 | Centralité (degré, proximité, intermédiarité) | Fait |
|
||||
| 9 | Coloration de graphes | Fait |
|
||||
| 10 | Page-rank et propagation d'étiquettes (label propagation) | Fait |
|
||||
| 11 | Séance tampon / rattrapage | réservée, pas de contenu fixe |
|
||||
| 12 | Révisions + partiel blanc | Fait |
|
||||
|
||||
### 2. Types de graphes
|
||||
### Applications sociologiques (fil rouge de tout le cours)
|
||||
|
||||
| Type | Description | Exemple |
|
||||
|------|-------------|---------|
|
||||
| **Non dirigé** | Relations réciproques | Amitié Facebook |
|
||||
| **Dirigé** | Relations orientées | Followers Twitter |
|
||||
| **Pondéré** | Force des relations | Fréquence d'interaction |
|
||||
| **Biparti** | Deux types d'entités | Individus ↔ Opinions |
|
||||
|
||||
### 3. Concepts fondamentaux
|
||||
|
||||
- Sommets et arêtes
|
||||
- Degré d'un sommet
|
||||
- Chemins et cycles
|
||||
- Connexité
|
||||
|
||||
### 4. Algorithmes
|
||||
|
||||
| Algorithme | Usage |
|
||||
|------------|-------|
|
||||
| **Parcours en largeur (BFS)** | Plus court chemin, exploration |
|
||||
| **Parcours en profondeur (DFS)** | Détection de cycles, composantes |
|
||||
| **Dijkstra** | Plus court chemin pondéré |
|
||||
| **Centralité** | Identifier les nœuds influents |
|
||||
|
||||
### 5. Applications sociologiques
|
||||
|
||||
- Analyse des réseaux sociaux
|
||||
- Analyse des réseaux sociaux, identification des individus influents
|
||||
- Détection de communautés
|
||||
- Étude des inégalités d'accès aux ressources
|
||||
- Diffusion des idées et innovations
|
||||
@@ -62,20 +49,30 @@ Les graphes en sciences sociales : modélisation des relations, réseaux sociaux
|
||||
## Structure du dépôt
|
||||
|
||||
```
|
||||
├── FONDAMENTAUX.md # Concepts de base
|
||||
├── Djikstra/ # Algorithme de Dijkstra
|
||||
├── Exercices/ # Exercices pratiques
|
||||
├── Corrige1/, 2/, 3/ # Corrigés
|
||||
├── copies/ # Travaux étudiants
|
||||
├── COURS.md # Chapitre 1 : introduction contextuelle
|
||||
├── FONDAMENTAUX.md # Chapitre 2 : concepts de base
|
||||
├── Seance_1.ipynb / Seance_1_TP.ipynb # Séance 1 : prise en main de NetworkX
|
||||
├── Seance_2_Representations.ipynb # Séance 2 : matrice/liste d'adjacence, incidence
|
||||
├── Seance_3_Mesures_Statistiques.ipynb # Séance 3 : mesures statistiques avancées
|
||||
├── Seance_4_BFS_DFS.ipynb # Séance 4 : parcours & distances (BFS/DFS)
|
||||
├── Seance_5_Composantes_Diffusion.ipynb (+ Corrigé) # Séance 5 : composantes connexes & diffusion
|
||||
├── Dijkstra/ # Séance 6 (recap + intuition), Séance 7 (implémentation), annexes optionnelles (preuve/complexité, version dictionnaire)
|
||||
├── Seance_8_Centralite.ipynb # Séance 8 : centralité
|
||||
├── Seance_9_Coloration.ipynb # Séance 9 : coloration de graphes
|
||||
├── Seance_10_PageRank_LabelPropagation.ipynb # Séance 10 : page-rank & label propagation
|
||||
├── Exercices_1.md, Exercices_2.md # Exercices complémentaires
|
||||
├── Seance_12_Partiel.md / Seance_12_Partiel_Corrige.md # Partiel blanc et son corrigé
|
||||
├── Exercices/, copies/ # Travaux et rendus d'étudiants
|
||||
└── assets/ # Images et schémas
|
||||
```
|
||||
|
||||
## Ressources
|
||||
|
||||
- 📖 [Introduction contextuelle (cours)](COURS.md)
|
||||
- 📄 [Cours complet (PDF)](Cours.pdf)
|
||||
- 📄 [Algorithmes des graphes (PDF)](coursAlgoGraphes.pdf)
|
||||
- 📝 [Exercices](Exercices.md)
|
||||
- [Introduction contextuelle (cours)](COURS.md)
|
||||
- [Concepts fondamentaux (cours)](FONDAMENTAUX.md)
|
||||
- [Exercices — série 1](Exercices_1.md)
|
||||
- [Exercices — série 2](Exercices_2.md)
|
||||
- [Partiel blanc](Seance_12_Partiel.md) / [Corrigé](Seance_12_Partiel_Corrige.md)
|
||||
|
||||
## Licence
|
||||
|
||||
|
||||
87
Seance_1.ipynb
Normal file
87
Seance_1.ipynb
Normal file
@@ -0,0 +1,87 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Séance 1 — Rappels & prise en main de NetworkX\n",
|
||||
"\n",
|
||||
"**Objectifs pédagogiques**\n",
|
||||
"- Réviser les définitions : nœuds, arêtes, graphes dirigés / non-dirigés / pondérés.\n",
|
||||
"- Prendre en main `networkx` pour créer un graphe et le visualiser.\n",
|
||||
"- Lire des mesures simples (degrés).\n",
|
||||
"\n",
|
||||
"**Consignes**\n",
|
||||
"- Exécutez cellule par cellule.\n",
|
||||
"- Commentez en 2-3 phrases ce que vous observez."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"metadata": {},
|
||||
"execution_count": null,
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Installation (si nécessaire) — peut être ignorée si networkx est déjà présent\n",
|
||||
"# %pip install networkx matplotlib\n",
|
||||
"\n",
|
||||
"import networkx as nx\n",
|
||||
"import matplotlib.pyplot as plt"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 1) Créer un premier graphe non dirigé"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"metadata": {},
|
||||
"execution_count": null,
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# TODO: Compléter la liste d'arêtes pour former votre mini-réseau social\n",
|
||||
"G = nx.Graph()\n",
|
||||
"G.add_edges_from([\n",
|
||||
" (\"Alice\", \"Bob\"),\n",
|
||||
" (\"Bob\", \"Claire\"),\n",
|
||||
" (\"Alice\", \"David\"),\n",
|
||||
" # (\"Claire\", \"David\"), # décommentez pour ajouter un lien\n",
|
||||
"])\n",
|
||||
"\n",
|
||||
"print(\"Nœuds :\", list(G.nodes()))\n",
|
||||
"print(\"Arêtes:\", list(G.edges()))\n",
|
||||
"print(\"Degrés:\", dict(G.degree()))\n",
|
||||
"\n",
|
||||
"plt.figure()\n",
|
||||
"nx.draw(G, with_labels=True)\n",
|
||||
"plt.show()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 2) Questions\n",
|
||||
"1. Quel nœud a le plus grand degré ?\n",
|
||||
"2. Que se passe-t-il si vous ajoutez l'arête ('Claire','David') ?\n",
|
||||
"3. Donnez une interprétation sociologique rapide (qui est au centre ?)."
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"name": "python",
|
||||
"pygments_lexer": "ipython3"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 4
|
||||
}
|
||||
190
Seance_10_PageRank_LabelPropagation.ipynb
Normal file
190
Seance_10_PageRank_LabelPropagation.ipynb
Normal file
@@ -0,0 +1,190 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# 🔍 Séance 10 — Page-rank et propagation d'étiquettes\n",
|
||||
"\n",
|
||||
"## Objectifs pédagogiques\n",
|
||||
"À la fin de cette séance, vous serez capables de :\n",
|
||||
"- comprendre l'intuition de l'algorithme du **page-rank** et le calculer avec `networkx` ;\n",
|
||||
"- comprendre le principe de la **propagation d'étiquettes** (label propagation) pour détecter des communautés ;\n",
|
||||
"- relier ces deux algorithmes au reste du cours (centralité, diffusion par BFS).\n",
|
||||
"\n",
|
||||
"## Introduction\n",
|
||||
"En Séance 8, on a vu trois façons de mesurer l'importance d'un individu : le degré, la proximité, l'intermédiarité. Le **page-rank** en propose une quatrième, plus subtile : être suivi par une personne elle-même très suivie compte plus que d'être suivi par dix personnes obscures. C'est l'algorithme qui a fait le succès de Google pour classer les pages web (une page est importante si des pages elles-mêmes importantes pointent vers elle).\n",
|
||||
"\n",
|
||||
"La **propagation d'étiquettes**, elle, répond à une tout autre question : comment détecter automatiquement des **communautés** dans un réseau, sans les connaître à l'avance ? On retrouve ici l'idée de diffusion vue en Séance 5, mais appliquée différemment : au lieu de propager une information depuis une seule source, chaque personne adopte peu à peu l'étiquette majoritaire de son entourage."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Partie 1 — Page-rank\n",
|
||||
"\n",
|
||||
"Le principe intuitif est celui du **\"surfeur aléatoire\"** : imaginez une personne qui clique aléatoirement sur les liens d'une page web à l'autre, indéfiniment. Le page-rank d'une page est la probabilité qu'à un instant donné, le surfeur se trouve sur cette page. Une page reçoit un score d'autant plus élevé qu'elle est pointée par des pages elles-mêmes très visitées.\n",
|
||||
"\n",
|
||||
"Transposé à un réseau social, cela devient : *qui suit qui* est une arête **orientée**, et le page-rank d'une personne dépend de la quantité **et de la qualité** de celles et ceux qui la suivent."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Un réseau de \"qui suit qui\""
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"metadata": {},
|
||||
"execution_count": null,
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import networkx as nx\n",
|
||||
"import matplotlib.pyplot as plt\n",
|
||||
"\n",
|
||||
"# Graphe orienté : une arête A -> B signifie \"A suit B\"\n",
|
||||
"G = nx.DiGraph()\n",
|
||||
"G.add_edges_from([\n",
|
||||
" (\"Bob\", \"Alice\"),\n",
|
||||
" (\"David\", \"Alice\"),\n",
|
||||
" (\"Emma\", \"Alice\"),\n",
|
||||
" (\"Alice\", \"Chloé\"), # Alice, très suivie, suit aussi Chloé\n",
|
||||
"])\n",
|
||||
"\n",
|
||||
"plt.figure()\n",
|
||||
"pos = nx.spring_layout(G, seed=0)\n",
|
||||
"nx.draw(G, pos, with_labels=True, node_color=\"lightblue\", node_size=1200, arrows=True, arrowsize=20)\n",
|
||||
"plt.show()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Calculer le page-rank\n",
|
||||
"\n",
|
||||
"NetworkX calcule directement le page-rank de chaque sommet.\n",
|
||||
"\n",
|
||||
"> Si vous obtenez `ModuleNotFoundError: No module named 'scipy'`, exécutez d'abord `%pip install scipy` dans une cellule, puis relancez."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"metadata": {},
|
||||
"execution_count": null,
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"pr = nx.pagerank(G)\n",
|
||||
"for personne, score in sorted(pr.items(), key=lambda x: -x[1]):\n",
|
||||
" print(f\"{personne} : {round(score, 3)}\")\n",
|
||||
"\n",
|
||||
"print(\"\\nPour comparaison, degré entrant (nombre de personnes qui suivent) :\")\n",
|
||||
"for personne, deg in sorted(G.in_degree(), key=lambda x: -x[1]):\n",
|
||||
" print(f\"{personne} : {deg}\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"**Interprétation :** Alice a 3 personnes qui la suivent (Bob, David, Emma), mais Chloé n'en a qu'**une seule** — Alice elle-même. Pourtant, le page-rank de Chloé dépasse celui d'Alice ! C'est exactement l'idée du \"surfeur aléatoire\" : être suivi par une seule personne très centrale (Alice, qui concentre le trafic entrant du réseau) rapporte plus que d'être suivi par plusieurs personnes elles-mêmes peu suivies.\n",
|
||||
"\n",
|
||||
"**Questions :**\n",
|
||||
"1. Le classement par page-rank est-il le même que le classement par simple degré entrant ? Que montre cette différence ?\n",
|
||||
"2. Si Bob, David et Emma se mettaient aussi à suivre Chloé (en plus d'Alice), que deviendrait le page-rank de Chloé ?\n",
|
||||
"3. En quoi ce principe rejoint-il la centralité d'intermédiarité vue en Séance 8 (une position stratégique compte plus qu'un grand nombre de liens) ?"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Partie 2 — Propagation d'étiquettes (détection de communautés)\n",
|
||||
"\n",
|
||||
"**Principe :** au départ, chaque personne a sa propre étiquette (son nom). À chaque étape, chaque personne adopte l'étiquette **la plus fréquente parmi ses voisin·es**. On répète jusqu'à ce que plus personne ne change d'étiquette. Les personnes qui finissent avec la même étiquette forment une **communauté** détectée automatiquement — sans qu'on ait eu besoin de la définir à l'avance.\n",
|
||||
"\n",
|
||||
"On reprend le réseau à 7 personnes des Séances 5 et 8."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"metadata": {},
|
||||
"execution_count": null,
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from networkx.algorithms.community import asyn_lpa_communities\n",
|
||||
"\n",
|
||||
"H = nx.Graph()\n",
|
||||
"H.add_edges_from([\n",
|
||||
" ('Alice', 'Bob'), ('Alice', 'Emma'),\n",
|
||||
" ('Bob', 'Chloé'), ('Bob', 'Félix'),\n",
|
||||
" ('Chloé', 'David'), ('Chloé', 'Gaël')\n",
|
||||
"])\n",
|
||||
"\n",
|
||||
"communautes = list(asyn_lpa_communities(H, seed=3))\n",
|
||||
"for i, communaute in enumerate(communautes):\n",
|
||||
" print(f\"Communauté {i+1} : {sorted(communaute)}\")\n",
|
||||
"\n",
|
||||
"# Visualisation avec une couleur par communauté détectée\n",
|
||||
"palette = [\"lightblue\", \"lightgreen\", \"lightcoral\", \"khaki\"]\n",
|
||||
"couleur_par_personne = {}\n",
|
||||
"for i, communaute in enumerate(communautes):\n",
|
||||
" for personne in communaute:\n",
|
||||
" couleur_par_personne[personne] = palette[i]\n",
|
||||
"\n",
|
||||
"plt.figure()\n",
|
||||
"pos = nx.spring_layout(H, seed=0)\n",
|
||||
"nx.draw(H, pos, with_labels=True, node_size=1200,\n",
|
||||
" node_color=[couleur_par_personne[n] for n in H.nodes()])\n",
|
||||
"plt.show()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"**Interprétation :** l'algorithme retrouve automatiquement deux groupes : {Alice, Bob, Emma, Félix} d'un côté, {Chloé, David, Gaël} de l'autre — sans qu'on lui ait jamais dit où étaient les frontières. Remarquez que **Bob** se retrouve dans le groupe d'Alice alors qu'en Séance 8 il avait la plus forte centralité d'intermédiarité avec Chloé : la propagation d'étiquettes doit \"trancher\", même pour les personnes-ponts qui, dans la réalité, appartiennent un peu aux deux groupes à la fois.\n",
|
||||
"\n",
|
||||
"**Questions :**\n",
|
||||
"1. Réexécutez la cellule en changeant le `seed`. Le résultat change-t-il ? Que cela vous apprend-il sur la fiabilité de cet algorithme sur de petits réseaux ?\n",
|
||||
"2. Bob et Chloé, identifié·es comme \"ponts\" en Séance 5-8, se retrouvent chacun·e dans un groupe différent ici. Est-ce cohérent avec leur rôle de pont ? Pourquoi la méthode a-t-elle du mal à les classer ?\n",
|
||||
"3. Sur un réseau de plusieurs milliers de personnes, pourquoi la détection automatique de communautés est-elle plus utile qu'une lecture \"à l'œil\" du graphe ?"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Synthèse\n",
|
||||
"\n",
|
||||
"| Algorithme | Question posée | Ce qu'il donne |\n",
|
||||
"|:------------|:------------------|:------------------|\n",
|
||||
"| Page-rank | Qui est important, en tenant compte de la qualité de ses connexions ? | Un score d'importance par personne |\n",
|
||||
"| Propagation d'étiquettes | Quels sous-groupes se dessinent naturellement dans ce réseau ? | Une partition du réseau en communautés |\n",
|
||||
"\n",
|
||||
"## Pour clore le cours\n",
|
||||
"Ce cours a parcouru tout le programme officiel de graphes : définitions et représentations (Séances 1-3), parcours et distances (Séances 4-5), graphes pondérés (Séances 6-7), et enfin ces mesures plus avancées (Séances 8-10). Un même fil rouge — un petit réseau social imaginaire — a servi de terrain d'expérimentation du début à la fin.\n",
|
||||
"\n",
|
||||
"**Questions de synthèse générale :**\n",
|
||||
"1. Parmi tous les algorithmes vus ce semestre (BFS, DFS, Dijkstra, centralité, coloration, page-rank, label propagation), lequel vous semble le plus directement utile pour un mémoire ou une enquête de sociologie ?\n",
|
||||
"2. Choisissez un phénomène social qui vous intéresse (mobilisation collective, diffusion d'une mode, ségrégation résidentielle...). Quel(s) outil(s) de ce cours utiliseriez-vous pour l'étudier, et pourquoi ?"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"name": "python",
|
||||
"pygments_lexer": "ipython3"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 4
|
||||
}
|
||||
88
Seance_12_Partiel.md
Normal file
88
Seance_12_Partiel.md
Normal file
@@ -0,0 +1,88 @@
|
||||
# Partiel L3 Sociologie Quantitative - Analyse des Graphes
|
||||
|
||||
## Introduction et Rappels
|
||||
|
||||
Ce partiel a pour objectif d'évaluer vos compétences sur les notions fondamentales des graphes et leurs applications sociologiques.
|
||||
Vous devez répondre aux questions en suivant les consignes.
|
||||
|
||||
### Définitions utiles :
|
||||
- **Graphe** : Un ensemble de sommets reliés par des arêtes (ou arcs pour les graphes orientés).
|
||||
- **Chemin** : Une suite de sommets connectés par des arêtes.
|
||||
- **Cycle** : Un chemin qui revient au sommet de départ.
|
||||
- **Matrice d'adjacence** : Représentation tabulaire des connexions entre sommets.
|
||||
- **Liste d'adjacence** : Représentation où chaque sommet est associé à ses voisins.
|
||||
- **BFS (parcours en largeur)** : Algorithme pour explorer un graphe niveau par niveau.
|
||||
- **Algorithme de Dijkstra** : Méthode pour trouver le chemin de coût minimal dans un graphe pondéré.
|
||||
|
||||
---
|
||||
|
||||
## Exercice 1 : Représentation des Graphes (7 points)
|
||||
|
||||
### Étude de cas :
|
||||
Un réseau universitaire connecte les départements suivants :
|
||||
- A (Sociologie) est connecté à B (Psychologie) et C (Histoire).
|
||||
- B est connecté à A, C, et D (Philosophie).
|
||||
- C est connecté à A, B, et E (Anthropologie).
|
||||
- D est connecté à B et E.
|
||||
- E est connecté à C et D.
|
||||
|
||||
### Consignes :
|
||||
1. Représentez ce graphe sous forme de **matrice d'adjacence**. (3 points)
|
||||
2. Représentez ce graphe sous forme de **liste d'adjacence**. (3 points)
|
||||
3. Justifiez si la liste ou la matrice est la plus adaptée pour analyser un réseau universitaire. (1 point)
|
||||
|
||||
---
|
||||
|
||||
## Exercice 2 : Recherche de Chemins (8 points)
|
||||
|
||||
### Étude de cas :
|
||||
Dans un graphe représentant un réseau social :
|
||||
- A (Alice) est amie avec B (Bob) et C (Carla).
|
||||
- B est ami avec A, C, et D (Dan).
|
||||
- C est amie avec A, B, et E (Eve).
|
||||
- D est ami avec B et E.
|
||||
- E est amie avec C et D.
|
||||
|
||||
### Consignes :
|
||||
1. Effectuez un **parcours en largeur (BFS)** depuis Alice. Listez les sommets dans l’ordre de leur visite. (4 points)
|
||||
2. Trouvez le chemin le plus court entre Alice et Eve en utilisant BFS. (2 points)
|
||||
3. Expliquez en quoi ce type d’analyse peut être utile pour étudier des dynamiques sociales. (2 points)
|
||||
|
||||
---
|
||||
|
||||
## Exercice 3 : Optimisation Logistique avec Dijkstra (10 points)
|
||||
|
||||
### Étude de cas :
|
||||
Une entreprise veut optimiser ses livraisons entre ses dépôts. Le graphe pondéré suivant représente les coûts (en minutes) entre les dépôts :
|
||||
- A ↔ B : 10
|
||||
- A ↔ C : 15
|
||||
- B ↔ C : 5
|
||||
- B ↔ D : 20
|
||||
- C ↔ D : 10
|
||||
- C ↔ E : 30
|
||||
- D ↔ E : 10
|
||||
|
||||
### Consignes :
|
||||
1. Représentez ce graphe sous forme de **matrice d'adjacence pondérée**. (3 points)
|
||||
2. Utilisez l’**algorithme de Dijkstra** pour trouver le chemin de coût minimal entre A et E. Montrez toutes les étapes. (5 points)
|
||||
3. Expliquez en quoi cet algorithme est utile dans des problématiques logistiques. (2 points)
|
||||
|
||||
---
|
||||
|
||||
## Exercice 4 : Analyse des Communautés (10 points)
|
||||
|
||||
### Étude de cas :
|
||||
On analyse la participation des étudiants à des projets collaboratifs :
|
||||
- A participe aux projets P1 et P2.
|
||||
- B participe aux projets P1 et P3.
|
||||
- C participe aux projets P2 et P4.
|
||||
- D participe aux projets P3 et P4.
|
||||
- E participe aux projets P1 et P4.
|
||||
|
||||
### Consignes :
|
||||
1. Représentez ce graphe biparti sous forme de **matrice d'adjacence**. (3 points)
|
||||
2. Identifiez les groupes d’étudiants qui collaborent régulièrement en analysant leurs participations communes. (4 points)
|
||||
3. Expliquez comment cette analyse peut aider à améliorer la collaboration dans un cadre universitaire. (3 points)
|
||||
|
||||
---
|
||||
|
||||
136
Seance_12_Partiel_Corrige.md
Normal file
136
Seance_12_Partiel_Corrige.md
Normal file
@@ -0,0 +1,136 @@
|
||||
# Corrigé
|
||||
|
||||
---
|
||||
|
||||
# Exercice 1 – Lecture d’un réseau
|
||||
|
||||
## 1. Sous-groupes naturels
|
||||
- Sous-groupe principal : {Alice, Bob, Chloé, David, Félix, Gaël}
|
||||
- Sous-groupe annexe : {Alice, Emma}
|
||||
|
||||
## 2. Sommets centraux
|
||||
Les plus centraux sont Bob, Chloé ou David (2 liens chacun + position intermédiaire).
|
||||
On peut aussi accepter Alice (3 liens mais position en bordure d’un des sous-groupes).
|
||||
|
||||
## 3. Ponts
|
||||
- Bob relie Alice/Emma au reste.
|
||||
- Chloé relie Bob/Alice au segment David/Félix/Gaël.
|
||||
- Félix relie David/Chloé à Gaël.
|
||||
|
||||
Tous ces sommets sont argumentables comme « ponts ».
|
||||
|
||||
## 4. Individus isolés ou périphériques
|
||||
- Pas d’isolés.
|
||||
- Individus périphériques : Emma et Gaël (degré 1).
|
||||
|
||||
---
|
||||
|
||||
# Exercice 2 – Matrice d’adjacence
|
||||
|
||||
Ordre des sommets : Alice, Bob, Chloé, David, Félix, Gaël, Emma.
|
||||
|
||||
## 1. Matrice complète
|
||||
|
||||
| | A | B | C | D | F | G | E |
|
||||
| ----- | ---- | ---- | ---- | ---- | ---- | ---- | ---- |
|
||||
| Alice | 0 | 1 | 0 | 0 | 0 | 0 | 1 |
|
||||
| Bob | 1 | 0 | 1 | 0 | 0 | 0 | 0 |
|
||||
| Chloé | 0 | 1 | 0 | 1 | 0 | 0 | 0 |
|
||||
| David | 0 | 0 | 1 | 0 | 1 | 0 | 0 |
|
||||
| Félix | 0 | 0 | 0 | 1 | 0 | 1 | 0 |
|
||||
| Gaël | 0 | 0 | 0 | 0 | 1 | 0 | 0 |
|
||||
| Emma | 1 | 0 | 0 | 0 | 0 | 0 | 0 |
|
||||
|
||||
## 2. Matrice symétrique ?
|
||||
Oui : relations **non orientées**, donc si i connaît j, alors j connaît i.
|
||||
|
||||
## 3. Signification d’un 1 en (i, j)
|
||||
Il existe une relation entre les individus i et j.
|
||||
|
||||
## 4. Degrés
|
||||
- Alice : 2
|
||||
- Bob : 2
|
||||
- Chloé : 2
|
||||
- David : 2
|
||||
- Félix : 2
|
||||
- Gaël : 1
|
||||
- Emma : 1
|
||||
|
||||
---
|
||||
|
||||
# Exercice 3 – BFS depuis Alice
|
||||
|
||||
## 1. Niveaux BFS (3 pts)
|
||||
|
||||
- Niveau 0 : Alice
|
||||
- Niveau 1 : Bob, Emma
|
||||
- Niveau 2 : Chloé
|
||||
- Niveau 3 : David
|
||||
- Niveau 4 : Félix
|
||||
- Niveau 5 : Gaël
|
||||
|
||||
Ordre BFS possible :
|
||||
**Alice → Bob → Emma → Chloé → David → Félix → Gaël**
|
||||
|
||||
## 2. Premiers / Derniers
|
||||
Premiers : Bob, Emma
|
||||
Dernier : Gaël
|
||||
|
||||
## 3. Sens sociologique de distance 2
|
||||
Deux relais nécessaires → relation indirecte, moins immédiate, moins proche.
|
||||
|
||||
## 4. Ce que montre le BFS
|
||||
La dynamique de **diffusion** dans un réseau.
|
||||
|
||||
---
|
||||
|
||||
# Exercice 4 – DFS depuis Alice
|
||||
|
||||
## 1. Ordre DFS possible
|
||||
Exemple :
|
||||
**Alice → Bob → Chloé → David → Félix → Gaël → Emma**
|
||||
|
||||
Toute variante respectant la logique « profondeur d’abord » est correcte.
|
||||
|
||||
## 2. Arbre DFS
|
||||
Forme linéaire typique :
|
||||
Alice → Bob → Chloé → David → Félix → Gaël
|
||||
Retour arrière → Emma
|
||||
|
||||
## 3. Comparaison BFS / DFS
|
||||
- BFS : exploration horizontale, diffusion collective.
|
||||
- DFS : exploration verticale, suit une piste jusqu’au bout.
|
||||
- Les arbres sont très différents dans leur forme.
|
||||
|
||||
## 4. Exemple sociologique pertinent
|
||||
Exemples acceptés :
|
||||
- chaîne d’influence
|
||||
- filiation (arbre généalogique)
|
||||
- enquête suivant une piste unique
|
||||
- propagation ciblée
|
||||
|
||||
---
|
||||
|
||||
# Exercice 5 – Composantes connexes avec Hugo
|
||||
|
||||
## 1. Graphe connexe ?
|
||||
Oui : Hugo est relié à Félix → tout est atteignable.
|
||||
|
||||
## 2. Nombre de composantes
|
||||
1 seule composante.
|
||||
|
||||
(Si Hugo n’avait aucun lien : 2 composantes.)
|
||||
|
||||
## 3. Interprétation sociologique
|
||||
Hugo est **mal intégré**, dépend d’un seul contact (Félix).
|
||||
Risque d’isolement informationnel ou social.
|
||||
|
||||
## 4. Arêtes pour mieux intégrer Hugo
|
||||
Exemples :
|
||||
- Hugo — David
|
||||
- Hugo — Bob
|
||||
- Hugo — Chloé
|
||||
|
||||
Justification : Augmente son degré, le relie à plusieurs sous-groupes, diminue sa dépendance à Félix.
|
||||
|
||||
---
|
||||
177
Seance_1_TP.ipynb
Normal file
177
Seance_1_TP.ipynb
Normal file
@@ -0,0 +1,177 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": "# Séance 1 TP — Rappels & prise en main de NetworkX\n\n## Objectifs pédagogiques\n- Réviser les définitions : nœuds, arêtes, graphes dirigés / non-dirigés / pondérés.\n- Prendre en main `networkx` pour créer un graphe et le visualiser.\n- Explorer des propriétés simples (degré, nombre de sommets et d'arêtes).\n- Introduire graphes dirigés et pondérés.\n\n## Contexte sociologique\nLes graphes servent à modéliser les relations sociales :\n- Graphe non dirigé : relations symétriques (amitié, collaboration).\n- Graphe dirigé : relations asymétriques (qui suit qui sur Twitter).\n- Graphe pondéré : intensité des relations (fréquence de contact)."
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# %pip install networkx matplotlib\n",
|
||||
"\n",
|
||||
"import networkx as nx\n",
|
||||
"import matplotlib.pyplot as plt"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Exercice 1 : Créer un graphe non dirigé"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Construisons un mini-réseau social\n",
|
||||
"G = nx.Graph()\n",
|
||||
"G.add_edges_from([\n",
|
||||
" (\"Alice\", \"Bob\"),\n",
|
||||
" (\"Bob\", \"Claire\"),\n",
|
||||
" (\"Alice\", \"David\"),\n",
|
||||
" (\"Claire\", \"David\"),\n",
|
||||
"])\n",
|
||||
"\n",
|
||||
"print(\"Nœuds :\", list(G.nodes()))\n",
|
||||
"print(\"Arêtes:\", list(G.edges()))\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": [
|
||||
"**Questions :**\n",
|
||||
"1. Qui a le plus de voisins / voisines (ami-e-s) ?\n",
|
||||
"2. Quelles sont les relations réciproques ?\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Exercice 2 : Explorer les propriétés du graphe"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"print(\"Nombre de sommets :\", G.number_of_nodes())\n",
|
||||
"print(\"Nombre d'arêtes :\", G.number_of_edges())\n",
|
||||
"print(\"Degrés de chaque sommet :\", dict(G.degree()))\n",
|
||||
"print(\"Degré moyen :\", sum(dict(G.degree()).values())/G.number_of_nodes())"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"**Question :** Quel est le degré moyen et comment l’interpréter sociologiquement ?"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Exercice 3 : Graphe dirigé"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"DG = nx.DiGraph()\n",
|
||||
"DG.add_edges_from([\n",
|
||||
" (\"Alice\", \"Bob\"),\n",
|
||||
" (\"Bob\", \"Claire\"),\n",
|
||||
" (\"Claire\", \"Alice\")\n",
|
||||
"])\n",
|
||||
"\n",
|
||||
"plt.figure()\n",
|
||||
"nx.draw(DG, with_labels=True, node_color=\"lightgreen\", node_size=1000, arrows=True)\n",
|
||||
"plt.show()\n",
|
||||
"\n",
|
||||
"print(\"Degré sortant :\", dict(DG.out_degree()))\n",
|
||||
"print(\"Degré entrant :\", dict(DG.in_degree()))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"**Questions :**\n",
|
||||
"1. Quelle différence avec le graphe non dirigé ?\n",
|
||||
"2. Que représentent les degrés entrants et sortants sociologiquement (ex : followers) ?"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Exercice 4 : Graphe pondéré"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"WG = nx.Graph()\n",
|
||||
"WG.add_edge(\"Alice\", \"Bob\", weight=5) # forte relation\n",
|
||||
"WG.add_edge(\"Alice\", \"Claire\", weight=1) # relation faible\n",
|
||||
"\n",
|
||||
"print(\"Arêtes avec poids :\", WG.edges(data=True))\n",
|
||||
"\n",
|
||||
"# Dessin avec poids visibles\n",
|
||||
"pos = nx.spring_layout(WG)\n",
|
||||
"nx.draw(WG, pos, with_labels=True, node_color=\"lightcoral\", node_size=1000)\n",
|
||||
"labels = nx.get_edge_attributes(WG, \"weight\")\n",
|
||||
"nx.draw_networkx_edge_labels(WG, pos, edge_labels=labels)\n",
|
||||
"plt.show()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"**Questions :**\n",
|
||||
"1. Comment interpréter le poids d’une relation en sociologie ?\n",
|
||||
"2. Donnez un exemple concret (fréquence de discussions, intensité d’une amitié)."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": []
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"name": "python",
|
||||
"version": "3.x"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
419
Seance_2_Representations.ipynb
Normal file
419
Seance_2_Representations.ipynb
Normal file
@@ -0,0 +1,419 @@
|
||||
{
|
||||
"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
|
||||
}
|
||||
214
Seance_3_Mesures_Statistiques.ipynb
Normal file
214
Seance_3_Mesures_Statistiques.ipynb
Normal file
@@ -0,0 +1,214 @@
|
||||
{
|
||||
"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
|
||||
}
|
||||
268
Seance_4_BFS_DFS.ipynb
Normal file
268
Seance_4_BFS_DFS.ipynb
Normal file
@@ -0,0 +1,268 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": "# Séance 4 — Parcours & distances (BFS / DFS)\n\n## Objectifs pédagogiques\n- Comprendre les notions de **chemin**, **distance** et ** composantes connexes**.\n- Distinguer **BFS** (parcours en largeur) et **DFS** (parcours en profondeur).\n- Utiliser `networkx` pour calculer des plus courts chemins dans un graphe **non pondéré**.\n- Relier les distances à des interprétations sociologiques (proximité, relais, \"six degrés\").\n\n## Rappels\n- Dans un **graphe non pondéré**, un **plus court chemin** est le chemin avec le **moins d'arêtes**.\n- **BFS** explore par **couches** (distance croissante) et permet de trouver un plus court chemin.\n- **DFS** explore en **profondeur** (par branches) ; utile pour détecter des cycles/composantes mais **ne garantit pas** un plus court chemin."
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# %pip install networkx matplotlib\n",
|
||||
"\n",
|
||||
"import networkx as nx\n",
|
||||
"import matplotlib.pyplot as plt\n",
|
||||
"from collections import deque"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 1) Graphe de départ : petit réseau social"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Vous pouvez modifier/étendre ce graphe\n",
|
||||
"G = nx.Graph()\n",
|
||||
"G.add_edges_from([\n",
|
||||
" (\"Alice\",\"Bob\"),\n",
|
||||
" (\"Bob\",\"Claire\"),\n",
|
||||
" (\"Claire\",\"Emma\"),\n",
|
||||
" (\"Alice\",\"David\"),\n",
|
||||
" (\"David\",\"Emma\"),\n",
|
||||
" (\"Emma\",\"Fanny\"),\n",
|
||||
" (\"Bob\",\"Gaston\"),\n",
|
||||
"])\n",
|
||||
"\n",
|
||||
"# Visualisation simple\n",
|
||||
"plt.figure()\n",
|
||||
"nx.draw(G, with_labels=True)\n",
|
||||
"plt.show()\n",
|
||||
"\n",
|
||||
"print(\"Nœuds :\", list(G.nodes()))\n",
|
||||
"print(\"Arêtes:\", list(G.edges()))\n",
|
||||
"print(\"Composantes connexes:\", nx.number_connected_components(G))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"**Question :** Y a-t-il un unique groupe connecté, ou plusieurs ? Impact sociologique : des individus isolés auront des distances infinies vers le reste."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 2) Plus courts chemins et distances avec NetworkX"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"source, cible = \"Alice\", \"Fanny\"\n",
|
||||
"chemin = nx.shortest_path(G, source=source, target=cible) # plus court chemin (non pondéré)\n",
|
||||
"dist = nx.shortest_path_length(G, source=source, target=cible)\n",
|
||||
"print(f\"Plus court chemin de {source} à {cible} :\", chemin)\n",
|
||||
"print(f\"Distance (nombre d'arêtes) :\", dist)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"**Exercice 2.1** : testez 3 autres paires (par ex. `('Gaston','Fanny')`, `('Alice','Claire')`, `('David','Bob')`).\n",
|
||||
" **Exercice 2.2** : si vous supprimez l'arête `('David','Emma')`, que devient la distance `Alice→Fanny` ?"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 3) Implémenter un BFS pédagogique (pour comprendre l'ordre d'exploration)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def bfs_layers(graph, start):\n",
|
||||
" \"\"\"Retourne l'ordre de visite et les couches de distance (dict: noeud -> distance)\n",
|
||||
" BFS avec file d'attente (deque).\n",
|
||||
" \"\"\"\n",
|
||||
" visited = set([start])\n",
|
||||
" dist = {start: 0}\n",
|
||||
" order = []\n",
|
||||
" q = deque([start])\n",
|
||||
"\n",
|
||||
" while q:\n",
|
||||
" u = q.popleft()\n",
|
||||
" order.append(u)\n",
|
||||
" for v in graph.neighbors(u):\n",
|
||||
" if v not in visited:\n",
|
||||
" visited.add(v)\n",
|
||||
" dist[v] = dist[u] + 1\n",
|
||||
" q.append(v)\n",
|
||||
" return order, dist\n",
|
||||
"\n",
|
||||
"order, dist = bfs_layers(G, \"Alice\")\n",
|
||||
"print(\"Ordre BFS depuis Alice:\", order)\n",
|
||||
"print(\"Distances depuis Alice:\", dist)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"**Exercice 3.1** : Comparez les **distances** renvoyées par `bfs_layers` et `nx.shortest_path_length` pour 3 nœuds.\n",
|
||||
" **Exercice 3.2** : Quel nœud est le plus proche (distance minimale) d'**Alice** ? Le plus lointain ?"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 4) Implémenter un DFS (ordre d'exploration par branches)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def dfs_order(graph, start, visited=None, order=None):\n",
|
||||
" if visited is None: visited = set()\n",
|
||||
" if order is None: order = []\n",
|
||||
" visited.add(start)\n",
|
||||
" order.append(start)\n",
|
||||
" for v in graph.neighbors(start):\n",
|
||||
" if v not in visited:\n",
|
||||
" dfs_order(graph, v, visited, order)\n",
|
||||
" return order\n",
|
||||
"\n",
|
||||
"print(\"Ordre DFS depuis Alice:\", dfs_order(G, \"Alice\"))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"**Exercice 4.1** : Pourquoi **l'ordre DFS** diffère-t-il souvent de l'ordre BFS ?\n",
|
||||
" **Exercice 4.2** : DFS trouve-t-il un plus court chemin ? Expliquez avec un contre-exemple si possible."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 5) Distances globales : diamètre & distance moyenne (si connexe)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"if nx.is_connected(G):\n",
|
||||
" # Diamètre = plus longue distance entre deux nœuds\n",
|
||||
" d = nx.diameter(G)\n",
|
||||
" # Distance moyenne (longueur moyenne des plus courts chemins)\n",
|
||||
" apl = nx.average_shortest_path_length(G)\n",
|
||||
" print(\"Diamètre:\", d)\n",
|
||||
" print(\"Distance moyenne:\", apl)\n",
|
||||
"else:\n",
|
||||
" print(\"Le graphe n'est pas connexe : diamètre et distance moyenne ne sont pas définis globalement.\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"**Exercice 5.1** : Ajoutez/supprimez une arête et observez l'effet sur la distance moyenne. Interprétez sociologiquement (ex. apparition d'un **pont** qui réduit les distances)."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 6) Étude guidée : \"six degrés de séparation\" (mini-expérience)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Construisons un graphe en anneau + quelques liens longue portée pour réduire drastiquement les distances\n",
|
||||
"H = nx.cycle_graph(12) # 12 individus en cercle (0..11)\n",
|
||||
"H = nx.relabel_nodes(H, {i: f\"P{i}\" for i in range(12)})\n",
|
||||
"H.add_edge(\"P0\",\"P6\") # raccourci longue portée\n",
|
||||
"H.add_edge(\"P3\",\"P9\") # autre raccourci\n",
|
||||
"\n",
|
||||
"plt.figure()\n",
|
||||
"nx.draw(H, with_labels=True)\n",
|
||||
"plt.show()\n",
|
||||
"\n",
|
||||
"print(\"Connexe:\", nx.is_connected(H))\n",
|
||||
"print(\"Distance moyenne:\", nx.average_shortest_path_length(H))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"**Questions**\n",
|
||||
"1. Que se passe-t-il si on **retire** les liens longue portée ?\n",
|
||||
"2. Pourquoi quelques liens inter-groupes peuvent-ils **réduire fortement** les distances ?\n",
|
||||
"3. Donnez une interprétation en termes de diffusion d'une rumeur/idée."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 7) Synthèse écrite\n",
|
||||
"- Comparez BFS et DFS sur votre graphe.\n",
|
||||
"- Donnez 2 exemples de distances pertinentes en sociologie (ex. accès à l'information, recrutement).\n",
|
||||
"- Proposez une **hypothèse** : l'ajout d'un lien entre deux groupes réduira X ; comment le tester ?"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "75d566ac",
|
||||
"metadata": {},
|
||||
"source": []
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"name": "python",
|
||||
"version": "3.x"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
263
Seance_5_Composantes_Diffusion.ipynb
Normal file
263
Seance_5_Composantes_Diffusion.ipynb
Normal file
@@ -0,0 +1,263 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Séance 5 — Diffusion d'une information (BFS) et composantes connexes\n",
|
||||
"\n",
|
||||
"## Objectifs\n",
|
||||
"- Comprendre intuitivement ce qu'est un **parcours en largeur** (BFS) et relier ce concept à la **diffusion d'une information, d'une rumeur ou d'une idée** dans un réseau social.\n",
|
||||
"- Mesurer des **distances sociales** et comparer l'effet du point de départ de la diffusion.\n",
|
||||
"- Comparer intuitivement le BFS au **parcours en profondeur (DFS)**.\n",
|
||||
"- Identifier les **composantes connexes** d'un réseau et interpréter la position d'un individu isolé."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 1. Introduction sociologique : une rumeur se propage\n",
|
||||
"Imaginez qu'une personne — appelons-la **Alice** — partage une nouvelle dans son groupe d'ami·es.\n",
|
||||
"Chacun·e la répète à ses proches, et ainsi de suite.\n",
|
||||
"\n",
|
||||
"L'information se diffuse **par cercles successifs** : d'abord les ami·es direct·es d'Alice, puis les ami·es des ami·es, etc.\n",
|
||||
"\n",
|
||||
"C'est exactement ce que fait un **parcours en largeur** : explorer un réseau *niveau par niveau*.\n",
|
||||
"\n",
|
||||
"→ En sociologie des réseaux, cela correspond à la **distance sociale** entre individus."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 2. Exemple manuel : propagation d'une information\n",
|
||||
"Considérons ce réseau à 7 personnes :\n",
|
||||
"\n",
|
||||
"```\n",
|
||||
"Alice — Bob — Chloé — David\n",
|
||||
" │ │ │\n",
|
||||
" Emma Félix Gaël\n",
|
||||
"```\n",
|
||||
"\n",
|
||||
"Supposons qu'Alice commence à diffuser une nouvelle.\n",
|
||||
"\n",
|
||||
"**Étapes :**\n",
|
||||
"1. Niveau 0 : Alice\n",
|
||||
"2. Niveau 1 : les personnes directement connectées à Alice → {Bob, Emma}\n",
|
||||
"3. Niveau 2 : les ami·es de Bob (hors Alice) → {Chloé, Félix}\n",
|
||||
"4. Niveau 3 : les ami·es de Chloé → {David, Gaël}\n",
|
||||
"\n",
|
||||
"Chaque *niveau* correspond à une **distance sociale** par rapport à la source."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 3. Construire le réseau avec Python"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"metadata": {},
|
||||
"execution_count": null,
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import networkx as nx\n",
|
||||
"import matplotlib.pyplot as plt\n",
|
||||
"\n",
|
||||
"# Création du graphe\n",
|
||||
"G = nx.Graph()\n",
|
||||
"G.add_edges_from([\n",
|
||||
" ('Alice', 'Bob'), ('Alice', 'Emma'),\n",
|
||||
" ('Bob', 'Chloé'), ('Bob', 'Félix'),\n",
|
||||
" ('Chloé', 'David'), ('Chloé', 'Gaël')\n",
|
||||
"])\n",
|
||||
"\n",
|
||||
"pos = nx.spring_layout(G, seed=0)\n",
|
||||
"plt.figure(figsize=(6,4))\n",
|
||||
"nx.draw(G, pos, with_labels=True, node_color='lightblue', node_size=1000)\n",
|
||||
"plt.title('Réseau social — Diffusion de la nouvelle')\n",
|
||||
"plt.show()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 4. Explorer le réseau en largeur depuis Alice\n",
|
||||
"On peut utiliser la fonction `nx.bfs_tree()` pour construire un arbre de parcours à partir d'un point de départ."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"metadata": {},
|
||||
"execution_count": null,
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"source = 'Alice'\n",
|
||||
"T = nx.bfs_tree(G, source=source)\n",
|
||||
"\n",
|
||||
"plt.figure(figsize=(6,4))\n",
|
||||
"nx.draw(T, with_labels=True, node_color='lightgreen', node_size=1000)\n",
|
||||
"plt.title(f'Arbre BFS à partir de {source}')\n",
|
||||
"plt.show()\n",
|
||||
"\n",
|
||||
"print('Ordre du parcours BFS :')\n",
|
||||
"print(list(nx.bfs_edges(G, source)))\n",
|
||||
"\n",
|
||||
"distances = nx.single_source_shortest_path_length(G, source)\n",
|
||||
"print('\\nDistances sociales depuis Alice :')\n",
|
||||
"for k, v in distances.items():\n",
|
||||
" print(f'{k} : {v}')"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Interprétation sociologique\n",
|
||||
"- Les **valeurs faibles** indiquent des individus **proches** d'Alice (accès direct à l'information).\n",
|
||||
"- Les **valeurs élevées** indiquent des individus **périphériques** : ils n'apprennent la nouvelle que tardivement.\n",
|
||||
"\n",
|
||||
"→ Cela permet d'analyser la **vitesse de diffusion** ou la **position sociale** dans le réseau.\n",
|
||||
"\n",
|
||||
"**Questions :**\n",
|
||||
"1. Dans quel ordre les personnes reçoivent-elles l'information ?\n",
|
||||
"2. Quel est le rôle d'Alice dans cette diffusion ?\n",
|
||||
"3. Quelle est la distance maximale observée ? Que signifie-t-elle socialement ?"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 5. Comparaison intuitive : BFS vs DFS\n",
|
||||
"Pour bien comprendre la logique du BFS (revu en Séance 4), comparons-le brièvement à l'autre stratégie : le **DFS**.\n",
|
||||
"\n",
|
||||
"| Stratégie | Métaphore | Manière d'explorer | Exemple |\n",
|
||||
"|:-----------|:-----------|:------------------|:---------|\n",
|
||||
"| BFS (largeur) | diffusion sociale | explore les cercles autour de la source | bouche-à-oreille, message collectif |\n",
|
||||
"| DFS (profondeur) | exploration ciblée | suit un chemin jusqu'au bout avant de revenir | enquête, filiation, exploration hiérarchique |\n",
|
||||
"\n",
|
||||
"Le BFS **propagera rapidement une information**, tandis que le DFS **creusera une piste en profondeur**."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 6. Le point de départ change-t-il tout ? Recommençons depuis Chloé\n",
|
||||
"Recommençons la diffusion mais cette fois **à partir de Chloé**. Observez les différences de structure et de distances."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"metadata": {},
|
||||
"execution_count": null,
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"source2 = 'Chloé'\n",
|
||||
"T2 = nx.bfs_tree(G, source=source2)\n",
|
||||
"\n",
|
||||
"plt.figure(figsize=(6,4))\n",
|
||||
"nx.draw(T2, with_labels=True, node_color='lightcoral', node_size=1000)\n",
|
||||
"plt.title(f'Arbre BFS à partir de {source2}')\n",
|
||||
"plt.show()\n",
|
||||
"\n",
|
||||
"print('Distances sociales depuis Chloé :')\n",
|
||||
"for k, v in nx.single_source_shortest_path_length(G, source2).items():\n",
|
||||
" print(f'{k} : {v}')"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"**Questions :**\n",
|
||||
"1. Quelles différences remarquez-vous par rapport au départ depuis Alice ?\n",
|
||||
"2. Quelle personne semble jouer un rôle « de pont » dans la diffusion (voir la notion de coefficient de clustering, Séance 3) ?\n",
|
||||
"3. Que peut-on dire de la position de Chloé dans le réseau ?"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 7. Composantes connexes : un acteur isolé\n",
|
||||
"\n",
|
||||
"Jusqu'ici, tout le monde pouvait recevoir l'information de proche en proche. Mais que se passe-t-il si une personne n'est reliée à **personne** ?\n",
|
||||
"\n",
|
||||
"Ajoutons une nouvelle personne, **Hugo**, qui n'est reliée à personne dans le réseau."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"metadata": {},
|
||||
"execution_count": null,
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Ajout d'une personne isolée\n",
|
||||
"G.add_node('Hugo')\n",
|
||||
"\n",
|
||||
"plt.figure(figsize=(6,4))\n",
|
||||
"pos = nx.spring_layout(G, seed=1)\n",
|
||||
"nx.draw(G, pos, with_labels=True, node_color='lightblue', node_size=1000)\n",
|
||||
"plt.title('Réseau social avec une personne isolée (Hugo)')\n",
|
||||
"plt.show()\n",
|
||||
"\n",
|
||||
"print('Composantes connexes du graphe :')\n",
|
||||
"for i, comp in enumerate(nx.connected_components(G)):\n",
|
||||
" print(f'Composante {i+1} : {comp}')"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"**Interprétation :** une **composante connexe** est un sous-ensemble du réseau où tout le monde peut s'atteindre par une suite de liens, mais qui n'a aucun lien vers le reste du réseau. Un individu isolé forme, à lui seul, sa propre composante.\n",
|
||||
"\n",
|
||||
"**Questions :**\n",
|
||||
"1. Hugo peut-il recevoir l'information ? Pourquoi ?\n",
|
||||
"2. Que représente un acteur isolé dans un réseau social réel ?\n",
|
||||
"3. Comment cette notion d'isolement peut-elle s'interpréter en sociologie (accès à l'information, exclusion sociale, marginalité) ?"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 8. Pour aller plus loin (facultatif)\n",
|
||||
"1. Ajoutez de nouvelles relations pour rendre le graphe plus connecté.\n",
|
||||
"2. Essayez de trouver une configuration où tout le monde est relié en deux étapes maximum.\n",
|
||||
"3. Reliez Hugo au reste du réseau : combien de composantes reste-t-il ?"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 9. Synthèse\n",
|
||||
"- Le **BFS** explore un réseau **par cercles successifs** à partir d'une source, et permet de mesurer la **distance sociale**.\n",
|
||||
"- Le point de départ de la diffusion **change radicalement** l'ordre et la vitesse à laquelle l'information circule.\n",
|
||||
"- Le **DFS** suit une logique d'exploration **en profondeur**, utile pour détecter des sous-groupes plutôt que pour simuler une diffusion rapide.\n",
|
||||
"- Une **composante connexe** est un sous-groupe où tout le monde est atteignable, mais coupé du reste. Un individu isolé ne reçoit jamais l'information : cela peut traduire une exclusion ou une marginalisation sociale."
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"name": "python",
|
||||
"pygments_lexer": "ipython3"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 4
|
||||
}
|
||||
105
Seance_5_Composantes_Diffusion_Corrige.md
Normal file
105
Seance_5_Composantes_Diffusion_Corrige.md
Normal file
@@ -0,0 +1,105 @@
|
||||
# Séance 5 — Diffusion d'une information (BFS) et composantes connexes
|
||||
### Fiche enseignant
|
||||
|
||||
## Objectifs pédagogiques
|
||||
- Introduire intuitivement le concept de **parcours en largeur (BFS)** à travers la métaphore de la **diffusion d'une information** dans un réseau social.
|
||||
- Faire le lien entre la **distance dans le graphe** et la **distance sociale** entre individus.
|
||||
- Montrer que le **point de départ** d'une diffusion change radicalement son déroulement.
|
||||
- Introduire la notion de **composantes connexes** à travers un acteur isolé.
|
||||
- Comparer BFS et DFS sans mathématiques : deux logiques d'exploration du réseau.
|
||||
|
||||
---
|
||||
|
||||
## Prérequis
|
||||
- Avoir compris les notions de **graphe**, **sommets**, **arêtes** (Séance 1).
|
||||
- Avoir manipulé BFS et DFS une première fois (Séance 4).
|
||||
- Avoir manipulé un graphe simple avec `networkx` (ajout de sommets, arêtes, visualisation).
|
||||
|
||||
---
|
||||
|
||||
## Durée estimée
|
||||
**2h**, séance unique (fusion des anciennes séances 6 et 6bis — leur contenu se recoupait largement).
|
||||
|
||||
---
|
||||
|
||||
## Matériel / outils
|
||||
- Notebook `Seance_5_Composantes_Diffusion.ipynb`
|
||||
- Python avec `networkx` et `matplotlib`
|
||||
|
||||
---
|
||||
|
||||
## Déroulé pédagogique
|
||||
|
||||
### 1. Introduction sociologique
|
||||
**But :** ancrer le BFS dans une situation concrète.
|
||||
|
||||
Expliquer :
|
||||
> Une information (rumeur, message, idée) se propage dans un réseau social.
|
||||
> Certain·es la reçoivent directement, d'autres plus tard.
|
||||
> Le BFS modélise cette propagation *par cercles de proximité*.
|
||||
|
||||
**Point clé à dire** : « En BFS, on explore *niveau par niveau* — comme une onde sociale. »
|
||||
|
||||
### 2. Exemple manuel : propagation pas à pas
|
||||
Simulation à la main sur le petit graphe (Alice — Bob/Emma — Chloé/Félix — David/Gaël) : faire remplir le tableau des niveaux avant de passer à Python.
|
||||
|
||||
### 3-4. Construction du réseau et parcours BFS depuis Alice
|
||||
**Résultat attendu (ordre BFS) :**
|
||||
```
|
||||
[('Alice', 'Bob'), ('Alice', 'Emma'), ('Bob', 'Chloé'), ('Bob', 'Félix'), ('Chloé', 'David'), ('Chloé', 'Gaël')]
|
||||
```
|
||||
**Distances depuis Alice :** Alice 0, Bob/Emma 1, Chloé/Félix 2, David/Gaël 3.
|
||||
|
||||
**À dire :** la distance = nombre d'étapes pour atteindre une personne ; plus elle est grande, plus la personne est éloignée du centre du réseau. Lien sociologique : proximité sociale, vitesse d'accès à l'information.
|
||||
|
||||
### 5. Comparaison BFS / DFS (à l'oral)
|
||||
| Stratégie | Métaphore | Manière d'explorer | Exemple |
|
||||
|:-----------|:-----------|:------------------|:---------|
|
||||
| BFS (largeur) | diffusion sociale | explore les cercles autour de la source | bouche-à-oreille, message collectif |
|
||||
| DFS (profondeur) | exploration ciblée | suit un chemin jusqu'au bout avant de revenir | enquête, filiation, exploration hiérarchique |
|
||||
|
||||
### 6. Recommencer depuis Chloé
|
||||
**Résultat attendu (exemple) :**
|
||||
```
|
||||
[('Chloé', 'Bob'), ('Chloé', 'David'), ('Chloé', 'Gaël'), ('Bob', 'Alice'), ('Bob', 'Félix'), ('Alice', 'Emma')]
|
||||
```
|
||||
**Analyse :** Chloé devient un nouveau centre de diffusion, la profondeur du graphe diminue (elle est plus "au milieu"). Chloé relie deux sous-groupes : c'est une **personne-pont** — à relier au coefficient de clustering vu en Séance 3 (un pont a un coefficient de clustering faible : ses ami·es ne se connaissent pas entre eux).
|
||||
|
||||
**Point clé à dire :** le point de départ de la diffusion influence sa vitesse et sa portée — une info ne se propage pas pareil selon qui la lance en premier.
|
||||
|
||||
### 7. Composantes connexes : Hugo l'isolé
|
||||
Après `G.add_node('Hugo')` :
|
||||
```
|
||||
Composante 1 : {'Alice', 'Bob', 'Chloé', 'Emma', 'Félix', 'David', 'Gaël'}
|
||||
Composante 2 : {'Hugo'}
|
||||
```
|
||||
**À dire :** le graphe n'est plus connexe : deux composantes distinctes. Hugo ne reçoit aucune information.
|
||||
|
||||
**Lien sociologique :** un acteur isolé symbolise une **exclusion sociale** : il n'est intégré à aucun cercle relationnel.
|
||||
|
||||
### 8. Discussion finale
|
||||
**Questions à lancer :**
|
||||
- Que se passe-t-il si un individu n'est relié à personne ?
|
||||
- Si plusieurs personnes lancent l'info en même temps ?
|
||||
- Qui apprend la nouvelle le plus vite ? Pourquoi ?
|
||||
|
||||
**Lien avec la sociologie des réseaux :** notions de **centralité**, **vitesse de diffusion**, **position périphérique**, **effet d'isolation**.
|
||||
|
||||
---
|
||||
|
||||
## Interprétations sociologiques clés à souligner
|
||||
| Concept Python | Traduction sociologique |
|
||||
|:----------------|:------------------------|
|
||||
| Distance | Proximité ou éloignement social |
|
||||
| Composante connexe | Groupe / sous-communauté |
|
||||
| Sommet central | Individu influent, pivot relationnel |
|
||||
| Sommet isolé | Exclusion, absence de lien social |
|
||||
| BFS | Diffusion collective, propagation rapide |
|
||||
|
||||
## Notes pédagogiques
|
||||
- Insister sur le **lien entre structure et diffusion** : le BFS donne une première intuition du "pouvoir de connexion" dans un réseau social.
|
||||
- Éviter tout formalisme algorithmique (pas de file, pas de pseudo-code) — le DFS/BFS formels ont déjà été codés en Séance 4, ici on reste sur l'interprétation sociologique.
|
||||
- Valoriser les représentations graphiques et le vocabulaire sociologique (proximité, cercles, diffusion, influence).
|
||||
|
||||
**Transition possible :**
|
||||
Cette séance consolide le BFS (Séance 4) et introduit les composantes connexes. Le DFS ayant déjà été vu et comparé au BFS dès la Séance 2/4, ces notions sont désormais réunies : elles préparent directement les étudiantes à l'évaluation (partiel blanc), qui reprend BFS, DFS et composantes connexes dans un contexte de lecture de réseau. La séance suivante (6-7) introduit les **graphes pondérés** et l'algorithme de **Dijkstra**.
|
||||
174
Seance_8_Centralite.ipynb
Normal file
174
Seance_8_Centralite.ipynb
Normal file
@@ -0,0 +1,174 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# 🎯 Séance 8 — Centralité : qui est influent dans un réseau ?\n",
|
||||
"\n",
|
||||
"## Objectifs pédagogiques\n",
|
||||
"À la fin de cette séance, vous serez capables de :\n",
|
||||
"- calculer et interpréter la **centralité de degré**, de **proximité** et d'**intermédiarité** ;\n",
|
||||
"- comprendre que ces trois mesures répondent à des questions différentes, et peuvent désigner des individus différents comme \"centraux\" ;\n",
|
||||
"- relier ces mesures à des situations sociologiques concrètes (popularité, accès rapide à l'information, position de pont).\n",
|
||||
"\n",
|
||||
"## Introduction\n",
|
||||
"En Séance 3, on a vu qu'un réseau peut être **inégalitaire** : certain·es individus concentrent beaucoup plus de connexions que d'autres. Mais \"avoir beaucoup d'ami·es\" n'est qu'une façon d'être important dans un réseau — il en existe d'autres. Cette séance formalise plusieurs notions de **centralité**, c'est-à-dire plusieurs manières de répondre à la question : *qui compte le plus dans ce réseau, et pourquoi ?*"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### On reprend le réseau de la Séance 5\n",
|
||||
"Le même réseau à 7 personnes que celui utilisé pour la diffusion en BFS :\n",
|
||||
"\n",
|
||||
"```\n",
|
||||
"Alice — Bob — Chloé — David\n",
|
||||
" │ │ │\n",
|
||||
" Emma Félix Gaël\n",
|
||||
"```"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"metadata": {},
|
||||
"execution_count": null,
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import networkx as nx\n",
|
||||
"import matplotlib.pyplot as plt\n",
|
||||
"\n",
|
||||
"G = nx.Graph()\n",
|
||||
"G.add_edges_from([\n",
|
||||
" ('Alice', 'Bob'), ('Alice', 'Emma'),\n",
|
||||
" ('Bob', 'Chloé'), ('Bob', 'Félix'),\n",
|
||||
" ('Chloé', 'David'), ('Chloé', 'Gaël')\n",
|
||||
"])\n",
|
||||
"\n",
|
||||
"plt.figure(figsize=(6,4))\n",
|
||||
"pos = nx.spring_layout(G, seed=0)\n",
|
||||
"nx.draw(G, pos, with_labels=True, node_color='lightblue', node_size=1000)\n",
|
||||
"plt.show()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Partie 1 — Centralité de degré\n",
|
||||
"\n",
|
||||
"C'est la mesure la plus simple, déjà rencontrée : **combien de connexions directes** une personne a-t-elle ? NetworkX la normalise entre 0 et 1 en divisant le degré par le nombre maximal de connexions possibles ($n - 1$).\n",
|
||||
"\n",
|
||||
"$$C_{degré}(i) = \\frac{\\deg(i)}{n - 1}$$\n",
|
||||
"\n",
|
||||
"**Interprétation sociologique :** popularité directe, nombre de contacts immédiats."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"metadata": {},
|
||||
"execution_count": null,
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"centralite_degre = nx.degree_centrality(G)\n",
|
||||
"for personne, c in sorted(centralite_degre.items(), key=lambda x: -x[1]):\n",
|
||||
" print(f\"{personne} : {round(c, 2)}\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Partie 2 — Centralité de proximité (closeness)\n",
|
||||
"\n",
|
||||
"Une personne peut avoir peu de connexions directes, mais être malgré tout **proche de tout le monde** grâce à des chemins courts. La centralité de proximité mesure l'inverse de la distance moyenne entre une personne et toutes les autres.\n",
|
||||
"\n",
|
||||
"$$C_{proximité}(i) = \\frac{n - 1}{\\sum_j d(i, j)}$$\n",
|
||||
"\n",
|
||||
"Plus une personne est proche (en moyenne) de tout le monde, plus sa centralité de proximité est élevée.\n",
|
||||
"\n",
|
||||
"**Interprétation sociologique :** vitesse d'accès à l'information — une personne avec une forte proximité reçoit et diffuse l'information rapidement dans tout le réseau (voir Séances 4 et 5, BFS)."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"metadata": {},
|
||||
"execution_count": null,
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"centralite_proximite = nx.closeness_centrality(G)\n",
|
||||
"for personne, c in sorted(centralite_proximite.items(), key=lambda x: -x[1]):\n",
|
||||
" print(f\"{personne} : {round(c, 2)}\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Partie 3 — Centralité d'intermédiarité (betweenness)\n",
|
||||
"\n",
|
||||
"Certaines personnes ne sont ni très connectées, ni particulièrement proches de tout le monde en moyenne — mais elles occupent une position stratégique : de nombreux **plus courts chemins entre les autres passent par elles**. Ce sont les **ponts** entre des groupes qui, sans elles, seraient déconnectés (cette notion de pont a déjà été rencontrée en Séance 3 avec le coefficient de clustering, et en Séance 5 avec Chloé).\n",
|
||||
"\n",
|
||||
"$$C_{intermédiarité}(i) = \\sum_{j \\neq k \\neq i} \\frac{\\text{nombre de plus courts chemins entre } j \\text{ et } k \\text{ passant par } i}{\\text{nombre total de plus courts chemins entre } j \\text{ et } k}$$\n",
|
||||
"\n",
|
||||
"**Interprétation sociologique :** pouvoir de contrôle sur la circulation de l'information — une personne avec une forte intermédiarité peut filtrer, ralentir ou accélérer ce qui passe d'un groupe à l'autre."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"metadata": {},
|
||||
"execution_count": null,
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"centralite_intermediarite = nx.betweenness_centrality(G)\n",
|
||||
"for personne, c in sorted(centralite_intermediarite.items(), key=lambda x: -x[1]):\n",
|
||||
" print(f\"{personne} : {round(c, 2)}\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Partie 4 — Comparer les trois mesures\n",
|
||||
"\n",
|
||||
"**Questions :**\n",
|
||||
"1. La personne la plus centrale change-t-elle selon la mesure utilisée ?\n",
|
||||
"2. Bob et Chloé ont le même degré (3 connexions chacun·e). Ont-ils/elles la même centralité d'intermédiarité ? Pourquoi ?\n",
|
||||
"3. Emma, Félix, David et Gaël ont tou·tes un degré de 1. Ont-iels tou·tes la même centralité de proximité ? Pourquoi ?\n",
|
||||
"4. Imaginez une personne qui a un très fort coefficient de clustering (Séance 3) — a-t-elle plutôt tendance à avoir une forte ou une faible centralité d'intermédiarité ? Justifiez."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Synthèse\n",
|
||||
"\n",
|
||||
"| Mesure | Question qu'elle pose | Type d'individu qu'elle repère |\n",
|
||||
"|:--------|:------------------------|:----------------------------------|\n",
|
||||
"| Centralité de degré | Combien de connexions directes ? | La personne \"populaire\" |\n",
|
||||
"| Centralité de proximité | À quelle distance moyenne de tout le monde ? | La personne qui diffuse/reçoit vite l'info |\n",
|
||||
"| Centralité d'intermédiarité | Combien de plus courts chemins passent par moi ? | La personne \"pont\", qui contrôle la circulation entre groupes |\n",
|
||||
"\n",
|
||||
"## Questions de réflexion\n",
|
||||
"1. Dans une entreprise, un individu avec une forte centralité d'intermédiarité mais un faible degré perd son emploi. Quelles conséquences cela peut-il avoir sur la circulation de l'information dans l'organisation ?\n",
|
||||
"2. Sur un réseau social, qui serait plutôt \"central en degré\" (beaucoup d'abonné·es) sans être \"central en intermédiarité\" ? Donnez un exemple.\n",
|
||||
"3. En quoi la centralité d'intermédiarité peut-elle représenter un **pouvoir**, y compris pour une personne peu visible dans le réseau ?"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"name": "python",
|
||||
"pygments_lexer": "ipython3"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 4
|
||||
}
|
||||
158
Seance_9_Coloration.ipynb
Normal file
158
Seance_9_Coloration.ipynb
Normal file
@@ -0,0 +1,158 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# 🎨 Séance 9 — Coloration de graphes\n",
|
||||
"\n",
|
||||
"## Objectifs pédagogiques\n",
|
||||
"À la fin de cette séance, vous serez capables de :\n",
|
||||
"- comprendre le **problème de coloration** d'un graphe et la notion de **nombre chromatique** ;\n",
|
||||
"- appliquer un algorithme glouton de coloration avec `networkx` ;\n",
|
||||
"- reconnaître des situations sociologiques et organisationnelles modélisables par la coloration de graphe.\n",
|
||||
"\n",
|
||||
"## Introduction\n",
|
||||
"Jusqu'ici, les arêtes de nos graphes représentaient des liens positifs (amitié, communication). La coloration de graphe part d'une idée différente : des arêtes qui représentent des **incompatibilités**, des **conflits**, des situations où deux sommets ne doivent surtout pas se retrouver \"dans la même case\".\n",
|
||||
"\n",
|
||||
"**Exemple concret :** vous devez organiser le planning des examens d'un semestre. Deux matières sont *en conflit* si des étudiant·es sont inscrit·es aux deux — dans ce cas, elles ne peuvent pas être programmées au même créneau. Combien de créneaux minimum faut-il pour que personne n'ait deux examens en même temps ?"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Partie 1 — Le problème de coloration\n",
|
||||
"\n",
|
||||
"**Colorier un graphe**, c'est attribuer une couleur (ou un créneau, une catégorie...) à chaque sommet, de telle sorte que **deux sommets reliés par une arête n'aient jamais la même couleur**.\n",
|
||||
"\n",
|
||||
"Le **nombre chromatique** d'un graphe est le nombre minimum de couleurs nécessaires pour y parvenir. Plus un graphe a de conflits imbriqués les uns dans les autres, plus son nombre chromatique est élevé.\n",
|
||||
"\n",
|
||||
"**Question de réflexion préalable :** si trois matières sont toutes en conflit deux à deux (Maths-Info, Info-Socio, Maths-Socio), combien de créneaux minimum faut-il pour elles trois ? Pourquoi ne peut-on pas faire avec moins ?"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Partie 2 — Un graphe de conflits d'examens\n",
|
||||
"\n",
|
||||
"Voici le graphe de conflits entre 5 matières :\n",
|
||||
"\n",
|
||||
"```\n",
|
||||
" Maths — Info\n",
|
||||
" │ │\n",
|
||||
" Socio Anglais\n",
|
||||
" \\ /\n",
|
||||
" Sport\n",
|
||||
"```\n",
|
||||
"\n",
|
||||
"(Maths-Info, Info-Anglais, Anglais-Sport, Sport-Socio, Socio-Maths sont en conflit)\n",
|
||||
"\n",
|
||||
"**À la main :** essayez de proposer une répartition en créneaux (couleurs) avant de passer à Python. Combien de créneaux minimum pensez-vous nécessaires ?"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"metadata": {},
|
||||
"execution_count": null,
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import networkx as nx\n",
|
||||
"import matplotlib.pyplot as plt\n",
|
||||
"\n",
|
||||
"G = nx.Graph()\n",
|
||||
"G.add_edges_from([\n",
|
||||
" (\"Maths\", \"Info\"),\n",
|
||||
" (\"Info\", \"Anglais\"),\n",
|
||||
" (\"Anglais\", \"Sport\"),\n",
|
||||
" (\"Sport\", \"Socio\"),\n",
|
||||
" (\"Socio\", \"Maths\"),\n",
|
||||
"])\n",
|
||||
"\n",
|
||||
"plt.figure()\n",
|
||||
"nx.draw(G, with_labels=True, node_color=\"lightblue\", node_size=1500)\n",
|
||||
"plt.show()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Partie 3 — Colorier automatiquement avec un algorithme glouton\n",
|
||||
"\n",
|
||||
"L'algorithme **glouton** de coloration parcourt les sommets un par un, et attribue à chacun **la plus petite couleur disponible** qui n'est pas déjà utilisée par l'un de ses voisins déjà colorié. C'est simple et rapide, mais il ne garantit **pas toujours** d'obtenir le nombre chromatique minimal — cela dépend de l'ordre dans lequel les sommets sont traités."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"metadata": {},
|
||||
"execution_count": null,
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# NetworkX propose directement un algorithme glouton de coloration\n",
|
||||
"coloration = nx.greedy_color(G, strategy=\"largest_first\")\n",
|
||||
"\n",
|
||||
"print(\"Créneau (couleur) attribué à chaque matière :\")\n",
|
||||
"for matiere, creneau in coloration.items():\n",
|
||||
" print(f\" {matiere} : créneau {creneau}\")\n",
|
||||
"\n",
|
||||
"nb_creneaux = len(set(coloration.values()))\n",
|
||||
"print(f\"\\nNombre de créneaux utilisés : {nb_creneaux}\")\n",
|
||||
"\n",
|
||||
"# Visualisation avec les couleurs attribuées\n",
|
||||
"palette = [\"lightblue\", \"lightgreen\", \"lightcoral\", \"khaki\", \"plum\"]\n",
|
||||
"couleurs_sommets = [palette[coloration[noeud]] for noeud in G.nodes()]\n",
|
||||
"\n",
|
||||
"plt.figure()\n",
|
||||
"nx.draw(G, with_labels=True, node_color=couleurs_sommets, node_size=1500)\n",
|
||||
"plt.show()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"**Interprétation :** ce graphe de conflits forme un **cycle** à 5 sommets (chaque matière est en conflit avec exactement 2 autres, en boucle). Sur un cycle à un nombre **impair** de sommets, il est mathématiquement impossible de s'en sortir avec seulement 2 créneaux : en alternant 2 couleurs autour du cycle, on finit toujours par faire se toucher deux sommets de la même couleur. Il faut donc au minimum 3 créneaux.\n",
|
||||
"\n",
|
||||
"**Questions :**\n",
|
||||
"1. Le résultat de l'algorithme glouton correspond-il à votre proposition manuelle ?\n",
|
||||
"2. Si vous ajoutiez une 6ᵉ matière en conflit uniquement avec Maths, combien de créneaux seraient nécessaires ? Pourquoi ?\n",
|
||||
"3. Un graphe **sans aucune arête** (aucun conflit) a quel nombre chromatique ?"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Synthèse\n",
|
||||
"\n",
|
||||
"| Concept | Signification | Exemple d'application |\n",
|
||||
"|:---------|:----------------|:--------------------------|\n",
|
||||
"| Sommet | Élément à catégoriser (matière, tâche, personne, fréquence radio...) | Un examen |\n",
|
||||
"| Arête | Incompatibilité entre deux sommets | Deux matières partagées par les mêmes étudiant·es |\n",
|
||||
"| Couleur | Catégorie/créneau attribué | Un créneau horaire |\n",
|
||||
"| Nombre chromatique | Nombre minimum de catégories nécessaires | Nombre minimum de créneaux d'examens |\n",
|
||||
"| Algorithme glouton | Attribue la plus petite couleur libre à chaque sommet, dans un ordre donné | Rapide, mais pas toujours optimal |\n",
|
||||
"\n",
|
||||
"## Questions de réflexion\n",
|
||||
"1. Donnez un autre exemple sociologique ou organisationnel modélisable par la coloration de graphe (répartition en groupes de travail, attribution de bureaux, allocation de fréquences...).\n",
|
||||
"2. Pourquoi un algorithme glouton, malgré sa simplicité, peut-il être un bon compromis en pratique même s'il ne garantit pas le minimum absolu ?\n",
|
||||
"3. En quoi la coloration de graphe diffère-t-elle fondamentalement des mesures vues en Séance 8 (centralité) ? Que cherche-t-on à optimiser dans chaque cas ?"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"name": "python",
|
||||
"pygments_lexer": "ipython3"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 4
|
||||
}
|
||||
191
TP_1.ipynb
191
TP_1.ipynb
@@ -1,191 +0,0 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Séance 1 TP — Rappels & prise en main de NetworkX\n",
|
||||
"\n",
|
||||
"## Objectifs pédagogiques\n",
|
||||
"- Réviser les définitions : nœuds, arêtes, graphes dirigés / non-dirigés / pondérés.\n",
|
||||
"- Prendre en main `networkx` pour créer un graphe et le visualiser.\n",
|
||||
"- Explorer des propriétés simples (degré, nombre de sommets et d’arêtes).\n",
|
||||
"- Introduire graphes dirigés et pondérés.\n",
|
||||
"\n",
|
||||
"## Contexte sociologique\n",
|
||||
"Les graphes servent à modéliser les relations sociales :\n",
|
||||
"- Graphe non dirigé : relations symétriques (amitié, collaboration).\n",
|
||||
"- Graphe dirigé : relations asymétriques (qui suit qui sur Twitter).\n",
|
||||
"- Graphe pondéré : intensité des relations (fréquence de contact).\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# %pip install networkx matplotlib\n",
|
||||
"\n",
|
||||
"import networkx as nx\n",
|
||||
"import matplotlib.pyplot as plt"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Exercice 1 : Créer un graphe non dirigé"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Construisons un mini-réseau social\n",
|
||||
"G = nx.Graph()\n",
|
||||
"G.add_edges_from([\n",
|
||||
" (\"Alice\", \"Bob\"),\n",
|
||||
" (\"Bob\", \"Claire\"),\n",
|
||||
" (\"Alice\", \"David\"),\n",
|
||||
" (\"Claire\", \"David\"),\n",
|
||||
"])\n",
|
||||
"\n",
|
||||
"print(\"Nœuds :\", list(G.nodes()))\n",
|
||||
"print(\"Arêtes:\", list(G.edges()))\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": [
|
||||
"**Questions :**\n",
|
||||
"1. Qui a le plus de voisins / voisines (ami-e-s) ?\n",
|
||||
"2. Quelles sont les relations réciproques ?\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Exercice 2 : Explorer les propriétés du graphe"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"print(\"Nombre de sommets :\", G.number_of_nodes())\n",
|
||||
"print(\"Nombre d'arêtes :\", G.number_of_edges())\n",
|
||||
"print(\"Degrés de chaque sommet :\", dict(G.degree()))\n",
|
||||
"print(\"Degré moyen :\", sum(dict(G.degree()).values())/G.number_of_nodes())"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"**Question :** Quel est le degré moyen et comment l’interpréter sociologiquement ?"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Exercice 3 : Graphe dirigé"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"DG = nx.DiGraph()\n",
|
||||
"DG.add_edges_from([\n",
|
||||
" (\"Alice\", \"Bob\"),\n",
|
||||
" (\"Bob\", \"Claire\"),\n",
|
||||
" (\"Claire\", \"Alice\")\n",
|
||||
"])\n",
|
||||
"\n",
|
||||
"plt.figure()\n",
|
||||
"nx.draw(DG, with_labels=True, node_color=\"lightgreen\", node_size=1000, arrows=True)\n",
|
||||
"plt.show()\n",
|
||||
"\n",
|
||||
"print(\"Degré sortant :\", dict(DG.out_degree()))\n",
|
||||
"print(\"Degré entrant :\", dict(DG.in_degree()))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"**Questions :**\n",
|
||||
"1. Quelle différence avec le graphe non dirigé ?\n",
|
||||
"2. Que représentent les degrés entrants et sortants sociologiquement (ex : followers) ?"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Exercice 4 : Graphe pondéré"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"WG = nx.Graph()\n",
|
||||
"WG.add_edge(\"Alice\", \"Bob\", weight=5) # forte relation\n",
|
||||
"WG.add_edge(\"Alice\", \"Claire\", weight=1) # relation faible\n",
|
||||
"\n",
|
||||
"print(\"Arêtes avec poids :\", WG.edges(data=True))\n",
|
||||
"\n",
|
||||
"# Dessin avec poids visibles\n",
|
||||
"pos = nx.spring_layout(WG)\n",
|
||||
"nx.draw(WG, pos, with_labels=True, node_color=\"lightcoral\", node_size=1000)\n",
|
||||
"labels = nx.get_edge_attributes(WG, \"weight\")\n",
|
||||
"nx.draw_networkx_edge_labels(WG, pos, edge_labels=labels)\n",
|
||||
"plt.show()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"**Questions :**\n",
|
||||
"1. Comment interpréter le poids d’une relation en sociologie ?\n",
|
||||
"2. Donnez un exemple concret (fréquence de discussions, intensité d’une amitié)."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": []
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"name": "python",
|
||||
"version": "3.x"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
284
TP_2.ipynb
284
TP_2.ipynb
@@ -1,284 +0,0 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Séance 2 — Parcours & distances (BFS / DFS)\n",
|
||||
"\n",
|
||||
"## Objectifs pédagogiques\n",
|
||||
"- Comprendre les notions de **chemin**, **distance** et ** composantes connexes**.\n",
|
||||
"- Distinguer **BFS** (parcours en largeur) et **DFS** (parcours en profondeur).\n",
|
||||
"- Utiliser `networkx` pour calculer des plus courts chemins dans un graphe **non pondéré**.\n",
|
||||
"- Relier les distances à des interprétations sociologiques (proximité, relais, \"six degrés\").\n",
|
||||
"\n",
|
||||
"## Rappels\n",
|
||||
"- Dans un **graphe non pondéré**, un **plus court chemin** est le chemin avec le **moins d'arêtes**.\n",
|
||||
"- **BFS** explore par **couches** (distance croissante) et permet de trouver un plus court chemin.\n",
|
||||
"- **DFS** explore en **profondeur** (par branches) ; utile pour détecter des cycles/composantes mais **ne garantit pas** un plus court chemin.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# %pip install networkx matplotlib\n",
|
||||
"\n",
|
||||
"import networkx as nx\n",
|
||||
"import matplotlib.pyplot as plt\n",
|
||||
"from collections import deque"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 1) Graphe de départ : petit réseau social"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Vous pouvez modifier/étendre ce graphe\n",
|
||||
"G = nx.Graph()\n",
|
||||
"G.add_edges_from([\n",
|
||||
" (\"Alice\",\"Bob\"),\n",
|
||||
" (\"Bob\",\"Claire\"),\n",
|
||||
" (\"Claire\",\"Emma\"),\n",
|
||||
" (\"Alice\",\"David\"),\n",
|
||||
" (\"David\",\"Emma\"),\n",
|
||||
" (\"Emma\",\"Fanny\"),\n",
|
||||
" (\"Bob\",\"Gaston\"),\n",
|
||||
"])\n",
|
||||
"\n",
|
||||
"# Visualisation simple\n",
|
||||
"plt.figure()\n",
|
||||
"nx.draw(G, with_labels=True)\n",
|
||||
"plt.show()\n",
|
||||
"\n",
|
||||
"print(\"Nœuds :\", list(G.nodes()))\n",
|
||||
"print(\"Arêtes:\", list(G.edges()))\n",
|
||||
"print(\"Composantes connexes:\", nx.number_connected_components(G))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"**Question :** Y a-t-il un unique groupe connecté, ou plusieurs ? Impact sociologique : des individus isolés auront des distances infinies vers le reste."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 2) Plus courts chemins et distances avec NetworkX"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"source, cible = \"Alice\", \"Fanny\"\n",
|
||||
"chemin = nx.shortest_path(G, source=source, target=cible) # plus court chemin (non pondéré)\n",
|
||||
"dist = nx.shortest_path_length(G, source=source, target=cible)\n",
|
||||
"print(f\"Plus court chemin de {source} à {cible} :\", chemin)\n",
|
||||
"print(f\"Distance (nombre d'arêtes) :\", dist)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"**Exercice 2.1** : testez 3 autres paires (par ex. `('Gaston','Fanny')`, `('Alice','Claire')`, `('David','Bob')`).\n",
|
||||
"\n",
|
||||
"**Exercice 2.2** : si vous supprimez l'arête `('David','Emma')`, que devient la distance `Alice→Fanny` ?"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 3) Implémenter un BFS pédagogique (pour comprendre l'ordre d'exploration)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def bfs_layers(graph, start):\n",
|
||||
" \"\"\"Retourne l'ordre de visite et les couches de distance (dict: noeud -> distance)\n",
|
||||
" BFS avec file d'attente (deque).\n",
|
||||
" \"\"\"\n",
|
||||
" visited = set([start])\n",
|
||||
" dist = {start: 0}\n",
|
||||
" order = []\n",
|
||||
" q = deque([start])\n",
|
||||
"\n",
|
||||
" while q:\n",
|
||||
" u = q.popleft()\n",
|
||||
" order.append(u)\n",
|
||||
" for v in graph.neighbors(u):\n",
|
||||
" if v not in visited:\n",
|
||||
" visited.add(v)\n",
|
||||
" dist[v] = dist[u] + 1\n",
|
||||
" q.append(v)\n",
|
||||
" return order, dist\n",
|
||||
"\n",
|
||||
"order, dist = bfs_layers(G, \"Alice\")\n",
|
||||
"print(\"Ordre BFS depuis Alice:\", order)\n",
|
||||
"print(\"Distances depuis Alice:\", dist)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"**Exercice 3.1** : Comparez les **distances** renvoyées par `bfs_layers` et `nx.shortest_path_length` pour 3 nœuds.\n",
|
||||
"\n",
|
||||
"**Exercice 3.2** : Quel nœud est le plus proche (distance minimale) d'**Alice** ? Le plus lointain ?"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 4) Implémenter un DFS (ordre d'exploration par branches)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def dfs_order(graph, start, visited=None, order=None):\n",
|
||||
" if visited is None: visited = set()\n",
|
||||
" if order is None: order = []\n",
|
||||
" visited.add(start)\n",
|
||||
" order.append(start)\n",
|
||||
" for v in graph.neighbors(start):\n",
|
||||
" if v not in visited:\n",
|
||||
" dfs_order(graph, v, visited, order)\n",
|
||||
" return order\n",
|
||||
"\n",
|
||||
"print(\"Ordre DFS depuis Alice:\", dfs_order(G, \"Alice\"))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"**Exercice 4.1** : Pourquoi **l'ordre DFS** diffère-t-il souvent de l'ordre BFS ?\n",
|
||||
"\n",
|
||||
"**Exercice 4.2** : DFS trouve-t-il un plus court chemin ? Expliquez avec un contre-exemple si possible."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 5) Distances globales : diamètre & distance moyenne (si connexe)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"if nx.is_connected(G):\n",
|
||||
" # Diamètre = plus longue distance entre deux nœuds\n",
|
||||
" d = nx.diameter(G)\n",
|
||||
" # Distance moyenne (longueur moyenne des plus courts chemins)\n",
|
||||
" apl = nx.average_shortest_path_length(G)\n",
|
||||
" print(\"Diamètre:\", d)\n",
|
||||
" print(\"Distance moyenne:\", apl)\n",
|
||||
"else:\n",
|
||||
" print(\"Le graphe n'est pas connexe : diamètre et distance moyenne ne sont pas définis globalement.\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"**Exercice 5.1** : Ajoutez/supprimez une arête et observez l'effet sur la distance moyenne. Interprétez sociologiquement (ex. apparition d'un **pont** qui réduit les distances)."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 6) Étude guidée : \"six degrés de séparation\" (mini-expérience)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Construisons un graphe en anneau + quelques liens longue portée pour réduire drastiquement les distances\n",
|
||||
"H = nx.cycle_graph(12) # 12 individus en cercle (0..11)\n",
|
||||
"H = nx.relabel_nodes(H, {i: f\"P{i}\" for i in range(12)})\n",
|
||||
"H.add_edge(\"P0\",\"P6\") # raccourci longue portée\n",
|
||||
"H.add_edge(\"P3\",\"P9\") # autre raccourci\n",
|
||||
"\n",
|
||||
"plt.figure()\n",
|
||||
"nx.draw(H, with_labels=True)\n",
|
||||
"plt.show()\n",
|
||||
"\n",
|
||||
"print(\"Connexe:\", nx.is_connected(H))\n",
|
||||
"print(\"Distance moyenne:\", nx.average_shortest_path_length(H))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"**Questions**\n",
|
||||
"1. Que se passe-t-il si on **retire** les liens longue portée ?\n",
|
||||
"2. Pourquoi quelques liens inter-groupes peuvent-ils **réduire fortement** les distances ?\n",
|
||||
"3. Donnez une interprétation en termes de diffusion d'une rumeur/idée."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 7) Synthèse écrite\n",
|
||||
"- Comparez BFS et DFS sur votre graphe.\n",
|
||||
"- Donnez 2 exemples de distances pertinentes en sociologie (ex. accès à l'information, recrutement).\n",
|
||||
"- Proposez une **hypothèse** : l'ajout d'un lien entre deux groupes réduira X ; comment le tester ?"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "75d566ac",
|
||||
"metadata": {},
|
||||
"source": []
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"name": "python",
|
||||
"version": "3.x"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
88
TP_4.ipynb
88
TP_4.ipynb
@@ -1,88 +0,0 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": "# Séance 6 — Parcours en largeur (BFS) : comment l’information se propage dans un réseau\n\n## Objectifs\n- Comprendre intuitivement ce qu’est un **parcours en largeur** (Breadth-First Search ou BFS).\n- Relier ce concept à la **diffusion d’une information, d’une rumeur ou d’une idée** dans un réseau social.\n- Explorer un réseau avec Python et `networkx` pour visualiser cette propagation.\n- Comparer avec une autre stratégie d’exploration : le **parcours en profondeur (DFS)**.\n"
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": "## 1. Introduction sociologique : une rumeur se propage\nImaginez qu’une personne — appelons-la **Alice** — partage une nouvelle dans son groupe d’amis.\nChacun la répète à ses proches, et ainsi de suite.\n\nL’information se diffuse **par cercles successifs** : d’abord les amis directs d’Alice, puis les amis des amis, etc.\n\nC’est exactement ce que fait un **parcours en largeur** : explorer un réseau *niveau par niveau*.\n\n→ En sociologie des réseaux, cela correspond à la **distance sociale** entre individus."
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": "## 2. Exemple manuel : propagation d’une information\nConsidérons ce réseau :\n\n```\nAlice — Bob — Chloé — David\n │ │ │\n Emma Félix Gaël\n```\n\nSupposons qu’Alice commence à diffuser une nouvelle.\n\n**Étapes :**\n1. Niveau 0 : Alice\n2. Niveau 1 : les personnes directement connectées à Alice → {Bob, Emma}\n3. Niveau 2 : les amis de Bob (hors Alice) → {Chloé, Félix}\n4. Niveau 3 : les amis de Chloé → {David, Gaël}\n\nChaque *niveau* correspond à une **distance sociale** par rapport à la source."
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": "## 3. Visualisation avec Python\nCréons ce réseau et voyons comment le BFS le parcourt."
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"metadata": {},
|
||||
"execution_count": null,
|
||||
"outputs": [],
|
||||
"source": "import networkx as nx\nimport matplotlib.pyplot as plt\n\n# Création du graphe\nG = nx.Graph()\nG.add_edges_from([\n ('Alice', 'Bob'), ('Alice', 'Emma'),\n ('Bob', 'Chloé'), ('Bob', 'Félix'),\n ('Chloé', 'David'), ('Chloé', 'Gaël')\n])\n\nplt.figure(figsize=(6,4))\npos = nx.spring_layout(G, seed=0)\nnx.draw(G, pos, with_labels=True, node_color='lightblue', node_size=1000)\nplt.title('Réseau social — Diffusion de la nouvelle')\nplt.show()"
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": "## 4. Explorer le réseau en largeur\nOn peut utiliser la fonction `nx.bfs_tree()` pour construire un arbre de parcours à partir d’un point de départ (ici, Alice)."
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"metadata": {},
|
||||
"execution_count": null,
|
||||
"outputs": [],
|
||||
"source": "source = 'Alice'\nT = nx.bfs_tree(G, source=source)\n\nplt.figure(figsize=(6,4))\nnx.draw(T, with_labels=True, node_color='lightgreen', node_size=1000)\nplt.title(f'Arbre BFS à partir de {source}')\nplt.show()\n\nprint('Ordre du parcours BFS :')\nprint(list(nx.bfs_edges(G, source)))"
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": "## 5. Distances sociales depuis Alice\nLe BFS permet aussi de mesurer la **distance minimale** entre Alice et chaque autre personne."
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"metadata": {},
|
||||
"execution_count": null,
|
||||
"outputs": [],
|
||||
"source": "distances = nx.single_source_shortest_path_length(G, source)\nprint('Distances sociales depuis Alice :')\nfor k, v in distances.items():\n print(f'{k} : {v}')"
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": "### Interprétation sociologique\n- Les **valeurs faibles** indiquent des individus **proches** d’Alice (accès direct à l’information).\n- Les **valeurs élevées** indiquent des individus **périphériques** : ils n’apprennent la nouvelle que tardivement.\n\n→ Cela permet d’analyser la **vitesse de diffusion** ou la **position sociale** dans le réseau."
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": "## 6. Comparaison intuitive : BFS vs DFS\nPour bien comprendre la logique du BFS, comparons-le brièvement à une autre stratégie : le **DFS**.\n\n| Stratégie | Métaphore | Manière d’explorer | Exemple |\n|:-----------|:-----------|:------------------|:---------|\n| BFS (largeur) | diffusion sociale | explore les cercles autour de la source | bouche-à-oreille, information publique |\n| DFS (profondeur) | exploration ciblée | suit un chemin jusqu’au bout avant de revenir | enquête individuelle, relation hiérarchique |\n\nLe BFS **propagera rapidement une information**, tandis que le DFS **creusera une piste en profondeur**."
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": "## 7. Pour aller plus loin\n- Comment interpréteriez-vous un individu qui n’est **atteint par personne** (non connexe) ?\n- Si plusieurs personnes diffusent simultanément une info, que se passe-t-il ?\n- Quels phénomènes réels le BFS peut-il modéliser ? (rumeur, contagion, propagation d’un hashtag, etc.)"
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": "## 8. Synthèse\n- Le **BFS** explore un réseau **par cercles successifs** à partir d’une source.\n- Il permet de mesurer la **distance sociale** et d’identifier les **positions centrales**.\n- Dans un graphe non connexe, certaines personnes ne reçoivent jamais l’information.\n- Le **DFS**, lui, suit une logique d’exploration **en profondeur**, utile pour détecter des **sous-groupes**.\n"
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"name": "python",
|
||||
"version": "3.x"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
117
TP_4_bis.ipynb
117
TP_4_bis.ipynb
@@ -1,117 +0,0 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": "# Séance 6 bis — TP dirigé : Diffusion d’une information dans un réseau (BFS)\n\n## Objectifs\n- Manipuler concrètement un **parcours en largeur (BFS)** avec Python.\n- Visualiser la **diffusion d’une information** dans un réseau social.\n- Interpréter sociologiquement la **distance**, la **proximité** et la **position** des individus.\n\nCe TP prolonge la séance précédente sur le parcours en largeur."
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": "## 1. Création du réseau\nCommençons par créer un petit réseau social entre 7 personnes. Nous utiliserons la bibliothèque `networkx` pour représenter et afficher le graphe."
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"metadata": {},
|
||||
"execution_count": null,
|
||||
"outputs": [],
|
||||
"source": "import networkx as nx\nimport matplotlib.pyplot as plt\n\n# Création d'un graphe non orienté\nG = nx.Graph()\n\n# Sommets et arêtes du réseau\nrelations = [\n ('Alice', 'Bob'), ('Alice', 'Emma'),\n ('Bob', 'Chloé'), ('Bob', 'Félix'),\n ('Chloé', 'David'), ('Chloé', 'Gaël')\n]\n\nG.add_edges_from(relations)\n\n# Affichage du graphe\npos = nx.spring_layout(G, seed=0)\nplt.figure(figsize=(6,4))\nnx.draw(G, pos, with_labels=True, node_color='lightblue', node_size=1000)\nplt.title('Réseau social : relations entre individus')\nplt.show()"
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": "**Questions :**\n1. Quelle personne semble la plus « centrale » dans le graphe ?\n2. Quelle personne paraît la plus isolée ?\n3. Ce graphe est-il connexe (toutes les personnes peuvent-elles être atteintes ?)"
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": "## 2. Parcours en largeur (BFS)\nNous allons simuler la diffusion d’une information à partir d’un individu : **Alice**."
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"metadata": {},
|
||||
"execution_count": null,
|
||||
"outputs": [],
|
||||
"source": "source = 'Alice'\n\n# Création de l'arbre de parcours BFS depuis Alice\nT = nx.bfs_tree(G, source=source)\n\nplt.figure(figsize=(6,4))\nnx.draw(T, with_labels=True, node_color='lightgreen', node_size=1000)\nplt.title(f'Arbre BFS à partir de {source}')\nplt.show()\n\n# Affichage de l'ordre du parcours\nprint('Ordre du parcours BFS :')\nprint(list(nx.bfs_edges(G, source)))"
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": "**Questions :**\n1. Dans quel ordre les personnes reçoivent-elles l'information ?\n2. Quel est le rôle d'Alice dans cette diffusion ?\n3. Que se passerait-il si l'information partait de Bob plutôt que d'Alice ?"
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": "## 3. Distances sociales depuis la source\nLe BFS permet de mesurer la distance sociale entre la source et chaque personne du réseau."
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"metadata": {},
|
||||
"execution_count": null,
|
||||
"outputs": [],
|
||||
"source": "distances = nx.single_source_shortest_path_length(G, source)\nprint('Distances sociales depuis Alice :')\nfor k, v in distances.items():\n print(f'{k} : {v}')"
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": "**Interprétation :**\n- Distance = nombre d’étapes pour atteindre une personne depuis Alice.\n- Plus la distance est grande, plus la personne est éloignée socialement.\n\n**Questions :**\n1. Qui apprend la nouvelle en premier ? En dernier ?\n2. Quelle distance maximale observe-t-on dans ce réseau ?\n3. Quelle est la signification sociologique d'une distance de 3 ?"
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": "## 4. Comparaison avec un autre point de départ\nRecommençons la diffusion mais cette fois **à partir de Chloé**. Observez les différences de structure et de distances."
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"metadata": {},
|
||||
"execution_count": null,
|
||||
"outputs": [],
|
||||
"source": "source2 = 'Chloé'\nT2 = nx.bfs_tree(G, source=source2)\n\nplt.figure(figsize=(6,4))\nnx.draw(T2, with_labels=True, node_color='lightcoral', node_size=1000)\nplt.title(f'Arbre BFS à partir de {source2}')\nplt.show()\n\nprint('Distances sociales depuis Chloé :')\nfor k, v in nx.single_source_shortest_path_length(G, source2).items():\n print(f'{k} : {v}')"
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": "**Questions :**\n1. Quelles différences remarquez-vous par rapport au départ depuis Alice ?\n2. Quelle personne semble jouer un rôle « de pont » dans la diffusion ?\n3. Que peut-on dire de la position de Chloé dans le réseau ?"
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": "## 5. Expérience sociologique : un acteur isolé\nAjoutons une nouvelle personne, **Hugo**, qui n’est reliée à personne. Que se passe-t-il lors du BFS ?"
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"metadata": {},
|
||||
"execution_count": null,
|
||||
"outputs": [],
|
||||
"source": "# Ajout d'une personne isolée\nG.add_node('Hugo')\n\nplt.figure(figsize=(6,4))\npos = nx.spring_layout(G, seed=1)\nnx.draw(G, pos, with_labels=True, node_color='lightblue', node_size=1000)\nplt.title('Réseau social avec une personne isolée (Hugo)')\nplt.show()\n\nprint('Composantes connexes du graphe :')\nfor i, comp in enumerate(nx.connected_components(G)):\n print(f'Composante {i+1} : {comp}')"
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": "**Questions :**\n1. Hugo peut-il recevoir l'information ? Pourquoi ?\n2. Que représente un acteur isolé dans un réseau social réel ?\n3. Comment cette notion d'isolement peut-elle s’interpréter en sociologie ?"
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": "## 6. Pour aller plus loin (facultatif)\n1. Ajoutez de nouvelles relations pour rendre le graphe plus connecté.\n2. Essayez de trouver une configuration où tout le monde est relié en deux étapes maximum.\n3. Testez d'autres points de départ pour le BFS et comparez les distances obtenues."
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": "## 7. Synthèse\n- Le **BFS** permet de simuler la **diffusion d’une information** dans un réseau.\n- Les **distances** indiquent le **niveau d’accès à l’information**.\n- Les **personnes centrales** propagent plus vite, les **isolées** restent à l’écart.\n- Ces observations permettent de **lier structure du réseau et dynamique sociale**."
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"name": "python",
|
||||
"version": "3.x"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
@@ -35,7 +35,7 @@
|
||||
"\n",
|
||||
"Il suffit de se rendre à l'url [https://notebook.basthon.fr/](https://notebook.basthon.fr/), l'interface suivante apparait :\n",
|
||||
"\n",
|
||||
""
|
||||
""
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user