diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..08c0aa9c25ceaa5274aaafb9551fe0b35fcd4b94 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +.idea +venv +*.csv +*.html \ No newline at end of file diff --git a/20230323-unsupervised-clustering-lda-first-experiments.ipynb b/20230323-unsupervised-clustering-lda-first-experiments.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..1a77a710a25d0694712acef6e89be19f35ddc8bf --- /dev/null +++ b/20230323-unsupervised-clustering-lda-first-experiments.ipynb @@ -0,0 +1,1291 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "source": [ + "# Unsupervised clustering\n", + "\n", + "## Dataset loading" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": 150, + "outputs": [ + { + "data": { + "text/plain": " title_texte 85 44 50 \\\n1 ipa supply equipment increase competitiveness... False True False \n3 provision language training service tender in... False False False \n4 service support eda helicopter portfolio main... False False False \n5 NUMBER cp op NUMBER pooling share cost non co... False False False \n6 edf supply transport household similar waste ... False False False \n\n 80 73 45 71 79 90 ... 18 03 24 43 \\\n1 False False False False False False ... False False False False \n3 True False False False False False ... False False False False \n4 True False False False False False ... False False False False \n5 False True False False False False ... False False False False \n6 False False True False False False ... False False False False \n\n 19 41 37 14 16 76 \n1 False False False False False False \n3 False False False False False False \n4 False False False False False False \n5 False False False False False False \n6 False False False False False False \n\n[5 rows x 46 columns]", + "text/html": "<div>\n<style scoped>\n .dataframe tbody tr th:only-of-type {\n vertical-align: middle;\n }\n\n .dataframe tbody tr th {\n vertical-align: top;\n }\n\n .dataframe thead th {\n text-align: right;\n }\n</style>\n<table border=\"1\" class=\"dataframe\">\n <thead>\n <tr style=\"text-align: right;\">\n <th></th>\n <th>title_texte</th>\n <th>85</th>\n <th>44</th>\n <th>50</th>\n <th>80</th>\n <th>73</th>\n <th>45</th>\n <th>71</th>\n <th>79</th>\n <th>90</th>\n <th>...</th>\n <th>18</th>\n <th>03</th>\n <th>24</th>\n <th>43</th>\n <th>19</th>\n <th>41</th>\n <th>37</th>\n <th>14</th>\n <th>16</th>\n <th>76</th>\n </tr>\n </thead>\n <tbody>\n <tr>\n <th>1</th>\n <td>ipa supply equipment increase competitiveness...</td>\n <td>False</td>\n <td>True</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>...</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n </tr>\n <tr>\n <th>3</th>\n <td>provision language training service tender in...</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>True</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>...</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n </tr>\n <tr>\n <th>4</th>\n <td>service support eda helicopter portfolio main...</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>True</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>...</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n </tr>\n <tr>\n <th>5</th>\n <td>NUMBER cp op NUMBER pooling share cost non co...</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>True</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>...</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n </tr>\n <tr>\n <th>6</th>\n <td>edf supply transport household similar waste ...</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>True</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>...</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n </tr>\n </tbody>\n</table>\n<p>5 rows × 46 columns</p>\n</div>" + }, + "execution_count": 150, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "import pandas as pd\n", + "\n", + "df = pd.read_csv(\"20230214-dataset_preprocessed_with_lemma.csv\", index_col=0)\n", + "df.head()" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": 151, + "outputs": [ + { + "data": { + "text/plain": "((11647, 46), (2912, 46))" + }, + "execution_count": 151, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from sklearn.model_selection import train_test_split\n", + "\n", + "cpvs = [c for c in df.columns if len(c) == 2]\n", + "df_train, df_test = train_test_split(df, test_size=0.2, shuffle=False)\n", + "(df_train.shape, df_test.shape)" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": 152, + "outputs": [ + { + "data": { + "text/plain": "{'85': 256,\n '44': 103,\n '50': 297,\n '80': 403,\n '73': 1067,\n '45': 731,\n '71': 1621,\n '79': 2682,\n '90': 629,\n '30': 266,\n '35': 145,\n '33': 158,\n '55': 117,\n '72': 914,\n '48': 199,\n '38': 289,\n '09': 128,\n '75': 277,\n '66': 206,\n '64': 148,\n '42': 159,\n '34': 199,\n '60': 122,\n '92': 169,\n '39': 188,\n '31': 139,\n '98': 123,\n '51': 50,\n '32': 185,\n '65': 29,\n '77': 83,\n '22': 61,\n '63': 144,\n '15': 43,\n '70': 44,\n '18': 35,\n '03': 31,\n '24': 30,\n '43': 17,\n '19': 7,\n '41': 13,\n '37': 13,\n '14': 16,\n '16': 5,\n '76': 5}" + }, + "execution_count": 152, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "{c: df_train[c].sum() for c in cpvs}" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": 153, + "outputs": [ + { + "data": { + "text/plain": "{'85': 62,\n '44': 23,\n '50': 68,\n '80': 99,\n '73': 249,\n '45': 191,\n '71': 404,\n '79': 688,\n '90': 176,\n '30': 70,\n '35': 36,\n '33': 38,\n '55': 30,\n '72': 197,\n '48': 43,\n '38': 61,\n '09': 34,\n '75': 77,\n '66': 46,\n '64': 37,\n '42': 27,\n '34': 50,\n '60': 41,\n '92': 43,\n '39': 49,\n '31': 36,\n '98': 35,\n '51': 8,\n '32': 40,\n '65': 15,\n '77': 22,\n '22': 20,\n '63': 34,\n '15': 5,\n '70': 14,\n '18': 5,\n '03': 5,\n '24': 11,\n '43': 1,\n '19': 5,\n '41': 2,\n '37': 3,\n '14': 6,\n '16': 4,\n '76': 3}" + }, + "execution_count": 153, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "{c: df_test[c].sum() for c in cpvs}" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "markdown", + "source": [ + "## Topic creation using LDA" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": 154, + "outputs": [ + { + "data": { + "text/plain": "1 [, ipa, supply, equipment, increase, competiti...\n3 [, provision, language, training, service, ten...\n4 [, service, support, eda, helicopter, portfoli...\n5 [, NUMBER, cp, op, NUMBER, pooling, share, cos...\n6 [, edf, supply, transport, household, similar,...\nName: title_texte, dtype: object" + }, + "execution_count": 154, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "processed_docs = df_train[\"title_texte\"].apply(lambda x: x.split(\" \"))\n", + "processed_docs.head()" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": 155, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[(0, 'NUMBER'), (1, 'ask'), (2, 'authority'), (3, 'black'), (4, 'cheap'), (5, 'competitiveness'), (6, 'compliant'), (7, 'contract'), (8, 'contracting'), (9, 'countersign')]\n" + ] + } + ], + "source": [ + "import gensim\n", + "\n", + "dictionary = gensim.corpora.Dictionary(processed_docs)\n", + "dictionary.filter_extremes(no_below=15, no_above=0.5, keep_n=100000)\n", + "print(list(dictionary.iteritems())[:10])" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "markdown", + "source": [ + "### Using bag-of-words" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": 156, + "outputs": [ + { + "data": { + "text/plain": "[[(0, 2),\n (1, 1),\n (2, 1),\n (3, 1),\n (4, 1),\n (5, 1),\n (6, 1),\n (7, 6),\n (8, 1),\n (9, 1),\n (10, 1),\n (11, 1),\n (12, 1),\n (13, 1),\n (14, 3),\n (15, 1),\n (16, 1),\n (17, 1),\n (18, 1),\n (19, 1),\n (20, 1),\n (21, 1),\n (22, 1),\n (23, 3),\n (24, 1),\n (25, 1),\n (26, 1),\n (27, 1),\n (28, 1),\n (29, 1),\n (30, 1),\n (31, 1),\n (32, 1),\n (33, 1),\n (34, 1),\n (35, 1),\n (36, 4),\n (37, 1)],\n [(7, 1),\n (35, 1),\n (38, 1),\n (39, 1),\n (40, 1),\n (41, 1),\n (42, 1),\n (43, 1),\n (44, 1),\n (45, 1),\n (46, 1),\n (47, 1),\n (48, 1)],\n [(7, 1),\n (23, 1),\n (45, 1),\n (47, 3),\n (48, 1),\n (49, 1),\n (50, 1),\n (51, 2),\n (52, 1),\n (53, 1),\n (54, 1),\n (55, 1),\n (56, 1),\n (57, 1),\n (58, 1),\n (59, 2),\n (60, 1),\n (61, 2),\n (62, 1)],\n [(0, 3),\n (18, 1),\n (21, 1),\n (50, 1),\n (62, 1),\n (63, 2),\n (64, 1),\n (65, 1),\n (66, 1),\n (67, 1),\n (68, 2),\n (69, 1),\n (70, 1),\n (71, 1),\n (72, 1),\n (73, 1),\n (74, 1),\n (75, 1),\n (76, 1),\n (77, 1),\n (78, 1),\n (79, 1),\n (80, 1),\n (81, 1),\n (82, 1),\n (83, 1),\n (84, 1),\n (85, 1),\n (86, 1),\n (87, 1),\n (88, 1),\n (89, 1),\n (90, 2),\n (91, 1),\n (92, 1),\n (93, 1),\n (94, 1),\n (95, 2),\n (96, 1),\n (97, 1),\n (98, 1),\n (99, 1)],\n [(0, 2),\n (12, 1),\n (34, 1),\n (100, 1),\n (101, 1),\n (102, 1),\n (103, 1),\n (104, 1),\n (105, 1),\n (106, 1),\n (107, 1)],\n [(0, 2),\n (16, 1),\n (30, 1),\n (32, 1),\n (107, 1),\n (108, 2),\n (109, 2),\n (110, 1),\n (111, 1),\n (112, 1),\n (113, 1),\n (114, 1),\n (115, 1),\n (116, 1),\n (117, 2),\n (118, 1),\n (119, 1),\n (120, 1),\n (121, 1),\n (122, 1),\n (123, 1),\n (124, 1),\n (125, 1),\n (126, 1),\n (127, 1)],\n [(0, 1),\n (7, 1),\n (35, 1),\n (47, 2),\n (57, 1),\n (75, 1),\n (119, 1),\n (128, 1),\n (129, 1),\n (130, 1),\n (131, 1),\n (132, 1),\n (133, 1),\n (134, 1),\n (135, 1),\n (136, 1),\n (137, 1),\n (138, 1),\n (139, 2),\n (140, 1),\n (141, 1),\n (142, 1)],\n [(22, 1),\n (28, 1),\n (35, 5),\n (36, 1),\n (47, 1),\n (48, 4),\n (109, 1),\n (112, 1),\n (143, 2),\n (144, 1),\n (145, 1),\n (146, 1),\n (147, 1),\n (148, 1),\n (149, 2),\n (150, 1),\n (151, 2),\n (152, 1),\n (153, 2),\n (154, 1),\n (155, 2),\n (156, 1),\n (157, 3),\n (158, 1),\n (159, 1),\n (160, 1),\n (161, 1),\n (162, 1),\n (163, 1),\n (164, 1),\n (165, 1),\n (166, 1),\n (167, 1),\n (168, 1),\n (169, 1),\n (170, 1),\n (171, 1),\n (172, 1)],\n [(0, 3),\n (2, 1),\n (8, 1),\n (10, 1),\n (17, 1),\n (34, 1),\n (35, 1),\n (95, 1),\n (102, 1),\n (120, 1),\n (124, 1),\n (147, 1),\n (150, 1),\n (169, 1),\n (173, 1),\n (174, 1),\n (175, 1),\n (176, 1),\n (177, 1),\n (178, 1),\n (179, 1),\n (180, 1),\n (181, 1),\n (182, 2)],\n [(0, 2),\n (7, 3),\n (12, 1),\n (23, 1),\n (35, 1),\n (47, 5),\n (57, 1),\n (75, 2),\n (99, 1),\n (117, 1),\n (119, 1),\n (128, 1),\n (129, 1),\n (130, 2),\n (132, 2),\n (135, 1),\n (138, 1),\n (141, 1),\n (142, 2),\n (183, 1),\n (184, 3),\n (185, 1),\n (186, 1),\n (187, 1),\n (188, 1),\n (189, 1),\n (190, 1),\n (191, 1),\n (192, 2),\n (193, 1),\n (194, 1),\n (195, 1)]]" + }, + "execution_count": 156, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "bow_corpus = [dictionary.doc2bow(doc) for doc in processed_docs]\n", + "bow_corpus[:10]" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": 157, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "-- Topic: 0 --\n", + "0.091*\"service\" + 0.064*\"event\" + 0.032*\"medical\" + 0.031*\"travel\" + 0.028*\"provision\" + 0.026*\"organisation\" + 0.023*\"agency\" + 0.019*\"conference\" + 0.017*\"provide\" + 0.015*\"online\"\n", + "\n", + "-- Topic: 1 --\n", + "0.074*\"medium\" + 0.052*\"service\" + 0.046*\"european\" + 0.027*\"social\" + 0.026*\"parliament\" + 0.023*\"monitoring\" + 0.022*\"committee\" + 0.022*\"communication\" + 0.019*\"contract\" + 0.019*\"NUMBER\"\n", + "\n", + "-- Topic: 2 --\n", + "0.128*\"NUMBER\" + 0.056*\"directive\" + 0.051*\"regulation\" + 0.035*\"council\" + 0.033*\"eu\" + 0.030*\"ec\" + 0.029*\"european\" + 0.025*\"commission\" + 0.019*\"article\" + 0.018*\"parliament\"\n", + "\n", + "-- Topic: 3 --\n", + "0.031*\"system\" + 0.028*\"contract\" + 0.021*\"support\" + 0.019*\"iter\" + 0.015*\"study\" + 0.014*\"service\" + 0.013*\"provide\" + 0.013*\"development\" + 0.013*\"capability\" + 0.012*\"NUMBER\"\n", + "\n", + "-- Topic: 4 --\n", + "0.053*\"service\" + 0.046*\"production\" + 0.045*\"document\" + 0.040*\"product\" + 0.030*\"language\" + 0.029*\"design\" + 0.024*\"material\" + 0.022*\"tender\" + 0.021*\"relate\" + 0.020*\"contract\"\n", + "\n", + "-- Topic: 5 --\n", + "0.077*\"maintenance\" + 0.071*\"installation\" + 0.055*\"supply\" + 0.044*\"system\" + 0.023*\"NUMBER\" + 0.023*\"jrc\" + 0.016*\"equipment\" + 0.015*\"plant\" + 0.014*\"contract\" + 0.014*\"include\"\n", + "\n", + "-- Topic: 6 --\n", + "0.301*\"NUMBER\" + 0.250*\"lot\" + 0.040*\"divide\" + 0.034*\"supply\" + 0.018*\"contract\" + 0.017*\"tender\" + 0.012*\"electricity\" + 0.012*\"ecb\" + 0.011*\"main\" + 0.011*\"follow\"\n", + "\n", + "-- Topic: 7 --\n", + "0.118*\"training\" + 0.036*\"NUMBER\" + 0.028*\"programme\" + 0.021*\"course\" + 0.020*\"activity\" + 0.019*\"protection\" + 0.019*\"organisation\" + 0.016*\"civil\" + 0.013*\"contract\" + 0.013*\"initiative\"\n", + "\n", + "-- Topic: 8 --\n", + "0.081*\"NUMBER\" + 0.057*\"authority\" + 0.051*\"contracting\" + 0.046*\"address\" + 0.042*\"day\" + 0.040*\"point\" + 0.039*\"email\" + 0.038*\"seek\" + 0.037*\"submission\" + 0.037*\"deadline\"\n", + "\n", + "-- Topic: 9 --\n", + "0.085*\"construction\" + 0.064*\"water\" + 0.056*\"work\" + 0.037*\"eib\" + 0.025*\"supervision\" + 0.025*\"project\" + 0.020*\"design\" + 0.018*\"treatment\" + 0.017*\"plant\" + 0.016*\"system\"\n", + "\n", + "-- Topic: 10 --\n", + "0.081*\"project\" + 0.065*\"technical\" + 0.061*\"assistance\" + 0.047*\"subject\" + 0.035*\"authority\" + 0.033*\"contract\" + 0.030*\"contracting\" + 0.029*\"extend\" + 0.028*\"scope\" + 0.028*\"availability\"\n", + "\n", + "-- Topic: 11 --\n", + "0.208*\"service\" + 0.085*\"provision\" + 0.022*\"procedure\" + 0.021*\"consultancy\" + 0.021*\"procurement\" + 0.020*\"relate\" + 0.019*\"provider\" + 0.017*\"ict\" + 0.016*\"management\" + 0.015*\"related\"\n", + "\n", + "-- Topic: 12 --\n", + "0.099*\"service\" + 0.049*\"european\" + 0.041*\"contract\" + 0.037*\"tender\" + 0.036*\"company\" + 0.035*\"framework\" + 0.034*\"eea\" + 0.033*\"security\" + 0.033*\"union\" + 0.033*\"NUMBER\"\n", + "\n", + "-- Topic: 13 --\n", + "0.077*\"translation\" + 0.035*\"european\" + 0.035*\"union\" + 0.035*\"french\" + 0.032*\"body\" + 0.029*\"centre\" + 0.029*\"english\" + 0.025*\"german\" + 0.023*\"property\" + 0.023*\"framework\"\n", + "\n", + "-- Topic: 14 --\n", + "0.054*\"emergency\" + 0.049*\"ipa\" + 0.026*\"woman\" + 0.023*\"georgia\" + 0.021*\"turkey\" + 0.020*\"country\" + 0.020*\"south\" + 0.018*\"response\" + 0.018*\"assistance\" + 0.018*\"negotiation\"\n", + "\n", + "-- Topic: 15 --\n", + "0.042*\"skill\" + 0.041*\"industrial\" + 0.032*\"labour\" + 0.029*\"improve\" + 0.025*\"rail\" + 0.021*\"market\" + 0.018*\"housing\" + 0.018*\"reporting\" + 0.016*\"purchase\" + 0.015*\"register\"\n", + "\n", + "-- Topic: 16 --\n", + "0.169*\"equipment\" + 0.089*\"supply\" + 0.064*\"insurance\" + 0.031*\"purchase\" + 0.027*\"computer\" + 0.027*\"installation\" + 0.023*\"service\" + 0.022*\"furniture\" + 0.021*\"hospital\" + 0.020*\"accident\"\n", + "\n", + "-- Topic: 17 --\n", + "0.037*\"project\" + 0.030*\"sme\" + 0.029*\"helpdesk\" + 0.025*\"payment\" + 0.022*\"financing\" + 0.022*\"pilot\" + 0.022*\"enterprise\" + 0.021*\"development\" + 0.016*\"high\" + 0.015*\"service\"\n", + "\n", + "-- Topic: 18 --\n", + "0.070*\"study\" + 0.051*\"eu\" + 0.043*\"analysis\" + 0.023*\"european\" + 0.021*\"provide\" + 0.020*\"market\" + 0.019*\"information\" + 0.015*\"product\" + 0.014*\"feasibility\" + 0.013*\"NUMBER\"\n", + "\n", + "-- Topic: 19 --\n", + "0.059*\"risk\" + 0.043*\"food\" + 0.043*\"assessment\" + 0.035*\"safety\" + 0.027*\"efsa\" + 0.024*\"health\" + 0.022*\"laboratory\" + 0.022*\"scientific\" + 0.020*\"chemical\" + 0.020*\"procurement\"\n", + "\n", + "-- Topic: 20 --\n", + "0.088*\"development\" + 0.069*\"system\" + 0.059*\"support\" + 0.054*\"maintenance\" + 0.046*\"software\" + 0.036*\"information\" + 0.031*\"service\" + 0.030*\"management\" + 0.023*\"solution\" + 0.022*\"application\"\n", + "\n", + "-- Topic: 21 --\n", + "0.116*\"service\" + 0.115*\"framework\" + 0.107*\"contract\" + 0.081*\"provision\" + 0.036*\"staff\" + 0.027*\"office\" + 0.023*\"multiple\" + 0.019*\"conclude\" + 0.018*\"tender\" + 0.016*\"agency\"\n", + "\n", + "-- Topic: 22 --\n", + "0.096*\"contract\" + 0.063*\"framework\" + 0.055*\"NUMBER\" + 0.053*\"year\" + 0.026*\"lot\" + 0.025*\"order\" + 0.024*\"award\" + 0.021*\"maximum\" + 0.021*\"contractor\" + 0.021*\"conclusion\"\n", + "\n", + "-- Topic: 23 --\n", + "0.032*\"NUMBER\" + 0.029*\"member\" + 0.028*\"eu\" + 0.026*\"state\" + 0.022*\"study\" + 0.015*\"national\" + 0.013*\"cost\" + 0.013*\"use\" + 0.013*\"provide\" + 0.012*\"objective\"\n", + "\n", + "-- Topic: 24 --\n", + "0.045*\"health\" + 0.027*\"mobile\" + 0.024*\"occupational\" + 0.023*\"radio\" + 0.021*\"statistical\" + 0.019*\"telecommunication\" + 0.018*\"transmission\" + 0.015*\"supply\" + 0.015*\"multi\" + 0.015*\"measurement\"\n", + "\n", + "-- Topic: 25 --\n", + "0.136*\"security\" + 0.072*\"service\" + 0.045*\"system\" + 0.044*\"provide\" + 0.040*\"delegation\" + 0.038*\"european\" + 0.036*\"union\" + 0.033*\"resource\" + 0.028*\"human\" + 0.025*\"provision\"\n", + "\n", + "-- Topic: 26 --\n", + "0.056*\"support\" + 0.039*\"policy\" + 0.035*\"eu\" + 0.024*\"environmental\" + 0.022*\"implementation\" + 0.021*\"commission\" + 0.020*\"contract\" + 0.019*\"provide\" + 0.018*\"development\" + 0.015*\"indicator\"\n", + "\n", + "-- Topic: 27 --\n", + "0.078*\"vehicle\" + 0.046*\"court\" + 0.044*\"european\" + 0.041*\"test\" + 0.032*\"justice\" + 0.032*\"emission\" + 0.031*\"union\" + 0.023*\"car\" + 0.023*\"supply\" + 0.023*\"fuel\"\n", + "\n", + "-- Topic: 28 --\n", + "0.166*\"energy\" + 0.050*\"house\" + 0.044*\"service\" + 0.043*\"eu\" + 0.037*\"reception\" + 0.031*\"efficiency\" + 0.030*\"tender\" + 0.028*\"renewable\" + 0.027*\"premise\" + 0.020*\"security\"\n", + "\n", + "-- Topic: 29 --\n", + "0.128*\"contract\" + 0.087*\"tenderer\" + 0.066*\"guarantee\" + 0.064*\"provide\" + 0.044*\"NUMBER\" + 0.023*\"return\" + 0.022*\"period\" + 0.022*\"later\" + 0.022*\"performance\" + 0.022*\"receive\"\n", + "\n", + "-- Topic: 30 --\n", + "0.045*\"NUMBER\" + 0.041*\"supply\" + 0.033*\"room\" + 0.032*\"gas\" + 0.023*\"galileo\" + 0.021*\"fuel\" + 0.020*\"use\" + 0.018*\"satellite\" + 0.016*\"natural\" + 0.014*\"heating\"\n", + "\n", + "-- Topic: 31 --\n", + "0.125*\"datum\" + 0.023*\"collection\" + 0.023*\"review\" + 0.022*\"eu\" + 0.018*\"data\" + 0.017*\"country\" + 0.014*\"approach\" + 0.013*\"research\" + 0.012*\"study\" + 0.012*\"objective\"\n", + "\n", + "-- Topic: 32 --\n", + "0.042*\"support\" + 0.029*\"task\" + 0.025*\"provide\" + 0.025*\"coordination\" + 0.021*\"specific\" + 0.019*\"contract\" + 0.017*\"technical\" + 0.017*\"service\" + 0.016*\"ecdc\" + 0.016*\"video\"\n", + "\n", + "-- Topic: 33 --\n", + "0.055*\"evaluation\" + 0.045*\"impact\" + 0.044*\"assessment\" + 0.037*\"study\" + 0.030*\"NUMBER\" + 0.028*\"economic\" + 0.024*\"policy\" + 0.023*\"contract\" + 0.022*\"analysis\" + 0.015*\"gender\"\n", + "\n", + "-- Topic: 34 --\n", + "0.108*\"contract\" + 0.075*\"tenderer\" + 0.052*\"bond\" + 0.045*\"successful\" + 0.045*\"NUMBER\" + 0.040*\"sign\" + 0.036*\"provide\" + 0.036*\"require\" + 0.022*\"price\" + 0.021*\"time\"\n", + "\n", + "-- Topic: 35 --\n", + "0.071*\"service\" + 0.058*\"eib\" + 0.049*\"management\" + 0.040*\"ecb\" + 0.032*\"consultancy\" + 0.028*\"provision\" + 0.026*\"support\" + 0.024*\"bank\" + 0.021*\"group\" + 0.019*\"framework\"\n", + "\n", + "-- Topic: 36 --\n", + "0.133*\"NUMBER\" + 0.030*\"audit\" + 0.027*\"general\" + 0.024*\"financial\" + 0.023*\"eu\" + 0.022*\"service\" + 0.021*\"contract\" + 0.020*\"gsa\" + 0.020*\"MONTH\" + 0.018*\"budget\"\n", + "\n", + "-- Topic: 37 --\n", + "0.055*\"infrastructure\" + 0.055*\"operation\" + 0.052*\"access\" + 0.030*\"database\" + 0.026*\"support\" + 0.024*\"investment\" + 0.020*\"implementation\" + 0.019*\"finance\" + 0.016*\"subscription\" + 0.016*\"platform\"\n", + "\n", + "-- Topic: 38 --\n", + "0.135*\"NUMBER\" + 0.077*\"road\" + 0.069*\"work\" + 0.041*\"construction\" + 0.037*\"km\" + 0.028*\"railway\" + 0.024*\"rehabilitation\" + 0.022*\"line\" + 0.018*\"section\" + 0.017*\"safety\"\n", + "\n", + "-- Topic: 39 --\n", + "0.096*\"european\" + 0.078*\"building\" + 0.057*\"parliament\" + 0.039*\"brussels\" + 0.038*\"service\" + 0.030*\"contract\" + 0.027*\"luxembourg\" + 0.022*\"NUMBER\" + 0.021*\"strasbourg\" + 0.019*\"work\"\n", + "\n", + "-- Topic: 40 --\n", + "0.056*\"administrative\" + 0.055*\"legal\" + 0.053*\"law\" + 0.050*\"advice\" + 0.039*\"sea\" + 0.027*\"service\" + 0.026*\"different\" + 0.021*\"cultural\" + 0.020*\"assistance\" + 0.018*\"learning\"\n", + "\n", + "-- Topic: 41 --\n", + "0.035*\"climate\" + 0.033*\"copernicus\" + 0.032*\"service\" + 0.030*\"NUMBER\" + 0.028*\"support\" + 0.027*\"change\" + 0.022*\"global\" + 0.022*\"activity\" + 0.019*\"land\" + 0.015*\"monitoring\"\n", + "\n", + "-- Topic: 42 --\n", + "0.074*\"service\" + 0.069*\"european\" + 0.054*\"communication\" + 0.049*\"commission\" + 0.029*\"information\" + 0.023*\"public\" + 0.022*\"representation\" + 0.021*\"provision\" + 0.019*\"activity\" + 0.016*\"provide\"\n", + "\n", + "-- Topic: 43 --\n", + "0.081*\"european\" + 0.077*\"network\" + 0.067*\"education\" + 0.038*\"school\" + 0.035*\"support\" + 0.023*\"commission\" + 0.018*\"high\" + 0.018*\"programme\" + 0.014*\"research\" + 0.013*\"procure\"\n", + "\n", + "-- Topic: 44 --\n", + "0.090*\"application\" + 0.087*\"form\" + 0.045*\"section\" + 0.045*\"standard\" + 0.045*\"NUMBER\" + 0.044*\"candidate\" + 0.043*\"situation\" + 0.043*\"submit\" + 0.042*\"sign\" + 0.042*\"include\"\n", + "\n", + "-- Topic: 45 --\n", + "0.105*\"transport\" + 0.066*\"survey\" + 0.038*\"european\" + 0.038*\"international\" + 0.030*\"statistic\" + 0.029*\"aviation\" + 0.022*\"safety\" + 0.021*\"level\" + 0.018*\"service\" + 0.018*\"agency\"\n", + "\n", + "-- Topic: 46 --\n", + "0.074*\"site\" + 0.069*\"jrc\" + 0.044*\"centre\" + 0.042*\"ispra\" + 0.036*\"joint\" + 0.034*\"research\" + 0.028*\"building\" + 0.027*\"service\" + 0.025*\"work\" + 0.024*\"NUMBER\"\n", + "\n", + "-- Topic: 47 --\n", + "0.046*\"NUMBER\" + 0.035*\"service\" + 0.030*\"device\" + 0.029*\"equipment\" + 0.022*\"time\" + 0.022*\"frontex\" + 0.020*\"acquisition\" + 0.019*\"provision\" + 0.017*\"supply\" + 0.016*\"purchase\"\n", + "\n", + "-- Topic: 48 --\n", + "0.030*\"study\" + 0.022*\"digital\" + 0.021*\"practice\" + 0.021*\"sector\" + 0.020*\"market\" + 0.020*\"eu\" + 0.019*\"good\" + 0.019*\"innovation\" + 0.018*\"objective\" + 0.017*\"smart\"\n", + "\n", + "-- Topic: 49 --\n", + "0.075*\"waste\" + 0.050*\"epo\" + 0.046*\"right\" + 0.043*\"nuclear\" + 0.042*\"allow\" + 0.041*\"de\" + 0.035*\"reduce\" + 0.035*\"safeguard\" + 0.034*\"proposal\" + 0.030*\"effect\"\n", + "\n" + ] + } + ], + "source": [ + "# 20 passes seems to give better results than 2\n", + "lda_model = gensim.models.LdaMulticore(bow_corpus, num_topics=50, id2word=dictionary, passes=20, workers=4,\n", + " minimum_probability=0.0)\n", + "for idx, topic in lda_model.print_topics(-1):\n", + " print('-- Topic: {} --\\n{}\\n'.format(idx, topic))" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "markdown", + "source": [ + "### Using TFIDF" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": 158, + "outputs": [ + { + "data": { + "text/plain": "[(0, 0.05665082498263276),\n (1, 0.12807236880516995),\n (2, 0.07933089660064745),\n (3, 0.25230586430222485),\n (4, 0.1299596107526431),\n (5, 0.20710308682000417),\n (6, 0.11895117349717746),\n (7, 0.22539927799416767),\n (8, 0.08384683915242945),\n (9, 0.12102247861336683),\n (10, 0.09923408933597923),\n (11, 0.11454595197543345),\n (12, 0.10104473789728612),\n (13, 0.1293204098325476),\n (14, 0.36893131914584504),\n (15, 0.16137963960080237),\n (16, 0.10373794343782906),\n (17, 0.12669292935869714),\n (18, 0.2590612962599304),\n (19, 0.09142624414586832),\n (20, 0.09991406288160162),\n (21, 0.11075409983330753),\n (22, 0.08652843993418363),\n (23, 0.19053225489254916),\n (24, 0.11678553178749576),\n (25, 0.14961287443002041),\n (26, 0.1268627351970124),\n (27, 0.19781064621306588),\n (28, 0.11075409983330753),\n (29, 0.11916045521835175),\n (30, 0.08038608899477187),\n (31, 0.1293204098325476),\n (32, 0.08192889826513393),\n (33, 0.11338158030715803),\n (34, 0.07677484702566353),\n (35, 0.06298784767348975),\n (36, 0.44598112748464985),\n (37, 0.12008044297830643)]" + }, + "execution_count": 158, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from gensim.models import TfidfModel\n", + "\n", + "tfidf = TfidfModel(bow_corpus)\n", + "tfidf_corpus = tfidf[bow_corpus]\n", + "tfidf_corpus[0]" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": 159, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "-- Topic: 0 --\n", + "0.076*\"bond\" + 0.063*\"tenderer\" + 0.034*\"successful\" + 0.032*\"contract\" + 0.031*\"require\" + 0.028*\"price\" + 0.025*\"low\" + 0.024*\"render\" + 0.024*\"sign\" + 0.024*\"date\"\n", + "\n", + "-- Topic: 1 --\n", + "0.044*\"security\" + 0.037*\"fwc\" + 0.037*\"delegation\" + 0.032*\"ser\" + 0.031*\"specialise\" + 0.029*\"company\" + 0.025*\"ref\" + 0.024*\"union\" + 0.023*\"eea\" + 0.022*\"person\"\n", + "\n", + "-- Topic: 2 --\n", + "0.039*\"copernicus\" + 0.017*\"land\" + 0.015*\"climate\" + 0.015*\"aid\" + 0.014*\"ecmwf\" + 0.012*\"cap\" + 0.011*\"global\" + 0.011*\"natura\" + 0.009*\"change\" + 0.009*\"service\"\n", + "\n", + "-- Topic: 3 --\n", + "0.033*\"electricity\" + 0.025*\"fuel\" + 0.019*\"corporate\" + 0.017*\"renewable\" + 0.017*\"radio\" + 0.016*\"ceb\" + 0.015*\"enhancement\" + 0.014*\"spatial\" + 0.014*\"energy\" + 0.013*\"planning\"\n", + "\n", + "-- Topic: 4 --\n", + "0.064*\"translation\" + 0.032*\"language\" + 0.026*\"french\" + 0.024*\"english\" + 0.022*\"german\" + 0.020*\"centre\" + 0.019*\"text\" + 0.018*\"body\" + 0.018*\"italian\" + 0.018*\"spanish\"\n", + "\n", + "-- Topic: 5 --\n", + "0.022*\"gas\" + 0.017*\"natural\" + 0.016*\"lift\" + 0.016*\"occupy\" + 0.014*\"european\" + 0.014*\"brussels\" + 0.013*\"competition\" + 0.013*\"building\" + 0.012*\"capital\" + 0.012*\"associated\"\n", + "\n", + "-- Topic: 6 --\n", + "0.062*\"training\" + 0.026*\"food\" + 0.024*\"safe\" + 0.019*\"gender\" + 0.017*\"initiative\" + 0.016*\"equality\" + 0.015*\"organisation\" + 0.015*\"activity\" + 0.014*\"participant\" + 0.014*\"certification\"\n", + "\n", + "-- Topic: 7 --\n", + "0.026*\"property\" + 0.026*\"frontex\" + 0.021*\"office\" + 0.020*\"ecdc\" + 0.020*\"floor\" + 0.020*\"accessory\" + 0.019*\"malta\" + 0.015*\"easo\" + 0.014*\"asylum\" + 0.014*\"end\"\n", + "\n", + "-- Topic: 8 --\n", + "0.076*\"form\" + 0.067*\"application\" + 0.040*\"exclusion\" + 0.040*\"declaration\" + 0.040*\"situation\" + 0.040*\"candidate\" + 0.039*\"section\" + 0.038*\"standard\" + 0.037*\"effect\" + 0.035*\"list\"\n", + "\n", + "-- Topic: 9 --\n", + "0.050*\"ict\" + 0.038*\"helpdesk\" + 0.027*\"service\" + 0.027*\"maintenance\" + 0.021*\"upgrade\" + 0.017*\"provision\" + 0.015*\"operational\" + 0.015*\"interpol\" + 0.015*\"specification\" + 0.014*\"data\"\n", + "\n", + "-- Topic: 10 --\n", + "0.062*\"epo\" + 0.040*\"safeguard\" + 0.038*\"variant\" + 0.037*\"allow\" + 0.037*\"significantly\" + 0.036*\"proposal\" + 0.036*\"right\" + 0.035*\"reduce\" + 0.024*\"munich\" + 0.020*\"effect\"\n", + "\n", + "-- Topic: 11 --\n", + "0.032*\"building\" + 0.024*\"jrc\" + 0.022*\"parliament\" + 0.021*\"brussels\" + 0.021*\"installation\" + 0.020*\"strasbourg\" + 0.019*\"site\" + 0.017*\"maintenance\" + 0.017*\"luxembourg\" + 0.016*\"karlsruhe\"\n", + "\n", + "-- Topic: 12 --\n", + "0.022*\"radiation\" + 0.019*\"protection\" + 0.016*\"surface\" + 0.015*\"fishery\" + 0.014*\"civil\" + 0.013*\"secondary\" + 0.013*\"university\" + 0.012*\"train\" + 0.011*\"international\" + 0.011*\"echo\"\n", + "\n", + "-- Topic: 13 --\n", + "0.052*\"software\" + 0.021*\"maintenance\" + 0.021*\"portal\" + 0.020*\"system\" + 0.019*\"development\" + 0.018*\"acquisition\" + 0.018*\"solution\" + 0.017*\"architecture\" + 0.017*\"housing\" + 0.016*\"hardware\"\n", + "\n", + "-- Topic: 14 --\n", + "0.061*\"court\" + 0.051*\"justice\" + 0.025*\"enisa\" + 0.021*\"real\" + 0.019*\"union\" + 0.017*\"auditor\" + 0.016*\"minimum\" + 0.015*\"strengthening\" + 0.015*\"metro\" + 0.015*\"dam\"\n", + "\n", + "-- Topic: 15 --\n", + "0.028*\"administration\" + 0.027*\"reform\" + 0.027*\"assistance\" + 0.027*\"technical\" + 0.021*\"financing\" + 0.021*\"government\" + 0.021*\"agreement\" + 0.021*\"finance\" + 0.020*\"society\" + 0.020*\"edf\"\n", + "\n", + "-- Topic: 16 --\n", + "0.032*\"print\" + 0.030*\"communication\" + 0.029*\"electronic\" + 0.025*\"logistic\" + 0.023*\"paper\" + 0.022*\"campaign\" + 0.021*\"book\" + 0.021*\"publication\" + 0.021*\"ohim\" + 0.018*\"creative\"\n", + "\n", + "-- Topic: 17 --\n", + "0.058*\"furniture\" + 0.052*\"cleaning\" + 0.040*\"clean\" + 0.034*\"office\" + 0.030*\"euipo\" + 0.028*\"waste\" + 0.023*\"premise\" + 0.022*\"ad\" + 0.020*\"hoc\" + 0.019*\"supply\"\n", + "\n", + "-- Topic: 18 --\n", + "0.046*\"audit\" + 0.025*\"auditing\" + 0.022*\"replacement\" + 0.022*\"board\" + 0.022*\"satellite\" + 0.021*\"tourism\" + 0.020*\"cater\" + 0.020*\"internal\" + 0.018*\"heat\" + 0.018*\"lead\"\n", + "\n", + "-- Topic: 19 --\n", + "0.037*\"easo\" + 0.033*\"event\" + 0.030*\"cedefop\" + 0.028*\"room\" + 0.028*\"meeting\" + 0.026*\"conference\" + 0.024*\"greece\" + 0.018*\"provision\" + 0.018*\"cleaning\" + 0.018*\"italy\"\n", + "\n", + "-- Topic: 20 --\n", + "0.020*\"western\" + 0.019*\"youth\" + 0.018*\"vehicle\" + 0.017*\"voltage\" + 0.016*\"electric\" + 0.015*\"police\" + 0.014*\"balkan\" + 0.013*\"register\" + 0.013*\"authorise\" + 0.013*\"officer\"\n", + "\n", + "-- Topic: 21 --\n", + "0.038*\"gov\" + 0.026*\"clarification\" + 0.026*\"ipa\" + 0.026*\"reception\" + 0.026*\"tr\" + 0.026*\"email\" + 0.024*\"deadline\" + 0.024*\"submission\" + 0.023*\"etf\" + 0.023*\"address\"\n", + "\n", + "-- Topic: 22 --\n", + "0.044*\"ecb\" + 0.034*\"mobile\" + 0.025*\"ispra\" + 0.020*\"telecommunication\" + 0.019*\"supplier\" + 0.017*\"supply\" + 0.017*\"water\" + 0.016*\"joint\" + 0.015*\"distribution\" + 0.014*\"research\"\n", + "\n", + "-- Topic: 23 --\n", + "0.010*\"indicator\" + 0.010*\"esm\" + 0.010*\"datum\" + 0.009*\"account\" + 0.008*\"support\" + 0.008*\"report\" + 0.008*\"eu\" + 0.008*\"country\" + 0.007*\"partner\" + 0.007*\"service\"\n", + "\n", + "-- Topic: 24 --\n", + "0.095*\"tenderer\" + 0.076*\"guarantee\" + 0.047*\"contract\" + 0.040*\"provide\" + 0.027*\"signing\" + 0.027*\"ask\" + 0.027*\"fail\" + 0.027*\"cheap\" + 0.027*\"return\" + 0.026*\"later\"\n", + "\n", + "-- Topic: 25 --\n", + "0.014*\"study\" + 0.011*\"eu\" + 0.008*\"objective\" + 0.008*\"support\" + 0.008*\"policy\" + 0.007*\"evaluation\" + 0.007*\"impact\" + 0.007*\"assessment\" + 0.006*\"european\" + 0.006*\"development\"\n", + "\n", + "-- Topic: 26 --\n", + "0.059*\"construction\" + 0.040*\"work\" + 0.035*\"eib\" + 0.031*\"plant\" + 0.023*\"supervision\" + 0.022*\"road\" + 0.022*\"design\" + 0.022*\"rehabilitation\" + 0.018*\"km\" + 0.017*\"treatment\"\n", + "\n", + "-- Topic: 27 --\n", + "0.055*\"subject\" + 0.047*\"availability\" + 0.046*\"extend\" + 0.046*\"duration\" + 0.044*\"discretion\" + 0.042*\"funding\" + 0.036*\"scope\" + 0.032*\"satisfactory\" + 0.031*\"eur\" + 0.031*\"extension\"\n", + "\n", + "-- Topic: 28 --\n", + "0.040*\"directive\" + 0.023*\"regulation\" + 0.020*\"cable\" + 0.020*\"budget\" + 0.019*\"council\" + 0.019*\"ec\" + 0.017*\"gap\" + 0.012*\"rural\" + 0.012*\"eu\" + 0.012*\"air\"\n", + "\n", + "-- Topic: 29 --\n", + "0.076*\"security\" + 0.038*\"delegation\" + 0.038*\"system\" + 0.037*\"human\" + 0.035*\"resource\" + 0.035*\"guard\" + 0.029*\"necessary\" + 0.028*\"material\" + 0.026*\"alarm\" + 0.026*\"responsibility\"\n", + "\n", + "-- Topic: 30 --\n", + "0.076*\"eib\" + 0.024*\"project\" + 0.021*\"group\" + 0.020*\"management\" + 0.019*\"investment\" + 0.018*\"assistance\" + 0.017*\"service\" + 0.016*\"consultancy\" + 0.015*\"advisory\" + 0.014*\"water\"\n", + "\n", + "-- Topic: 31 --\n", + "0.079*\"insurance\" + 0.032*\"product\" + 0.026*\"modernisation\" + 0.025*\"promotional\" + 0.024*\"carbon\" + 0.023*\"chain\" + 0.021*\"animal\" + 0.021*\"item\" + 0.021*\"scheme\" + 0.018*\"pension\"\n", + "\n", + "-- Topic: 32 --\n", + "0.051*\"medical\" + 0.022*\"cascade\" + 0.020*\"park\" + 0.020*\"multiple\" + 0.019*\"car\" + 0.019*\"eif\" + 0.018*\"hire\" + 0.017*\"manufacturing\" + 0.016*\"provision\" + 0.016*\"beam\"\n", + "\n", + "-- Topic: 33 --\n", + "0.031*\"review\" + 0.029*\"peer\" + 0.025*\"morocco\" + 0.022*\"portfolio\" + 0.021*\"registration\" + 0.021*\"bNUMBER\" + 0.021*\"eige\" + 0.017*\"literature\" + 0.017*\"sensor\" + 0.016*\"foresight\"\n", + "\n", + "-- Topic: 34 --\n", + "0.038*\"cfsp\" + 0.026*\"video\" + 0.025*\"audio\" + 0.023*\"kosovo\" + 0.019*\"visual\" + 0.017*\"georgia\" + 0.017*\"special\" + 0.016*\"multimedia\" + 0.015*\"co\" + 0.015*\"sanitary\"\n", + "\n", + "-- Topic: 35 --\n", + "0.035*\"measurement\" + 0.029*\"feed\" + 0.025*\"light\" + 0.024*\"emission\" + 0.024*\"hydrogen\" + 0.023*\"vehicle\" + 0.022*\"big\" + 0.022*\"portable\" + 0.020*\"workplace\" + 0.020*\"liquid\"\n", + "\n", + "-- Topic: 36 --\n", + "0.048*\"de\" + 0.046*\"statistic\" + 0.037*\"statistical\" + 0.036*\"la\" + 0.021*\"fit\" + 0.020*\"paris\" + 0.020*\"du\" + 0.019*\"et\" + 0.019*\"le\" + 0.018*\"fusion\"\n", + "\n", + "-- Topic: 37 --\n", + "0.061*\"travel\" + 0.035*\"agency\" + 0.029*\"service\" + 0.021*\"provision\" + 0.018*\"desk\" + 0.015*\"annex\" + 0.014*\"fix\" + 0.012*\"evolution\" + 0.012*\"accommodation\" + 0.011*\"arrangement\"\n", + "\n", + "-- Topic: 38 --\n", + "0.063*\"financial\" + 0.063*\"expression\" + 0.057*\"interest\" + 0.030*\"europe\" + 0.029*\"fund\" + 0.026*\"party\" + 0.024*\"preparatory\" + 0.021*\"council\" + 0.021*\"leadership\" + 0.021*\"accounting\"\n", + "\n", + "-- Topic: 39 --\n", + "0.030*\"engineering\" + 0.023*\"ukraine\" + 0.020*\"server\" + 0.016*\"architectural\" + 0.013*\"bus\" + 0.012*\"press\" + 0.012*\"port\" + 0.012*\"municipal\" + 0.010*\"passenger\" + 0.010*\"medicine\"\n", + "\n", + "-- Topic: 40 --\n", + "0.034*\"emsa\" + 0.032*\"aviation\" + 0.028*\"emcdda\" + 0.028*\"crisis\" + 0.026*\"tunnel\" + 0.025*\"exercise\" + 0.022*\"corridor\" + 0.021*\"station\" + 0.019*\"el\" + 0.019*\"preliminary\"\n", + "\n", + "-- Topic: 41 --\n", + "0.042*\"catering\" + 0.026*\"maintenance\" + 0.026*\"geel\" + 0.025*\"canteen\" + 0.024*\"repair\" + 0.021*\"preventive\" + 0.020*\"council\" + 0.018*\"building\" + 0.018*\"jrc\" + 0.018*\"irmm\"\n", + "\n", + "-- Topic: 42 --\n", + "0.038*\"survey\" + 0.032*\"occupational\" + 0.031*\"serbia\" + 0.029*\"health\" + 0.021*\"fundamental\" + 0.018*\"medical\" + 0.016*\"matter\" + 0.016*\"datum\" + 0.015*\"migration\" + 0.014*\"firm\"\n", + "\n", + "-- Topic: 43 --\n", + "0.034*\"representation\" + 0.032*\"house\" + 0.026*\"temporary\" + 0.024*\"european\" + 0.023*\"information\" + 0.021*\"visitor\" + 0.018*\"space\" + 0.018*\"exhibition\" + 0.018*\"office\" + 0.018*\"parliament\"\n", + "\n", + "-- Topic: 44 --\n", + "0.072*\"device\" + 0.028*\"rental\" + 0.026*\"www\" + 0.025*\"verification\" + 0.023*\"rs\" + 0.022*\"woman\" + 0.022*\"hybrid\" + 0.020*\"procurement\" + 0.020*\"portugal\" + 0.019*\"interactive\"\n", + "\n", + "-- Topic: 45 --\n", + "0.026*\"database\" + 0.026*\"computer\" + 0.023*\"scientific\" + 0.023*\"laboratory\" + 0.020*\"permit\" + 0.017*\"instrument\" + 0.017*\"custom\" + 0.017*\"subscription\" + 0.015*\"mail\" + 0.013*\"analytical\"\n", + "\n", + "-- Topic: 46 --\n", + "0.036*\"audiovisual\" + 0.034*\"medium\" + 0.030*\"hospital\" + 0.027*\"supply\" + 0.027*\"consumable\" + 0.023*\"sale\" + 0.023*\"monitoring\" + 0.020*\"equipment\" + 0.016*\"installation\" + 0.014*\"purchase\"\n", + "\n", + "-- Topic: 47 --\n", + "0.042*\"address\" + 0.041*\"email\" + 0.038*\"submission\" + 0.038*\"deadline\" + 0.038*\"clarification\" + 0.037*\"following\" + 0.036*\"late\" + 0.033*\"europa\" + 0.032*\"point\" + 0.031*\"seek\"\n", + "\n", + "-- Topic: 48 --\n", + "0.025*\"interim\" + 0.019*\"framework\" + 0.019*\"gsa\" + 0.017*\"lot\" + 0.017*\"disease\" + 0.017*\"committee\" + 0.016*\"surveillance\" + 0.015*\"contract\" + 0.014*\"classification\" + 0.011*\"year\"\n", + "\n", + "-- Topic: 49 --\n", + "0.018*\"chemical\" + 0.017*\"defence\" + 0.016*\"service\" + 0.015*\"web\" + 0.014*\"technology\" + 0.013*\"provision\" + 0.013*\"framework\" + 0.011*\"environment\" + 0.011*\"eea\" + 0.010*\"digital\"\n", + "\n" + ] + } + ], + "source": [ + "lda_model_tfidf = gensim.models.LdaMulticore(tfidf_corpus, num_topics=50, id2word=dictionary, passes=20, workers=4,\n", + " minimum_probability=0.0)\n", + "for idx, topic in lda_model_tfidf.print_topics(-1):\n", + " print('-- Topic: {} --\\n{}\\n'.format(idx, topic))" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "markdown", + "source": [ + "## Link between topics and CPVs" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": 160, + "outputs": [ + { + "data": { + "text/plain": " title_texte 85 44 50 \\\n1 ipa supply equipment increase competitiveness... False True False \n3 provision language training service tender in... False False False \n4 service support eda helicopter portfolio main... False False False \n5 NUMBER cp op NUMBER pooling share cost non co... False False False \n6 edf supply transport household similar waste ... False False False \n\n 80 73 45 71 79 90 ... 03 24 43 19 \\\n1 False False False False False False ... False False False False \n3 True False False False False False ... False False False False \n4 True False False False False False ... False False False False \n5 False True False False False False ... False False False False \n6 False False True False False False ... False False False False \n\n 41 37 14 16 76 topic \n1 False False False False False 29 \n3 False False False False False 40 \n4 False False False False False 11 \n5 False False False False False 23 \n6 False False False False False 49 \n\n[5 rows x 47 columns]", + "text/html": "<div>\n<style scoped>\n .dataframe tbody tr th:only-of-type {\n vertical-align: middle;\n }\n\n .dataframe tbody tr th {\n vertical-align: top;\n }\n\n .dataframe thead th {\n text-align: right;\n }\n</style>\n<table border=\"1\" class=\"dataframe\">\n <thead>\n <tr style=\"text-align: right;\">\n <th></th>\n <th>title_texte</th>\n <th>85</th>\n <th>44</th>\n <th>50</th>\n <th>80</th>\n <th>73</th>\n <th>45</th>\n <th>71</th>\n <th>79</th>\n <th>90</th>\n <th>...</th>\n <th>03</th>\n <th>24</th>\n <th>43</th>\n <th>19</th>\n <th>41</th>\n <th>37</th>\n <th>14</th>\n <th>16</th>\n <th>76</th>\n <th>topic</th>\n </tr>\n </thead>\n <tbody>\n <tr>\n <th>1</th>\n <td>ipa supply equipment increase competitiveness...</td>\n <td>False</td>\n <td>True</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>...</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>29</td>\n </tr>\n <tr>\n <th>3</th>\n <td>provision language training service tender in...</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>True</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>...</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>40</td>\n </tr>\n <tr>\n <th>4</th>\n <td>service support eda helicopter portfolio main...</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>True</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>...</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>11</td>\n </tr>\n <tr>\n <th>5</th>\n <td>NUMBER cp op NUMBER pooling share cost non co...</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>True</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>...</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>23</td>\n </tr>\n <tr>\n <th>6</th>\n <td>edf supply transport household similar waste ...</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>True</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>...</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>49</td>\n </tr>\n </tbody>\n</table>\n<p>5 rows × 47 columns</p>\n</div>" + }, + "execution_count": 160, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "def find_topic(text: str):\n", + " text_bow = dictionary.doc2bow(text.split(\" \"))\n", + " return sorted(lda_model[text_bow], key=lambda x: x[1])[-1][0]\n", + "\n", + "\n", + "df_topic = df_train.assign(topic=df_train['title_texte'].apply(find_topic))\n", + "df_topic.head()" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "markdown", + "source": [ + "### Top CPVs per topic" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": 161, + "outputs": [], + "source": [ + "cpv_labels = {\n", + " \"03\": \"Agricultural, farming, fishing, forestry and related products\",\n", + " \"09\": \"Petroleum products, fuel, electricity and other sources of energy\",\n", + " \"14\": \"Mining, basic metals and related products\",\n", + " \"15\": \"Food, beverages, tobacco and related products\",\n", + " \"16\": \"Agricultural machinery\",\n", + " \"18\": \"Clothing, footwear, luggage articles and accessories\",\n", + " \"19\": \"Leather and textile fabrics, plastic and rubber materials\",\n", + " \"22\": \"Printed matter and related products\",\n", + " \"24\": \"Chemical products\",\n", + " \"30\": \"Office and computing machinery, equipment and supplies except furniture and software packages\",\n", + " \"31\": \"Electrical machinery, apparatus, equipment and consumables; Lighting\",\n", + " \"32\": \"Radio, television, communication, telecommunication and related equipment\",\n", + " \"33\": \"Medical equipments, pharmaceuticals and personal care products\",\n", + " \"34\": \"Transport equipment and auxiliary products to transportation\",\n", + " \"35\": \"Security, fire-fighting, police and defence equipment\",\n", + " \"37\": \"Musical instruments, sport goods, games, toys, handicraft, art materials and accessories\",\n", + " \"38\": \"Laboratory, optical and precision equipments (excl. glasses)\",\n", + " \"39\": \"Furniture (incl. office furniture), furnishings, domestic appliances (excl. lighting) and cleaning products\",\n", + " \"41\": \"Collected and purified water\",\n", + " \"42\": \"Industrial machinery\",\n", + " \"43\": \"Machinery for mining, quarrying, construction equipment\",\n", + " \"44\": \"Construction structures and materials; auxiliary products to construction (excepts electric apparatus)\",\n", + " \"45\": \"Construction work\",\n", + " \"48\": \"Software package and information systems\",\n", + " \"50\": \"Repair and maintenance services\",\n", + " \"51\": \"Installation services (except software)\",\n", + " \"55\": \"Hotel, restaurant and retail trade services\",\n", + " \"60\": \"Transport services (excl. Waste transport)\",\n", + " \"63\": \"Supporting and auxiliary transport services; travel agencies services\",\n", + " \"64\": \"Postal and telecommunications services\",\n", + " \"65\": \"Public utilities\",\n", + " \"66\": \"Financial and insurance services\",\n", + " \"70\": \"Real estate services\",\n", + " \"71\": \"Architectural, construction, engineering and inspection services\",\n", + " \"72\": \"IT services: consulting, software development, Internet and support\",\n", + " \"73\": \"Research and development services and related consultancy services\",\n", + " \"75\": \"Administration, defence and social security services\",\n", + " \"76\": \"Services related to the oil and gas industry\",\n", + " \"77\": \"Agricultural, forestry, horticultural, aquacultural and apicultural services\",\n", + " \"79\": \"Business services: law, marketing, consulting, recruitment, printing and security\",\n", + " \"80\": \"Education and training services\",\n", + " \"85\": \"Health and social work services\",\n", + " \"90\": \"Sewage-, refuse-, cleaning-, and environmental services\",\n", + " \"92\": \"Recreational, cultural and sporting services\",\n", + " \"98\": \"Other community, social and personal services\",\n", + "}" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": 162, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "===\n", + "0 -> 0.091*\"service\" + 0.064*\"event\" + 0.032*\"medical\" + 0.031*\"travel\" + 0.028*\"provision\" + 0.026*\"organisation\" + 0.023*\"agency\" + 0.019*\"conference\" + 0.017*\"provide\" + 0.015*\"online\"\n", + "---\n", + "('Business services: law, marketing, consulting, recruitment, printing and security', 93)\n", + "('Supporting and auxiliary transport services; travel agencies services', 49)\n", + "('Health and social work services', 32)\n", + "===\n", + "1 -> 0.074*\"medium\" + 0.052*\"service\" + 0.046*\"european\" + 0.027*\"social\" + 0.026*\"parliament\" + 0.023*\"monitoring\" + 0.022*\"committee\" + 0.022*\"communication\" + 0.019*\"contract\" + 0.019*\"NUMBER\"\n", + "---\n", + "('Recreational, cultural and sporting services', 65)\n", + "('Business services: law, marketing, consulting, recruitment, printing and security', 45)\n", + "('Research and development services and related consultancy services', 29)\n", + "===\n", + "2 -> 0.128*\"NUMBER\" + 0.056*\"directive\" + 0.051*\"regulation\" + 0.035*\"council\" + 0.033*\"eu\" + 0.030*\"ec\" + 0.029*\"european\" + 0.025*\"commission\" + 0.019*\"article\" + 0.018*\"parliament\"\n", + "---\n", + "('Business services: law, marketing, consulting, recruitment, printing and security', 66)\n", + "('Research and development services and related consultancy services', 30)\n", + "('Sewage-, refuse-, cleaning-, and environmental services', 30)\n", + "===\n", + "3 -> 0.031*\"system\" + 0.028*\"contract\" + 0.021*\"support\" + 0.019*\"iter\" + 0.015*\"study\" + 0.014*\"service\" + 0.013*\"provide\" + 0.013*\"development\" + 0.013*\"capability\" + 0.012*\"NUMBER\"\n", + "---\n", + "('Research and development services and related consultancy services', 73)\n", + "('Architectural, construction, engineering and inspection services', 47)\n", + "('Laboratory, optical and precision equipments (excl. glasses)', 19)\n", + "===\n", + "4 -> 0.053*\"service\" + 0.046*\"production\" + 0.045*\"document\" + 0.040*\"product\" + 0.030*\"language\" + 0.029*\"design\" + 0.024*\"material\" + 0.022*\"tender\" + 0.021*\"relate\" + 0.020*\"contract\"\n", + "---\n", + "('Business services: law, marketing, consulting, recruitment, printing and security', 103)\n", + "('IT services: consulting, software development, Internet and support', 27)\n", + "('Recreational, cultural and sporting services', 20)\n", + "===\n", + "5 -> 0.077*\"maintenance\" + 0.071*\"installation\" + 0.055*\"supply\" + 0.044*\"system\" + 0.023*\"NUMBER\" + 0.023*\"jrc\" + 0.016*\"equipment\" + 0.015*\"plant\" + 0.014*\"contract\" + 0.014*\"include\"\n", + "---\n", + "('Laboratory, optical and precision equipments (excl. glasses)', 82)\n", + "('Repair and maintenance services', 77)\n", + "('Construction work', 62)\n", + "===\n", + "6 -> 0.301*\"NUMBER\" + 0.250*\"lot\" + 0.040*\"divide\" + 0.034*\"supply\" + 0.018*\"contract\" + 0.017*\"tender\" + 0.012*\"electricity\" + 0.012*\"ecb\" + 0.011*\"main\" + 0.011*\"follow\"\n", + "---\n", + "('Business services: law, marketing, consulting, recruitment, printing and security', 38)\n", + "('Construction work', 12)\n", + "('IT services: consulting, software development, Internet and support', 11)\n", + "===\n", + "7 -> 0.118*\"training\" + 0.036*\"NUMBER\" + 0.028*\"programme\" + 0.021*\"course\" + 0.020*\"activity\" + 0.019*\"protection\" + 0.019*\"organisation\" + 0.016*\"civil\" + 0.013*\"contract\" + 0.013*\"initiative\"\n", + "---\n", + "('Education and training services', 135)\n", + "('Business services: law, marketing, consulting, recruitment, printing and security', 33)\n", + "('Research and development services and related consultancy services', 18)\n", + "===\n", + "8 -> 0.081*\"NUMBER\" + 0.057*\"authority\" + 0.051*\"contracting\" + 0.046*\"address\" + 0.042*\"day\" + 0.040*\"point\" + 0.039*\"email\" + 0.038*\"seek\" + 0.037*\"submission\" + 0.037*\"deadline\"\n", + "---\n", + "('Architectural, construction, engineering and inspection services', 226)\n", + "('Business services: law, marketing, consulting, recruitment, printing and security', 22)\n", + "('Administration, defence and social security services', 17)\n", + "===\n", + "9 -> 0.085*\"construction\" + 0.064*\"water\" + 0.056*\"work\" + 0.037*\"eib\" + 0.025*\"supervision\" + 0.025*\"project\" + 0.020*\"design\" + 0.018*\"treatment\" + 0.017*\"plant\" + 0.016*\"system\"\n", + "---\n", + "('Construction work', 201)\n", + "('Architectural, construction, engineering and inspection services', 111)\n", + "('Electrical machinery, apparatus, equipment and consumables; Lighting', 17)\n", + "===\n", + "10 -> 0.081*\"project\" + 0.065*\"technical\" + 0.061*\"assistance\" + 0.047*\"subject\" + 0.035*\"authority\" + 0.033*\"contract\" + 0.030*\"contracting\" + 0.029*\"extend\" + 0.028*\"scope\" + 0.028*\"availability\"\n", + "---\n", + "('Architectural, construction, engineering and inspection services', 206)\n", + "('Business services: law, marketing, consulting, recruitment, printing and security', 59)\n", + "('Administration, defence and social security services', 32)\n", + "===\n", + "11 -> 0.208*\"service\" + 0.085*\"provision\" + 0.022*\"procedure\" + 0.021*\"consultancy\" + 0.021*\"procurement\" + 0.020*\"relate\" + 0.019*\"provider\" + 0.017*\"ict\" + 0.016*\"management\" + 0.015*\"related\"\n", + "---\n", + "('IT services: consulting, software development, Internet and support', 134)\n", + "('Business services: law, marketing, consulting, recruitment, printing and security', 97)\n", + "('Sewage-, refuse-, cleaning-, and environmental services', 30)\n", + "===\n", + "12 -> 0.099*\"service\" + 0.049*\"european\" + 0.041*\"contract\" + 0.037*\"tender\" + 0.036*\"company\" + 0.035*\"framework\" + 0.034*\"eea\" + 0.033*\"security\" + 0.033*\"union\" + 0.033*\"NUMBER\"\n", + "---\n", + "('Business services: law, marketing, consulting, recruitment, printing and security', 104)\n", + "('Sewage-, refuse-, cleaning-, and environmental services', 43)\n", + "('Supporting and auxiliary transport services; travel agencies services', 11)\n", + "===\n", + "13 -> 0.077*\"translation\" + 0.035*\"european\" + 0.035*\"union\" + 0.035*\"french\" + 0.032*\"body\" + 0.029*\"centre\" + 0.029*\"english\" + 0.025*\"german\" + 0.023*\"property\" + 0.023*\"framework\"\n", + "---\n", + "('Business services: law, marketing, consulting, recruitment, printing and security', 96)\n", + "('Education and training services', 6)\n", + "('Sewage-, refuse-, cleaning-, and environmental services', 1)\n", + "===\n", + "14 -> 0.054*\"emergency\" + 0.049*\"ipa\" + 0.026*\"woman\" + 0.023*\"georgia\" + 0.021*\"turkey\" + 0.020*\"country\" + 0.020*\"south\" + 0.018*\"response\" + 0.018*\"assistance\" + 0.018*\"negotiation\"\n", + "---\n", + "('Architectural, construction, engineering and inspection services', 11)\n", + "('Business services: law, marketing, consulting, recruitment, printing and security', 5)\n", + "('Medical equipments, pharmaceuticals and personal care products', 5)\n", + "===\n", + "15 -> 0.042*\"skill\" + 0.041*\"industrial\" + 0.032*\"labour\" + 0.029*\"improve\" + 0.025*\"rail\" + 0.021*\"market\" + 0.018*\"housing\" + 0.018*\"reporting\" + 0.016*\"purchase\" + 0.015*\"register\"\n", + "---\n", + "('Business services: law, marketing, consulting, recruitment, printing and security', 13)\n", + "('Research and development services and related consultancy services', 12)\n", + "('Architectural, construction, engineering and inspection services', 9)\n", + "===\n", + "16 -> 0.169*\"equipment\" + 0.089*\"supply\" + 0.064*\"insurance\" + 0.031*\"purchase\" + 0.027*\"computer\" + 0.027*\"installation\" + 0.023*\"service\" + 0.022*\"furniture\" + 0.021*\"hospital\" + 0.020*\"accident\"\n", + "---\n", + "('Financial and insurance services', 32)\n", + "('Office and computing machinery, equipment and supplies except furniture and software packages', 28)\n", + "('Medical equipments, pharmaceuticals and personal care products', 26)\n", + "===\n", + "17 -> 0.037*\"project\" + 0.030*\"sme\" + 0.029*\"helpdesk\" + 0.025*\"payment\" + 0.022*\"financing\" + 0.022*\"pilot\" + 0.022*\"enterprise\" + 0.021*\"development\" + 0.016*\"high\" + 0.015*\"service\"\n", + "---\n", + "('Business services: law, marketing, consulting, recruitment, printing and security', 44)\n", + "('Financial and insurance services', 16)\n", + "('Research and development services and related consultancy services', 11)\n", + "===\n", + "18 -> 0.070*\"study\" + 0.051*\"eu\" + 0.043*\"analysis\" + 0.023*\"european\" + 0.021*\"provide\" + 0.020*\"market\" + 0.019*\"information\" + 0.015*\"product\" + 0.014*\"feasibility\" + 0.013*\"NUMBER\"\n", + "---\n", + "('Business services: law, marketing, consulting, recruitment, printing and security', 95)\n", + "('Research and development services and related consultancy services', 68)\n", + "('Sewage-, refuse-, cleaning-, and environmental services', 20)\n", + "===\n", + "19 -> 0.059*\"risk\" + 0.043*\"food\" + 0.043*\"assessment\" + 0.035*\"safety\" + 0.027*\"efsa\" + 0.024*\"health\" + 0.022*\"laboratory\" + 0.022*\"scientific\" + 0.020*\"chemical\" + 0.020*\"procurement\"\n", + "---\n", + "('Research and development services and related consultancy services', 88)\n", + "('Health and social work services', 24)\n", + "('Architectural, construction, engineering and inspection services', 17)\n", + "===\n", + "20 -> 0.088*\"development\" + 0.069*\"system\" + 0.059*\"support\" + 0.054*\"maintenance\" + 0.046*\"software\" + 0.036*\"information\" + 0.031*\"service\" + 0.030*\"management\" + 0.023*\"solution\" + 0.022*\"application\"\n", + "---\n", + "('IT services: consulting, software development, Internet and support', 185)\n", + "('Software package and information systems', 52)\n", + "('Business services: law, marketing, consulting, recruitment, printing and security', 27)\n", + "===\n", + "21 -> 0.116*\"service\" + 0.115*\"framework\" + 0.107*\"contract\" + 0.081*\"provision\" + 0.036*\"staff\" + 0.027*\"office\" + 0.023*\"multiple\" + 0.019*\"conclude\" + 0.018*\"tender\" + 0.016*\"agency\"\n", + "---\n", + "('Business services: law, marketing, consulting, recruitment, printing and security', 173)\n", + "('IT services: consulting, software development, Internet and support', 37)\n", + "('Research and development services and related consultancy services', 30)\n", + "===\n", + "22 -> 0.096*\"contract\" + 0.063*\"framework\" + 0.055*\"NUMBER\" + 0.053*\"year\" + 0.026*\"lot\" + 0.025*\"order\" + 0.024*\"award\" + 0.021*\"maximum\" + 0.021*\"contractor\" + 0.021*\"conclusion\"\n", + "---\n", + "('Business services: law, marketing, consulting, recruitment, printing and security', 49)\n", + "('IT services: consulting, software development, Internet and support', 6)\n", + "('Construction work', 4)\n", + "===\n", + "23 -> 0.032*\"NUMBER\" + 0.029*\"member\" + 0.028*\"eu\" + 0.026*\"state\" + 0.022*\"study\" + 0.015*\"national\" + 0.013*\"cost\" + 0.013*\"use\" + 0.013*\"provide\" + 0.012*\"objective\"\n", + "---\n", + "('Research and development services and related consultancy services', 150)\n", + "('Business services: law, marketing, consulting, recruitment, printing and security', 94)\n", + "('Sewage-, refuse-, cleaning-, and environmental services', 59)\n", + "===\n", + "24 -> 0.045*\"health\" + 0.027*\"mobile\" + 0.024*\"occupational\" + 0.023*\"radio\" + 0.021*\"statistical\" + 0.019*\"telecommunication\" + 0.018*\"transmission\" + 0.015*\"supply\" + 0.015*\"multi\" + 0.015*\"measurement\"\n", + "---\n", + "('Business services: law, marketing, consulting, recruitment, printing and security', 23)\n", + "('Postal and telecommunications services', 7)\n", + "('Laboratory, optical and precision equipments (excl. glasses)', 6)\n", + "===\n", + "25 -> 0.136*\"security\" + 0.072*\"service\" + 0.045*\"system\" + 0.044*\"provide\" + 0.040*\"delegation\" + 0.038*\"european\" + 0.036*\"union\" + 0.033*\"resource\" + 0.028*\"human\" + 0.025*\"provision\"\n", + "---\n", + "('Business services: law, marketing, consulting, recruitment, printing and security', 138)\n", + "('Sewage-, refuse-, cleaning-, and environmental services', 9)\n", + "('Supporting and auxiliary transport services; travel agencies services', 8)\n", + "===\n", + "26 -> 0.056*\"support\" + 0.039*\"policy\" + 0.035*\"eu\" + 0.024*\"environmental\" + 0.022*\"implementation\" + 0.021*\"commission\" + 0.020*\"contract\" + 0.019*\"provide\" + 0.018*\"development\" + 0.015*\"indicator\"\n", + "---\n", + "('Business services: law, marketing, consulting, recruitment, printing and security', 103)\n", + "('Sewage-, refuse-, cleaning-, and environmental services', 75)\n", + "('Research and development services and related consultancy services', 47)\n", + "===\n", + "27 -> 0.078*\"vehicle\" + 0.046*\"court\" + 0.044*\"european\" + 0.041*\"test\" + 0.032*\"justice\" + 0.032*\"emission\" + 0.031*\"union\" + 0.023*\"car\" + 0.023*\"supply\" + 0.023*\"fuel\"\n", + "---\n", + "('Transport equipment and auxiliary products to transportation', 22)\n", + "('Architectural, construction, engineering and inspection services', 11)\n", + "('Business services: law, marketing, consulting, recruitment, printing and security', 9)\n", + "===\n", + "28 -> 0.166*\"energy\" + 0.050*\"house\" + 0.044*\"service\" + 0.043*\"eu\" + 0.037*\"reception\" + 0.031*\"efficiency\" + 0.030*\"tender\" + 0.028*\"renewable\" + 0.027*\"premise\" + 0.020*\"security\"\n", + "---\n", + "('Business services: law, marketing, consulting, recruitment, printing and security', 40)\n", + "('Sewage-, refuse-, cleaning-, and environmental services', 19)\n", + "('Research and development services and related consultancy services', 10)\n", + "===\n", + "29 -> 0.128*\"contract\" + 0.087*\"tenderer\" + 0.066*\"guarantee\" + 0.064*\"provide\" + 0.044*\"NUMBER\" + 0.023*\"return\" + 0.022*\"period\" + 0.022*\"later\" + 0.022*\"performance\" + 0.022*\"receive\"\n", + "---\n", + "('Construction work', 71)\n", + "('Office and computing machinery, equipment and supplies except furniture and software packages', 56)\n", + "('Architectural, construction, engineering and inspection services', 55)\n", + "===\n", + "30 -> 0.045*\"NUMBER\" + 0.041*\"supply\" + 0.033*\"room\" + 0.032*\"gas\" + 0.023*\"galileo\" + 0.021*\"fuel\" + 0.020*\"use\" + 0.018*\"satellite\" + 0.016*\"natural\" + 0.014*\"heating\"\n", + "---\n", + "('Petroleum products, fuel, electricity and other sources of energy', 23)\n", + "('Laboratory, optical and precision equipments (excl. glasses)', 15)\n", + "('Research and development services and related consultancy services', 11)\n", + "===\n", + "31 -> 0.125*\"datum\" + 0.023*\"collection\" + 0.023*\"review\" + 0.022*\"eu\" + 0.018*\"data\" + 0.017*\"country\" + 0.014*\"approach\" + 0.013*\"research\" + 0.012*\"study\" + 0.012*\"objective\"\n", + "---\n", + "('Research and development services and related consultancy services', 59)\n", + "('Business services: law, marketing, consulting, recruitment, printing and security', 43)\n", + "('IT services: consulting, software development, Internet and support', 28)\n", + "===\n", + "32 -> 0.042*\"support\" + 0.029*\"task\" + 0.025*\"provide\" + 0.025*\"coordination\" + 0.021*\"specific\" + 0.019*\"contract\" + 0.017*\"technical\" + 0.017*\"service\" + 0.016*\"ecdc\" + 0.016*\"video\"\n", + "---\n", + "('Research and development services and related consultancy services', 28)\n", + "('Business services: law, marketing, consulting, recruitment, printing and security', 28)\n", + "('Health and social work services', 17)\n", + "===\n", + "33 -> 0.055*\"evaluation\" + 0.045*\"impact\" + 0.044*\"assessment\" + 0.037*\"study\" + 0.030*\"NUMBER\" + 0.028*\"economic\" + 0.024*\"policy\" + 0.023*\"contract\" + 0.022*\"analysis\" + 0.015*\"gender\"\n", + "---\n", + "('Business services: law, marketing, consulting, recruitment, printing and security', 73)\n", + "('Research and development services and related consultancy services', 43)\n", + "('Agricultural, forestry, horticultural, aquacultural and apicultural services', 11)\n", + "===\n", + "34 -> 0.108*\"contract\" + 0.075*\"tenderer\" + 0.052*\"bond\" + 0.045*\"successful\" + 0.045*\"NUMBER\" + 0.040*\"sign\" + 0.036*\"provide\" + 0.036*\"require\" + 0.022*\"price\" + 0.021*\"time\"\n", + "---\n", + "('Office and computing machinery, equipment and supplies except furniture and software packages', 26)\n", + "('Construction work', 24)\n", + "('Transport equipment and auxiliary products to transportation', 19)\n", + "===\n", + "35 -> 0.071*\"service\" + 0.058*\"eib\" + 0.049*\"management\" + 0.040*\"ecb\" + 0.032*\"consultancy\" + 0.028*\"provision\" + 0.026*\"support\" + 0.024*\"bank\" + 0.021*\"group\" + 0.019*\"framework\"\n", + "---\n", + "('Business services: law, marketing, consulting, recruitment, printing and security', 118)\n", + "('IT services: consulting, software development, Internet and support', 49)\n", + "('Architectural, construction, engineering and inspection services', 35)\n", + "===\n", + "36 -> 0.133*\"NUMBER\" + 0.030*\"audit\" + 0.027*\"general\" + 0.024*\"financial\" + 0.023*\"eu\" + 0.022*\"service\" + 0.021*\"contract\" + 0.020*\"gsa\" + 0.020*\"MONTH\" + 0.018*\"budget\"\n", + "---\n", + "('Business services: law, marketing, consulting, recruitment, printing and security', 29)\n", + "('Architectural, construction, engineering and inspection services', 14)\n", + "('Research and development services and related consultancy services', 13)\n", + "===\n", + "37 -> 0.055*\"infrastructure\" + 0.055*\"operation\" + 0.052*\"access\" + 0.030*\"database\" + 0.026*\"support\" + 0.024*\"investment\" + 0.020*\"implementation\" + 0.019*\"finance\" + 0.016*\"subscription\" + 0.016*\"platform\"\n", + "---\n", + "('Architectural, construction, engineering and inspection services', 34)\n", + "('Business services: law, marketing, consulting, recruitment, printing and security', 17)\n", + "('IT services: consulting, software development, Internet and support', 13)\n", + "===\n", + "38 -> 0.135*\"NUMBER\" + 0.077*\"road\" + 0.069*\"work\" + 0.041*\"construction\" + 0.037*\"km\" + 0.028*\"railway\" + 0.024*\"rehabilitation\" + 0.022*\"line\" + 0.018*\"section\" + 0.017*\"safety\"\n", + "---\n", + "('Construction work', 88)\n", + "('Architectural, construction, engineering and inspection services', 39)\n", + "('Research and development services and related consultancy services', 6)\n", + "===\n", + "39 -> 0.096*\"european\" + 0.078*\"building\" + 0.057*\"parliament\" + 0.039*\"brussels\" + 0.038*\"service\" + 0.030*\"contract\" + 0.027*\"luxembourg\" + 0.022*\"NUMBER\" + 0.021*\"strasbourg\" + 0.019*\"work\"\n", + "---\n", + "('Construction work', 87)\n", + "('Architectural, construction, engineering and inspection services', 66)\n", + "('Repair and maintenance services', 59)\n", + "===\n", + "40 -> 0.056*\"administrative\" + 0.055*\"legal\" + 0.053*\"law\" + 0.050*\"advice\" + 0.039*\"sea\" + 0.027*\"service\" + 0.026*\"different\" + 0.021*\"cultural\" + 0.020*\"assistance\" + 0.018*\"learning\"\n", + "---\n", + "('Business services: law, marketing, consulting, recruitment, printing and security', 20)\n", + "('Architectural, construction, engineering and inspection services', 6)\n", + "('Education and training services', 3)\n", + "===\n", + "41 -> 0.035*\"climate\" + 0.033*\"copernicus\" + 0.032*\"service\" + 0.030*\"NUMBER\" + 0.028*\"support\" + 0.027*\"change\" + 0.022*\"global\" + 0.022*\"activity\" + 0.019*\"land\" + 0.015*\"monitoring\"\n", + "---\n", + "('Business services: law, marketing, consulting, recruitment, printing and security', 72)\n", + "('Research and development services and related consultancy services', 57)\n", + "('IT services: consulting, software development, Internet and support', 54)\n", + "===\n", + "42 -> 0.074*\"service\" + 0.069*\"european\" + 0.054*\"communication\" + 0.049*\"commission\" + 0.029*\"information\" + 0.023*\"public\" + 0.022*\"representation\" + 0.021*\"provision\" + 0.019*\"activity\" + 0.016*\"provide\"\n", + "---\n", + "('Business services: law, marketing, consulting, recruitment, printing and security', 119)\n", + "('Postal and telecommunications services', 37)\n", + "('IT services: consulting, software development, Internet and support', 27)\n", + "===\n", + "43 -> 0.081*\"european\" + 0.077*\"network\" + 0.067*\"education\" + 0.038*\"school\" + 0.035*\"support\" + 0.023*\"commission\" + 0.018*\"high\" + 0.018*\"programme\" + 0.014*\"research\" + 0.013*\"procure\"\n", + "---\n", + "('Education and training services', 30)\n", + "('Architectural, construction, engineering and inspection services', 21)\n", + "('Business services: law, marketing, consulting, recruitment, printing and security', 17)\n", + "===\n", + "44 -> 0.090*\"application\" + 0.087*\"form\" + 0.045*\"section\" + 0.045*\"standard\" + 0.045*\"NUMBER\" + 0.044*\"candidate\" + 0.043*\"situation\" + 0.043*\"submit\" + 0.042*\"sign\" + 0.042*\"include\"\n", + "---\n", + "('Architectural, construction, engineering and inspection services', 405)\n", + "('Business services: law, marketing, consulting, recruitment, printing and security', 205)\n", + "('Administration, defence and social security services', 129)\n", + "===\n", + "45 -> 0.105*\"transport\" + 0.066*\"survey\" + 0.038*\"european\" + 0.038*\"international\" + 0.030*\"statistic\" + 0.029*\"aviation\" + 0.022*\"safety\" + 0.021*\"level\" + 0.018*\"service\" + 0.018*\"agency\"\n", + "---\n", + "('Business services: law, marketing, consulting, recruitment, printing and security', 40)\n", + "('Research and development services and related consultancy services', 18)\n", + "('IT services: consulting, software development, Internet and support', 8)\n", + "===\n", + "46 -> 0.074*\"site\" + 0.069*\"jrc\" + 0.044*\"centre\" + 0.042*\"ispra\" + 0.036*\"joint\" + 0.034*\"research\" + 0.028*\"building\" + 0.027*\"service\" + 0.025*\"work\" + 0.024*\"NUMBER\"\n", + "---\n", + "('Construction work', 49)\n", + "('Architectural, construction, engineering and inspection services', 36)\n", + "('Repair and maintenance services', 19)\n", + "===\n", + "47 -> 0.046*\"NUMBER\" + 0.035*\"service\" + 0.030*\"device\" + 0.029*\"equipment\" + 0.022*\"time\" + 0.022*\"frontex\" + 0.020*\"acquisition\" + 0.019*\"provision\" + 0.017*\"supply\" + 0.016*\"purchase\"\n", + "---\n", + "('IT services: consulting, software development, Internet and support', 23)\n", + "('Office and computing machinery, equipment and supplies except furniture and software packages', 20)\n", + "('Repair and maintenance services', 18)\n", + "===\n", + "48 -> 0.030*\"study\" + 0.022*\"digital\" + 0.021*\"practice\" + 0.021*\"sector\" + 0.020*\"market\" + 0.020*\"eu\" + 0.019*\"good\" + 0.019*\"innovation\" + 0.018*\"objective\" + 0.017*\"smart\"\n", + "---\n", + "('Business services: law, marketing, consulting, recruitment, printing and security', 182)\n", + "('Research and development services and related consultancy services', 124)\n", + "('IT services: consulting, software development, Internet and support', 24)\n", + "===\n", + "49 -> 0.075*\"waste\" + 0.050*\"epo\" + 0.046*\"right\" + 0.043*\"nuclear\" + 0.042*\"allow\" + 0.041*\"de\" + 0.035*\"reduce\" + 0.035*\"safeguard\" + 0.034*\"proposal\" + 0.030*\"effect\"\n", + "---\n", + "('Sewage-, refuse-, cleaning-, and environmental services', 21)\n", + "('Business services: law, marketing, consulting, recruitment, printing and security', 18)\n", + "('Construction work', 14)\n" + ] + } + ], + "source": [ + "for idx, topic in lda_model.print_topics(-1):\n", + " print(\"===\")\n", + " print(idx, \"->\", topic)\n", + " print(\"---\")\n", + " df_filtered = df_topic[df_topic[\"topic\"] == idx]\n", + " df_filtered = df_filtered.drop([\"title_texte\", \"topic\"], axis=1)\n", + " count_per_cpv = {cpv_labels[c]: df_filtered[c].sum() for c in df_filtered.columns}\n", + " top_cpvs = sorted(count_per_cpv.items(), key=lambda x: x[1], reverse=True)\n", + " for j in range(3):\n", + " print(top_cpvs[j])" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "markdown", + "source": [ + "## Evaluation of CPV classification" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": 163, + "outputs": [], + "source": [ + "from scipy.stats import entropy\n", + "import numpy as np\n", + "\n", + "\n", + "# https://github.com/soberbichler/Using-LDA-and-Jensen-Shannon-distance-to-separate-relevant-from-non-relevant-articles/blob/master/news_article_similarity_remigration_notebook.ipynb\n", + "def jensen_shannon(query, matrix):\n", + " p = query[None, :].T\n", + " q = matrix.T\n", + " m = 0.5 * (p + q)\n", + " return np.sqrt(0.5 * (entropy(p, m) + entropy(q, m)))\n", + "\n", + "\n", + "def get_doc_similarities(query, matrix):\n", + " sims = jensen_shannon(query, matrix) # list of jensen shannon distances\n", + " return sims" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": 164, + "outputs": [], + "source": [ + "corpus = df_train[\"title_texte\"].apply(lambda x: dictionary.doc2bow(x.split(\" \")))\n", + "all_dists = np.stack([np.array([tup[1] for tup in lst]) for lst in lda_model[list(corpus)]])" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "markdown", + "source": [ + "### With bag-of-words" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": 165, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + " 1%| | 20/2912 [00:01<02:35, 18.59it/s]/tmp/ipykernel_24919/3601534538.py:10: RuntimeWarning: invalid value encountered in sqrt\n", + " return np.sqrt(0.5 * (entropy(p, m) + entropy(q, m)))\n", + "100%|██████████| 2912/2912 [02:47<00:00, 17.42it/s]\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + " precision recall f1-score support\n", + "\n", + " Health and social work services... | 85 0.03 0.08 0.04 62\n", + "Construction structures and materials; a... | 44 0.01 0.04 0.02 23\n", + " Repair and maintenance services... | 50 0.04 0.10 0.05 68\n", + " Education and training services... | 80 0.02 0.05 0.03 99\n", + "Research and development services and re... | 73 0.08 0.22 0.12 249\n", + " Construction work... | 45 0.08 0.19 0.12 191\n", + "Architectural, construction, engineering... | 71 0.13 0.32 0.19 404\n", + "Business services: law, marketing, consu... | 79 0.24 0.54 0.34 688\n", + "Sewage-, refuse-, cleaning-, and environ... | 90 0.05 0.11 0.07 176\n", + "Office and computing machinery, equipmen... | 30 0.03 0.07 0.04 70\n", + "Security, fire-fighting, police and defe... | 35 0.04 0.08 0.05 36\n", + "Medical equipments, pharmaceuticals and ... | 33 0.01 0.03 0.01 38\n", + "Hotel, restaurant and retail trade servi... | 55 0.03 0.07 0.04 30\n", + "IT services: consulting, software develo... | 72 0.06 0.19 0.09 197\n", + "Software package and information systems... | 48 0.01 0.05 0.02 43\n", + "Laboratory, optical and precision equipm... | 38 0.03 0.10 0.05 61\n", + "Petroleum products, fuel, electricity an... | 09 0.01 0.03 0.02 34\n", + "Administration, defence and social secur... | 75 0.03 0.08 0.04 77\n", + " Financial and insurance services... | 66 0.01 0.04 0.02 46\n", + " Postal and telecommunications services... | 64 0.01 0.03 0.01 37\n", + " Industrial machinery... | 42 0.00 0.00 0.00 27\n", + "Transport equipment and auxiliary produc... | 34 0.02 0.06 0.03 50\n", + "Transport services (excl. Waste transpor... | 60 0.03 0.05 0.03 41\n", + "Recreational, cultural and sporting serv... | 92 0.01 0.02 0.01 43\n", + "Furniture (incl. office furniture), furn... | 39 0.01 0.04 0.02 49\n", + "Electrical machinery, apparatus, equipme... | 31 0.00 0.00 0.00 36\n", + "Other community, social and personal ser... | 98 0.00 0.00 0.00 35\n", + " Installation services (except software)... | 51 0.00 0.00 0.00 8\n", + "Radio, television, communication, teleco... | 32 0.03 0.07 0.04 40\n", + " Public utilities... | 65 0.00 0.00 0.00 15\n", + "Agricultural, forestry, horticultural, a... | 77 0.00 0.00 0.00 22\n", + " Printed matter and related products... | 22 0.00 0.00 0.00 20\n", + "Supporting and auxiliary transport servi... | 63 0.00 0.00 0.00 34\n", + "Food, beverages, tobacco and related pro... | 15 0.03 0.20 0.06 5\n", + " Real estate services... | 70 0.00 0.00 0.00 14\n", + "Clothing, footwear, luggage articles and... | 18 0.00 0.00 0.00 5\n", + "Agricultural, farming, fishing, forestry... | 03 0.00 0.00 0.00 5\n", + " Chemical products... | 24 0.00 0.00 0.00 11\n", + "Machinery for mining, quarrying, constru... | 43 0.00 0.00 0.00 1\n", + "Leather and textile fabrics, plastic and... | 19 0.00 0.00 0.00 5\n", + " Collected and purified water... | 41 0.00 0.00 0.00 2\n", + "Musical instruments, sport goods, games,... | 37 0.00 0.00 0.00 3\n", + "Mining, basic metals and related product... | 14 0.00 0.00 0.00 6\n", + " Agricultural machinery... | 16 0.00 0.00 0.00 4\n", + "Services related to the oil and gas indu... | 76 0.00 0.00 0.00 3\n", + "\n", + " micro avg 0.09 0.23 0.13 3113\n", + " macro avg 0.02 0.06 0.04 3113\n", + " weighted avg 0.10 0.23 0.14 3113\n", + " samples avg 0.10 0.23 0.13 3113\n", + "\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/ferreni/Projects/TED AI/Repositories/tedai-cpv-classification/venv/lib/python3.10/site-packages/sklearn/metrics/_classification.py:1344: UndefinedMetricWarning: Precision and F-score are ill-defined and being set to 0.0 in samples with no predicted labels. Use `zero_division` parameter to control this behavior.\n", + " _warn_prf(average, modifier, msg_start, len(result))\n" + ] + } + ], + "source": [ + "from tqdm import tqdm\n", + "from sklearn.metrics import classification_report\n", + "\n", + "y_true = []\n", + "y_pred = []\n", + "notices = list(df_test.iloc)\n", + "for notice in tqdm(notices, total=len(notices)):\n", + " notice_bow = dictionary.doc2bow(notice[\"title_texte\"].split(\" \"))\n", + " dist = np.array([tup[1] for tup in lda_model.get_document_topics(bow=notice_bow)])\n", + " sims = get_doc_similarities(dist, all_dists)\n", + " most_sim_ids = sims.argsort()[:3] # the top k positional index of the smallest Jensen Shannon distances\n", + " most_similar_df = df_train[df_train.index.isin(most_sim_ids)]\n", + " y_true.append([int(notice[c] == True) for c in cpvs])\n", + " y_pred.append([int(most_similar_df[c].sum() > 0) for c in cpvs])\n", + "print(classification_report(y_true, y_pred, target_names=[f\"{cpv_labels[c][:40]}... | {c}\" for c in cpvs]))" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "markdown", + "source": [ + "### With TFIDF" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": 166, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + " 1%| | 34/2912 [00:02<03:32, 13.52it/s]/tmp/ipykernel_24919/3601534538.py:10: RuntimeWarning: invalid value encountered in sqrt\n", + " return np.sqrt(0.5 * (entropy(p, m) + entropy(q, m)))\n", + "100%|██████████| 2912/2912 [03:00<00:00, 16.11it/s]\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + " precision recall f1-score support\n", + "\n", + " Health and social work services... | 85 0.01 0.02 0.01 62\n", + "Construction structures and materials; a... | 44 0.00 0.00 0.00 23\n", + " Repair and maintenance services... | 50 0.02 0.04 0.03 68\n", + " Education and training services... | 80 0.04 0.07 0.05 99\n", + "Research and development services and re... | 73 0.08 0.20 0.11 249\n", + " Construction work... | 45 0.04 0.09 0.05 191\n", + "Architectural, construction, engineering... | 71 0.11 0.27 0.15 404\n", + "Business services: law, marketing, consu... | 79 0.22 0.49 0.30 688\n", + "Sewage-, refuse-, cleaning-, and environ... | 90 0.02 0.03 0.02 176\n", + "Office and computing machinery, equipmen... | 30 0.02 0.06 0.03 70\n", + "Security, fire-fighting, police and defe... | 35 0.01 0.03 0.02 36\n", + "Medical equipments, pharmaceuticals and ... | 33 0.02 0.05 0.03 38\n", + "Hotel, restaurant and retail trade servi... | 55 0.00 0.00 0.00 30\n", + "IT services: consulting, software develo... | 72 0.07 0.26 0.11 197\n", + "Software package and information systems... | 48 0.01 0.02 0.01 43\n", + "Laboratory, optical and precision equipm... | 38 0.01 0.07 0.02 61\n", + "Petroleum products, fuel, electricity an... | 09 0.02 0.03 0.02 34\n", + "Administration, defence and social secur... | 75 0.03 0.09 0.04 77\n", + " Financial and insurance services... | 66 0.01 0.02 0.01 46\n", + " Postal and telecommunications services... | 64 0.02 0.03 0.02 37\n", + " Industrial machinery... | 42 0.00 0.00 0.00 27\n", + "Transport equipment and auxiliary produc... | 34 0.01 0.02 0.02 50\n", + "Transport services (excl. Waste transpor... | 60 0.00 0.00 0.00 41\n", + "Recreational, cultural and sporting serv... | 92 0.00 0.00 0.00 43\n", + "Furniture (incl. office furniture), furn... | 39 0.03 0.12 0.05 49\n", + "Electrical machinery, apparatus, equipme... | 31 0.00 0.00 0.00 36\n", + "Other community, social and personal ser... | 98 0.02 0.03 0.02 35\n", + " Installation services (except software)... | 51 0.00 0.00 0.00 8\n", + "Radio, television, communication, teleco... | 32 0.02 0.07 0.03 40\n", + " Public utilities... | 65 0.00 0.00 0.00 15\n", + "Agricultural, forestry, horticultural, a... | 77 0.00 0.00 0.00 22\n", + " Printed matter and related products... | 22 0.00 0.00 0.00 20\n", + "Supporting and auxiliary transport servi... | 63 0.00 0.00 0.00 34\n", + "Food, beverages, tobacco and related pro... | 15 0.00 0.00 0.00 5\n", + " Real estate services... | 70 0.00 0.00 0.00 14\n", + "Clothing, footwear, luggage articles and... | 18 0.00 0.00 0.00 5\n", + "Agricultural, farming, fishing, forestry... | 03 0.00 0.00 0.00 5\n", + " Chemical products... | 24 0.00 0.00 0.00 11\n", + "Machinery for mining, quarrying, constru... | 43 0.00 0.00 0.00 1\n", + "Leather and textile fabrics, plastic and... | 19 0.00 0.00 0.00 5\n", + " Collected and purified water... | 41 0.00 0.00 0.00 2\n", + "Musical instruments, sport goods, games,... | 37 0.00 0.00 0.00 3\n", + "Mining, basic metals and related product... | 14 0.00 0.00 0.00 6\n", + " Agricultural machinery... | 16 0.00 0.00 0.00 4\n", + "Services related to the oil and gas indu... | 76 0.00 0.00 0.00 3\n", + "\n", + " micro avg 0.08 0.20 0.11 3113\n", + " macro avg 0.02 0.05 0.03 3113\n", + " weighted avg 0.08 0.20 0.11 3113\n", + " samples avg 0.08 0.20 0.11 3113\n", + "\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/ferreni/Projects/TED AI/Repositories/tedai-cpv-classification/venv/lib/python3.10/site-packages/sklearn/metrics/_classification.py:1344: UndefinedMetricWarning: Precision and F-score are ill-defined and being set to 0.0 in labels with no predicted samples. Use `zero_division` parameter to control this behavior.\n", + " _warn_prf(average, modifier, msg_start, len(result))\n", + "/home/ferreni/Projects/TED AI/Repositories/tedai-cpv-classification/venv/lib/python3.10/site-packages/sklearn/metrics/_classification.py:1344: UndefinedMetricWarning: Precision and F-score are ill-defined and being set to 0.0 in samples with no predicted labels. Use `zero_division` parameter to control this behavior.\n", + " _warn_prf(average, modifier, msg_start, len(result))\n" + ] + } + ], + "source": [ + "from tqdm import tqdm\n", + "from sklearn.metrics import classification_report\n", + "\n", + "y_true = []\n", + "y_pred = []\n", + "notices = list(df_test.iloc)\n", + "for notice in tqdm(notices, total=len(notices)):\n", + " notice_bow = dictionary.doc2bow(notice[\"title_texte\"].split(\" \"))\n", + " dist = np.array([tup[1] for tup in lda_model_tfidf.get_document_topics(bow=notice_bow)])\n", + " sims = get_doc_similarities(dist, all_dists)\n", + " most_sim_ids = sims.argsort()[:3] # the top k positional index of the smallest Jensen Shannon distances\n", + " most_similar_df = df_train[df_train.index.isin(most_sim_ids)]\n", + " y_true.append([int(notice[c] == True) for c in cpvs])\n", + " y_pred.append([int(most_similar_df[c].sum() > 0) for c in cpvs])\n", + "print(classification_report(y_true, y_pred, target_names=[f\"{cpv_labels[c][:40]}... | {c}\" for c in cpvs]))" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": null, + "outputs": [], + "source": [], + "metadata": { + "collapsed": false + } + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 2 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython2", + "version": "2.7.6" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/20230327-unsupervised-clustering-lda-coherence.ipynb b/20230327-unsupervised-clustering-lda-coherence.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..3e509895371abb0145395eef454b623fc568beea --- /dev/null +++ b/20230327-unsupervised-clustering-lda-coherence.ipynb @@ -0,0 +1,460 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "source": [ + "# Unsupervised clustering - find best LDA model based on coherence\n", + "\n", + "## Dataset loading" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": 1, + "outputs": [ + { + "data": { + "text/plain": " title_texte 85 44 50 \\\n1 ipa supply equipment increase competitiveness... False True False \n3 provision language training service tender in... False False False \n4 service support eda helicopter portfolio main... False False False \n5 NUMBER cp op NUMBER pooling share cost non co... False False False \n6 edf supply transport household similar waste ... False False False \n\n 80 73 45 71 79 90 ... 18 03 24 43 \\\n1 False False False False False False ... False False False False \n3 True False False False False False ... False False False False \n4 True False False False False False ... False False False False \n5 False True False False False False ... False False False False \n6 False False True False False False ... False False False False \n\n 19 41 37 14 16 76 \n1 False False False False False False \n3 False False False False False False \n4 False False False False False False \n5 False False False False False False \n6 False False False False False False \n\n[5 rows x 46 columns]", + "text/html": "<div>\n<style scoped>\n .dataframe tbody tr th:only-of-type {\n vertical-align: middle;\n }\n\n .dataframe tbody tr th {\n vertical-align: top;\n }\n\n .dataframe thead th {\n text-align: right;\n }\n</style>\n<table border=\"1\" class=\"dataframe\">\n <thead>\n <tr style=\"text-align: right;\">\n <th></th>\n <th>title_texte</th>\n <th>85</th>\n <th>44</th>\n <th>50</th>\n <th>80</th>\n <th>73</th>\n <th>45</th>\n <th>71</th>\n <th>79</th>\n <th>90</th>\n <th>...</th>\n <th>18</th>\n <th>03</th>\n <th>24</th>\n <th>43</th>\n <th>19</th>\n <th>41</th>\n <th>37</th>\n <th>14</th>\n <th>16</th>\n <th>76</th>\n </tr>\n </thead>\n <tbody>\n <tr>\n <th>1</th>\n <td>ipa supply equipment increase competitiveness...</td>\n <td>False</td>\n <td>True</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>...</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n </tr>\n <tr>\n <th>3</th>\n <td>provision language training service tender in...</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>True</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>...</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n </tr>\n <tr>\n <th>4</th>\n <td>service support eda helicopter portfolio main...</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>True</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>...</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n </tr>\n <tr>\n <th>5</th>\n <td>NUMBER cp op NUMBER pooling share cost non co...</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>True</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>...</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n </tr>\n <tr>\n <th>6</th>\n <td>edf supply transport household similar waste ...</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>True</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>...</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n </tr>\n </tbody>\n</table>\n<p>5 rows × 46 columns</p>\n</div>" + }, + "execution_count": 1, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "import pandas as pd\n", + "\n", + "df = pd.read_csv(\"20230214-dataset_preprocessed_with_lemma.csv\", index_col=0)\n", + "df.head()" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": 2, + "outputs": [ + { + "data": { + "text/plain": "((11647, 46), (2912, 46))" + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from sklearn.model_selection import train_test_split\n", + "\n", + "cpvs = [c for c in df.columns if len(c) == 2]\n", + "df_train, df_test = train_test_split(df, test_size=0.2, shuffle=False)\n", + "(df_train.shape, df_test.shape)" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": 3, + "outputs": [ + { + "data": { + "text/plain": "{'85': 256,\n '44': 103,\n '50': 297,\n '80': 403,\n '73': 1067,\n '45': 731,\n '71': 1621,\n '79': 2682,\n '90': 629,\n '30': 266,\n '35': 145,\n '33': 158,\n '55': 117,\n '72': 914,\n '48': 199,\n '38': 289,\n '09': 128,\n '75': 277,\n '66': 206,\n '64': 148,\n '42': 159,\n '34': 199,\n '60': 122,\n '92': 169,\n '39': 188,\n '31': 139,\n '98': 123,\n '51': 50,\n '32': 185,\n '65': 29,\n '77': 83,\n '22': 61,\n '63': 144,\n '15': 43,\n '70': 44,\n '18': 35,\n '03': 31,\n '24': 30,\n '43': 17,\n '19': 7,\n '41': 13,\n '37': 13,\n '14': 16,\n '16': 5,\n '76': 5}" + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "{c: df_train[c].sum() for c in cpvs}" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": 4, + "outputs": [ + { + "data": { + "text/plain": "{'85': 62,\n '44': 23,\n '50': 68,\n '80': 99,\n '73': 249,\n '45': 191,\n '71': 404,\n '79': 688,\n '90': 176,\n '30': 70,\n '35': 36,\n '33': 38,\n '55': 30,\n '72': 197,\n '48': 43,\n '38': 61,\n '09': 34,\n '75': 77,\n '66': 46,\n '64': 37,\n '42': 27,\n '34': 50,\n '60': 41,\n '92': 43,\n '39': 49,\n '31': 36,\n '98': 35,\n '51': 8,\n '32': 40,\n '65': 15,\n '77': 22,\n '22': 20,\n '63': 34,\n '15': 5,\n '70': 14,\n '18': 5,\n '03': 5,\n '24': 11,\n '43': 1,\n '19': 5,\n '41': 2,\n '37': 3,\n '14': 6,\n '16': 4,\n '76': 3}" + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "{c: df_test[c].sum() for c in cpvs}" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": 5, + "outputs": [], + "source": [ + "import gensim\n", + "\n", + "processed_docs = df_train[\"title_texte\"].apply(lambda x: x.split(\" \"))\n", + "processed_docs_test = df_train[\"title_texte\"].apply(lambda x: x.split(\" \"))\n", + "\n", + "dictionary = gensim.corpora.Dictionary(processed_docs)\n", + "dictionary.filter_extremes(no_below=15, no_above=0.5, keep_n=100000)\n", + "bow_corpus = [dictionary.doc2bow(doc) for doc in processed_docs]" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "markdown", + "source": [ + "## Find good number of topics" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": 6, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "100%|██████████| 14/14 [01:49<00:00, 7.81s/it]\n" + ] + } + ], + "source": [ + "from tqdm import tqdm\n", + "from gensim.models import CoherenceModel\n", + "\n", + "topic_counts = range(2, 30, 2)\n", + "coherences = []\n", + "\n", + "for topic_count in tqdm(topic_counts, total=len(topic_counts)):\n", + " lda_model = gensim.models.LdaModel(bow_corpus, num_topics=topic_count, id2word=dictionary, passes=2,\n", + " minimum_probability=0.0, random_state=0)\n", + " lda_coherence_model = CoherenceModel(model=lda_model, texts=processed_docs_test, dictionary=dictionary,\n", + " coherence='c_v')\n", + " coherences.append(lda_coherence_model.get_coherence())" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": 7, + "outputs": [ + { + "data": { + "text/plain": "<Figure size 640x480 with 1 Axes>", + "image/png": "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" + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "from matplotlib import pyplot as plt\n", + "\n", + "plt.plot(topic_counts, coherences)\n", + "plt.xticks(topic_counts, topic_counts)\n", + "plt.show()" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "markdown", + "source": [ + "Two local maxima found: at 6 and in range [14; 18].\n", + "Let's take 18 topics." + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "markdown", + "source": [ + "## Find good number of passes" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": 9, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "100%|██████████| 12/12 [12:07<00:00, 60.60s/it]\n" + ] + } + ], + "source": [ + "from tqdm import tqdm\n", + "from gensim.models import CoherenceModel\n", + "\n", + "pass_counts = range(2, 50, 4)\n", + "coherences = []\n", + "\n", + "for pass_count in tqdm(pass_counts, total=len(pass_counts)):\n", + " lda_model = gensim.models.LdaModel(bow_corpus, num_topics=18, id2word=dictionary, passes=pass_count,\n", + " minimum_probability=0.0, random_state=0)\n", + " lda_coherence_model = CoherenceModel(model=lda_model, texts=processed_docs_test, dictionary=dictionary,\n", + " coherence='c_v')\n", + " coherences.append(lda_coherence_model.get_coherence())" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": 10, + "outputs": [ + { + "data": { + "text/plain": "<Figure size 640x480 with 1 Axes>", + "image/png": "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" + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "plt.plot(pass_counts, coherences)\n", + "plt.xticks(pass_counts, pass_counts)\n", + "plt.show()" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "markdown", + "source": [ + "10 passes seems to be a good balance between training time and performance" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "markdown", + "source": [ + "## Grid search for alpha and eta hyperparameters" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": 13, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + " 0%| | 0/8 [00:00<?, ?it/s]\n", + " 0%| | 0/7 [00:00<?, ?it/s]\u001B[A\n", + " 14%|█▍ | 1/7 [00:21<02:09, 21.60s/it]\u001B[A\n", + " 29%|██▊ | 2/7 [00:45<01:53, 22.67s/it]\u001B[A\n", + " 43%|████▎ | 3/7 [01:10<01:35, 23.82s/it]\u001B[A\n", + " 57%|█████▋ | 4/7 [01:36<01:14, 24.82s/it]\u001B[A\n", + " 71%|███████▏ | 5/7 [02:03<00:51, 25.67s/it]\u001B[A\n", + " 86%|████████▌ | 6/7 [02:27<00:25, 25.04s/it]\u001B[A\n", + "100%|██████████| 7/7 [02:52<00:00, 24.62s/it]\u001B[A\n", + " 12%|█▎ | 1/8 [02:52<20:06, 172.32s/it]\n", + " 0%| | 0/7 [00:00<?, ?it/s]\u001B[A\n", + " 14%|█▍ | 1/7 [00:28<02:49, 28.25s/it]\u001B[A\n", + " 29%|██▊ | 2/7 [00:54<02:15, 27.03s/it]\u001B[A\n", + " 43%|████▎ | 3/7 [01:19<01:44, 26.06s/it]\u001B[A\n", + " 57%|█████▋ | 4/7 [01:44<01:16, 25.64s/it]\u001B[A\n", + " 71%|███████▏ | 5/7 [02:11<00:52, 26.29s/it]\u001B[A\n", + " 86%|████████▌ | 6/7 [02:34<00:25, 25.16s/it]\u001B[A\n", + "100%|██████████| 7/7 [02:57<00:00, 25.35s/it]\u001B[A\n", + " 25%|██▌ | 2/8 [05:49<17:31, 175.32s/it]\n", + " 0%| | 0/7 [00:00<?, ?it/s]\u001B[A\n", + " 14%|█▍ | 1/7 [00:19<01:56, 19.48s/it]\u001B[A\n", + " 29%|██▊ | 2/7 [00:39<01:38, 19.64s/it]\u001B[A\n", + " 43%|████▎ | 3/7 [00:59<01:19, 19.78s/it]\u001B[A\n", + " 57%|█████▋ | 4/7 [01:19<00:59, 19.93s/it]\u001B[A\n", + " 71%|███████▏ | 5/7 [01:40<00:40, 20.45s/it]\u001B[A\n", + " 86%|████████▌ | 6/7 [02:01<00:20, 20.62s/it]\u001B[A\n", + "100%|██████████| 7/7 [02:21<00:00, 20.25s/it]\u001B[A\n", + " 38%|███▊ | 3/8 [08:11<13:20, 160.01s/it]\n", + " 0%| | 0/7 [00:00<?, ?it/s]\u001B[A\n", + " 14%|█▍ | 1/7 [00:17<01:47, 17.89s/it]\u001B[A\n", + " 29%|██▊ | 2/7 [00:35<01:28, 17.71s/it]\u001B[A\n", + " 43%|████▎ | 3/7 [00:53<01:11, 17.77s/it]\u001B[A\n", + " 57%|█████▋ | 4/7 [01:14<00:57, 19.27s/it]\u001B[A\n", + " 71%|███████▏ | 5/7 [01:37<00:41, 20.55s/it]\u001B[A\n", + " 86%|████████▌ | 6/7 [01:59<00:21, 21.09s/it]\u001B[A\n", + "100%|██████████| 7/7 [02:22<00:00, 20.38s/it]\u001B[A\n", + " 50%|█████ | 4/8 [10:34<10:12, 153.17s/it]\n", + " 0%| | 0/7 [00:00<?, ?it/s]\u001B[A\n", + " 14%|█▍ | 1/7 [00:18<01:50, 18.37s/it]\u001B[A\n", + " 29%|██▊ | 2/7 [00:36<01:32, 18.51s/it]\u001B[A\n", + " 43%|████▎ | 3/7 [00:55<01:14, 18.60s/it]\u001B[A\n", + " 57%|█████▋ | 4/7 [01:14<00:56, 18.84s/it]\u001B[A\n", + " 71%|███████▏ | 5/7 [01:34<00:38, 19.25s/it]\u001B[A\n", + " 86%|████████▌ | 6/7 [01:55<00:19, 19.59s/it]\u001B[A\n", + "100%|██████████| 7/7 [02:14<00:00, 19.14s/it]\u001B[A\n", + " 62%|██████▎ | 5/8 [12:48<07:18, 146.26s/it]\n", + " 0%| | 0/7 [00:00<?, ?it/s]\u001B[A\n", + " 14%|█▍ | 1/7 [00:23<02:22, 23.69s/it]\u001B[A\n", + " 29%|██▊ | 2/7 [00:46<01:56, 23.37s/it]\u001B[A\n", + " 43%|████▎ | 3/7 [01:10<01:33, 23.46s/it]\u001B[A\n", + " 57%|█████▋ | 4/7 [01:37<01:14, 24.83s/it]\u001B[A\n", + " 71%|███████▏ | 5/7 [02:02<00:50, 25.05s/it]\u001B[A\n", + " 86%|████████▌ | 6/7 [02:26<00:24, 24.59s/it]\u001B[A\n", + "100%|██████████| 7/7 [02:50<00:00, 24.30s/it]\u001B[A\n", + " 75%|███████▌ | 6/8 [15:38<05:08, 154.36s/it]\n", + " 0%| | 0/7 [00:00<?, ?it/s]\u001B[A\n", + " 14%|█▍ | 1/7 [00:23<02:18, 23.12s/it]\u001B[A\n", + " 29%|██▊ | 2/7 [00:46<01:57, 23.45s/it]\u001B[A\n", + " 43%|████▎ | 3/7 [01:11<01:35, 23.85s/it]\u001B[A\n", + " 57%|█████▋ | 4/7 [01:35<01:12, 24.08s/it]\u001B[A\n", + " 71%|███████▏ | 5/7 [01:59<00:47, 23.96s/it]\u001B[A\n", + " 86%|████████▌ | 6/7 [02:22<00:23, 23.67s/it]\u001B[A\n", + "100%|██████████| 7/7 [02:45<00:00, 23.66s/it]\u001B[A\n", + " 88%|████████▊ | 7/8 [18:23<02:38, 158.04s/it]\n", + " 0%| | 0/7 [00:00<?, ?it/s]\u001B[A\n", + " 14%|█▍ | 1/7 [00:23<02:23, 23.88s/it]\u001B[A\n", + " 29%|██▊ | 2/7 [00:48<02:00, 24.05s/it]\u001B[A\n", + " 43%|████▎ | 3/7 [01:12<01:36, 24.19s/it]\u001B[A\n", + " 57%|█████▋ | 4/7 [01:36<01:12, 24.01s/it]\u001B[A\n", + " 71%|███████▏ | 5/7 [02:00<00:47, 23.97s/it]\u001B[A\n", + " 86%|████████▌ | 6/7 [02:22<00:23, 23.35s/it]\u001B[A\n", + "100%|██████████| 7/7 [02:44<00:00, 23.56s/it]\u001B[A\n", + "100%|██████████| 8/8 [21:08<00:00, 158.61s/it]\n" + ] + } + ], + "source": [ + "from numpy import arange\n", + "from tqdm import tqdm\n", + "from gensim.models import CoherenceModel\n", + "\n", + "topic_count = 18\n", + "pass_count = 10\n", + "alpha_vals = list(arange(0.1, 1., 0.2)) + ['symmetric', 'asymmetric', 'auto']\n", + "eta_vals = list(arange(0.1, 1., 0.2)) + ['symmetric', 'auto']\n", + "\n", + "params = []\n", + "coherences = []\n", + "for alpha in tqdm(alpha_vals, total=len(alpha_vals)):\n", + " for eta in tqdm(eta_vals, total=len(eta_vals)):\n", + " lda_model = gensim.models.LdaModel(bow_corpus, num_topics=topic_count, id2word=dictionary, passes=pass_count,\n", + " minimum_probability=0.0, random_state=0, alpha=alpha, eta=eta)\n", + " lda_coherence_model = CoherenceModel(model=lda_model, texts=processed_docs_test, dictionary=dictionary,\n", + " coherence='c_v')\n", + " coherences.append(lda_coherence_model.get_coherence())\n", + " params.append((alpha, eta))" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": 19, + "outputs": [ + { + "data": { + "text/plain": " alpha eta coherence\n0 0.1 0.1 0.591410\n1 0.1 0.3 0.590619\n2 0.1 0.5 0.604507\n3 0.1 0.7 0.593771\n4 0.1 0.9 0.588428\n5 0.1 symmetric 0.593764\n6 0.1 auto 0.593764\n7 0.3 0.1 0.545852\n8 0.3 0.3 0.543587\n9 0.3 0.5 0.545267\n10 0.3 0.7 0.547948\n11 0.3 0.9 0.545067\n12 0.3 symmetric 0.542743\n13 0.3 auto 0.542743\n14 0.5 0.1 0.594534\n15 0.5 0.3 0.603920\n16 0.5 0.5 0.605625\n17 0.5 0.7 0.602780\n18 0.5 0.9 0.602691\n19 0.5 symmetric 0.590724\n20 0.5 auto 0.590724\n21 0.7 0.1 0.573548\n22 0.7 0.3 0.571211\n23 0.7 0.5 0.573821\n24 0.7 0.7 0.571404\n25 0.7 0.9 0.575992\n26 0.7 symmetric 0.580720\n27 0.7 auto 0.580720\n28 0.9 0.1 0.598190\n29 0.9 0.3 0.589366\n30 0.9 0.5 0.590532\n31 0.9 0.7 0.593980\n32 0.9 0.9 0.598783\n33 0.9 symmetric 0.596835\n34 0.9 auto 0.596835\n35 symmetric 0.1 0.609470\n36 symmetric 0.3 0.610043\n37 symmetric 0.5 0.618075\n38 symmetric 0.7 0.602709\n39 symmetric 0.9 0.608895\n40 symmetric symmetric 0.606818\n41 symmetric auto 0.606818\n42 asymmetric 0.1 0.603349\n43 asymmetric 0.3 0.605684\n44 asymmetric 0.5 0.613109\n45 asymmetric 0.7 0.612604\n46 asymmetric 0.9 0.612228\n47 asymmetric symmetric 0.602633\n48 asymmetric auto 0.602633\n49 auto 0.1 0.611178\n50 auto 0.3 0.611255\n51 auto 0.5 0.615496\n52 auto 0.7 0.603420\n53 auto 0.9 0.600422\n54 auto symmetric 0.608649\n55 auto auto 0.608649", + "text/html": "<div>\n<style scoped>\n .dataframe tbody tr th:only-of-type {\n vertical-align: middle;\n }\n\n .dataframe tbody tr th {\n vertical-align: top;\n }\n\n .dataframe thead th {\n text-align: right;\n }\n</style>\n<table border=\"1\" class=\"dataframe\">\n <thead>\n <tr style=\"text-align: right;\">\n <th></th>\n <th>alpha</th>\n <th>eta</th>\n <th>coherence</th>\n </tr>\n </thead>\n <tbody>\n <tr>\n <th>0</th>\n <td>0.1</td>\n <td>0.1</td>\n <td>0.591410</td>\n </tr>\n <tr>\n <th>1</th>\n <td>0.1</td>\n <td>0.3</td>\n <td>0.590619</td>\n </tr>\n <tr>\n <th>2</th>\n <td>0.1</td>\n <td>0.5</td>\n <td>0.604507</td>\n </tr>\n <tr>\n <th>3</th>\n <td>0.1</td>\n <td>0.7</td>\n <td>0.593771</td>\n </tr>\n <tr>\n <th>4</th>\n <td>0.1</td>\n <td>0.9</td>\n <td>0.588428</td>\n </tr>\n <tr>\n <th>5</th>\n <td>0.1</td>\n <td>symmetric</td>\n <td>0.593764</td>\n </tr>\n <tr>\n <th>6</th>\n <td>0.1</td>\n <td>auto</td>\n <td>0.593764</td>\n </tr>\n <tr>\n <th>7</th>\n <td>0.3</td>\n <td>0.1</td>\n <td>0.545852</td>\n </tr>\n <tr>\n <th>8</th>\n <td>0.3</td>\n <td>0.3</td>\n <td>0.543587</td>\n </tr>\n <tr>\n <th>9</th>\n <td>0.3</td>\n <td>0.5</td>\n <td>0.545267</td>\n </tr>\n <tr>\n <th>10</th>\n <td>0.3</td>\n <td>0.7</td>\n <td>0.547948</td>\n </tr>\n <tr>\n <th>11</th>\n <td>0.3</td>\n <td>0.9</td>\n <td>0.545067</td>\n </tr>\n <tr>\n <th>12</th>\n <td>0.3</td>\n <td>symmetric</td>\n <td>0.542743</td>\n </tr>\n <tr>\n <th>13</th>\n <td>0.3</td>\n <td>auto</td>\n <td>0.542743</td>\n </tr>\n <tr>\n <th>14</th>\n <td>0.5</td>\n <td>0.1</td>\n <td>0.594534</td>\n </tr>\n <tr>\n <th>15</th>\n <td>0.5</td>\n <td>0.3</td>\n <td>0.603920</td>\n </tr>\n <tr>\n <th>16</th>\n <td>0.5</td>\n <td>0.5</td>\n <td>0.605625</td>\n </tr>\n <tr>\n <th>17</th>\n <td>0.5</td>\n <td>0.7</td>\n <td>0.602780</td>\n </tr>\n <tr>\n <th>18</th>\n <td>0.5</td>\n <td>0.9</td>\n <td>0.602691</td>\n </tr>\n <tr>\n <th>19</th>\n <td>0.5</td>\n <td>symmetric</td>\n <td>0.590724</td>\n </tr>\n <tr>\n <th>20</th>\n <td>0.5</td>\n <td>auto</td>\n <td>0.590724</td>\n </tr>\n <tr>\n <th>21</th>\n <td>0.7</td>\n <td>0.1</td>\n <td>0.573548</td>\n </tr>\n <tr>\n <th>22</th>\n <td>0.7</td>\n <td>0.3</td>\n <td>0.571211</td>\n </tr>\n <tr>\n <th>23</th>\n <td>0.7</td>\n <td>0.5</td>\n <td>0.573821</td>\n </tr>\n <tr>\n <th>24</th>\n <td>0.7</td>\n <td>0.7</td>\n <td>0.571404</td>\n </tr>\n <tr>\n <th>25</th>\n <td>0.7</td>\n <td>0.9</td>\n <td>0.575992</td>\n </tr>\n <tr>\n <th>26</th>\n <td>0.7</td>\n <td>symmetric</td>\n <td>0.580720</td>\n </tr>\n <tr>\n <th>27</th>\n <td>0.7</td>\n <td>auto</td>\n <td>0.580720</td>\n </tr>\n <tr>\n <th>28</th>\n <td>0.9</td>\n <td>0.1</td>\n <td>0.598190</td>\n </tr>\n <tr>\n <th>29</th>\n <td>0.9</td>\n <td>0.3</td>\n <td>0.589366</td>\n </tr>\n <tr>\n <th>30</th>\n <td>0.9</td>\n <td>0.5</td>\n <td>0.590532</td>\n </tr>\n <tr>\n <th>31</th>\n <td>0.9</td>\n <td>0.7</td>\n <td>0.593980</td>\n </tr>\n <tr>\n <th>32</th>\n <td>0.9</td>\n <td>0.9</td>\n <td>0.598783</td>\n </tr>\n <tr>\n <th>33</th>\n <td>0.9</td>\n <td>symmetric</td>\n <td>0.596835</td>\n </tr>\n <tr>\n <th>34</th>\n <td>0.9</td>\n <td>auto</td>\n <td>0.596835</td>\n </tr>\n <tr>\n <th>35</th>\n <td>symmetric</td>\n <td>0.1</td>\n <td>0.609470</td>\n </tr>\n <tr>\n <th>36</th>\n <td>symmetric</td>\n <td>0.3</td>\n <td>0.610043</td>\n </tr>\n <tr>\n <th>37</th>\n <td>symmetric</td>\n <td>0.5</td>\n <td>0.618075</td>\n </tr>\n <tr>\n <th>38</th>\n <td>symmetric</td>\n <td>0.7</td>\n <td>0.602709</td>\n </tr>\n <tr>\n <th>39</th>\n <td>symmetric</td>\n <td>0.9</td>\n <td>0.608895</td>\n </tr>\n <tr>\n <th>40</th>\n <td>symmetric</td>\n <td>symmetric</td>\n <td>0.606818</td>\n </tr>\n <tr>\n <th>41</th>\n <td>symmetric</td>\n <td>auto</td>\n <td>0.606818</td>\n </tr>\n <tr>\n <th>42</th>\n <td>asymmetric</td>\n <td>0.1</td>\n <td>0.603349</td>\n </tr>\n <tr>\n <th>43</th>\n <td>asymmetric</td>\n <td>0.3</td>\n <td>0.605684</td>\n </tr>\n <tr>\n <th>44</th>\n <td>asymmetric</td>\n <td>0.5</td>\n <td>0.613109</td>\n </tr>\n <tr>\n <th>45</th>\n <td>asymmetric</td>\n <td>0.7</td>\n <td>0.612604</td>\n </tr>\n <tr>\n <th>46</th>\n <td>asymmetric</td>\n <td>0.9</td>\n <td>0.612228</td>\n </tr>\n <tr>\n <th>47</th>\n <td>asymmetric</td>\n <td>symmetric</td>\n <td>0.602633</td>\n </tr>\n <tr>\n <th>48</th>\n <td>asymmetric</td>\n <td>auto</td>\n <td>0.602633</td>\n </tr>\n <tr>\n <th>49</th>\n <td>auto</td>\n <td>0.1</td>\n <td>0.611178</td>\n </tr>\n <tr>\n <th>50</th>\n <td>auto</td>\n <td>0.3</td>\n <td>0.611255</td>\n </tr>\n <tr>\n <th>51</th>\n <td>auto</td>\n <td>0.5</td>\n <td>0.615496</td>\n </tr>\n <tr>\n <th>52</th>\n <td>auto</td>\n <td>0.7</td>\n <td>0.603420</td>\n </tr>\n <tr>\n <th>53</th>\n <td>auto</td>\n <td>0.9</td>\n <td>0.600422</td>\n </tr>\n <tr>\n <th>54</th>\n <td>auto</td>\n <td>symmetric</td>\n <td>0.608649</td>\n </tr>\n <tr>\n <th>55</th>\n <td>auto</td>\n <td>auto</td>\n <td>0.608649</td>\n </tr>\n </tbody>\n</table>\n</div>" + }, + "execution_count": 19, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "results = pd.DataFrame(params, columns=[\"alpha\", \"eta\"])\n", + "results['coherence'] = coherences\n", + "results" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": 20, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Max coherence of 0.6180751847981156 for alpha=symmetric and eta=0.5000000000000001\n" + ] + } + ], + "source": [ + "max_params = (0, 0)\n", + "max_coherence = 0\n", + "for (alpha, eta), coherence in zip(params, coherences):\n", + " if coherence > max_coherence:\n", + " max_coherence = coherence\n", + " max_params = (alpha, eta)\n", + "print(f\"Max coherence of {max_coherence} for alpha={max_params[0]} and eta={max_params[1]}\")" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": null, + "outputs": [], + "source": [], + "metadata": { + "collapsed": false + } + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 2 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython2", + "version": "2.7.6" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/20230328-unsupervised-clustering-lda-classification.ipynb b/20230328-unsupervised-clustering-lda-classification.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..8663fcb231698d3b7ac007c60ac747ac22d82a74 --- /dev/null +++ b/20230328-unsupervised-clustering-lda-classification.ipynb @@ -0,0 +1,618 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "source": [ + "# Unsupervised clustering - evaluation of good model\n", + "\n", + "## Dataset loading" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": 1, + "outputs": [ + { + "data": { + "text/plain": " title_texte 85 44 50 \\\n1 ipa supply equipment increase competitiveness... False True False \n3 provision language training service tender in... False False False \n4 service support eda helicopter portfolio main... False False False \n5 NUMBER cp op NUMBER pooling share cost non co... False False False \n6 edf supply transport household similar waste ... False False False \n\n 80 73 45 71 79 90 ... 18 03 24 43 \\\n1 False False False False False False ... False False False False \n3 True False False False False False ... False False False False \n4 True False False False False False ... False False False False \n5 False True False False False False ... False False False False \n6 False False True False False False ... False False False False \n\n 19 41 37 14 16 76 \n1 False False False False False False \n3 False False False False False False \n4 False False False False False False \n5 False False False False False False \n6 False False False False False False \n\n[5 rows x 46 columns]", + "text/html": "<div>\n<style scoped>\n .dataframe tbody tr th:only-of-type {\n vertical-align: middle;\n }\n\n .dataframe tbody tr th {\n vertical-align: top;\n }\n\n .dataframe thead th {\n text-align: right;\n }\n</style>\n<table border=\"1\" class=\"dataframe\">\n <thead>\n <tr style=\"text-align: right;\">\n <th></th>\n <th>title_texte</th>\n <th>85</th>\n <th>44</th>\n <th>50</th>\n <th>80</th>\n <th>73</th>\n <th>45</th>\n <th>71</th>\n <th>79</th>\n <th>90</th>\n <th>...</th>\n <th>18</th>\n <th>03</th>\n <th>24</th>\n <th>43</th>\n <th>19</th>\n <th>41</th>\n <th>37</th>\n <th>14</th>\n <th>16</th>\n <th>76</th>\n </tr>\n </thead>\n <tbody>\n <tr>\n <th>1</th>\n <td>ipa supply equipment increase competitiveness...</td>\n <td>False</td>\n <td>True</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>...</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n </tr>\n <tr>\n <th>3</th>\n <td>provision language training service tender in...</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>True</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>...</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n </tr>\n <tr>\n <th>4</th>\n <td>service support eda helicopter portfolio main...</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>True</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>...</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n </tr>\n <tr>\n <th>5</th>\n <td>NUMBER cp op NUMBER pooling share cost non co...</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>True</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>...</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n </tr>\n <tr>\n <th>6</th>\n <td>edf supply transport household similar waste ...</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>True</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>...</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n <td>False</td>\n </tr>\n </tbody>\n</table>\n<p>5 rows × 46 columns</p>\n</div>" + }, + "execution_count": 1, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "import pandas as pd\n", + "\n", + "df = pd.read_csv(\"20230214-dataset_preprocessed_with_lemma.csv\", index_col=0)\n", + "df.head()" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": 2, + "outputs": [ + { + "data": { + "text/plain": "((11647, 46), (2912, 46))" + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from sklearn.model_selection import train_test_split\n", + "\n", + "cpvs = [c for c in df.columns if len(c) == 2]\n", + "df_train, df_test = train_test_split(df, test_size=0.2, shuffle=False)\n", + "(df_train.shape, df_test.shape)" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": 3, + "outputs": [ + { + "data": { + "text/plain": "{'85': 256,\n '44': 103,\n '50': 297,\n '80': 403,\n '73': 1067,\n '45': 731,\n '71': 1621,\n '79': 2682,\n '90': 629,\n '30': 266,\n '35': 145,\n '33': 158,\n '55': 117,\n '72': 914,\n '48': 199,\n '38': 289,\n '09': 128,\n '75': 277,\n '66': 206,\n '64': 148,\n '42': 159,\n '34': 199,\n '60': 122,\n '92': 169,\n '39': 188,\n '31': 139,\n '98': 123,\n '51': 50,\n '32': 185,\n '65': 29,\n '77': 83,\n '22': 61,\n '63': 144,\n '15': 43,\n '70': 44,\n '18': 35,\n '03': 31,\n '24': 30,\n '43': 17,\n '19': 7,\n '41': 13,\n '37': 13,\n '14': 16,\n '16': 5,\n '76': 5}" + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "{c: df_train[c].sum() for c in cpvs}" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": 4, + "outputs": [ + { + "data": { + "text/plain": "{'85': 62,\n '44': 23,\n '50': 68,\n '80': 99,\n '73': 249,\n '45': 191,\n '71': 404,\n '79': 688,\n '90': 176,\n '30': 70,\n '35': 36,\n '33': 38,\n '55': 30,\n '72': 197,\n '48': 43,\n '38': 61,\n '09': 34,\n '75': 77,\n '66': 46,\n '64': 37,\n '42': 27,\n '34': 50,\n '60': 41,\n '92': 43,\n '39': 49,\n '31': 36,\n '98': 35,\n '51': 8,\n '32': 40,\n '65': 15,\n '77': 22,\n '22': 20,\n '63': 34,\n '15': 5,\n '70': 14,\n '18': 5,\n '03': 5,\n '24': 11,\n '43': 1,\n '19': 5,\n '41': 2,\n '37': 3,\n '14': 6,\n '16': 4,\n '76': 3}" + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "{c: df_test[c].sum() for c in cpvs}" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": 6, + "outputs": [], + "source": [ + "import gensim\n", + "\n", + "processed_docs = df_train[\"title_texte\"].apply(lambda x: x.split(\" \"))\n", + "processed_docs_test = df_train[\"title_texte\"].apply(lambda x: x.split(\" \"))\n", + "\n", + "dictionary = gensim.corpora.Dictionary(processed_docs)\n", + "dictionary.filter_extremes(no_below=15, no_above=0.5, keep_n=100000)\n", + "bow_corpus = [dictionary.doc2bow(doc) for doc in processed_docs]" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "markdown", + "source": [ + "## Model creation" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": 9, + "outputs": [], + "source": [ + "lda_model = gensim.models.LdaModel(bow_corpus, num_topics=18, id2word=dictionary, passes=10,\n", + " minimum_probability=0.0, random_state=0, alpha='symmetric', eta=0.5)" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "markdown", + "source": [ + "## Evaluation of CPV classification" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": 14, + "outputs": [], + "source": [ + "cpv_labels = {\n", + " \"03\": \"Agricultural, farming, fishing, forestry and related products\",\n", + " \"09\": \"Petroleum products, fuel, electricity and other sources of energy\",\n", + " \"14\": \"Mining, basic metals and related products\",\n", + " \"15\": \"Food, beverages, tobacco and related products\",\n", + " \"16\": \"Agricultural machinery\",\n", + " \"18\": \"Clothing, footwear, luggage articles and accessories\",\n", + " \"19\": \"Leather and textile fabrics, plastic and rubber materials\",\n", + " \"22\": \"Printed matter and related products\",\n", + " \"24\": \"Chemical products\",\n", + " \"30\": \"Office and computing machinery, equipment and supplies except furniture and software packages\",\n", + " \"31\": \"Electrical machinery, apparatus, equipment and consumables; Lighting\",\n", + " \"32\": \"Radio, television, communication, telecommunication and related equipment\",\n", + " \"33\": \"Medical equipments, pharmaceuticals and personal care products\",\n", + " \"34\": \"Transport equipment and auxiliary products to transportation\",\n", + " \"35\": \"Security, fire-fighting, police and defence equipment\",\n", + " \"37\": \"Musical instruments, sport goods, games, toys, handicraft, art materials and accessories\",\n", + " \"38\": \"Laboratory, optical and precision equipments (excl. glasses)\",\n", + " \"39\": \"Furniture (incl. office furniture), furnishings, domestic appliances (excl. lighting) and cleaning products\",\n", + " \"41\": \"Collected and purified water\",\n", + " \"42\": \"Industrial machinery\",\n", + " \"43\": \"Machinery for mining, quarrying, construction equipment\",\n", + " \"44\": \"Construction structures and materials; auxiliary products to construction (excepts electric apparatus)\",\n", + " \"45\": \"Construction work\",\n", + " \"48\": \"Software package and information systems\",\n", + " \"50\": \"Repair and maintenance services\",\n", + " \"51\": \"Installation services (except software)\",\n", + " \"55\": \"Hotel, restaurant and retail trade services\",\n", + " \"60\": \"Transport services (excl. Waste transport)\",\n", + " \"63\": \"Supporting and auxiliary transport services; travel agencies services\",\n", + " \"64\": \"Postal and telecommunications services\",\n", + " \"65\": \"Public utilities\",\n", + " \"66\": \"Financial and insurance services\",\n", + " \"70\": \"Real estate services\",\n", + " \"71\": \"Architectural, construction, engineering and inspection services\",\n", + " \"72\": \"IT services: consulting, software development, Internet and support\",\n", + " \"73\": \"Research and development services and related consultancy services\",\n", + " \"75\": \"Administration, defence and social security services\",\n", + " \"76\": \"Services related to the oil and gas industry\",\n", + " \"77\": \"Agricultural, forestry, horticultural, aquacultural and apicultural services\",\n", + " \"79\": \"Business services: law, marketing, consulting, recruitment, printing and security\",\n", + " \"80\": \"Education and training services\",\n", + " \"85\": \"Health and social work services\",\n", + " \"90\": \"Sewage-, refuse-, cleaning-, and environmental services\",\n", + " \"92\": \"Recreational, cultural and sporting services\",\n", + " \"98\": \"Other community, social and personal services\",\n", + "}" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": 11, + "outputs": [], + "source": [ + "from scipy.stats import entropy\n", + "import numpy as np\n", + "\n", + "\n", + "# https://github.com/soberbichler/Using-LDA-and-Jensen-Shannon-distance-to-separate-relevant-from-non-relevant-articles/blob/master/news_article_similarity_remigration_notebook.ipynb\n", + "def jensen_shannon(query, matrix):\n", + " p = query[None, :].T\n", + " q = matrix.T\n", + " m = 0.5 * (p + q)\n", + " return np.sqrt(0.5 * (entropy(p, m) + entropy(q, m)))\n", + "\n", + "\n", + "def get_doc_similarities(query, matrix):\n", + " sims = jensen_shannon(query, matrix) # list of jensen shannon distances\n", + " return sims" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": 12, + "outputs": [], + "source": [ + "corpus = df_train[\"title_texte\"].apply(lambda x: dictionary.doc2bow(x.split(\" \")))\n", + "all_dists = np.stack([np.array([tup[1] for tup in lst]) for lst in lda_model[list(corpus)]])" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "markdown", + "source": [ + "### Take the 3 closest notices" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": 15, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + " 0%| | 0/2912 [00:00<?, ?it/s]/tmp/ipykernel_30175/2252406494.py:10: RuntimeWarning: invalid value encountered in sqrt\n", + " return np.sqrt(0.5 * (entropy(p, m) + entropy(q, m)))\n", + "100%|██████████| 2912/2912 [00:30<00:00, 95.58it/s] " + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + " precision recall f1-score support\n", + "\n", + " Health and social work services... | 85 0.02 0.06 0.04 62\n", + "Construction structures and materials; a... | 44 0.00 0.00 0.00 23\n", + " Repair and maintenance services... | 50 0.02 0.07 0.03 68\n", + " Education and training services... | 80 0.03 0.07 0.04 99\n", + "Research and development services and re... | 73 0.08 0.22 0.12 249\n", + " Construction work... | 45 0.08 0.19 0.11 191\n", + "Architectural, construction, engineering... | 71 0.14 0.35 0.20 404\n", + "Business services: law, marketing, consu... | 79 0.24 0.50 0.32 688\n", + "Sewage-, refuse-, cleaning-, and environ... | 90 0.05 0.12 0.07 176\n", + "Office and computing machinery, equipmen... | 30 0.01 0.01 0.01 70\n", + "Security, fire-fighting, police and defe... | 35 0.03 0.08 0.05 36\n", + "Medical equipments, pharmaceuticals and ... | 33 0.02 0.05 0.03 38\n", + "Hotel, restaurant and retail trade servi... | 55 0.00 0.00 0.00 30\n", + "IT services: consulting, software develo... | 72 0.06 0.18 0.09 197\n", + "Software package and information systems... | 48 0.02 0.07 0.03 43\n", + "Laboratory, optical and precision equipm... | 38 0.03 0.08 0.04 61\n", + "Petroleum products, fuel, electricity an... | 09 0.01 0.03 0.02 34\n", + "Administration, defence and social secur... | 75 0.05 0.10 0.06 77\n", + " Financial and insurance services... | 66 0.00 0.00 0.00 46\n", + " Postal and telecommunications services... | 64 0.00 0.00 0.00 37\n", + " Industrial machinery... | 42 0.00 0.00 0.00 27\n", + "Transport equipment and auxiliary produc... | 34 0.01 0.04 0.02 50\n", + "Transport services (excl. Waste transpor... | 60 0.07 0.12 0.09 41\n", + "Recreational, cultural and sporting serv... | 92 0.01 0.02 0.01 43\n", + "Furniture (incl. office furniture), furn... | 39 0.00 0.00 0.00 49\n", + "Electrical machinery, apparatus, equipme... | 31 0.02 0.06 0.03 36\n", + "Other community, social and personal ser... | 98 0.05 0.14 0.08 35\n", + " Installation services (except software)... | 51 0.00 0.00 0.00 8\n", + "Radio, television, communication, teleco... | 32 0.01 0.03 0.01 40\n", + " Public utilities... | 65 0.05 0.07 0.06 15\n", + "Agricultural, forestry, horticultural, a... | 77 0.00 0.00 0.00 22\n", + " Printed matter and related products... | 22 0.00 0.00 0.00 20\n", + "Supporting and auxiliary transport servi... | 63 0.00 0.00 0.00 34\n", + "Food, beverages, tobacco and related pro... | 15 0.00 0.00 0.00 5\n", + " Real estate services... | 70 0.00 0.00 0.00 14\n", + "Clothing, footwear, luggage articles and... | 18 0.00 0.00 0.00 5\n", + "Agricultural, farming, fishing, forestry... | 03 0.00 0.00 0.00 5\n", + " Chemical products... | 24 0.00 0.00 0.00 11\n", + "Machinery for mining, quarrying, constru... | 43 0.00 0.00 0.00 1\n", + "Leather and textile fabrics, plastic and... | 19 0.00 0.00 0.00 5\n", + " Collected and purified water... | 41 0.00 0.00 0.00 2\n", + "Musical instruments, sport goods, games,... | 37 0.00 0.00 0.00 3\n", + "Mining, basic metals and related product... | 14 0.00 0.00 0.00 6\n", + " Agricultural machinery... | 16 0.00 0.00 0.00 4\n", + "Services related to the oil and gas indu... | 76 0.00 0.00 0.00 3\n", + "\n", + " micro avg 0.09 0.22 0.12 3113\n", + " macro avg 0.02 0.06 0.03 3113\n", + " weighted avg 0.10 0.22 0.13 3113\n", + " samples avg 0.09 0.23 0.13 3113\n", + "\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\n", + "/home/ferreni/Projects/TED AI/Repositories/tedai-cpv-classification/venv/lib/python3.10/site-packages/sklearn/metrics/_classification.py:1344: UndefinedMetricWarning: Precision and F-score are ill-defined and being set to 0.0 in samples with no predicted labels. Use `zero_division` parameter to control this behavior.\n", + " _warn_prf(average, modifier, msg_start, len(result))\n" + ] + } + ], + "source": [ + "from tqdm import tqdm\n", + "from sklearn.metrics import classification_report\n", + "\n", + "y_true = []\n", + "y_pred = []\n", + "notices = list(df_test.iloc)\n", + "for notice in tqdm(notices, total=len(notices)):\n", + " notice_bow = dictionary.doc2bow(notice[\"title_texte\"].split(\" \"))\n", + " dist = np.array([tup[1] for tup in lda_model.get_document_topics(bow=notice_bow)])\n", + " sims = get_doc_similarities(dist, all_dists)\n", + " most_sim_ids = sims.argsort()[:3] # the top k positional index of the smallest Jensen Shannon distances\n", + " most_similar_df = df_train[df_train.index.isin(most_sim_ids)]\n", + " y_true.append([int(notice[c] == True) for c in cpvs])\n", + " y_pred.append([int(most_similar_df[c].sum() > 0) for c in cpvs])\n", + "print(classification_report(y_true, y_pred, target_names=[f\"{cpv_labels[c][:40]}... | {c}\" for c in cpvs]))" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "markdown", + "source": [ + "### Take the 10 closest notices" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": 16, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + " 0%| | 0/2912 [00:00<?, ?it/s]/tmp/ipykernel_30175/2252406494.py:10: RuntimeWarning: invalid value encountered in sqrt\n", + " return np.sqrt(0.5 * (entropy(p, m) + entropy(q, m)))\n", + "100%|██████████| 2912/2912 [00:27<00:00, 104.65it/s]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + " precision recall f1-score support\n", + "\n", + " Health and social work services... | 85 0.03 0.26 0.05 62\n", + "Construction structures and materials; a... | 44 0.00 0.04 0.01 23\n", + " Repair and maintenance services... | 50 0.02 0.21 0.04 68\n", + " Education and training services... | 80 0.04 0.32 0.07 99\n", + "Research and development services and re... | 73 0.08 0.54 0.14 249\n", + " Construction work... | 45 0.07 0.50 0.13 191\n", + "Architectural, construction, engineering... | 71 0.15 0.78 0.25 404\n", + "Business services: law, marketing, consu... | 79 0.24 0.91 0.37 688\n", + "Sewage-, refuse-, cleaning-, and environ... | 90 0.07 0.46 0.12 176\n", + "Office and computing machinery, equipmen... | 30 0.02 0.13 0.03 70\n", + "Security, fire-fighting, police and defe... | 35 0.03 0.19 0.05 36\n", + "Medical equipments, pharmaceuticals and ... | 33 0.03 0.21 0.05 38\n", + "Hotel, restaurant and retail trade servi... | 55 0.00 0.00 0.00 30\n", + "IT services: consulting, software develo... | 72 0.06 0.46 0.11 197\n", + "Software package and information systems... | 48 0.01 0.09 0.02 43\n", + "Laboratory, optical and precision equipm... | 38 0.03 0.30 0.06 61\n", + "Petroleum products, fuel, electricity an... | 09 0.00 0.03 0.01 34\n", + "Administration, defence and social secur... | 75 0.02 0.17 0.04 77\n", + " Financial and insurance services... | 66 0.01 0.09 0.02 46\n", + " Postal and telecommunications services... | 64 0.01 0.05 0.01 37\n", + " Industrial machinery... | 42 0.01 0.19 0.03 27\n", + "Transport equipment and auxiliary produc... | 34 0.02 0.16 0.03 50\n", + "Transport services (excl. Waste transpor... | 60 0.03 0.17 0.05 41\n", + "Recreational, cultural and sporting serv... | 92 0.01 0.07 0.01 43\n", + "Furniture (incl. office furniture), furn... | 39 0.01 0.10 0.02 49\n", + "Electrical machinery, apparatus, equipme... | 31 0.01 0.11 0.02 36\n", + "Other community, social and personal ser... | 98 0.03 0.23 0.05 35\n", + " Installation services (except software)... | 51 0.00 0.00 0.00 8\n", + "Radio, television, communication, teleco... | 32 0.01 0.10 0.02 40\n", + " Public utilities... | 65 0.02 0.07 0.03 15\n", + "Agricultural, forestry, horticultural, a... | 77 0.01 0.05 0.01 22\n", + " Printed matter and related products... | 22 0.02 0.10 0.03 20\n", + "Supporting and auxiliary transport servi... | 63 0.02 0.15 0.03 34\n", + "Food, beverages, tobacco and related pro... | 15 0.00 0.00 0.00 5\n", + " Real estate services... | 70 0.00 0.00 0.00 14\n", + "Clothing, footwear, luggage articles and... | 18 0.00 0.00 0.00 5\n", + "Agricultural, farming, fishing, forestry... | 03 0.00 0.00 0.00 5\n", + " Chemical products... | 24 0.00 0.00 0.00 11\n", + "Machinery for mining, quarrying, constru... | 43 0.00 0.00 0.00 1\n", + "Leather and textile fabrics, plastic and... | 19 0.00 0.00 0.00 5\n", + " Collected and purified water... | 41 0.00 0.00 0.00 2\n", + "Musical instruments, sport goods, games,... | 37 0.00 0.00 0.00 3\n", + "Mining, basic metals and related product... | 14 0.00 0.00 0.00 6\n", + " Agricultural machinery... | 16 0.00 0.00 0.00 4\n", + "Services related to the oil and gas indu... | 76 0.00 0.00 0.00 3\n", + "\n", + " micro avg 0.07 0.49 0.13 3113\n", + " macro avg 0.02 0.16 0.04 3113\n", + " weighted avg 0.10 0.49 0.16 3113\n", + " samples avg 0.08 0.50 0.13 3113\n", + "\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\n" + ] + } + ], + "source": [ + "from tqdm import tqdm\n", + "from sklearn.metrics import classification_report\n", + "\n", + "y_true = []\n", + "y_pred = []\n", + "notices = list(df_test.iloc)\n", + "for notice in tqdm(notices, total=len(notices)):\n", + " notice_bow = dictionary.doc2bow(notice[\"title_texte\"].split(\" \"))\n", + " dist = np.array([tup[1] for tup in lda_model.get_document_topics(bow=notice_bow)])\n", + " sims = get_doc_similarities(dist, all_dists)\n", + " most_sim_ids = sims.argsort()[:10] # the top k positional index of the smallest Jensen Shannon distances\n", + " most_similar_df = df_train[df_train.index.isin(most_sim_ids)]\n", + " y_true.append([int(notice[c] == True) for c in cpvs])\n", + " y_pred.append([int(most_similar_df[c].sum() > 0) for c in cpvs])\n", + "print(classification_report(y_true, y_pred, target_names=[f\"{cpv_labels[c][:40]}... | {c}\" for c in cpvs]))" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "markdown", + "source": [ + "### Take the 10 closest notices (infer on training set)" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": 17, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + " 0%| | 0/11647 [00:00<?, ?it/s]/tmp/ipykernel_30175/2252406494.py:10: RuntimeWarning: invalid value encountered in sqrt\n", + " return np.sqrt(0.5 * (entropy(p, m) + entropy(q, m)))\n", + "100%|██████████| 11647/11647 [01:52<00:00, 103.23it/s]\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + " precision recall f1-score support\n", + "\n", + " Health and social work services... | 85 0.02 0.20 0.04 256\n", + "Construction structures and materials; a... | 44 0.00 0.05 0.01 103\n", + " Repair and maintenance services... | 50 0.03 0.23 0.05 297\n", + " Education and training services... | 80 0.04 0.33 0.07 403\n", + "Research and development services and re... | 73 0.08 0.52 0.14 1067\n", + " Construction work... | 45 0.07 0.49 0.12 731\n", + "Architectural, construction, engineering... | 71 0.14 0.73 0.23 1621\n", + "Business services: law, marketing, consu... | 79 0.23 0.91 0.37 2682\n", + "Sewage-, refuse-, cleaning-, and environ... | 90 0.06 0.46 0.11 629\n", + "Office and computing machinery, equipmen... | 30 0.02 0.20 0.04 266\n", + "Security, fire-fighting, police and defe... | 35 0.01 0.09 0.02 145\n", + "Medical equipments, pharmaceuticals and ... | 33 0.02 0.17 0.04 158\n", + "Hotel, restaurant and retail trade servi... | 55 0.00 0.03 0.01 117\n", + "IT services: consulting, software develo... | 72 0.08 0.51 0.13 914\n", + "Software package and information systems... | 48 0.02 0.13 0.03 199\n", + "Laboratory, optical and precision equipm... | 38 0.03 0.23 0.05 289\n", + "Petroleum products, fuel, electricity an... | 09 0.01 0.08 0.02 128\n", + "Administration, defence and social secur... | 75 0.02 0.15 0.03 277\n", + " Financial and insurance services... | 66 0.01 0.12 0.03 206\n", + " Postal and telecommunications services... | 64 0.01 0.10 0.02 148\n", + " Industrial machinery... | 42 0.02 0.14 0.03 159\n", + "Transport equipment and auxiliary produc... | 34 0.02 0.15 0.03 199\n", + "Transport services (excl. Waste transpor... | 60 0.01 0.11 0.02 122\n", + "Recreational, cultural and sporting serv... | 92 0.01 0.07 0.02 169\n", + "Furniture (incl. office furniture), furn... | 39 0.02 0.13 0.03 188\n", + "Electrical machinery, apparatus, equipme... | 31 0.01 0.09 0.02 139\n", + "Other community, social and personal ser... | 98 0.01 0.11 0.02 123\n", + " Installation services (except software)... | 51 0.00 0.00 0.00 50\n", + "Radio, television, communication, teleco... | 32 0.01 0.12 0.02 185\n", + " Public utilities... | 65 0.00 0.03 0.01 29\n", + "Agricultural, forestry, horticultural, a... | 77 0.01 0.06 0.01 83\n", + " Printed matter and related products... | 22 0.00 0.03 0.01 61\n", + "Supporting and auxiliary transport servi... | 63 0.01 0.06 0.01 144\n", + "Food, beverages, tobacco and related pro... | 15 0.00 0.00 0.00 43\n", + " Real estate services... | 70 0.01 0.05 0.01 44\n", + "Clothing, footwear, luggage articles and... | 18 0.00 0.03 0.01 35\n", + "Agricultural, farming, fishing, forestry... | 03 0.00 0.00 0.00 31\n", + " Chemical products... | 24 0.00 0.00 0.00 30\n", + "Machinery for mining, quarrying, constru... | 43 0.00 0.00 0.00 17\n", + "Leather and textile fabrics, plastic and... | 19 0.00 0.00 0.00 7\n", + " Collected and purified water... | 41 0.00 0.00 0.00 13\n", + "Musical instruments, sport goods, games,... | 37 0.00 0.00 0.00 13\n", + "Mining, basic metals and related product... | 14 0.00 0.00 0.00 16\n", + " Agricultural machinery... | 16 0.00 0.00 0.00 5\n", + "Services related to the oil and gas indu... | 76 0.00 0.00 0.00 5\n", + "\n", + " micro avg 0.07 0.48 0.12 12546\n", + " macro avg 0.02 0.15 0.04 12546\n", + " weighted avg 0.09 0.48 0.15 12546\n", + " samples avg 0.07 0.49 0.13 12546\n", + "\n" + ] + } + ], + "source": [ + "from tqdm import tqdm\n", + "from sklearn.metrics import classification_report\n", + "\n", + "y_true = []\n", + "y_pred = []\n", + "notices = list(df_train.iloc)\n", + "for notice in tqdm(notices, total=len(notices)):\n", + " notice_bow = dictionary.doc2bow(notice[\"title_texte\"].split(\" \"))\n", + " dist = np.array([tup[1] for tup in lda_model.get_document_topics(bow=notice_bow)])\n", + " sims = get_doc_similarities(dist, all_dists)\n", + " most_sim_ids = sims.argsort()[:10] # the top k positional index of the smallest Jensen Shannon distances\n", + " most_similar_df = df_train[df_train.index.isin(most_sim_ids)]\n", + " y_true.append([int(notice[c] == True) for c in cpvs])\n", + " y_pred.append([int(most_similar_df[c].sum() > 0) for c in cpvs])\n", + "print(classification_report(y_true, y_pred, target_names=[f\"{cpv_labels[c][:40]}... | {c}\" for c in cpvs]))" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "markdown", + "source": [ + "Even on training set, performance remains low" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "markdown", + "source": [ + "More globally, it seems that increasing coherence of the model does not significantly increase CPV classification performance" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": null, + "outputs": [], + "source": [], + "metadata": { + "collapsed": false + } + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 2 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython2", + "version": "2.7.6" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +}