{"cells": [{"cell_type": "markdown", "metadata": {}, "source": ["# 2A.i - Donn\u00e9es non structur\u00e9es et programmation fonctionnelle\n", " \n", "Calculs de moyennes et autres statistiques sur une base twitter au format JSON avec de la programmation fonctionnelle (module [cytoolz](https://github.com/pytoolz/cytoolz))."]}, {"cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [{"data": {"text/html": ["
run previous cell, wait for 2 seconds
\n", ""], "text/plain": [""]}, "execution_count": 2, "metadata": {}, "output_type": "execute_result"}], "source": ["from jyquickhelper import add_notebook_menu\n", "add_notebook_menu()"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Commencez par t\u00e9l\u00e9charger la base de donn\u00e9e [twitter_for_network_100000.db](https://drive.google.com/file/d/0B6jkqYitZ0uTWjFjd3lpREpFYVE/view?usp=sharing). Vous pourrez \u00e9ventuellement t\u00e9l\u00e9charger la base compl\u00e8te (3,4 millions d'utilisateurs, plut\u00f4t que 100000) ult\u00e9rieurement si vous souhaitez tester vos fonctions. Ne perdez pas de temps avec ceci dans ce TP : [twitter_for_network_full.db](https://drive.google.com/file/d/0B6jkqYitZ0uTWkR6cDZQUTlVSWM/view?usp=sharing). Vous pouvez consulter l'aide de [pytoolz](http://toolz.readthedocs.org/en/latest/) (m\u00eame interface que cytoolz).\n", "La section sur l'[API](http://toolz.readthedocs.org/en/latest/api.html) est particuli\u00e8rement utile car elle r\u00e9sume bien les diff\u00e9rentes fonctions. \n", "\n", "Liens alternatifs :\n", "\n", "* [twitter_for_network_100000.db.zip](http://www.xavierdupre.fr/enseignement/complements/twitter_for_network_100000.db.zip)\n", "* [twitter_for_network_full.db.zip](http://www.xavierdupre.fr/enseignement/complements/twitter_for_network_full.db.zip)\n", "\n", "Ensuite ex\u00e9cutez la cellule suivante :"]}, {"cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [{"data": {"text/plain": ["['twitter_for_network_100000.db']"]}, "execution_count": 3, "metadata": {}, "output_type": "execute_result"}], "source": ["import pyensae.datasource\n", "pyensae.datasource.download_data(\"twitter_for_network_100000.db.zip\")"]}, {"cell_type": "code", "execution_count": 3, "metadata": {"collapsed": true}, "outputs": [], "source": ["import cytoolz as ct \n", "import cytoolz.curried as ctc\n", "import sqlite3\n", "import pprint\n", "import json\n", "\n", "conn_sqlite = sqlite3.connect(\"twitter_for_network_100000.db\")\n", "cursor_sqlite = conn_sqlite.cursor()"]}, {"cell_type": "markdown", "metadata": {}, "source": ["## Description de la base de donn\u00e9e"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Nous nous int\u00e9resserons \u00e0 3 tables : **tw_users**, **tw_status** et **tw_followers_id**. \n", "\n", "La premi\u00e8re (**tw_users**) contient des profils utilisateurs tels que retourn\u00e9s par l'api twitter (\u00e0 noter que les profils ont \u00e9t\u00e9 \"\u00e9pur\u00e9s\" d'informations jug\u00e9es inutiles pour limiter la taille de la base de donn\u00e9e).\n", "\n", "La deuxi\u00e8me (**tw_status**) contient des status twitter (tweet, retweet, ou r\u00e9ponse \u00e0 un tweet), complets, issus d'une certaine cat\u00e9gorie d'utilisateurs (les tweets sont tous issus d'environ 70 profils).\n", "\n", "La troisi\u00e8me (**tw_followers_id**) contient des listes d'id d'users, qui suivent les utilisateurs r\u00e9f\u00e9renc\u00e9s par la colonne user_id. L\u00e0 encore ce ne sont les followers que de environ 70 profils. Chaque entr\u00e9e contient au plus 5000 id de followers (il s'agit d'une limitation de twitter).\n", "\n", "Elles ont les structures suivantes :"]}, {"cell_type": "raw", "metadata": {}, "source": ["CREATE TABLE tw_followers_id( user_id bigint NOT NULL, cursor bigint NOT NULL, \n", " prev_cursor bigint NOT NULL, next_cursor bigint NOT NULL, \n", " update_time timestamp NOT NULL, content json NOT NULL);\n", "CREATE TABLE tw_users( id bigint NOT NULL, last_update timestamp NOT NULL, \n", " content json, screen_name character varying(512));\n", "CREATE TABLE tw_status( id bigint NOT NULL, user_id bigint NOT NULL, \n", " last_update timestamp NOT NULL, content json);"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Les trois poss\u00e8dent un champ content, de type json, qui sera celui qui nous interessera le plus. Vous pouvez acc\u00e9dez aux donn\u00e9es dans les tables avec les syntaxes suivantes (vous pouvez commenter/d\u00e9commenter les diff\u00e9rentes requ\u00eates)."]}, {"cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [{"data": {"text/plain": ["(1103159180,\n", " '{\"utc_offset\": 7200, \"friends_count\": 454, \"entities\": {\"description\": {\"urls\": []}, \"url\": {\"urls\": [{\"expanded_url\": \"http://www.havas.com\", \"display_url\": \"havas.com\", \"indices\": [0, 22], \"url\": \"http://t.co/8GcZtydjWh\"}]}}, \"description\": \"Havas Group CEO\", \"id\": 1103159180, \"contributors_enabled\": false, \"geo_enabled\": false, \"name\": \"Yannick Bollor\\\\u00e9\", \"favourites_count\": 873, \"verified\": true, \"protected\": false, \"created_at\": \"Sat Jan 19 08:23:33 +0000 2013\", \"statuses_count\": 654, \"lang\": \"en\", \"time_zone\": \"Ljubljana\", \"screen_name\": \"YannickBollore\", \"location\": \"\", \"id_str\": \"1103159180\", \"url\": \"http://t.co/8GcZtydjWh\", \"followers_count\": 7345, \"listed_count\": 118, \"has_extended_profile\": false}',\n", " 'YannickBollore')"]}, "execution_count": 5, "metadata": {}, "output_type": "execute_result"}], "source": ["# cursor_sqlite.execute(\"SELECT user_id, content FROM tw_followers_id\")\n", "cursor_sqlite.execute(\"SELECT id, content, screen_name FROM tw_users\")\n", "# cursor_sqlite.execute(\"SELECT id, content, user_id FROM tw_status\")\n", "\n", "for it_elt in cursor_sqlite:\n", " ## do something here\n", " pass\n", "\n", "# ou, pour acc\u00e9der \u00e0 un \u00e9l\u00e9ment :\n", "cursor_sqlite.execute(\"SELECT id, content, screen_name FROM tw_users\")\n", "cursor_sqlite.fetchone()"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Toutefois les curseurs de base de donn\u00e9e en python se comportent comme des \"iterables\" (i.e. comme une liste ou une s\u00e9quence, mais sans n\u00e9cessairement charger toutes les donn\u00e9es en m\u00e9moire). On peut donc les passer directement en argument aux fonctions de [cytoolz](https://github.com/pytoolz/cytoolz)."]}, {"cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["2079\n", "16092\n", "100071\n"]}], "source": ["cursor_sqlite.execute( \"SELECT user_id, content FROM tw_followers_id\")\n", "print( ct.count( cursor_sqlite ) )\n", "cursor_sqlite.execute( \"SELECT id, content, user_id FROM tw_status\")\n", "print( ct.count( cursor_sqlite ) )\n", "cursor_sqlite.execute( \"SELECT id, content, screen_name FROM tw_users\")\n", "print( ct.count( cursor_sqlite ) )"]}, {"cell_type": "markdown", "metadata": {}, "source": ["**Attention au fait que le curseur garde un \u00e9tat**. \n", "Par exemple ex\u00e9cutez le code suivant : "]}, {"cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["2079\n", "0\n"]}], "source": ["cursor_sqlite.execute( \"SELECT user_id, content FROM tw_followers_id\")\n", "print( ct.count( cursor_sqlite ) )\n", "print( ct.count( cursor_sqlite ) )"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Le deuxi\u00e8me count renvoit 0 car le curseur se rappelle qu'il est d\u00e9j\u00e0 arriv\u00e9 \u00e0 la fin des donn\u00e9es qu'il devait parcourir. Il faut donc r\u00e9initialiser le curseur :"]}, {"cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["2079\n", "2079\n"]}], "source": ["cursor_sqlite.execute( \"SELECT user_id, content FROM tw_followers_id\")\n", "print( ct.count( cursor_sqlite ) )\n", "cursor_sqlite.execute( \"SELECT user_id, content FROM tw_followers_id\")\n", "print( ct.count( cursor_sqlite ) )"]}, {"cell_type": "markdown", "metadata": {}, "source": ["On peut \u00e9galement mettre la commande execute \u00e0 l'int\u00e9rieur d'une fonction, que l'on appelle ensuite :"]}, {"cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["2079\n", "2079\n"]}], "source": ["def get_tw_followers_id():\n", " return cursor_sqlite.execute( \"SELECT user_id, content FROM tw_followers_id\")\n", "print( ct.count( get_tw_followers_id() ) )\n", "print( ct.count( get_tw_followers_id() ) )"]}, {"cell_type": "markdown", "metadata": {}, "source": ["La commande ex\u00e9cute en elle-m\u00eame ne prend pas du tout de temps, car elle ne fait que pr\u00e9parer la requ\u00eate, n'h\u00e9sitez donc pas \u00e0 en mettre syst\u00e9matiquement dans vos cellules, plut\u00f4t que de risquer d'avoir un curseur dont vous ne vous souvenez plus de l'\u00e9tat."]}, {"cell_type": "markdown", "metadata": {}, "source": ["## Partie 1 - description de la base de donn\u00e9e"]}, {"cell_type": "markdown", "metadata": {}, "source": ["#### Question 1 - \u00e9l\u00e9ments unique d'une table"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Trouvez la liste des ``user_id`` diff\u00e9rents dans la table ``tw_followers_id``, en utilisant les fonctions [cytoolz](https://github.com/pytoolz/cytoolz). \n", "La fonction qui pourra vous \u00eatre utiles ici : \n", "\n", " - [ct.unique(seq)](http://toolz.readthedocs.io/en/latest/api.html#toolz.itertoolz.unique) $\\Rightarrow$ \u00e0 partir d'une s\u00e9quence, renvoit une s\u00e9quence o\u00f9 tous les doublons ont \u00e9t\u00e9 supprim\u00e9s\n", " \n", "Vous vous rappelez sans doute que nous utilisions syst\u00e9matiquement [pluck](http://toolz.readthedocs.io/en/latest/api.html?highlight=pluck#toolz.itertoolz.pluck) et [map](https://docs.python.org/3/library/functions.html#map) pour les exemples du cours, ceux-ci ne sont pas n\u00e9cessaires ici. A noter qu'il faudra sans doute utilisez la fonction ``list( ... )``, ou une boucle ``for`` pour forcer l'\u00e9valuation des fonctions [cytoolz](https://github.com/pytoolz/cytoolz)."]}, {"cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [], "source": ["import cytoolz as ct \n", "import cytoolz.curried as ctc"]}, {"cell_type": "markdown", "metadata": {}, "source": ["A noter que si vous voyez appara\u00eetre vos r\u00e9sultats sous la forme (79145543,), c'est normal, le curseur sqlite renvoit toujours ces r\u00e9sultats sous forme de tuple : *(colonne1, colonne2, colonne3, ...)* et ce m\u00eame si il n'y a qu'une seule colonne dans la requ\u00eate. Nous utiliserons [pluck](http://toolz.readthedocs.io/en/latest/api.html?highlight=pluck#toolz.itertoolz.pluck) pour extraire le premier \u00e9l\u00e9ment du tuple."]}, {"cell_type": "markdown", "metadata": {}, "source": ["#### Question 2 - nombre d'\u00e9lements unique d'une table"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Trouvez le nombre de user_id diff\u00e9rents dans la table tw_followers_id, en utilisant les fonctions cytoolz. \n", "Les fonctions qui pourront vous \u00eatres utiles ici : \n", "\n", " - ct.count(seq) => compte le nombre d'\u00e9l\u00e9ments d'une s\u00e9quence\n", " - ct.unique(seq) => \u00e0 partir d'une s\u00e9quence, renvoit une s\u00e9quence o\u00f9 tous les doublons ont \u00e9t\u00e9 supprim\u00e9s\n", " \n", "Vous vous rappelez sans doute que nous utilisions syst\u00e9matiquement pluck et map pour les exemples du cours, ceux-ci ne sont pas n\u00e9cessaires, ici."]}, {"cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [], "source": ["import cytoolz as ct \n", "import cytoolz.curried as ctc"]}, {"cell_type": "markdown", "metadata": {}, "source": ["#### Question 3 : cr\u00e9ation d'une fonction comptez_unique"]}, {"cell_type": "markdown", "metadata": {}, "source": ["A l'aide de [ct.compose](http://toolz.readthedocs.io/en/latest/api.html?highlight=compose#toolz.functoolz.compose), cr\u00e9ez une fonction ``comptez_unique`` qui effectue directement cette op\u00e9ration. Pour rappel, ``ct.compose( f, g, h, ...)`` renvoit une fonction qui appel\u00e9e sur ``x`` ex\u00e9cute ``(f(g(h(x)))``. [ct.compose](http://toolz.readthedocs.io/en/latest/api.html?highlight=compose#toolz.functoolz.compose) prend un nombre d'arguments quelconque. A noter que les fonctions donn\u00e9es en argument doivent ne prendre qu'un seul argument, ce qui est le cas ici. Pensez bien que comme vous manipulez ici les fonctions elle-m\u00eame, il ne faut pas mettre de parenth\u00e8ses apr\u00e8s "]}, {"cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [], "source": ["import cytoolz as ct \n", "import cytoolz.curried as ctc"]}, {"cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [], "source": ["## Pour tester votre code, cette ligne doit renvoyer le m\u00eame nombre qu'\u00e0 la question 2\n", "# comptez_unique( cursor_sqlite.execute( \"SELECT user_id FROM tw_followers_id\") )"]}, {"cell_type": "markdown", "metadata": {}, "source": ["#### Question 4 : compte du nombre de valeurs de \"location\" diff\u00e9rentes dans la table tw_users"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Nous allons utiliser la fonction comptez_unique d\u00e9finie pr\u00e9c\u00e9demment pour comptez le nombre de \"location\" diff\u00e9rentes dans la table **tw_users**. \n", "Pour cela il faudra faire appel \u00e0 deux fonctions : \n", "\n", " - [ct.pluck](http://toolz.readthedocs.io/en/latest/api.html#toolz.itertoolz.pluck) pour extraire une valeur de tous les \u00e9l\u00e9ments d'une s\u00e9quence\n", " - **ct.map** (ie ``map = cytoolz.curry(map)``) pour appliquer une fonction (ici [json.loads](https://docs.python.org/3/library/json.html#json.loads) pour transformer une cha\u00eene de caract\u00e8re au format json en objet python).\n", " \n", "Il faudra sans doute appliquer [ct.pluck](http://toolz.readthedocs.io/en/latest/api.html#toolz.itertoolz.pluck) deux fois, une fois pour extraire la colonne content du r\u00e9sultat de la requ\u00eate (m\u00eame si celle-ci ne comprend qu'une colonne) et une fois pour extraire le champ \"location\" du json.\n", "\n", "Les syntaxes de ces fonctions sont les suivantes :\n", "\n", " - ``ct.pluck( 0, seq )`` (cas d'une s\u00e9quence de liste ou de tuple) ou ``ct.pluck( key, seq )`` (cas d'une s\u00e9quence de dictionnaire).\n", " - ``ct.map( f, seq )`` o\u00f9 f est la fonction que l'on souhaite appliquer (ne mettez pas les parenth\u00e8ses apr\u00e8s le f, ici vous faites r\u00e9f\u00e9rences \u00e0 la fonction, pas son r\u00e9sultat)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["**Astuce :** dans le cas improbable o\u00f9 vous auriez un ordinateur sensiblement plus lent que le r\u00e9dacteur du tp, rajoutez LIMIT 10000 \u00e0 la fin des requ\u00eates"]}, {"cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [{"data": {"text/plain": [""]}, "execution_count": 14, "metadata": {}, "output_type": "execute_result"}], "source": ["import cytoolz as ct \n", "\n", "cursor_sqlite.execute( \"SELECT content FROM tw_users\")\n", "# Le r\u00e9sultat attendu est 13730"]}, {"cell_type": "markdown", "metadata": {}, "source": ["#### Question 5 : curly fonctions\n", "\n", "Comme on risque de beaucoup utiliser les fonctions ``ct.map`` et [ct.pluck](http://toolz.readthedocs.io/en/latest/api.html#toolz.itertoolz.pluck), on veut se simplifier la vie en utilisant la notation suivante : "]}, {"cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [{"name": "stderr", "output_type": "stream", "text": ["c:\\python35_x64\\lib\\site-packages\\ipykernel\\__main__.py:1: DeprecationWarning: inspect.getargspec() is deprecated, use inspect.signature() instead\n", " if __name__ == '__main__':\n", "c:\\python35_x64\\lib\\site-packages\\ipykernel\\__main__.py:3: DeprecationWarning: inspect.getargspec() is deprecated, use inspect.signature() instead\n", " app.launch_new_instance()\n"]}], "source": ["pluck_loc = ctc.pluck(\"location\")\n", "map_loads = ctc.map(json.loads)\n", "pluck_0 = ctc.pluck(0)"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Notez bien que nous utilisons ctc.pluck et non pas ct.pluck, car le package [cytoolz.curry](https://toolz.readthedocs.io/en/latest/curry.html) (ici import\u00e9 en temps que ctc) contient les versions de ces fonctions qui supportent l'\u00e9valuation partielle. \n", "\n", "Les objets **pluck_loc**, **map_loads**, **pluck_0** sont donc des fonctions \u00e0 un argument, construites \u00e0 partir de fonctions \u00e0 deux arguments. Utilisez ces 3 fonctions pour simplifier l'\u00e9criture de la question 4"]}, {"cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [{"data": {"text/plain": [""]}, "execution_count": 16, "metadata": {}, "output_type": "execute_result"}], "source": ["import cytoolz as ct \n", "\n", "cursor_sqlite.execute( \"SELECT content FROM tw_users\")\n", "# Le r\u00e9sultat attendu est 13730"]}, {"cell_type": "markdown", "metadata": {}, "source": ["#### Question 6 : fonction get_json_seq\n", "\n", "A partir des fonctions pr\u00e9c\u00e9dentes et de la fonction compose, cr\u00e9ez une fonction get_json_seq, qui \u00e0 partir d'un curseur d'une requ\u00eate dont la colonne content est en premi\u00e8re position, renvoit une s\u00e9quence des objets json load\u00e9s.\n", "\n", "Vous devez pouvoir l'utiliser pour r\u00e9\u00e9crire le code de la question pr\u00e9c\u00e9dente ainsi :"]}, {"cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [{"data": {"text/plain": [""]}, "execution_count": 17, "metadata": {}, "output_type": "execute_result"}], "source": ["import cytoolz as ct \n", "\n", "cursor_sqlite.execute( \"SELECT content FROM tw_users\")\n", "# comptez_unique( pluck_loc( get_json_seq(cursor_sqlite)))"]}, {"cell_type": "markdown", "metadata": {}, "source": ["#### Question 7 : liste des localisations avec Paris\n", "\n", "On peut v\u00e9rifier si une localisation contient le mot \"Paris\", avec toutes ces variations de casse possible avec la fonction suivante :"]}, {"cell_type": "code", "execution_count": 17, "metadata": {"collapsed": true}, "outputs": [], "source": ["def contains_paris(loc):\n", " return \"paris\" in loc.lower()"]}, {"cell_type": "markdown", "metadata": {}, "source": ["En utilisant cette fonction et la fonction ``ct.filter``, trouvez : \n", "\n", " - le nombre d'utilisateur dont la location contient Paris sous une forme ou une autre (question 7.1)\n", " - tous les variantes de location contenant Paris (pour info il y en a 977)\n", " \n", "ct.filter s'utilise avec la syntaxe ``ct.filter( f, seq )`` (voir [filter](https://docs.python.org/3/library/functions.html?highlight=filter#filter)) et renvoit une s\u00e9quence de tous les \u00e9l\u00e9ments de la s\u00e9quence en entr\u00e9e pour lesquels f renvoit true. Vous aurez besoin des fonctions [ct.unique](http://toolz.readthedocs.io/en/latest/api.html#toolz.itertoolz.unique) et [ct.count](http://toolz.readthedocs.io/en/latest/api.html#toolz.itertoolz.count). Si vous avez une sortie du type ````, rajouter la fonction ``list( ... )`` autour pour forcer l'\u00e9valuation."]}, {"cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [{"data": {"text/plain": [""]}, "execution_count": 19, "metadata": {}, "output_type": "execute_result"}], "source": ["## Question 7.1\n", "import cytoolz as ct \n", "\n", "cursor_sqlite.execute( \"SELECT content FROM tw_users\")\n", "## le r\u00e9sultat attendu est 5470"]}, {"cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [{"data": {"text/plain": [""]}, "execution_count": 20, "metadata": {}, "output_type": "execute_result"}], "source": ["## Question 7.2\n", "import cytoolz as ct \n", "\n", "cursor_sqlite.execute( \"SELECT content FROM tw_users\")\n", "## la liste doit contenir 977 \u00e9l\u00e9ments"]}, {"cell_type": "markdown", "metadata": {}, "source": ["#### Question 8 : somme des tweets de tous les utilisateurs dont la location contient Paris"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Calculez le nombre de tweets total par les utilisateurs dont la *location* contient Paris. \n", "Dans le json de twitter, la cl\u00e9 pour cela est ``statuses_count``. Pour cela plusieurs possibilit\u00e9s : \n", "\n", "- la plus simple est de red\u00e9finir une fonction ``contains_paris``, qui prenne en entr\u00e9e un user json \n", "- [groupby](http://toolz.readthedocs.io/en/latest/api.html#toolz.itertoolz.groupby) *(\"location\", seq)* vous renvoit les r\u00e9ponses group\u00e9es par location. Cette m\u00e9thode poss\u00e8de l'inconv\u00e9nient de charger toutes les donn\u00e9es en m\u00e9moire\n", "- [reduceby](http://toolz.readthedocs.io/en/latest/api.html#toolz.itertoolz.reduceby) *(\"location\", lambda x,y: x + y[\"statuses_count\"], seq, 0)* vous renvoit la somme par location, il ne reste plus qu'\u00e0 filtrer et additionner\n", "- [pluck](http://toolz.readthedocs.io/en/latest/api.html#toolz.itertoolz.pluck) *([\"location\", \"statuses_count\"], seq)* vous permet de garder les deux informations. Il faudra changer la fonction contains paris pour celle suivante (``contains_paris_tuple``)\n", " \n", "R\u00e9ponse attendue : 9811612"]}, {"cell_type": "code", "execution_count": 20, "metadata": {}, "outputs": [], "source": ["import cytoolz as ct "]}, {"cell_type": "markdown", "metadata": {}, "source": ["#### Question 9 : comparaison des followers d'homme politique\n", "\n", "On va maintenant s'int\u00e9resser \u00e0 la proximit\u00e9 / corr\u00e9lation entre les hommes politiques, que l'on mesurera \u00e0 partir de la formule :\n", "\n", "$\\frac{1}{2}*( \\frac{nbFollowersCommun}{nbFollowersHommePolitique_1} + \\frac{nbFollowersCommun}{nbFollowersHommePolitique_2}$)\n", "\n", "On prend donc la moyenne des ratios des followers de chaque homme politique suivant l'autre (cette formule semble s'accommoder assez bien des diff\u00e9rences du nombre de followers entre homme politiques). On s'int\u00e9ressera notamment aux hommes politiques suivants :\n", "\n", "```\n", "benoithamon | 14389177\n", "montebourg | 69255422\n", "alainjuppe | 258345629\n", "```\n", " \n", "De fait vous pouvez prendre n'importe quel homme ou femme politique, les r\u00e9sultats de cette m\u00e9thode sont assez probants malgr\u00e9 sa rusticit\u00e9. "]}, {"cell_type": "markdown", "metadata": {}, "source": ["**Important : pensez \u00e0 appliquer la cellule ci-dessous**"]}, {"cell_type": "code", "execution_count": 21, "metadata": {}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["Index created\n"]}], "source": ["try:\n", " cursor_sqlite.execute(\"CREATE UNIQUE INDEX tw_users_id_index ON tw_users(id)\")\n", " print(\"Index created\")\n", "except sqlite3.OperationalError as e:\n", " if( \"index tw_users_id_index already exists\" in str(e)):\n", " print(\"Ok, index already exists\")\n", " else:\n", " raise e"]}, {"cell_type": "markdown", "metadata": {}, "source": ["La fa\u00e7on la plus simple est de charger les listes d'id de followers en m\u00e9moire, dans des objets de type set, et de les comparer avec les op\u00e9rateurs & (intersection) - (diff\u00e9rences). On peut aussi chercher une m\u00e9thode approch\u00e9e, en comparant de fa\u00e7on al\u00e9atoire les listes contenues dans ``tw_follower_id``.\n", "\n", "*Tips : si vous trouvez que Montebourg est plus proche de Jupp\u00e9 que de Hamon, vous vous \u00eates plant\u00e9 ...*"]}, {"cell_type": "code", "execution_count": 22, "metadata": {}, "outputs": [], "source": []}, {"cell_type": "markdown", "metadata": {}, "source": ["## Partie 2 : avec dask"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Essayez d'ex\u00e9cuter le code suivant"]}, {"cell_type": "code", "execution_count": 23, "metadata": {"collapsed": true}, "outputs": [], "source": ["import dask"]}, {"cell_type": "markdown", "metadata": {}, "source": ["[dask](http://dask.pydata.org/en/latest/bag.html) peut vous permettre de parall\u00e9liser de fa\u00e7on efficace votre code entre plusieurs processeurs. Utilisez le code suivant pour splitter la base *twitter_for_network_full.db* en plusieurs fichiers plats (NB: pensez \u00e0 nettoyer votre disque dur apr\u00e8s ce tp)."]}, {"cell_type": "code", "execution_count": 24, "metadata": {"collapsed": true}, "outputs": [], "source": ["import cytoolz as ct # import groupby, valmap, compose\n", "import cytoolz.curried as ctc ## pipe, map, filter, get\n", "import sqlite3\n", "import pprint\n", "try:\n", " import ujson as json\n", "except:\n", " import json\n", "\n", "conn_sqlite_f = sqlite3.connect(\"twitter_for_network_100000.db\")\n", "cursor_sqlite_f = conn_sqlite_f.cursor()"]}, {"cell_type": "code", "execution_count": 25, "metadata": {}, "outputs": [], "source": ["cursor_sqlite_f.execute(\"SELECT content FROM tw_users\")\n", "\n", "for it in range(100):\n", " with open( \"tw_users_split_{0:02d}.json\".format(it), 'w') as f:\n", " for it_index, it_json in enumerate( cursor_sqlite_f ):\n", " f.write( it_json[0] )\n", " f.write(\"\\n\")\n", " if it_index == 100000:\n", " break\n", " else:\n", " break"]}, {"cell_type": "markdown", "metadata": {}, "source": ["Calculez maintenant, en utilisant [dask.bag](http://dask.pydata.org/en/latest/bag.html) :\n", "\n", "- le nombre total de status\n", "- le nombre de status moyen par location\n", "- la distribution du nombre de followers par puissance de 10 sur l'ensemble des users"]}, {"cell_type": "code", "execution_count": 26, "metadata": {}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["ujson unavailable\n"]}], "source": ["## Code commun n\u00e9cessaire\n", "\n", "import dask.bag as dbag\n", "try:\n", " import ujson as json\n", "except:\n", " print(\"ujson unavailable\")\n", " import json\n", "from operator import add\n", "\n", "a = dbag.read_text('tw_users_split*.json')"]}, {"cell_type": "code", "execution_count": 27, "metadata": {}, "outputs": [], "source": ["# Le nombre total de status"]}, {"cell_type": "code", "execution_count": 28, "metadata": {}, "outputs": [], "source": ["# Le nombre moyen de tweet par location.\n", "import cytoolz\n", "import cytoolz.curried as ctc"]}, {"cell_type": "code", "execution_count": 29, "metadata": {}, "outputs": [], "source": ["# La distribution du nombre de followers par puissance de 10\n", "import math"]}, {"cell_type": "code", "execution_count": 30, "metadata": {"collapsed": true}, "outputs": [], "source": []}], "metadata": {"kernelspec": {"display_name": "Python 3", "language": "python", "name": "python3"}, "language_info": {"codemirror_mode": {"name": "ipython", "version": 3}, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.6.1"}}, "nbformat": 4, "nbformat_minor": 2}