diff --git a/Analyse_Articles.ipynb b/Analyse_Articles.ipynb index 6367c5d..ad87ac5 100644 --- a/Analyse_Articles.ipynb +++ b/Analyse_Articles.ipynb @@ -8,7 +8,7 @@ "\n", "## Lecture des fichiers de données et affichage d'un échantillon de données\n", "\n", - "### Articles" + "### Lecture des données des articles" ] }, { @@ -17,7 +17,27 @@ "metadata": {}, "outputs": [], "source": [ - "import pickle" + "import pickle\n", + "import pandas as pd\n", + "import wn_pos_from_ud_pos as wnud\n", + "from nltk.corpus import wordnet as wn\n", + "from french_lefff_lemmatizer.french_lefff_lemmatizer import FrenchLefffLemmatizer\n", + "import seaborn as sns\n", + "import numpy as np\n", + "import emoji\n", + "import ast\n", + "from nltk.tokenize import TweetTokenizer" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from nltk.parse.corenlp import CoreNLPServer\n", + "from nltk.parse.corenlp import CoreNLPParser\n", + "parser = CoreNLPParser()" ] }, { @@ -31,6 +51,13 @@ "f.close()" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Aperçu des données" + ] + }, { "cell_type": "code", "execution_count": null, @@ -44,7 +71,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### Commentaires" + "### Lecture des données des commentaires" ] }, { @@ -61,12 +88,23 @@ { "cell_type": "code", "execution_count": null, - "metadata": {}, + "metadata": { + "jupyter": { + "source_hidden": true + } + }, "outputs": [], "source": [ "commentaires_df = commentaires_df[commentaires_df[\"media\"]!='CNN']" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Aperçu des données" + ] + }, { "cell_type": "code", "execution_count": null, @@ -148,7 +186,7 @@ "metadata": {}, "outputs": [], "source": [ - "decompte_commentaires.to_latex(\"decompte_comm_medias.tex\",)" + "decompte_commentaires.to_latex(\"decompte_comm_medias.tex\")" ] }, { @@ -181,8 +219,9 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Niveau de langage\n", - "## Nombre de jetons dans WordNet\n", + "# Analyse des commentaires\n", + "## Niveau de langage\n", + "### Nombre de jetons dans WordNet\n", "\n", "On utilise le POS tag identifié depuis Stanford POS Tagger, puis on le convertis en tag compatible pour Wordnet. On recherche ensuite le mot lemmatisé dans Wordnet en français, puis on filtre les résultats avec le POS. Ceci permet d'identifier tous les synsets réalistes pour les mots du commentaire." ] @@ -193,37 +232,9 @@ "metadata": {}, "outputs": [], "source": [ - "from nltk.corpus import wordnet as wn\n", - "from french_lefff_lemmatizer.french_lefff_lemmatizer import FrenchLefffLemmatizer\n", "lemmatizer = FrenchLefffLemmatizer()" ] }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Conversion du tag de Stanford POS vers Wordnet POS" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "def wn_tag_from_ud(tag):\n", - " if tag=='ADJ':\n", - " return wn.ADJ\n", - " if tag=='NOUN':\n", - " return wn.NOUN\n", - " if tag=='VERB':\n", - " return wn.VERB\n", - " if tag=='ADV':\n", - " return wn.ADV\n", - " else:\n", - " return None" - ] - }, { "cell_type": "markdown", "metadata": {}, @@ -240,7 +251,7 @@ "def lem_fr(tokens):\n", " list_tokens = []\n", " for token in tokens:\n", - " wn_pos = wn_tag_from_ud(token[1])\n", + " wn_pos = wnud.wn_pos_from_ud_pos(token[1]) # conversion de Universal Dependancies vers WordNet\n", " if wn_pos is not None:\n", " lem_token = lemmatizer.lemmatize(token[0],pos=wn_pos)\n", " else:\n", @@ -274,7 +285,7 @@ "def synsets_fr(tokens):\n", " list_synsets = []\n", " for token in tokens:\n", - " wn_pos = wn_tag_from_ud(token[1])\n", + " wn_pos = wnud.wn_pos_from_ud_pos(token[1])\n", " if wn_pos is not None:\n", " synset = wn.synsets(token[0], lang='fra', pos=wn_pos)\n", " list_synsets.append(synset)\n", @@ -290,6 +301,515 @@ "commentaires_df[\"synsets\"] = commentaires_df.apply(lambda x: synsets_fr(x[\"pos_dict_lem\"]), axis=1)" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Nombre de mots ayant au moins un synset associé" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "commentaires_df[\"nb_synsets\"]=commentaires_df.apply(lambda y: sum([len(x)>0 for x in y[\"synsets\"]]), axis=1)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "commentaires_df[\"nb_pos\"]=commentaires_df.apply(lambda y: len(y[\"pos_dict\"]), axis=1)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "commentaires_df[\"prop_synsets_pos\"]=commentaires_df.apply(lambda y: y[\"nb_synsets\"]/max(y[\"nb_pos\"],1), axis=1)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "d000 = commentaires_df[[\"media\",\"nb_synsets\"]].copy()\n", + "d000[\"nb_synsets_tronq\"] = d000.apply(lambda x: min(20,x[\"nb_synsets\"]), axis=1)\n", + "g000 = sns.catplot(x=\"media\", y=\"nb_synsets_tronq\",\n", + " kind=\"boxen\",\n", + " data=d000);\n", + "g000.set_axis_labels(\"Média\", \"Nombre de POS ayant un synset\\n(tronqué à 20)\")\n", + "g000.despine(offset=10, trim=True)\n", + "g000.savefig(\"g000.pdf\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "d001 = commentaires_df[[\"media\",\"prop_synsets_pos\"]].copy()\n", + "g001 = sns.catplot(x=\"media\", y=\"prop_synsets_pos\",\n", + " kind=\"boxen\",\n", + " data=d001);\n", + "g001.set_axis_labels(\"Média\", \"Proportion de POS ayant un synset\")\n", + "g001.despine(offset=10, trim=True)\n", + "g001.savefig(\"g001.pdf\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Nombre de types de POS dans le commentaire" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "commentaires_df[\"nb_type_pos\"] = commentaires_df.apply(lambda y: len(set([x[1] for x in y[\"pos_dict\"]])), axis=1)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "d002 = commentaires_df[[\"media\",\"nb_type_pos\"]].copy()\n", + "g002 = sns.catplot(x=\"media\", y=\"nb_type_pos\",\n", + " kind=\"boxen\",\n", + " data=d002);\n", + "g002.set_axis_labels(\"Média\", \"Nombre de types de POS\")\n", + "g002.despine(offset=10, trim=True)\n", + "g002.savefig(\"g002.pdf\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Marqueurs d'emphase\n", + "\n", + "### Nombre d'emojis" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def dict_emojis_to_list(emoji_dict):\n", + " emojis = []\n", + " for key in emoji_dict:\n", + " for value in emoji_dict[key]:\n", + " emojis.append(key)\n", + " return emojis" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "commentaires_df[\"emoji_list\"] = commentaires_df.apply(lambda x: dict_emojis_to_list(x[\"emoji_dict\"]), axis=1)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "d003 = commentaires_df[[\"media\",\"emoji_list\"]].copy()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "d003df = pd.DataFrame({'media':np.repeat(d003.media.values, d003.emoji_list.str.len()),\n", + " 'emoji':np.concatenate(d003.emoji_list.values),\n", + " 'value':1})" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "d003cnt1 = d003df.groupby([\"media\",\"emoji\"]).agg(['sum']).reset_index(['media','emoji'])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "d003cnt1.columns = [\"media\",\"emoji\",\"media_count\"]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "d003cnt2 = d003df.groupby(\"emoji\").agg(['count']).reset_index(\"emoji\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "d003cnt2 = d003cnt2[[\"emoji\",\"media\"]]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "d003cnt2.columns = [\"emoji\",\"total_count\"]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "d003cntx = d003cnt1[d003cnt1.emoji != \":\"].merge(d003cnt2,on=\"emoji\").sort_values(\"total_count\",ascending=False).head(30)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "del d003cntx[\"total_count\"]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "g003 = sns.catplot(y=\"emoji\", x=\"media_count\", hue=\"media\", \n", + " kind=\"bar\",\n", + " palette=\"pastel\", edgecolor=\".6\",\n", + " data=d003cntx)\n", + "g003.set_axis_labels(\"Fréquence\", \"Forme textuelle des emojis\")\n", + "g003.despine(offset=10, trim=True)\n", + "g003.savefig(\"g003.pdf\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Mots en majuscules" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "commentaires_df['nb_mots_majuscules'] = commentaires_df.apply(lambda x: len([word for word,_ in x[\"pos_dict\"] if word.isupper()]), axis=1)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "d004 = commentaires_df[['media','nb_mots_majuscules']].copy()\n", + "g004 = sns.catplot(x=\"media\", y=\"nb_mots_majuscules\",\n", + " kind=\"boxen\",\n", + " data=d004);\n", + "g004.set_axis_labels(\"Média\", \"Nombre de mots en majuscules\")\n", + "g004.despine(offset=10, trim=True)\n", + "g004.savefig(\"g004.pdf\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Ponctuations successives" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import re\n", + "punct_re = re.compile(r'([^\\w\\s\\:]{2,})')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "demo = emoji.demojize(commentaires_df['comment_clean'][37765])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "### Ponctuations successives\n", + "def nb_punct_succ(x):\n", + " seq_punct = re.findall(punct_re,x)\n", + " return sum([len(s) for s in seq_punct])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "commentaires_df['nb_punct_succ'] = commentaires_df.apply(lambda x: nb_punct_succ(x['comment_clean']), axis=1)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "d005 = commentaires_df[commentaires_df.nb_punct_succ > 0][['media','nb_punct_succ']].copy()\n", + "g005 = sns.catplot(x=\"media\", y=\"nb_punct_succ\",\n", + " kind=\"boxen\",\n", + " data=d005);\n", + "g005.set_axis_labels(\"Média\", \"Nombre ponctuations successives totales\")\n", + "g005.despine(offset=10, trim=True)\n", + "g005.savefig(\"g005.pdf\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Intertextualité\n", + "\n", + "### Mention de l'auteur d'un autre commentaire" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "commentaires_df['ind_auteurs_referes'] = commentaires_df.apply(lambda x: len(ast.literal_eval(x['auteurs_referes']))>0, axis=1) # j'avais sauvegardé une représentation littérale de la liste ..." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "decompte_auteur_refere = commentaires_df[['media','ind_auteurs_referes']].groupby('media').agg('mean').reset_index('media')\n", + "decompte_auteur_refere.columns = ['Media','Proportion']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "decompte_auteur_refere.to_latex(\"decompte_auteur_refere.tex\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Pronoms et articles possessifs à la deuxième personne" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "tok = TweetTokenizer(preserve_case=False)\n", + "mots_2epers = ['ton','ta','tes','toi','te','tu','t\\'','vous','votre','vos']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "commentaires_df['ind_2e_pers'] = commentaires_df.apply(lambda x: len([token for token in tok.tokenize(x['comment_clean']) if token in mots_2epers])>0, axis=1)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "decompte_2e_pers = commentaires_df[['media','ind_2e_pers']].groupby('media').agg('mean').reset_index('media')\n", + "decompte_2e_pers.columns = ['Media','Proportion']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "decompte_2e_pers.to_latex(\"decompte_2e_pers.tex\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Analyse des liens entre les commentaires et les articles\n", + "\n", + "## Entités nommées" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "d006a = commentaires_df[commentaires_df['ner_dict'] != {}][['media','id','post_id','ner_dict']].copy()\n", + "d006a['ner_list_comm'] = commentaires_df.apply(lambda x: list(x['ner_dict'].keys()), axis=1)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "del d006a['ner_dict']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "d006b = textes_articles_df[textes_articles_df['ner_dict'] != {}][['media','post_id','ner_dict']].copy()\n", + "d006b['ner_list_art'] = textes_articles_df.apply(lambda x: list(x['ner_dict'].keys()), axis=1)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "del d006b['ner_dict']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "d006 = d006a.merge(d006b, on=['post_id','media'])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def intersection(lst1, lst2): \n", + " lst3 = [value for value in lst1 if value in lst2] \n", + " return lst3 " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "d006['ner_list_inter'] = d006.apply(lambda x: len(intersection(x['ner_list_comm'],x['ner_list_art'])), axis=1)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "prop_ner_inter_post = d006[['media','post_id','ner_list_inter']].groupby(['media','post_id']).agg('mean').reset_index(['media','post_id'])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "prop_ner_inter_post.columns = ['media','post_id','prop_ner_list_inter']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "g006 = sns.catplot(x=\"media\", y=\"prop_ner_list_inter\",\n", + " kind=\"boxen\",\n", + " data=prop_ner_inter_post);\n", + "g006.set_axis_labels(\"Média\", \"Nombre moyen d'entités nommées communes par commentaire\")\n", + "g006.despine(offset=10, trim=True)\n", + "g006.savefig(\"g006.pdf\")" + ] + }, { "cell_type": "code", "execution_count": null, diff --git a/Makefile b/Makefile index 2623e8a..ead98d1 100644 --- a/Makefile +++ b/Makefile @@ -1,2 +1,2 @@ build: rapport.md - pandoc --filter=pandoc-citeproc -f markdown+raw_tex+latex_macros rapport.md -o rapport.pdf + pandoc --filter=pandoc-citeproc --dpi=180 -f markdown+raw_tex+latex_macros rapport.md -o rapport.pdf diff --git a/NLP-TP3.bib b/NLP-TP3.bib index 2c63a9c..d7e7b4e 100644 --- a/NLP-TP3.bib +++ b/NLP-TP3.bib @@ -100,15 +100,6 @@ year = {2002} } -@book{zeman_universal_2019, - title = {Universal {Dependencies} 2.5}, - copyright = {Licence Universal Dependencies v2.5}, - url = {http://hdl.handle.net/11234/1-3105}, - author = {Zeman, Daniel and Nivre, Joakim and Abrams, Mitchell and Aepli, Noëmi and Agić, Željko and Ahrenberg, Lars and Aleksandravičiūtė, Gabrielė and Antonsen, Lene and Aplonova, Katya and Aranzabe, Maria Jesus and Arutie, Gashaw and Asahara, Masayuki and Ateyah, Luma and Attia, Mohammed and Atutxa, Aitziber and Augustinus, Liesbeth and Badmaeva, Elena and Ballesteros, Miguel and Banerjee, Esha and Bank, Sebastian and Barbu Mititelu, Verginica and Basmov, Victoria and Batchelor, Colin and Bauer, John and Bellato, Sandra and Bengoetxea, Kepa and Berzak, Yevgeni and Bhat, Irshad Ahmad and Bhat, Riyaz Ahmad and Biagetti, Erica and Bick, Eckhard and Bielinskienė, Agnė and Blokland, Rogier and Bobicev, Victoria and Boizou, Loïc and Borges Völker, Emanuel and Börstell, Carl and Bosco, Cristina and Bouma, Gosse and Bowman, Sam and Boyd, Adriane and Brokaitė, Kristina and Burchardt, Aljoscha and Candito, Marie and Caron, Bernard and Caron, Gauthier and Cavalcanti, Tatiana and Cebiroğlu Eryiğit, Gülşen and Cecchini, Flavio Massimiliano and Celano, Giuseppe G. A. and Čéplö, Slavomír and Cetin, Savas and Chalub, Fabricio and Choi, Jinho and Cho, Yongseok and Chun, Jayeol and Cignarella, Alessandra T. and Cinková, Silvie and Collomb, Aurélie and Çöltekin, Çağrı and Connor, Miriam and Courtin, Marine and Davidson, Elizabeth and de Marneffe, Marie-Catherine and de Paiva, Valeria and de Souza, Elvis and Diaz de Ilarraza, Arantza and Dickerson, Carly and Dione, Bamba and Dirix, Peter and Dobrovoljc, Kaja and Dozat, Timothy and Droganova, Kira and Dwivedi, Puneet and Eckhoff, Hanne and Eli, Marhaba and Elkahky, Ali and Ephrem, Binyam and Erina, Olga and Erjavec, Tomaž and Etienne, Aline and Evelyn, Wograine and Farkas, Richárd and Fernandez Alcalde, Hector and Foster, Jennifer and Freitas, Cláudia and Fujita, Kazunori and Gajdošová, Katarína and Galbraith, Daniel and Garcia, Marcos and Gärdenfors, Moa and Garza, Sebastian and Gerdes, Kim and Ginter, Filip and Goenaga, Iakes and Gojenola, Koldo and Gökırmak, Memduh and Goldberg, Yoav and Gómez Guinovart, Xavier and González Saavedra, Berta and Griciūtė, Bernadeta and Grioni, Matias and Gr{\textbackslash}= uzītis, Normunds and Guillaume, Bruno and Guillot-Barbance, Céline and Habash, Nizar and Hajič, Jan and Hajič jr., Jan and Hämäläinen, Mika and Hà Mỹ, Linh and Han, Na-Rae and Harris, Kim and Haug, Dag and Heinecke, Johannes and Hennig, Felix and Hladká, Barbora and Hlaváčová, Jaroslava and Hociung, Florinel and Hohle, Petter and Hwang, Jena and Ikeda, Takumi and Ion, Radu and Irimia, Elena and Ishola, Ọlájídé and Jelínek, Tomáš and Johannsen, Anders and Jørgensen, Fredrik and Juutinen, Markus and Kaşıkara, Hüner and Kaasen, Andre and Kabaeva, Nadezhda and Kahane, Sylvain and Kanayama, Hiroshi and Kanerva, Jenna and Katz, Boris and Kayadelen, Tolga and Kenney, Jessica and Kettnerová, Václava and Kirchner, Jesse and Klementieva, Elena and Köhn, Arne and Kopacewicz, Kamil and Kotsyba, Natalia and Kovalevskaitė, Jolanta and Krek, Simon and Kwak, Sookyoung and Laippala, Veronika and Lambertino, Lorenzo and Lam, Lucia and Lando, Tatiana and Larasati, Septina Dian and Lavrentiev, Alexei and Lee, John and Lê H{\textbackslash}`ông, Phương and Lenci, Alessandro and Lertpradit, Saran and Leung, Herman and Li, Cheuk Ying and Li, Josie and Li, Keying and Lim, KyungTae and Liovina, Maria and Li, Yuan and Ljubešić, Nikola and Loginova, Olga and Lyashevskaya, Olga and Lynn, Teresa and Macketanz, Vivien and Makazhanov, Aibek and Mandl, Michael and Manning, Christopher and Manurung, Ruli and Mărănduc, Cătălina and Mareček, David and Marheinecke, Katrin and Martínez Alonso, Héctor and Martins, André and Mašek, Jan and Matsumoto, Yuji and McDonald, Ryan and McGuinness, Sarah and Mendonça, Gustavo and Miekka, Niko and Misirpashayeva, Margarita and Missilä, Anna and Mititelu, Cătălin and Mitrofan, Maria and Miyao, Yusuke and Montemagni, Simonetta and More, Amir and Moreno Romero, Laura and Mori, Keiko Sophie and Morioka, Tomohiko and Mori, Shinsuke and Moro, Shigeki and Mortensen, Bjartur and Moskalevskyi, Bohdan and Muischnek, Kadri and Munro, Robert and Murawaki, Yugo and Müürisep, Kaili and Nainwani, Pinkey and Navarro Horñiacek, Juan Ignacio and Nedoluzhko, Anna and Nešpore-Bērzkalne, Gunta and Nguy∼ên Thị, Lương and Nguy∼ên Thị Minh, Huy{\textbackslash}`ên and Nikaido, Yoshihiro and Nikolaev, Vitaly and Nitisaroj, Rattima and Nurmi, Hanna and Ojala, Stina and Ojha, Atul Kr. and Olúòkun, Adédayọ̀ and Omura, Mai and Osenova, Petya and Östling, Robert and Øvrelid, Lilja and Partanen, Niko and Pascual, Elena and Passarotti, Marco and Patejuk, Agnieszka and Paulino-Passos, Guilherme and Peljak-{\textbackslash}Lapińska, Angelika and Peng, Siyao and Perez, Cenel-Augusto and Perrier, Guy and Petrova, Daria and Petrov, Slav and Phelan, Jason and Piitulainen, Jussi and Pirinen, Tommi A and Pitler, Emily and Plank, Barbara and Poibeau, Thierry and Ponomareva, Larisa and Popel, Martin and Pretkalniņa, Lauma and Prévost, Sophie and Prokopidis, Prokopis and Przepiórkowski, Adam and Puolakainen, Tiina and Pyysalo, Sampo and Qi, Peng and Rääbis, Andriela and Rademaker, Alexandre and Ramasamy, Loganathan and Rama, Taraka and Ramisch, Carlos and Ravishankar, Vinit and Real, Livy and Reddy, Siva and Rehm, Georg and Riabov, Ivan and Rießler, Michael and Rimkutė, Erika and Rinaldi, Larissa and Rituma, Laura and Rocha, Luisa and Romanenko, Mykhailo and Rosa, Rudolf and Rovati, Davide and Roșca, Valentin and Rudina, Olga and Rueter, Jack and Sadde, Shoval and Sagot, Benoît and Saleh, Shadi and Salomoni, Alessio and Samardžić, Tanja and Samson, Stephanie and Sanguinetti, Manuela and Särg, Dage and Saulīte, Baiba and Sawanakunanon, Yanin and Schneider, Nathan and Schuster, Sebastian and Seddah, Djamé and Seeker, Wolfgang and Seraji, Mojgan and Shen, Mo and Shimada, Atsuko and Shirasu, Hiroyuki and Shohibussirri, Muh and Sichinava, Dmitry and Silveira, Aline and Silveira, Natalia and Simi, Maria and Simionescu, Radu and Simkó, Katalin and Šimková, Mária and Simov, Kiril and Smith, Aaron and Soares-Bastos, Isabela and Spadine, Carolyn and Stella, Antonio and Straka, Milan and Strnadová, Jana and Suhr, Alane and Sulubacak, Umut and Suzuki, Shingo and Szántó, Zsolt and Taji, Dima and Takahashi, Yuta and Tamburini, Fabio and Tanaka, Takaaki and Tellier, Isabelle and Thomas, Guillaume and Torga, Liisi and Trosterud, Trond and Trukhina, Anna and Tsarfaty, Reut and Tyers, Francis and Uematsu, Sumire and Urešová, Zdeňka and Uria, Larraitz and Uszkoreit, Hans and Utka, Andrius and Vajjala, Sowmya and van Niekerk, Daniel and van Noord, Gertjan and Varga, Viktor and Villemonte de la Clergerie, Eric and Vincze, Veronika and Wallin, Lars and Walsh, Abigail and Wang, Jing Xian and Washington, Jonathan North and Wendt, Maximilan and Williams, Seyi and Wirén, Mats and Wittern, Christian and Woldemariam, Tsegay and Wong, Tak-sum and Wróblewska, Alina and Yako, Mary and Yamazaki, Naoki and Yan, Chunxiao and Yasuoka, Koichi and Yavrumyan, Marat M. and Yu, Zhuoran and Žabokrtský, Zdeněk and Zeldes, Amir and Zhang, Manying and Zhu, Hanzhi}, - year = {2019}, - annote = {LINDAT/CLARIN digital library at the Institute of Formal and Applied Linguistics (ÚFAL), Faculty of Mathematics and Physics, Charles University} -} - @misc{coulombe_french_2019, title = {French {LEFFF} {Lemmatizer}}, url = {https://github.com/ClaudeCoulombe/FrenchLefffLemmatizer/commit/91c2f469bbd317213b8438072496eb1ed696a8e7}, @@ -117,4 +108,12 @@ author = {Coulombe, Claude}, month = jul, year = {2019} +} + +@book{noauthor_universal_2019, + title = {Universal {Dependencies} 2.5}, + copyright = {Licence Universal Dependencies v2.5}, + url = {http://hdl.handle.net/11234/1-3105}, + year = {2019}, + annote = {LINDAT/CLARIN digital library at the Institute of Formal and Applied Linguistics (ÚFAL), Faculty of Mathematics and Physics, Charles University} } \ No newline at end of file diff --git a/Traitement Articles.ipynb b/Traitement Articles.ipynb index 4d01720..41d4cc7 100644 --- a/Traitement Articles.ipynb +++ b/Traitement Articles.ipynb @@ -118,7 +118,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.7.3" + "version": "3.7.4" } }, "nbformat": 4, diff --git a/rapport.md b/rapport.md index 301df3a..b117704 100644 --- a/rapport.md +++ b/rapport.md @@ -8,7 +8,7 @@ toc: yes toc-depth: 1 lang: fr-FR documentclass: "article" -fontsize: 12pt +fontsize: 11pt geometry: margin=1in bibliography: NLP-TP3.bib csl: transactions-on-speech-and-language-processing.csl @@ -107,7 +107,7 @@ Cette expertise est illustrée par un choix de vocabulaire spécifique au domain La prise de position relie le commentaire aux réalités socio-culturelles traitées dans l'article journalistique. Ces positions sont exprimées par des adjectifs et des adverbes évaluatifs, des verbes affectifs et cognitifs, des modalités, des pronoms génériques, de l'ironie, des questions rhétoriques, des citations et des paroles de chansons ainsi que l'utilisations de mécanismes d'emphase. On notera aussi l'usage de nombreuses images, notamment les *memes*, quoique ce n'est pas le sujet de ce rapport. -**Observations**: On remarque ici que l'on devra utiliser des étiquettes plus détaillés que celles qu'on retrouve notamment dans Universal Dependancies [@zeman_universal_2019] pour identifier les parties du discours impliquées dans la prise de position. Pour ce faire, il sera nécessaire d'utiliser conjointement les parties du discours et une base sémantique telle que WordNet pour augmenter le niveau de détail contenu dans les étiquettes. +**Observations**: On remarque ici que l'on devra utiliser des étiquettes plus détaillés que celles qu'on retrouve notamment dans Universal Dependancies [@noauthor_universal_2019] pour identifier les parties du discours impliquées dans la prise de position. Pour ce faire, il sera nécessaire d'utiliser conjointement les parties du discours et une base sémantique telle que WordNet pour augmenter le niveau de détail contenu dans les étiquettes. # Relations entre les commentaires @@ -147,16 +147,16 @@ Nous analyserons les articles provenant des pages Facebook de trois médias écr \begin{figure} \centering -\caption{Décompte des articles par médias} \input{decompte_articles_medias} +\caption{Décompte des articles par médias} \end{figure} Le premier corpus étudié est constitué du texte de chacun des articles qui sont liés dans les publications (l'utilisateur de Facebook devant cliquer sur le lien pour y accéder). Le titre de l'article n'est pas inclus dans ce corpus. Le second corpus est constitué d'un ensemble de commentaires publiés par des utilisateurs du réseau social et associés à chacune des publications précédentes. \begin{figure} \centering -\caption{Décompte des commentaires par médias} \input{decompte_comm_medias} +\caption{Décompte des commentaires par médias} \end{figure} Ces deux corpus ont été créés à l'aide des données de commentaires extraites depuis l'application en ligne exportcomments.com @noauthor_exportcomments.com_2019 dans des fichiers XLSX. Les fichiers ont par la suite été utilisés par les programmes Python suivants : @@ -164,42 +164,63 @@ Ces deux corpus ont été créés à l'aide des données de commentaires extrait - `commentaires.ipynb` pour extraire les commentaires depuis les fichiers téléchargés à l'aide de Pandas @mckinney_data_2010. - `textes_articles.ipynb` pour extraire les textes depuis les URL disponibles dans les fichiers, par récupération de données (*web scraping*), en utilisant la librairie Python `newspaper` @ou-yang_newspaper3k:_2019. Du même coup, cette librairie permet d'extraction d'entités nommées et l'étiquetage des parties du discours. -## Métriques considérées - -Niveau de langage: - -- Nombre de jetons dans WordNet -- Est-ce que le commentaire contient une phrase validée par une analyse syntaxique -- Nombre de POS par type - -Marqueurs d'emphase - -- Nombre d'emojis -- Présence de mots tout en majuscules -- Présence de ponctuations successives - -Entités - -- Correspondance du lieu avec l'article -- Correspondance des personnes avec l'article -- Nombre d'entités en commun avec l'article - -Expertise et prise de position - -- Correspondance du sujet -- Interrogation -- Mots-clés - -Intertextualité - -- Mention de l'auteur d'un autre commentaire -- Présence de pronoms à la deuxième personne - ## Méthodologie et algorithmes J'ai effectué la lemmatisation en français à l'aide du French LEFFF Lemmatizer de Claude Coulombe [@coulombe_french_2019], qui est compatible avec la syntaxe utilisée dans la librairie NLTK et les étiquettes POS utilisées dans WordNet. -## Quelques résultats +## Analyse statistique des commentaires + +### Distribution du niveau de langage + +![Nombre de jetons avec POS dans WordNet, par commentaire, par média](g000.pdf) + +![Proportion de jetons avec POS dans WordNet, par commentaire, par média](g001.pdf) + +![Nombre de types de POS avec classes fermées, par commentaire, par média](g002.pdf) + +### Distribution des marqueurs d'emphase + +![Emojis les plus fréquents, par médias](g003.pdf) + +![Nombre de mots en majuscules, par commentaire, par média](g004.pdf) + +![Nombre de ponctuations successives totales, par commentaire, par média](g005.pdf) + +### Indicateurs d'intertextualité + +Le principal indicateur de l'intertextualité est la référence directe à l'auteur d'un commentaire précédent. + +\begin{figure} +\centering +\input{decompte_auteur_refere} +\caption{Proportion de références d'auteurs de commentaires par médias} +\end{figure} + +On remarque ici que la proportion de commentaires qui contiennent de telles références est constante et est aussi relativement élevée, peu importe le média. On pourrait donc conclure qu'il s'agit d'un attribut possédant de bonnes caractéristiques pour un modèle de classification binaire. + +Un autre indicateur de l'intertextualité est l'usage de la deuxième personne, autant pour les pronoms que pous les articles possessifs. Cependant, ici, il n'est pas possible de savoir, sans devoir faire une analyse des coréférences, si la personne référée est l'auteur d'un commentaire ou une personne mentionnée dans l'article. + +\begin{figure} +\centering +\input{decompte_2e_pers} +\caption{Proportion de commentaires avec des pronoms ou articles à la 2e personne, par médias} +\end{figure} + +Cet indicateur possède aussi de bonnes caractéristiques pour être un attribut dans un modèle de classification. + +## Analyse statistique des relations entre commentaires et articles + +### Entités nommées en commun + +Une façon d'évaluer la pertinence d'un commentaire est de dénombrer le nombre d'entités nommées communes entre ce commentaire et l'article auquel il réfère. Ici, on calcule, pour chaque commentaire, le nombre d'entités en commun avec l'article, puis on effectue la moyenne par article. C'est cette statistique qui est représentée sur la figure qui suit. + +![Nombre moyen d'entités nommées en commun entre le commentaire et l'article, par article et par média](g006.pdf) + +On remarque que le nombre moyen d'entités référées par commentaire est d'environ 1/5. On peut donc dire qu'en moyenne, 20% des commentaires réfèrent à une entité nommée de l'article. Afin d'augmenter potentiellement ce nombre, on pourrait étendre la liste des entités nommées à l'aide de relations sémantiques de méronymie. + +### Groupes sémantiques en commun + +- Synsets # Conclusion