{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Sujet 5 : Analyse des dialogues dans l'Avare de Molière" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "__Rappel du contexte du sujet__ :\n", "\n", "L’Observatoire de la vie littéraire ([OBVIL](http://obvil.sorbonne-universite.site/obvil/presentation)) promeut une approche de l'analyse des textes littéraires fondée sur le numérique. \n", "Dans le cadre du [Projet Molière](http://obvil.sorbonne-universite.site/projets/projet-moliere), des pièces de cet auteur ont été numérisées et sont accessibles librement dans différents formats utilisables par un programme informatique. \n", "\n", "Grâce à ces numérisations, il est possible d'écrire des programmes pour réaliser des analyses syntaxiques et sémantiques. Ce sujet se propose de reproduire une étude réalisée par l'OBVIL sur les dialogues de l'Avare de Molière.\n", "\n", "__Rappel des objectifs de ce sujet__ :\n", "\n", "1. Classez les personnages selon la quantité de parole grâce à une analyse syntaxique du texte (scènes / répliques / mots). En particulier, quel est celui qui parle le plus ? Quel est celui qui ne parle pas du tout ? Attention, les noms des personnages ne sont pas forcément homogènes (casse et accents par exemple).\n", "2. Réalisez un graphique qui montrera le nombre de mots que chaque acteur prononce dans chaque scène. Pour cela, vous pouvez vous inspirer de l'[étude de l'Avare de Molière réalisée par l'OBVIL](https://obvil.sorbonne-universite.fr/corpus/moliere/moliere_avare) (graphe de gauche). Dans ce graphique, les lignes sont de longueur égale et la hauteur représente le nombre de mots prononcés au total dans la scène. La largeur de chaque rectangle indique le pourcentage de la scène qu’un acteur occupe. \n", "3. Facultatif : Construisez un graphe d’interlocution permettant de visualiser les échanges entre les personnages. Pour cela, vous pouvez vous inspirer de l'[étude de l'Avare de Molière réalisée par l'OBVIL](https://obvil.sorbonne-universite.fr/corpus/moliere/moliere_avare) (graphe de droite).\n", "4. Déposer votre résultat dans FUN\n", "\n", "La version numérisée que l'on se propose d'utiliser est le fichier texte au format markdown disponible ici [moliere_avare](http://dramacode.github.io/markdown/moliere_avare.txt)." ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "data_url = \"http://dramacode.github.io/markdown/moliere_avare.txt\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "On va s'assurer qu'un fichier texte en local au format markdown contienne la pièce. Si le fichier \"moliere_avare.md\" existe on considère que c'est bon et s'il n'existe pas nous allons télécharger le contenu disponible à l'URL renseignée ci-dessus et l'écrire dans ce fichier local \"moliere_avare.md\". " ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "from os import path as pth\n", "import requests\n", "\n", "local_filename = \"moliere_avare.md\"\n", "# Si le fichier csv des données d'incidence existe en local\n", "# il n'est pas nécessaire de le télécharger par l'URL\n", "if not pth.exists(local_filename):\n", " print(\"Le fichier local contenant la pièce de théâtre n'existe pas.\")\n", " # Si le fichier n'existe pas en local dans le dossier courant\n", " # nous téléchargons les données et les écrivons\n", " # dans un fichier en local\n", " # Téléchargement des données\n", " response = requests.get(data_url)\n", " # Ecriture des données téléchargées dans le fichier local\n", " with open(local_filename, \"wb\") as f:\n", " f.write(response.content)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Maintenant que nous sommes assurés d'avoir un fichier en local contenant le texte de l'Avare dont on va faire l'analyse, on va donc l'ouvrir, parcourir son contenu et le traiter au fur et à mesure.\n", "\n", "Ce que l'on sait déjà c'est que l'on va devoir créer une structure de données pour l'analyse.\n", "On va passer par la bibliothèque Pandas et la création d'un dataframe, permettant de différencier les différents personnages et de qualifier leur \"activité\" au travers des différents actes et scènes. Néanmoins, après avoir parcouru le web, il est recommandé de passer par une structure intermédiaire pour la création du dataframe pandas. Nous allons choisir la structure de données native de python des dictionnaires en tant que structure de données intermédiaire. \n", "\n", "Il y a plusieurs choses auxquelles il est déjà nécessaire de penser vis-à-vis de la problématique posée et des représentations graphiques demandées, notamment celle relative à la question facultative.\n", "\n", "Pour commencer, afin d'obtenir une bonne lisibilité des graphiques, il sera intéressant d'associer à chaque personnage une couleur différente.\n", "\n", "Ensuite, la question facultative demande de déterminer à qui s'adresse chaque réplique afin d'avoir le graphe directionnel des interactions entre les personnages de la pièce. La structure de données devra donc permettre de savoir pour chaque réplique l'auteur mais aussi le destinataire de cette dernière. Ce sont des informations assez simples à obtenir mais à prendre en compte dans la manière de \"parser\" le fichier texte." ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "# On va d'ores et déjà utiliser une instruction afin que les graphiques s'affichent directement au sein du notebook\n", "%matplotlib inline" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [], "source": [ "# On déclare l'utilisation de la bibliothèque pandas et on crée également le dictionnaire\n", "# qui va nous servir d'intermédiaire avant la création du dataframe pandas\n", "import pandas as pd\n", "# Le dictionnaire établi une structure en tableau à 5 colonnes permettant d'enregistrer\n", "# l'auteur, le destinataire, l'acte, la scène, ainsi que la longueur en termes de mots \n", "# pour chaque réplique de la pièce\n", "avareAnalysisDict = {'author':[],'recipient':[],'act':[],'scene':[],'speech_length':[]}\n", "\n", "# Nous créons également d'ores et déjà un dictionnaire des personnages de la scène\n", "# permettant d'enregistrer les informations de liens avec les autres personnages\n", "# de définir une couleur de représentation.\n", "\n", "# Ce dictionnaire est initialisé vide car il sera rempli en utilisant le nom de chaque personnage\n", "# comme clés associées à des valeurs qui seront des dictionnaires à deux entrées\n", "# 'links' donnant les liens avec les autres personnages sous la forme d'une liste\n", "# 'color' permettant de régler une couleur de représentation\n", "avarePersoDict = {}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Afin de parser le fichier en s'appuyant notamment sur les symboles de titres utilisés par le format Markdown,\n", "il nous faut avoir recours à l'utilisation d'un outil d'analyse des expressions régulières (cf. [Wikipedia_Expression_régulière](https://fr.wikipedia.org/wiki/Expression_r%C3%A9guli%C3%A8re)). La bibliothèque\n", "[re](https://docs.python.org/3/library/re.html) disponible nativement dans python permet de faire ce travail.\n", "Un rapide parcours du fichier montre que:\n", "- les actes sont indiqués par des titres header 2, par une ligne commençant par ##\n", "- les scènes sont indiquées par des titres header 3, par une ligne commençant par ###\n", "- que les personnages d'une scène sont donnés sur la ligne suivant l'indication de la scène\n", "- que la ligne précédant chaque réplique contient le \"nom\" de son auteur en majuscule\n", "\n", "Voici un extrait illustrant ces propos:\\\n", "\"\\\n", "_\\##_ _Acte_ _Premier_.\n", "\n", "\n", "_\\### Scène Première.\\\n", "Valère, Élise_\n", "\n", "\n", " VALÈRE.\n", "_Hé quoi, charmante Élise, vous devenez mélancolique, après les obligeantes assurances que vous avez eu la bonté de me donner de votre foi ?Je vous vois soupirer, hélas, au milieu de ma joie !Est-ce du regret, dites-moi, de m'avoir fait heureux ? et vous repentez-vous de cet engagement où mes feux ont pu vous contraindre ?_\n", "\n", " ÉLISE.\n", "_Non, Valère, je ne puis pas me repentir de tout ce que je fais pour vous. Je m'y sens entraîner par une trop douce puissance, et je n'ai pas même la force de souhaiter que les choses ne fussent pas. Mais, à vous dire vrai, le succès me donne de l'inquiétude ; et je crains fort de vous aimer un peu plus que je ne devrais._\n", "\\\n", "\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Nota Bene: en continuant à travailler sur le sujet et en approfdondissant le parcours du texte, on peut se rendre compte qu'il y a parfois quelques indications de mise en scène qui viennent perturber ce schéma. Différente options s'offrent à nous:\n", "- modifier le texte afin d'utiliser les mécanismes du markdown et mettre ces indications en italiques ou en gras --> utilisations de balises permettant de reconnaître facilement ces éléments\n", "- ou bien repérer les numéros de ces lignes car il n'y en pas beaucoup afin de les ignorer (il nous faudra alors un moyen de connaître dans les fonctions d'analyse le numéro de la ligne du fichier en cours d'analyse.)\n", "- pour être plus générique, l'idéal serait d'avoir un traitement intelligent, un réseaux de neurones par exemple spécialisé dans l'analyse du langage pour identifier ces éléments de mis en scène.\n", "De plus les lignes indiquant la fin des actes sont également à ignorer en tant que réplique. Et pour finir, il y a au moins une scène ne donnant pas la liste des personnages, Harpagon étant le seul protagoniste, il faudra prendre en compte ce cas." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Déclaration des numéros de lignes à ignorer\n", "playInstructionsLineNumbersList = [201, 204, 213, 251, 272, 384, 623, 782, 815, 1280, 1283,\\\n", " 1342, 1369, 1411, 1527, 1610, 1633, 1665, 1866, 1874, 1970, \\\n", " 2299, 2319, 2327, 2357, 2521, 2524]" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [], "source": [ "import re" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'ACTEURS.'" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Chaîne de caractère indiquant le début d'analyse et de récupération des personnages\n", "persoCaptureStartLine = \"# ACTEURS.\"\n", "# L'expression régulière suivante permet de valider qu'une chaîne de caractère\n", "# contenue dans une ligne lue correspond à la ligne précédant la définition\n", "# de la liste des personnages\n", "m = re.search('(?<=# )ACTEURS\\.$', persoCaptureStartLine)\n", "m.group(0)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "En effet, le résultats obtenus n'est pas un objet null, à savoir une valeur None en python" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "\"Harpagon, Père de Cléante et d'Élise, et Amoureux de Mariane\"" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Exemple d'une ligne de la liste des personnages\n", "persoLineExample = \" – Harpagon, Père de Cléante et d'Élise, et Amoureux de Mariane.\"\n", "# L'expression régulière proposée est la suivante\n", "# On cherche une chaîne de caractères commencant par une majuscule --> [A-ZÀ-Ÿ]{1}\n", "# potentiellement accentuée et ensuite composées de lettres potentiellement\n", "# accentuées elles aussi, contenant des espaces, des virgules, des apostrophes --> [a-zA-ZÀ-ÿ\\s,\\']+\n", "# et précédée d'un espace suivi d'un tiret suivi d'un espace (ce n'est pas le tiret du 6) --> (?<=\\s–\\s)\n", "# , chaîne de caractères qui ne sera pas capturée\n", "m1 = re.search('(?<=\\s–\\s)[A-ZÀ-Ÿ]{1}[a-zA-ZÀ-ÿ\\s,\\']+',persoLineExample)\n", "m1.group(0)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Voici un lien vers un site de test en ligne d'expressions régulières python \\([regexp_test](https://pythex.org/)\\) qui a aidé à mettre en place l'expression régulière précédente." ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [], "source": [ "# Fonction qui permet d'extraire la liste des personnages\n", "# de la stocker dans un dictionnaire en parcourant ligne par ligne\n", "# un fichier texte passé en entrée.\n", "def fill_perso_dict(fileToAnalyse, emptyPersoDict):\n", " \"\"\"\n", " Fonction remplissant un dictionnaire d'entrée avec la liste\n", " des personnages de la pièce de théâtre lue dans le fichier\n", " passé en entrée. On considère que le fichier en entrée est lu\n", " à partir du début, de sa première ligne.\n", " \n", " :param fileToAnalyse: pointeur obtenu par ouverture d'un fichier\n", " :param emptyPersoDict: dictionnaire à remplir par la fonction\n", " :return: numéro de la dernière ligne lue\n", " \"\"\"\n", " lineNum = 0\n", " currentLine = fileToAnalyse.readline()\n", " lineNum += 1\n", " isStartPersoListLine = False\n", " while (currentLine and not isStartPersoListLine):\n", " m = re.search('(?<=# )ACTEURS\\.$', currentLine)\n", " if m is not None:\n", " isStartPersoListLine = True\n", " currentLine = fileToAnalyse.readline()\n", " lineNum += 1\n", " \n", " # La lecture s'est arrêtée car la ligne de début de définition de la liste des personnages a été rencontrée.\n", " # Nous avons néanmoins lu la ligne suivante qui est obligatoirement un personnage.\n", " # Nous devons maintenant lire ligne par ligne, la liste des personnages au format suivant:\n", " # \"- NomPersonnage, lien, lien, ...\"\n", " # Ainsi dès que la ligne ne commence plus par un tiret nous pouvons arrêter la lecture et le remplissage\n", " # du dictionnaire.\n", " isAPersoLine = True\n", " # On extrait la chaîne de caractères qui nous intéresse\n", " m = re.search('(?<=\\s–\\s)[A-ZÀ-Ÿ]{1}[a-zA-ZÀ-ÿ\\s,\\']+', persoLineExample)\n", " extractedString = m.group(0)\n", " while (currentLine and isAPersoLine):\n", " # Traitement de la ligne courante qui est obligatoirement\n", " # une ligne listant un personnage de la pièce.\n", " # Comme le montre l'exemple au-dessus, le nom ainsi que\n", " # les différents types de liens sont séparés par une virgule.\n", " parts = extractedString.split(',')\n", " # La ligne au-dessus crée une liste dont chaque élément\n", " # est séparé par des virgules\n", " # typiquement \"tata, titi, toto\".split(',') --> ['tata', ' titi', ' toto']\n", " # Création d'un dictionnaire vide temporaire pour enregistrer les liens\n", " # ainsi que la couleur à paramétrer\n", " persoCaracs = {\"links\":[],\"color\":None}\n", " # Le premier élément donne le nom du personnage, indice 0 de la liste\n", " # Les éléments suivants donnent les liens avec les autres personnages\n", " # On parcours le reste de ces derniers\n", " for elt in parts[1:]:\n", " # En regardant cette partie dans le fichier,\n", " # on voit que soit les parties commencent\n", " # par un espace et une lettre,\n", " # soit un espace et un \"et\" que l'on ne\n", " # souhaite pas capturer\n", " # On vérifie si l'élément commence par \" et \" ou non\n", " if elt.startswith(\" et \"):\n", " # L'élément commence bien par \" et \"\n", " # on enlève cette partie\n", " currentLink = elt[4:]\n", " else:\n", " # Ce n'est pas le cas, on enlève juste\n", " # l'espace\n", " currentLink = elt[1:]\n", " persoCaracs[\"links\"].append(currentLink)\n", " # Fin de la boucle for\n", " \n", " # On enregistre le dictionnaire temporaire des caractéristiques\n", " # du personnage courant rempli, dans le dictionnaire\n", " # global des personnages\n", " emptyPersoDict[parts[0]] = persoCaracs\n", " currentLine = fileToAnalyse.readline()\n", " lineNum += 1\n", " m = re.search('(?<=\\s–\\s)[A-ZÀ-Ÿ]{1}[a-zA-ZÀ-ÿ\\s,\\']+', currentLine)\n", " if m is not None:\n", " extractedString = m.group(0)\n", " else:\n", " isAPersoLine = False\n", " \n", " # Fin de la boucle while\n", " \n", " return lineNum" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Définition de la fonction permettant de compter le nombre de mots d'une ligne constituant une réplique." ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [], "source": [ "def count_words(speechLine):\n", " \"\"\"\n", " Fonction qui compte le nombre de mots contenus dans une ligne de texte.\n", " \n", " :param speechLine: ligne de texte en entrée dont il faut compter le nombre de mots\n", " :return: dataframe pandas dont le template est donné par le dictionnaire\n", " \"\"\"\n", " " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Définition de la fonction principale d'analyse du fichier texte générant un tableau de données de synthèse." ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [], "source": [ "def generate_text_synthesis_data_table(fileToAnalyse, persoDict, emptyAnalysisDict, lineNum):\n", " \"\"\"\n", " Fonction principale d'analyse du texte générant un dataframe pandas\n", " de synthèse des informations permettant l'analyse globale.\n", " \n", " :param fileToAnalyse: pointeur obtenu par ouverture d'un fichier\n", " :param persoDict: dictionnaire donnant la liste des personnages de la pièce\n", " :param emptyAnalysisDict: dictionnaire à remplir par la fonction\n", " :param lineNum: numéro de le ligne à partir de laquelle nous commencons la lecture\n", " :return: dataframe pandas dont le template est donné par le dictionnaire\n", " \"\"\"\n", " currentLine = fileToAnalyse.readline()\n", " lineNum += 1\n", " # Expression régulière de détection de déclaration des actes\n", " # Ligne qui commence par exactement 2 #, ni plus ni moins, donc tout autre caractère qu'un #\n", " actLinePattern = re.compile('^#{2}[^#]')\n", " # Expression régulière de détection de déclaration des scènes\n", " # Ligne qui commence par exactement 3 #, ni plus ni moins, donc tout autre caractère qu'un #\n", " sceneLinePattern = re.compile('^#{3}[^#]')\n", " # Variable pour enregistrer le numéro de l'acte courant\n", " actNum = 0\n", " # Variable pour enregistrer le numéro de la scène courante\n", " sceneNum = 0\n", " # Variable indiquant si la ligne précédente déclare une scène\n", " previousLineIsScene = False\n", " # Variable contenant l'auteur courant de la réplique analysée\n", " # Nous l'initialisons à la valeur vide None\n", " currentAuthor = None\n", " # Variable donnant le nombre de mots de la réplique courante\n", " # analysée. On l'initialise à 0.\n", " currentSpeechLen = 0\n", " \n", " # Boucle principale de parcours du fichier texte\n", " while (currentLine):\n", " if actLinePattern.match(currentLine):\n", " print(currentLine)\n", " # Déclaration d'un acte de la pièce\n", " # On incrémente le numéro d'acte\n", " actNum += 1\n", " # On remet à zéro le numéro de la scène\n", " sceneNum = 0\n", " elif sceneLinePattern.match(currentLine):\n", " print(currentLine)\n", " # Déclaration d'une scène de la pièce\n", " # On incrémente le numéro de scène\n", " sceneNum += 1\n", " # Si ce n'est pas la première scène de l'acte,\n", " # il faut traiter le cas de la dernière réplique.\n", " # On considère que l'auteur courant s'adresse forcément\n", " # au dernier auteur enregistré.\n", " if sceneNum > 1:\n", " # Récupération du dernier élément de la colonne des auteurs\n", " # qui est l'auteur de la réplique précédente\n", " previousAuthor = emptyAnalysisDict['author'][-1]\n", " emptyAnalysisDict['author'].append(currentAuthor)\n", " emptyAnalysisDict['recipient'].append(previousAuthor)\n", " emptyAnalysisDict['act'].append(actNum)\n", " emptyAnalysisDict['scene'].append(sceneNum-1)\n", " emptyAnalysisDict['speech_length'].append(currentSpeechLen)\n", " # On met temporairement la variable\n", " # previousLineIsScene à vrai pour indiquer\n", " # que l'on a détecté le début d'une scène\n", " previousLineIsScene = True\n", " # On déclare la liste des protagonistes de la scène courante\n", " scenePersoList = []\n", " # On réinitialise les autres variables de l'auteur et de la taille d'une réplique\n", " currentAuthor = None\n", " currentSpeechLen = 0\n", " else:\n", " # La ligne courante est une ligne autre,\n", " # comme on a pu le voir dans l'exemple un peu plus haut\n", " # elle peut être:\n", " # - vide\n", " # - une déclaration des personnages de la scène\n", " # - une identification de l'auteur de la réplique qui va suivre\n", " # - une ligne composant une réplique\n", " # - une ligne donnant des indications sur la mise en scène\n", " # - une ligne indiquant la fin d'un acte\n", " if not previousLineIsScene:\n", " # La ligne courante ne contient forcément pas la liste des protagonistes de la scène\n", " # Testons si la ligne est vide auquel cas on peut quasiment passer directement à la suivante.\n", " # Notons qu'une chaîne de caractères vide est interprêtées par python comme une valeur booléénne fausse\n", " if not currentLine:\n", " # La ligne n'est pas vide, c'est soit la déclaration de l'auteur d'une réplique,\n", " # soit une ligne contenant constituant une réplique.\n", " # On va donc traiter cette ligne soit de manière à identifier l'auteur qui,\n", " # il faut le noter est le destinataire de la réplique de l'auteur précédent,\n", " # soit de manière à compter le nombre de mots total d'une réplique afin\n", " # de remplir avec toutes les informations glannées à cette étape\n", " # de synthèse des données de la pièce.\n", " # On peut noter également que l'on peut facilement différencier les lignes de répliques\n", " # des lignes donnant l'auteur d'une réplique. En effet, les lignes de répliques commencent\n", " # par une lettre majuscule tandis que les lignes donnant l'auteur d'une réplique\n", " # commencent par des espaces.\n", " if currentLine.startsWith(\" \"):\n", " # La ligne commence avec un espace, elle déclare donc l'auteur d'une réplique\n", " # on va parcourir la liste des protagonistes de la scène et identifier l'auteur\n", " for perso in scenePersoList:\n", " # On fait une recherche ignorant si les lettres sont en majuscules ou minuscules\n", " # grâce à l'option passée à la fonction de recherche d'expression régulière\n", " # re.IGNORECASE\n", " m = re.search(perso, re.IGNORECASE)\n", " if m:\n", " # le résultats de la recherche n'est pas vide\n", " # L'auteur est trouvé. Vérifions si quelqu'un\n", " # a déjà parlé dans la scène auquel cas l'auteur\n", " # courant est le destinataire de l'auteur précédent.\n", " if currentAuthor is not None:\n", " # Il y avait déjà un auteur d'une réplique, currentAuthor\n", " # le nouvel auteur est donc aussi le destinataire de la réplique précédente.\n", " # On peut maintenant remplir le dictionnaire pour la réplique précédente,\n", " # car nous avons toutes les informations nécessaires sur celles-ci.\n", " # Pour rappel, la structure du dictionnaire,\n", " # {'author':[],'recipient':[],'act':[],'scene':[],'speech_length':[]}\n", " emptyAnalysisDict['author'].append(currentAuthor)\n", " emptyAnalysisDict['recipient'].append(perso)\n", " emptyAnalysisDict['act'].append(actNum)\n", " emptyAnalysisDict['scene'].append(sceneNum)\n", " emptyAnalysisDict['speech_length'].append(currentSpeechLen)\n", " # On réinitialise la variable mesurant le nombre de mot d'une réplique\n", " currentSpeechLen = 0\n", " # Fin du si\n", " # Dans tous les cas l'auteur courant est donné par la valeur \"perso\"\n", " # qui a été trouvée dans la ligne\n", " currentAuthor = perso\n", " else:\n", " # Nous sommes désormais dans le cas d'une ligne qui constitue une réplique\n", " # comptons le nombre de mots de celle-ci et ajoutons le au nombre de mots\n", " # déjà comptabilisé pour la réplique en cours.\n", " # Nous faison appel ici à une fonction spécialisée dans le comptage des mots d'une ligne.\n", " currentSpeechLen += count_words(currentLine)\n", " \n", "\n", " else:\n", " # La ligne courante présente forcément les personnages de la scène\n", " # car c'est la ligne qui suit la déclaration de scène,\n", " # à l'exception d'un seule scène, la 7 de l'acte 4\n", " # On peut donc réduire l'analyse du texte à ces seuls\n", " # protagonistes.\n", " # Boucle sur les clés du dictionnaire de personnage\n", " # qui sont leur nom.\n", " if currentLine:\n", " # La ligne n'est pas vide, ce n'est pas l'exception\n", " for namePerso in persoDict:\n", " # On vérifie si namePerso fait partie des protagonistes\n", " m = re.search(namePerso, currentLine, re.IGNORECASE)\n", " if m is not None:\n", " # Le résultat de la recherche n'est pas vide,\n", " # le personnage fait partie des protagonistes\n", " # on l'ajoute à la liste des personnages de la scène\n", " scenePersoList.append(m.group(0))\n", " print(scenePersoList)\n", " else:\n", " # La ligne est vide on sait que c'est le cas particulier\n", " # le seul protagoniste est Harpagon\n", " scenePersoList.append('Harpagon')\n", " previousLineIsScene = False\n", " \n", " \n", " currentLine = fileToAnalyse.readline()\n", " # On lit une ligne on incrémente donc le numéro de la ligne courante dans le fichier\n", " lineNum += 1\n" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "##################################################\n", "Contenu du dictionnaire des personnages initialisé\n", "##################################################\n", "\n", "{'Harpagon': {'links': [\"Père de Cléante et d'Élise\", 'Amoureux de Mariane'], 'color': None}, 'Cléante': {'links': [\"Fils d'Harpagon\", 'Amant de Mariane'], 'color': None}, 'Élise': {'links': [\"Fille d'Harpagon\", 'Amante de Valère'], 'color': None}, 'Valère': {'links': [\"Fils d'Anselme\", \"Amant d'Élise\"], 'color': None}, 'Mariane': {'links': ['Amante de Cléante', \"aimée d'Harpagon\"], 'color': None}, 'Anselme': {'links': ['Père de Valère et de Mariane'], 'color': None}, 'Frosine': {'links': [\"Femme d'Intrigue\"], 'color': None}, 'Maitre Simon': {'links': ['Courtier'], 'color': None}, 'Maitre Jacques': {'links': [\"Cuisinier et Cocher d'Harpagon\"], 'color': None}, 'La Flèche': {'links': ['Valet de Cléante'], 'color': None}, 'Dame Claude': {'links': [\"Servante d'Harpagon\"], 'color': None}, 'Brindavoine': {'links': [\"laquais d'Harpagon\"], 'color': None}, 'La Merluche': {'links': [\"laquais d'Harpagon\"], 'color': None}, 'Le commissaire': {'links': ['son clerc'], 'color': None}}\n", "\n", "##################################################\n", "## Acte Premier.\n", "\n", "### Scène Première.\n", "\n", "['Élise', 'Valère']\n", "### Scène II.\n", "\n", "['Cléante', 'Élise']\n", "### Scène III.\n", "\n", "['Harpagon', 'La Flèche']\n", "### Scène IV.\n", "\n", "['Harpagon', 'Cléante', 'Élise']\n", "### Scène V.\n", "\n", "['Harpagon', 'Élise', 'Valère']\n", "## Acte II.\n", "\n", "### Scène Première.\n", "\n", "['Cléante', 'La Flèche']\n", "### Scène II.\n", "\n", "['Harpagon', 'Cléante', 'La Flèche']\n", "### Scène III.\n", "\n", "['Harpagon', 'Frosine']\n", "### Scène IV.\n", "\n", "['Frosine', 'La Flèche']\n", "### Scène V.\n", "\n", "['Harpagon', 'Frosine']\n", "## Acte III.\n", "\n", "### Scène Première.\n", "\n", "['Harpagon', 'Cléante', 'Élise', 'Valère', 'Dame Claude', 'Brindavoine', 'La Merluche']\n", "### Scène II.\n", "\n", "['Valère']\n", "### Scène III.\n", "\n", "['Mariane', 'Frosine']\n", "### Scène IV.\n", "\n", "['Mariane', 'Frosine']\n", "### Scène V.\n", "\n", "['Harpagon', 'Mariane', 'Frosine']\n", "### Scène VI.\n", "\n", "['Harpagon', 'Élise', 'Mariane', 'Frosine']\n", "### Scène VII.\n", "\n", "['Harpagon', 'Cléante', 'Élise', 'Mariane', 'Frosine']\n", "### Scène VIII.\n", "\n", "['Harpagon', 'Cléante', 'Élise', 'Mariane', 'Frosine', 'Brindavoine']\n", "### Scène IX.\n", "\n", "['Harpagon', 'Cléante', 'Élise', 'Mariane', 'Frosine', 'La Merluche']\n", "## Acte IV.\n", "\n", "### Scène Première.\n", "\n", "['Cléante', 'Élise', 'Mariane', 'Frosine']\n", "### Scène II.\n", "\n", "['Harpagon', 'Cléante', 'Élise', 'Mariane', 'Frosine']\n", "### Scène III.\n", "\n", "['Harpagon', 'Cléante']\n", "### Scène IV.\n", "\n", "['Harpagon', 'Cléante']\n", "### Scène V.\n", "\n", "['Harpagon', 'Cléante']\n", "### Scène VI.\n", "\n", "['Cléante', 'La Flèche']\n", "### Scène VII.\n", "\n", "[]\n", "## Acte V.\n", "\n", "### Scène Première.\n", "\n", "['Harpagon', 'Le Commissaire']\n", "### Scène II.\n", "\n", "['Harpagon', 'Le Commissaire']\n", "### Scène III.\n", "\n", "['Harpagon', 'Valère', 'le Commissaire']\n", "### Scène IV.\n", "\n", "['Harpagon', 'Élise', 'Valère', 'Mariane', 'Frosine', 'le Commissaire']\n", "### Scène V.\n", "\n", "['Harpagon', 'Élise', 'Valère', 'Mariane', 'Anselme', 'Frosine', 'le Commissaire']\n", "### Scène VI.\n", "\n", "['Harpagon', 'Cléante', 'Élise', 'Valère', 'Mariane', 'Anselme', 'Frosine', 'La Flèche', 'le Commissaire']\n" ] } ], "source": [ "# On ouvre le fichier local en lecture 'r' pour en faire l'analyse\n", "# en utilisant l'instruction with qui se chargera de fermer\n", "# le fichier une fois sortie de l'instruction.\n", "# (pas d'erreur possible par oubli d'appel à l'instruction close)\n", "# Un rapide coup d'oeil au fichier texte nous montre une organisation\n", "# , des symboles en début de ligne etc, que l'on va utiliser pour \"parser\"\n", "# le fichier, à savoir le lire de manière à ranger les données\n", "# de manière intelligente dans une structure de données facilitant\n", "# la manipulation et l'analyse. \n", "with open(local_filename,'r') as avareFile:\n", " # On va commencer par parser le fichier afin de récupérer la liste des personnages\n", " lineNum = fill_perso_dict(avareFile, avarePersoDict)\n", " \n", " print(\"##################################################\")\n", " print(\"Contenu du dictionnaire des personnages initialisé\")\n", " print(\"##################################################\\n\")\n", " print(avarePersoDict)\n", " print(\"\\n##################################################\")\n", " \n", " textDataSynthesisTableDf = generate_text_synthesis_data_table(avareFile, avarePersoDict, avareAnalysisDict)\n", "\n", "# Le tableau de données récapitulatif est maintenant prêt à être utilisé\n", "\n", " " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Notes pour plus tard: lien stack overflow vers code de customisation de graphes de la bibliothèque python networkx\n", "https://stackoverflow.com/questions/25639169/networkx-change-color-width-according-to-edge-attributes-inconsistent-result\n", "lien github vers morceau de code ajoutant de la couleur et le réglage de l'épaisseur des arêtes sur un graphe\n", "https://gist.github.com/AruniRC/2c53fe7680eeb578593ec816bbfb1653\n", "Lien vers une page donnant un exemple d'affichage par ensemble de barres\n", "https://www.geeksforgeeks.org/stacked-percentage-bar-plot-in-matplotlib/" ] } ], "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.6.4" } }, "nbformat": 4, "nbformat_minor": 2 }