-
-
Save sotsugov/01a53bd323d61994c5d28606642c59db to your computer and use it in GitHub Desktop.
Data Manipulation and Visualization with Pandas and Seaborn — A Practical Introduction
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"cells": [ | |
{ | |
"cell_type": "code", | |
"execution_count": 2, | |
"metadata": { | |
"collapsed": false | |
}, | |
"outputs": [], | |
"source": [ | |
"%matplotlib notebook\n", | |
"\n", | |
"import numpy as np\n", | |
"import seaborn as sns\n", | |
"import pandas as pd\n", | |
"\n", | |
"sns.set_context(\"paper\")" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"# Table of Contents\n", | |
"* [Intro](#Intro)\n", | |
"* [Data Loading](#Data-Loading)\n", | |
"* [Function Application](#Function-Application)\n", | |
"* [Group-By and Aggregation](#Group-By-and-Aggregation)\n", | |
"\t* [Plotting](#Plotting)\n", | |
"\t* [Multi-Index](#Multi-Index)\n", | |
"* [Pivoting, Stacking and Melting](#Pivoting,-Stacking-and-Melting)\n", | |
"\t* [Facet Grid](#Facet-Grid)\n", | |
"* [Filling time ranges](#Filling-time-ranges)\n", | |
"* [Notes](#Notes)\n" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"# Intro" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"In this notebook, I'm going to demonstrate with practical examples various concepts and methods related to Pandas and Seaborn. I will rely on the data format I used for my [Facebook Conversation Analyzer project](https://github.com/5agado/conversation-analyzer). For seemingly obvious reasons I didn't use a personal conversation but automatically generated a fake and nonsensical one. Projecting or imagining some conversation relevant to you will most likely help you to better understand and memorize the content of this notebook, even greater if you can play around with your actual data.\n", | |
"\n", | |
"The main topic is data manipulation with Pandas, for example function application, groupby, aggregation and multi-indexes. All along I'll mention handy tricks that you can use for various tasks and demonstrate how we can plot results in different ways using [Seaborn](http://seaborn.pydata.org/index.html) (based on matplotlib). Given the data format, special focus is put on time-series data manipulation." | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"# Data Loading" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"Assuming you already have your data in a valid csv format, loading it in a Pandas dataframe is as easy as calling [*pd.read_csv*](http://pandas.pydata.org/pandas-docs/stable/generated/pandas.read_csv.html).\n", | |
"\n", | |
"We additionally care about the **automatic datetime parsing** for certain columns. With the *parse_dates* argument, we tell Pandas which date should be forced to datetime format. You can also pass a custom parser, something like: *date_parser = lambda x : pd.datetime.strptime(x, '%H:%M:%S'))*" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 3, | |
"metadata": { | |
"collapsed": false, | |
"format": "row" | |
}, | |
"outputs": [ | |
{ | |
"data": { | |
"text/html": [ | |
"<div>\n", | |
"<table border=\"1\" class=\"dataframe\">\n", | |
" <thead>\n", | |
" <tr style=\"text-align: right;\">\n", | |
" <th></th>\n", | |
" <th>datetime</th>\n", | |
" <th>sender</th>\n", | |
" <th>text</th>\n", | |
" </tr>\n", | |
" </thead>\n", | |
" <tbody>\n", | |
" <tr>\n", | |
" <th>0</th>\n", | |
" <td>2016-01-11 00:16:51</td>\n", | |
" <td>Donnie</td>\n", | |
" <td>I... remember.</td>\n", | |
" </tr>\n", | |
" <tr>\n", | |
" <th>1</th>\n", | |
" <td>2016-01-11 19:06:48</td>\n", | |
" <td>Donnie</td>\n", | |
" <td>I'm sure you would.</td>\n", | |
" </tr>\n", | |
" <tr>\n", | |
" <th>2</th>\n", | |
" <td>2016-01-12 18:10:15</td>\n", | |
" <td>Donnie</td>\n", | |
" <td>Where is my mother! How oddly thou repliest! Y...</td>\n", | |
" </tr>\n", | |
" <tr>\n", | |
" <th>3</th>\n", | |
" <td>2016-01-13 21:45:27</td>\n", | |
" <td>Donnie</td>\n", | |
" <td>I'm at Space Station Five, darling. How are you?</td>\n", | |
" </tr>\n", | |
" <tr>\n", | |
" <th>4</th>\n", | |
" <td>2016-01-14 15:44:06</td>\n", | |
" <td>Frank</td>\n", | |
" <td>You were never invited to my house.</td>\n", | |
" </tr>\n", | |
" </tbody>\n", | |
"</table>\n", | |
"</div>" | |
], | |
"text/plain": [ | |
" datetime sender \\\n", | |
"0 2016-01-11 00:16:51 Donnie \n", | |
"1 2016-01-11 19:06:48 Donnie \n", | |
"2 2016-01-12 18:10:15 Donnie \n", | |
"3 2016-01-13 21:45:27 Donnie \n", | |
"4 2016-01-14 15:44:06 Frank \n", | |
"\n", | |
" text \n", | |
"0 I... remember. \n", | |
"1 I'm sure you would. \n", | |
"2 Where is my mother! How oddly thou repliest! Y... \n", | |
"3 I'm at Space Station Five, darling. How are you? \n", | |
"4 You were never invited to my house. " | |
] | |
}, | |
"execution_count": 3, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
], | |
"source": [ | |
"# Load conversation messages\n", | |
"msgs = pd.read_csv('test_conv.csv', parse_dates=['datetime'])\n", | |
"# Display first 5 entries of our dataframe\n", | |
"msgs.head()" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 4, | |
"metadata": { | |
"collapsed": false | |
}, | |
"outputs": [ | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": [ | |
"<class 'pandas.core.frame.DataFrame'>\n", | |
"RangeIndex: 1000 entries, 0 to 999\n", | |
"Data columns (total 3 columns):\n", | |
"datetime 1000 non-null datetime64[ns]\n", | |
"sender 1000 non-null object\n", | |
"text 1000 non-null object\n", | |
"dtypes: datetime64[ns](1), object(2)\n", | |
"memory usage: 23.5+ KB\n" | |
] | |
} | |
], | |
"source": [ | |
"# Double check of types\n", | |
"msgs.info()" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"# Function Application" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"Function application allows you to map a function to elements (cells, rows, columns) of your dataframe (or series) in a computationally efficient way. \n", | |
"\n", | |
"We will try out function application in the preprocessing step for the textual component of our messages.\n", | |
"We are going to use a dummy method for tokenization (transform text to list of words) and then compute some stats on the spot (e.g. text length, word count).\n", | |
"\n", | |
"When using [*apply*](http://pandas.pydata.org/pandas-docs/stable/generated/pandas.DataFrame.apply.html) on a dataframe, always double check on the *axis* parameter, it's easy to forget or confuse it, causing annoying errors. 0 (default) is for applying the function to each column, 1 to each row." | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 5, | |
"metadata": { | |
"collapsed": true | |
}, | |
"outputs": [], | |
"source": [ | |
"# Most simple and dummy method possible for tokenizing your text\n", | |
"def get_words(text):\n", | |
" return text.strip().split()" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 6, | |
"metadata": { | |
"collapsed": false | |
}, | |
"outputs": [ | |
{ | |
"data": { | |
"text/html": [ | |
"<div>\n", | |
"<table border=\"1\" class=\"dataframe\">\n", | |
" <thead>\n", | |
" <tr style=\"text-align: right;\">\n", | |
" <th></th>\n", | |
" <th>datetime</th>\n", | |
" <th>sender</th>\n", | |
" <th>text</th>\n", | |
" <th>text_len</th>\n", | |
" <th>num_tokens</th>\n", | |
" <th>num_types</th>\n", | |
" </tr>\n", | |
" </thead>\n", | |
" <tbody>\n", | |
" <tr>\n", | |
" <th>0</th>\n", | |
" <td>2016-01-11 00:16:51</td>\n", | |
" <td>Donnie</td>\n", | |
" <td>I... remember.</td>\n", | |
" <td>14</td>\n", | |
" <td>2</td>\n", | |
" <td>2</td>\n", | |
" </tr>\n", | |
" <tr>\n", | |
" <th>1</th>\n", | |
" <td>2016-01-11 19:06:48</td>\n", | |
" <td>Donnie</td>\n", | |
" <td>I'm sure you would.</td>\n", | |
" <td>19</td>\n", | |
" <td>4</td>\n", | |
" <td>4</td>\n", | |
" </tr>\n", | |
" <tr>\n", | |
" <th>2</th>\n", | |
" <td>2016-01-12 18:10:15</td>\n", | |
" <td>Donnie</td>\n", | |
" <td>Where is my mother! How oddly thou repliest! Y...</td>\n", | |
" <td>100</td>\n", | |
" <td>18</td>\n", | |
" <td>16</td>\n", | |
" </tr>\n", | |
" <tr>\n", | |
" <th>3</th>\n", | |
" <td>2016-01-13 21:45:27</td>\n", | |
" <td>Donnie</td>\n", | |
" <td>I'm at Space Station Five, darling. How are you?</td>\n", | |
" <td>48</td>\n", | |
" <td>9</td>\n", | |
" <td>9</td>\n", | |
" </tr>\n", | |
" <tr>\n", | |
" <th>4</th>\n", | |
" <td>2016-01-14 15:44:06</td>\n", | |
" <td>Frank</td>\n", | |
" <td>You were never invited to my house.</td>\n", | |
" <td>35</td>\n", | |
" <td>7</td>\n", | |
" <td>7</td>\n", | |
" </tr>\n", | |
" </tbody>\n", | |
"</table>\n", | |
"</div>" | |
], | |
"text/plain": [ | |
" datetime sender \\\n", | |
"0 2016-01-11 00:16:51 Donnie \n", | |
"1 2016-01-11 19:06:48 Donnie \n", | |
"2 2016-01-12 18:10:15 Donnie \n", | |
"3 2016-01-13 21:45:27 Donnie \n", | |
"4 2016-01-14 15:44:06 Frank \n", | |
"\n", | |
" text text_len num_tokens \\\n", | |
"0 I... remember. 14 2 \n", | |
"1 I'm sure you would. 19 4 \n", | |
"2 Where is my mother! How oddly thou repliest! Y... 100 18 \n", | |
"3 I'm at Space Station Five, darling. How are you? 48 9 \n", | |
"4 You were never invited to my house. 35 7 \n", | |
"\n", | |
" num_types \n", | |
"0 2 \n", | |
"1 4 \n", | |
"2 16 \n", | |
"3 9 \n", | |
"4 7 " | |
] | |
}, | |
"execution_count": 6, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
], | |
"source": [ | |
"# Our function to apply. When applied to a dataframe this will get a row as input\n", | |
"def extract_text_basic_stats(row):\n", | |
" # tokenize our message text\n", | |
" words = get_words(row['text'])\n", | |
" # Compute message stats and add entries to the row\n", | |
" # For demonstration purposes, but otherwise clearly inefficient way to do it\n", | |
" row['text_len'] = len(row['text'])\n", | |
" row['num_tokens'] = len(words)\n", | |
" row['num_types'] = len(set(words))\n", | |
" return row\n", | |
"\n", | |
"# We apply row wise, so axis = 1\n", | |
"msgs_stats = msgs.apply(extract_text_basic_stats, axis=1)\n", | |
"msgs_stats.head()" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"With previous approach (i.e. applying function on dataframe) we can rely on the entire information present in a row. For this specific case is a bit of an overhead, mostly cause we are using only the *text* field. \n", | |
"We can then instead simply **apply our function directly to our target column**, and pack the result values in a new dataframe that will be joined with our original one." | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 7, | |
"metadata": { | |
"collapsed": false | |
}, | |
"outputs": [ | |
{ | |
"data": { | |
"text/html": [ | |
"<div>\n", | |
"<table border=\"1\" class=\"dataframe\">\n", | |
" <thead>\n", | |
" <tr style=\"text-align: right;\">\n", | |
" <th></th>\n", | |
" <th>datetime</th>\n", | |
" <th>sender</th>\n", | |
" <th>text</th>\n", | |
" <th>text_len</th>\n", | |
" <th>num_tokens</th>\n", | |
" <th>num_types</th>\n", | |
" </tr>\n", | |
" </thead>\n", | |
" <tbody>\n", | |
" <tr>\n", | |
" <th>0</th>\n", | |
" <td>2016-01-11 00:16:51</td>\n", | |
" <td>Donnie</td>\n", | |
" <td>I... remember.</td>\n", | |
" <td>14</td>\n", | |
" <td>2</td>\n", | |
" <td>2</td>\n", | |
" </tr>\n", | |
" <tr>\n", | |
" <th>1</th>\n", | |
" <td>2016-01-11 19:06:48</td>\n", | |
" <td>Donnie</td>\n", | |
" <td>I'm sure you would.</td>\n", | |
" <td>19</td>\n", | |
" <td>4</td>\n", | |
" <td>4</td>\n", | |
" </tr>\n", | |
" <tr>\n", | |
" <th>2</th>\n", | |
" <td>2016-01-12 18:10:15</td>\n", | |
" <td>Donnie</td>\n", | |
" <td>Where is my mother! How oddly thou repliest! Y...</td>\n", | |
" <td>100</td>\n", | |
" <td>18</td>\n", | |
" <td>16</td>\n", | |
" </tr>\n", | |
" <tr>\n", | |
" <th>3</th>\n", | |
" <td>2016-01-13 21:45:27</td>\n", | |
" <td>Donnie</td>\n", | |
" <td>I'm at Space Station Five, darling. How are you?</td>\n", | |
" <td>48</td>\n", | |
" <td>9</td>\n", | |
" <td>9</td>\n", | |
" </tr>\n", | |
" <tr>\n", | |
" <th>4</th>\n", | |
" <td>2016-01-14 15:44:06</td>\n", | |
" <td>Frank</td>\n", | |
" <td>You were never invited to my house.</td>\n", | |
" <td>35</td>\n", | |
" <td>7</td>\n", | |
" <td>7</td>\n", | |
" </tr>\n", | |
" </tbody>\n", | |
"</table>\n", | |
"</div>" | |
], | |
"text/plain": [ | |
" datetime sender \\\n", | |
"0 2016-01-11 00:16:51 Donnie \n", | |
"1 2016-01-11 19:06:48 Donnie \n", | |
"2 2016-01-12 18:10:15 Donnie \n", | |
"3 2016-01-13 21:45:27 Donnie \n", | |
"4 2016-01-14 15:44:06 Frank \n", | |
"\n", | |
" text text_len num_tokens \\\n", | |
"0 I... remember. 14 2 \n", | |
"1 I'm sure you would. 19 4 \n", | |
"2 Where is my mother! How oddly thou repliest! Y... 100 18 \n", | |
"3 I'm at Space Station Five, darling. How are you? 48 9 \n", | |
"4 You were never invited to my house. 35 7 \n", | |
"\n", | |
" num_types \n", | |
"0 2 \n", | |
"1 4 \n", | |
"2 16 \n", | |
"3 9 \n", | |
"4 7 " | |
] | |
}, | |
"execution_count": 7, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
], | |
"source": [ | |
"# Our function to apply. When applied to a series/column this will get a cell value as input\n", | |
"def extract_text_basic_stats(text):\n", | |
" words = get_words(text)\n", | |
" # Return results as tuples. You can also return a dictionary for automatic insertion in a dataframe\n", | |
" return len(text), len(words), len(set(words))\n", | |
"\n", | |
"# Apply function to text column\n", | |
"stats = msgs['text'].apply(extract_text_basic_stats).values\n", | |
"# Pack results in a new dataframe and join it with our original one\n", | |
"msgs_stats = pd.DataFrame(list(stats), columns=['text_len', 'num_tokens', 'num_types'])\n", | |
"msgs_stats = msgs.join(msgs_stats)\n", | |
"msgs_stats.head()" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"# Group-By and Aggregation" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"Stats by day can be too granular for our interests, while seeing the trend per months can provide a better and clearer understanding on what's going on. Seems the right time to rely on [group-by and aggregation](http://pandas.pydata.org/pandas-docs/stable/groupby.html).\n", | |
"\n", | |
"*groupby* groups your entries by common values, where the values come from field (or set of) you specify. For example, I might group my messages by sender and month, and for each combination of them will obtain a list of associated and recorded values. **Aggregation** is about telling what do do with such list: sum, average, min, max, standard deviation are just some common examples. In the case of more complex types like list or string, you might instead perform concatenation or set operations.\n", | |
"\n", | |
"When dealing with time-series data is good to consider also the [resample](http://pandas.pydata.org/pandas-docs/stable/generated/pandas.DataFrame.resample.html) method." | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 8, | |
"metadata": { | |
"collapsed": false, | |
"format": "row" | |
}, | |
"outputs": [ | |
{ | |
"data": { | |
"text/html": [ | |
"<div>\n", | |
"<table border=\"1\" class=\"dataframe\">\n", | |
" <thead>\n", | |
" <tr style=\"text-align: right;\">\n", | |
" <th></th>\n", | |
" <th></th>\n", | |
" <th>text_len</th>\n", | |
" <th>num_tokens</th>\n", | |
" <th>num_types</th>\n", | |
" </tr>\n", | |
" <tr>\n", | |
" <th>sender</th>\n", | |
" <th>month</th>\n", | |
" <th></th>\n", | |
" <th></th>\n", | |
" <th></th>\n", | |
" </tr>\n", | |
" </thead>\n", | |
" <tbody>\n", | |
" <tr>\n", | |
" <th rowspan=\"5\" valign=\"top\">Donnie</th>\n", | |
" <th>1</th>\n", | |
" <td>1311</td>\n", | |
" <td>251</td>\n", | |
" <td>239</td>\n", | |
" </tr>\n", | |
" <tr>\n", | |
" <th>2</th>\n", | |
" <td>1487</td>\n", | |
" <td>280</td>\n", | |
" <td>265</td>\n", | |
" </tr>\n", | |
" <tr>\n", | |
" <th>3</th>\n", | |
" <td>2236</td>\n", | |
" <td>436</td>\n", | |
" <td>417</td>\n", | |
" </tr>\n", | |
" <tr>\n", | |
" <th>4</th>\n", | |
" <td>2379</td>\n", | |
" <td>449</td>\n", | |
" <td>430</td>\n", | |
" </tr>\n", | |
" <tr>\n", | |
" <th>5</th>\n", | |
" <td>1369</td>\n", | |
" <td>260</td>\n", | |
" <td>248</td>\n", | |
" </tr>\n", | |
" </tbody>\n", | |
"</table>\n", | |
"</div>" | |
], | |
"text/plain": [ | |
" text_len num_tokens num_types\n", | |
"sender month \n", | |
"Donnie 1 1311 251 239\n", | |
" 2 1487 280 265\n", | |
" 3 2236 436 417\n", | |
" 4 2379 449 430\n", | |
" 5 1369 260 248" | |
] | |
}, | |
"execution_count": 8, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
], | |
"source": [ | |
"# Create new feature by extracting the month from the date\n", | |
"msgs_stats['month'] = msgs_stats['datetime'].dt.month\n", | |
"# Group by month and sender and aggregate by sum\n", | |
"grouped_msgs = msgs_stats.groupby(['sender','month']).sum()\n", | |
"grouped_msgs.head()" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"*.dt* is a quick and nice way to access just the part of your datetime object you interested in, something like *.dt.month*, *.dt.year*, *.dt.hour*. Notice you could directly group by month by passing *msgs_stats['datetime'].dt.month* to the groupby list, thus avoiding the need of creating a new month column beforehand.\n", | |
"\n", | |
"While the previous is a simple example of aggregation, we can also get further and **derive several stats in one go**, by specifying which function to apply for each new column. Again, you could also rely on lambda or previously defines functions, and pass them to the *agg* method." | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 9, | |
"metadata": { | |
"collapsed": false, | |
"scrolled": true | |
}, | |
"outputs": [ | |
{ | |
"data": { | |
"text/html": [ | |
"<div>\n", | |
"<table border=\"1\" class=\"dataframe\">\n", | |
" <thead>\n", | |
" <tr style=\"text-align: right;\">\n", | |
" <th></th>\n", | |
" <th></th>\n", | |
" <th>avg_tokens</th>\n", | |
" <th>num_types</th>\n", | |
" <th>max_tokens</th>\n", | |
" <th>num_tokens</th>\n", | |
" </tr>\n", | |
" <tr>\n", | |
" <th>sender</th>\n", | |
" <th>month</th>\n", | |
" <th></th>\n", | |
" <th></th>\n", | |
" <th></th>\n", | |
" <th></th>\n", | |
" </tr>\n", | |
" </thead>\n", | |
" <tbody>\n", | |
" <tr>\n", | |
" <th rowspan=\"5\" valign=\"top\">Donnie</th>\n", | |
" <th>1</th>\n", | |
" <td>7.606061</td>\n", | |
" <td>239</td>\n", | |
" <td>20</td>\n", | |
" <td>251</td>\n", | |
" </tr>\n", | |
" <tr>\n", | |
" <th>2</th>\n", | |
" <td>8.750000</td>\n", | |
" <td>265</td>\n", | |
" <td>20</td>\n", | |
" <td>280</td>\n", | |
" </tr>\n", | |
" <tr>\n", | |
" <th>3</th>\n", | |
" <td>9.478261</td>\n", | |
" <td>417</td>\n", | |
" <td>24</td>\n", | |
" <td>436</td>\n", | |
" </tr>\n", | |
" <tr>\n", | |
" <th>4</th>\n", | |
" <td>8.803922</td>\n", | |
" <td>430</td>\n", | |
" <td>20</td>\n", | |
" <td>449</td>\n", | |
" </tr>\n", | |
" <tr>\n", | |
" <th>5</th>\n", | |
" <td>7.222222</td>\n", | |
" <td>248</td>\n", | |
" <td>19</td>\n", | |
" <td>260</td>\n", | |
" </tr>\n", | |
" </tbody>\n", | |
"</table>\n", | |
"</div>" | |
], | |
"text/plain": [ | |
" avg_tokens num_types max_tokens num_tokens\n", | |
"sender month \n", | |
"Donnie 1 7.606061 239 20 251\n", | |
" 2 8.750000 265 20 280\n", | |
" 3 9.478261 417 24 436\n", | |
" 4 8.803922 430 20 449\n", | |
" 5 7.222222 248 19 260" | |
] | |
}, | |
"execution_count": 9, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
], | |
"source": [ | |
"# Create destination column for new stats\n", | |
"msgs_stats['max_tokens'] = msgs_stats['num_tokens']\n", | |
"msgs_stats['avg_tokens'] = msgs_stats['num_tokens']\n", | |
"# Groupby and apply specific aggregation to each column we are interested in\n", | |
"grouped_msgs = msgs_stats.groupby(['sender','month']).agg({'max_tokens' : np.max, 'avg_tokens' : np.mean,\n", | |
" 'num_tokens': sum, 'num_types' : sum})\n", | |
"grouped_msgs.head()" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"## Plotting" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"Let's look into some basic plotting with the current data. A pointplot seems a reasonable representation, and we just have to pass our data to the corresponding method. We can pass directly the x and y values, or pass the entire dataframe as *data* and specify column names associated to x and y. The *hue* parameter allows to specify the column which values will be plotted separately, with different colors. As an example see the representation of sender-specific stats in the following plot.\n", | |
"\n", | |
"Notice that I often call *reset_index()* before passing the data to the plot function, cause in such a way I can access also index columns by names. I'm currently still not aware of any way to use index names directly.\n", | |
"\n", | |
"Is good to also consider **alternative options for plotting**, like calling the *plot* function directly provided by Pandas. Or accessing Matplotlibs plot methods via *sns.plt.*" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 10, | |
"metadata": { | |
"collapsed": false | |
}, | |
"outputs": [ | |
{ | |
"data": { | |
"application/javascript": [ | |
"/* Put everything inside the global mpl namespace */\n", | |
"window.mpl = {};\n", | |
"\n", | |
"mpl.get_websocket_type = function() {\n", | |
" if (typeof(WebSocket) !== 'undefined') {\n", | |
" return WebSocket;\n", | |
" } else if (typeof(MozWebSocket) !== 'undefined') {\n", | |
" return MozWebSocket;\n", | |
" } else {\n", | |
" alert('Your browser does not have WebSocket support.' +\n", | |
" 'Please try Chrome, Safari or Firefox ≥ 6. ' +\n", | |
" 'Firefox 4 and 5 are also supported but you ' +\n", | |
" 'have to enable WebSockets in about:config.');\n", | |
" };\n", | |
"}\n", | |
"\n", | |
"mpl.figure = function(figure_id, websocket, ondownload, parent_element) {\n", | |
" this.id = figure_id;\n", | |
"\n", | |
" this.ws = websocket;\n", | |
"\n", | |
" this.supports_binary = (this.ws.binaryType != undefined);\n", | |
"\n", | |
" if (!this.supports_binary) {\n", | |
" var warnings = document.getElementById(\"mpl-warnings\");\n", | |
" if (warnings) {\n", | |
" warnings.style.display = 'block';\n", | |
" warnings.textContent = (\n", | |
" \"This browser does not support binary websocket messages. \" +\n", | |
" \"Performance may be slow.\");\n", | |
" }\n", | |
" }\n", | |
"\n", | |
" this.imageObj = new Image();\n", | |
"\n", | |
" this.context = undefined;\n", | |
" this.message = undefined;\n", | |
" this.canvas = undefined;\n", | |
" this.rubberband_canvas = undefined;\n", | |
" this.rubberband_context = undefined;\n", | |
" this.format_dropdown = undefined;\n", | |
"\n", | |
" this.image_mode = 'full';\n", | |
"\n", | |
" this.root = $('<div/>');\n", | |
" this._root_extra_style(this.root)\n", | |
" this.root.attr('style', 'display: inline-block');\n", | |
"\n", | |
" $(parent_element).append(this.root);\n", | |
"\n", | |
" this._init_header(this);\n", | |
" this._init_canvas(this);\n", | |
" this._init_toolbar(this);\n", | |
"\n", | |
" var fig = this;\n", | |
"\n", | |
" this.waiting = false;\n", | |
"\n", | |
" this.ws.onopen = function () {\n", | |
" fig.send_message(\"supports_binary\", {value: fig.supports_binary});\n", | |
" fig.send_message(\"send_image_mode\", {});\n", | |
" fig.send_message(\"refresh\", {});\n", | |
" }\n", | |
"\n", | |
" this.imageObj.onload = function() {\n", | |
" if (fig.image_mode == 'full') {\n", | |
" // Full images could contain transparency (where diff images\n", | |
" // almost always do), so we need to clear the canvas so that\n", | |
" // there is no ghosting.\n", | |
" fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n", | |
" }\n", | |
" fig.context.drawImage(fig.imageObj, 0, 0);\n", | |
" };\n", | |
"\n", | |
" this.imageObj.onunload = function() {\n", | |
" this.ws.close();\n", | |
" }\n", | |
"\n", | |
" this.ws.onmessage = this._make_on_message_function(this);\n", | |
"\n", | |
" this.ondownload = ondownload;\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._init_header = function() {\n", | |
" var titlebar = $(\n", | |
" '<div class=\"ui-dialog-titlebar ui-widget-header ui-corner-all ' +\n", | |
" 'ui-helper-clearfix\"/>');\n", | |
" var titletext = $(\n", | |
" '<div class=\"ui-dialog-title\" style=\"width: 100%; ' +\n", | |
" 'text-align: center; padding: 3px;\"/>');\n", | |
" titlebar.append(titletext)\n", | |
" this.root.append(titlebar);\n", | |
" this.header = titletext[0];\n", | |
"}\n", | |
"\n", | |
"\n", | |
"\n", | |
"mpl.figure.prototype._canvas_extra_style = function(canvas_div) {\n", | |
"\n", | |
"}\n", | |
"\n", | |
"\n", | |
"mpl.figure.prototype._root_extra_style = function(canvas_div) {\n", | |
"\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._init_canvas = function() {\n", | |
" var fig = this;\n", | |
"\n", | |
" var canvas_div = $('<div/>');\n", | |
"\n", | |
" canvas_div.attr('style', 'position: relative; clear: both; outline: 0');\n", | |
"\n", | |
" function canvas_keyboard_event(event) {\n", | |
" return fig.key_event(event, event['data']);\n", | |
" }\n", | |
"\n", | |
" canvas_div.keydown('key_press', canvas_keyboard_event);\n", | |
" canvas_div.keyup('key_release', canvas_keyboard_event);\n", | |
" this.canvas_div = canvas_div\n", | |
" this._canvas_extra_style(canvas_div)\n", | |
" this.root.append(canvas_div);\n", | |
"\n", | |
" var canvas = $('<canvas/>');\n", | |
" canvas.addClass('mpl-canvas');\n", | |
" canvas.attr('style', \"left: 0; top: 0; z-index: 0; outline: 0\")\n", | |
"\n", | |
" this.canvas = canvas[0];\n", | |
" this.context = canvas[0].getContext(\"2d\");\n", | |
"\n", | |
" var rubberband = $('<canvas/>');\n", | |
" rubberband.attr('style', \"position: absolute; left: 0; top: 0; z-index: 1;\")\n", | |
"\n", | |
" var pass_mouse_events = true;\n", | |
"\n", | |
" canvas_div.resizable({\n", | |
" start: function(event, ui) {\n", | |
" pass_mouse_events = false;\n", | |
" },\n", | |
" resize: function(event, ui) {\n", | |
" fig.request_resize(ui.size.width, ui.size.height);\n", | |
" },\n", | |
" stop: function(event, ui) {\n", | |
" pass_mouse_events = true;\n", | |
" fig.request_resize(ui.size.width, ui.size.height);\n", | |
" },\n", | |
" });\n", | |
"\n", | |
" function mouse_event_fn(event) {\n", | |
" if (pass_mouse_events)\n", | |
" return fig.mouse_event(event, event['data']);\n", | |
" }\n", | |
"\n", | |
" rubberband.mousedown('button_press', mouse_event_fn);\n", | |
" rubberband.mouseup('button_release', mouse_event_fn);\n", | |
" // Throttle sequential mouse events to 1 every 20ms.\n", | |
" rubberband.mousemove('motion_notify', mouse_event_fn);\n", | |
"\n", | |
" rubberband.mouseenter('figure_enter', mouse_event_fn);\n", | |
" rubberband.mouseleave('figure_leave', mouse_event_fn);\n", | |
"\n", | |
" canvas_div.on(\"wheel\", function (event) {\n", | |
" event = event.originalEvent;\n", | |
" event['data'] = 'scroll'\n", | |
" if (event.deltaY < 0) {\n", | |
" event.step = 1;\n", | |
" } else {\n", | |
" event.step = -1;\n", | |
" }\n", | |
" mouse_event_fn(event);\n", | |
" });\n", | |
"\n", | |
" canvas_div.append(canvas);\n", | |
" canvas_div.append(rubberband);\n", | |
"\n", | |
" this.rubberband = rubberband;\n", | |
" this.rubberband_canvas = rubberband[0];\n", | |
" this.rubberband_context = rubberband[0].getContext(\"2d\");\n", | |
" this.rubberband_context.strokeStyle = \"#000000\";\n", | |
"\n", | |
" this._resize_canvas = function(width, height) {\n", | |
" // Keep the size of the canvas, canvas container, and rubber band\n", | |
" // canvas in synch.\n", | |
" canvas_div.css('width', width)\n", | |
" canvas_div.css('height', height)\n", | |
"\n", | |
" canvas.attr('width', width);\n", | |
" canvas.attr('height', height);\n", | |
"\n", | |
" rubberband.attr('width', width);\n", | |
" rubberband.attr('height', height);\n", | |
" }\n", | |
"\n", | |
" // Set the figure to an initial 600x600px, this will subsequently be updated\n", | |
" // upon first draw.\n", | |
" this._resize_canvas(600, 600);\n", | |
"\n", | |
" // Disable right mouse context menu.\n", | |
" $(this.rubberband_canvas).bind(\"contextmenu\",function(e){\n", | |
" return false;\n", | |
" });\n", | |
"\n", | |
" function set_focus () {\n", | |
" canvas.focus();\n", | |
" canvas_div.focus();\n", | |
" }\n", | |
"\n", | |
" window.setTimeout(set_focus, 100);\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._init_toolbar = function() {\n", | |
" var fig = this;\n", | |
"\n", | |
" var nav_element = $('<div/>')\n", | |
" nav_element.attr('style', 'width: 100%');\n", | |
" this.root.append(nav_element);\n", | |
"\n", | |
" // Define a callback function for later on.\n", | |
" function toolbar_event(event) {\n", | |
" return fig.toolbar_button_onclick(event['data']);\n", | |
" }\n", | |
" function toolbar_mouse_event(event) {\n", | |
" return fig.toolbar_button_onmouseover(event['data']);\n", | |
" }\n", | |
"\n", | |
" for(var toolbar_ind in mpl.toolbar_items) {\n", | |
" var name = mpl.toolbar_items[toolbar_ind][0];\n", | |
" var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", | |
" var image = mpl.toolbar_items[toolbar_ind][2];\n", | |
" var method_name = mpl.toolbar_items[toolbar_ind][3];\n", | |
"\n", | |
" if (!name) {\n", | |
" // put a spacer in here.\n", | |
" continue;\n", | |
" }\n", | |
" var button = $('<button/>');\n", | |
" button.addClass('ui-button ui-widget ui-state-default ui-corner-all ' +\n", | |
" 'ui-button-icon-only');\n", | |
" button.attr('role', 'button');\n", | |
" button.attr('aria-disabled', 'false');\n", | |
" button.click(method_name, toolbar_event);\n", | |
" button.mouseover(tooltip, toolbar_mouse_event);\n", | |
"\n", | |
" var icon_img = $('<span/>');\n", | |
" icon_img.addClass('ui-button-icon-primary ui-icon');\n", | |
" icon_img.addClass(image);\n", | |
" icon_img.addClass('ui-corner-all');\n", | |
"\n", | |
" var tooltip_span = $('<span/>');\n", | |
" tooltip_span.addClass('ui-button-text');\n", | |
" tooltip_span.html(tooltip);\n", | |
"\n", | |
" button.append(icon_img);\n", | |
" button.append(tooltip_span);\n", | |
"\n", | |
" nav_element.append(button);\n", | |
" }\n", | |
"\n", | |
" var fmt_picker_span = $('<span/>');\n", | |
"\n", | |
" var fmt_picker = $('<select/>');\n", | |
" fmt_picker.addClass('mpl-toolbar-option ui-widget ui-widget-content');\n", | |
" fmt_picker_span.append(fmt_picker);\n", | |
" nav_element.append(fmt_picker_span);\n", | |
" this.format_dropdown = fmt_picker[0];\n", | |
"\n", | |
" for (var ind in mpl.extensions) {\n", | |
" var fmt = mpl.extensions[ind];\n", | |
" var option = $(\n", | |
" '<option/>', {selected: fmt === mpl.default_extension}).html(fmt);\n", | |
" fmt_picker.append(option)\n", | |
" }\n", | |
"\n", | |
" // Add hover states to the ui-buttons\n", | |
" $( \".ui-button\" ).hover(\n", | |
" function() { $(this).addClass(\"ui-state-hover\");},\n", | |
" function() { $(this).removeClass(\"ui-state-hover\");}\n", | |
" );\n", | |
"\n", | |
" var status_bar = $('<span class=\"mpl-message\"/>');\n", | |
" nav_element.append(status_bar);\n", | |
" this.message = status_bar[0];\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.request_resize = function(x_pixels, y_pixels) {\n", | |
" // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n", | |
" // which will in turn request a refresh of the image.\n", | |
" this.send_message('resize', {'width': x_pixels, 'height': y_pixels});\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.send_message = function(type, properties) {\n", | |
" properties['type'] = type;\n", | |
" properties['figure_id'] = this.id;\n", | |
" this.ws.send(JSON.stringify(properties));\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.send_draw_message = function() {\n", | |
" if (!this.waiting) {\n", | |
" this.waiting = true;\n", | |
" this.ws.send(JSON.stringify({type: \"draw\", figure_id: this.id}));\n", | |
" }\n", | |
"}\n", | |
"\n", | |
"\n", | |
"mpl.figure.prototype.handle_save = function(fig, msg) {\n", | |
" var format_dropdown = fig.format_dropdown;\n", | |
" var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n", | |
" fig.ondownload(fig, format);\n", | |
"}\n", | |
"\n", | |
"\n", | |
"mpl.figure.prototype.handle_resize = function(fig, msg) {\n", | |
" var size = msg['size'];\n", | |
" if (size[0] != fig.canvas.width || size[1] != fig.canvas.height) {\n", | |
" fig._resize_canvas(size[0], size[1]);\n", | |
" fig.send_message(\"refresh\", {});\n", | |
" };\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.handle_rubberband = function(fig, msg) {\n", | |
" var x0 = msg['x0'];\n", | |
" var y0 = fig.canvas.height - msg['y0'];\n", | |
" var x1 = msg['x1'];\n", | |
" var y1 = fig.canvas.height - msg['y1'];\n", | |
" x0 = Math.floor(x0) + 0.5;\n", | |
" y0 = Math.floor(y0) + 0.5;\n", | |
" x1 = Math.floor(x1) + 0.5;\n", | |
" y1 = Math.floor(y1) + 0.5;\n", | |
" var min_x = Math.min(x0, x1);\n", | |
" var min_y = Math.min(y0, y1);\n", | |
" var width = Math.abs(x1 - x0);\n", | |
" var height = Math.abs(y1 - y0);\n", | |
"\n", | |
" fig.rubberband_context.clearRect(\n", | |
" 0, 0, fig.canvas.width, fig.canvas.height);\n", | |
"\n", | |
" fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.handle_figure_label = function(fig, msg) {\n", | |
" // Updates the figure title.\n", | |
" fig.header.textContent = msg['label'];\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.handle_cursor = function(fig, msg) {\n", | |
" var cursor = msg['cursor'];\n", | |
" switch(cursor)\n", | |
" {\n", | |
" case 0:\n", | |
" cursor = 'pointer';\n", | |
" break;\n", | |
" case 1:\n", | |
" cursor = 'default';\n", | |
" break;\n", | |
" case 2:\n", | |
" cursor = 'crosshair';\n", | |
" break;\n", | |
" case 3:\n", | |
" cursor = 'move';\n", | |
" break;\n", | |
" }\n", | |
" fig.rubberband_canvas.style.cursor = cursor;\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.handle_message = function(fig, msg) {\n", | |
" fig.message.textContent = msg['message'];\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.handle_draw = function(fig, msg) {\n", | |
" // Request the server to send over a new figure.\n", | |
" fig.send_draw_message();\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.handle_image_mode = function(fig, msg) {\n", | |
" fig.image_mode = msg['mode'];\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.updated_canvas_event = function() {\n", | |
" // Called whenever the canvas gets updated.\n", | |
" this.send_message(\"ack\", {});\n", | |
"}\n", | |
"\n", | |
"// A function to construct a web socket function for onmessage handling.\n", | |
"// Called in the figure constructor.\n", | |
"mpl.figure.prototype._make_on_message_function = function(fig) {\n", | |
" return function socket_on_message(evt) {\n", | |
" if (evt.data instanceof Blob) {\n", | |
" /* FIXME: We get \"Resource interpreted as Image but\n", | |
" * transferred with MIME type text/plain:\" errors on\n", | |
" * Chrome. But how to set the MIME type? It doesn't seem\n", | |
" * to be part of the websocket stream */\n", | |
" evt.data.type = \"image/png\";\n", | |
"\n", | |
" /* Free the memory for the previous frames */\n", | |
" if (fig.imageObj.src) {\n", | |
" (window.URL || window.webkitURL).revokeObjectURL(\n", | |
" fig.imageObj.src);\n", | |
" }\n", | |
"\n", | |
" fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n", | |
" evt.data);\n", | |
" fig.updated_canvas_event();\n", | |
" fig.waiting = false;\n", | |
" return;\n", | |
" }\n", | |
" else if (typeof evt.data === 'string' && evt.data.slice(0, 21) == \"data:image/png;base64\") {\n", | |
" fig.imageObj.src = evt.data;\n", | |
" fig.updated_canvas_event();\n", | |
" fig.waiting = false;\n", | |
" return;\n", | |
" }\n", | |
"\n", | |
" var msg = JSON.parse(evt.data);\n", | |
" var msg_type = msg['type'];\n", | |
"\n", | |
" // Call the \"handle_{type}\" callback, which takes\n", | |
" // the figure and JSON message as its only arguments.\n", | |
" try {\n", | |
" var callback = fig[\"handle_\" + msg_type];\n", | |
" } catch (e) {\n", | |
" console.log(\"No handler for the '\" + msg_type + \"' message type: \", msg);\n", | |
" return;\n", | |
" }\n", | |
"\n", | |
" if (callback) {\n", | |
" try {\n", | |
" // console.log(\"Handling '\" + msg_type + \"' message: \", msg);\n", | |
" callback(fig, msg);\n", | |
" } catch (e) {\n", | |
" console.log(\"Exception inside the 'handler_\" + msg_type + \"' callback:\", e, e.stack, msg);\n", | |
" }\n", | |
" }\n", | |
" };\n", | |
"}\n", | |
"\n", | |
"// from http://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\n", | |
"mpl.findpos = function(e) {\n", | |
" //this section is from http://www.quirksmode.org/js/events_properties.html\n", | |
" var targ;\n", | |
" if (!e)\n", | |
" e = window.event;\n", | |
" if (e.target)\n", | |
" targ = e.target;\n", | |
" else if (e.srcElement)\n", | |
" targ = e.srcElement;\n", | |
" if (targ.nodeType == 3) // defeat Safari bug\n", | |
" targ = targ.parentNode;\n", | |
"\n", | |
" // jQuery normalizes the pageX and pageY\n", | |
" // pageX,Y are the mouse positions relative to the document\n", | |
" // offset() returns the position of the element relative to the document\n", | |
" var x = e.pageX - $(targ).offset().left;\n", | |
" var y = e.pageY - $(targ).offset().top;\n", | |
"\n", | |
" return {\"x\": x, \"y\": y};\n", | |
"};\n", | |
"\n", | |
"/*\n", | |
" * return a copy of an object with only non-object keys\n", | |
" * we need this to avoid circular references\n", | |
" * http://stackoverflow.com/a/24161582/3208463\n", | |
" */\n", | |
"function simpleKeys (original) {\n", | |
" return Object.keys(original).reduce(function (obj, key) {\n", | |
" if (typeof original[key] !== 'object')\n", | |
" obj[key] = original[key]\n", | |
" return obj;\n", | |
" }, {});\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.mouse_event = function(event, name) {\n", | |
" var canvas_pos = mpl.findpos(event)\n", | |
"\n", | |
" if (name === 'button_press')\n", | |
" {\n", | |
" this.canvas.focus();\n", | |
" this.canvas_div.focus();\n", | |
" }\n", | |
"\n", | |
" var x = canvas_pos.x;\n", | |
" var y = canvas_pos.y;\n", | |
"\n", | |
" this.send_message(name, {x: x, y: y, button: event.button,\n", | |
" step: event.step,\n", | |
" guiEvent: simpleKeys(event)});\n", | |
"\n", | |
" /* This prevents the web browser from automatically changing to\n", | |
" * the text insertion cursor when the button is pressed. We want\n", | |
" * to control all of the cursor setting manually through the\n", | |
" * 'cursor' event from matplotlib */\n", | |
" event.preventDefault();\n", | |
" return false;\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._key_event_extra = function(event, name) {\n", | |
" // Handle any extra behaviour associated with a key event\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.key_event = function(event, name) {\n", | |
"\n", | |
" // Prevent repeat events\n", | |
" if (name == 'key_press')\n", | |
" {\n", | |
" if (event.which === this._key)\n", | |
" return;\n", | |
" else\n", | |
" this._key = event.which;\n", | |
" }\n", | |
" if (name == 'key_release')\n", | |
" this._key = null;\n", | |
"\n", | |
" var value = '';\n", | |
" if (event.ctrlKey && event.which != 17)\n", | |
" value += \"ctrl+\";\n", | |
" if (event.altKey && event.which != 18)\n", | |
" value += \"alt+\";\n", | |
" if (event.shiftKey && event.which != 16)\n", | |
" value += \"shift+\";\n", | |
"\n", | |
" value += 'k';\n", | |
" value += event.which.toString();\n", | |
"\n", | |
" this._key_event_extra(event, name);\n", | |
"\n", | |
" this.send_message(name, {key: value,\n", | |
" guiEvent: simpleKeys(event)});\n", | |
" return false;\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.toolbar_button_onclick = function(name) {\n", | |
" if (name == 'download') {\n", | |
" this.handle_save(this, null);\n", | |
" } else {\n", | |
" this.send_message(\"toolbar_button\", {name: name});\n", | |
" }\n", | |
"};\n", | |
"\n", | |
"mpl.figure.prototype.toolbar_button_onmouseover = function(tooltip) {\n", | |
" this.message.textContent = tooltip;\n", | |
"};\n", | |
"mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Pan axes with left mouse, zoom with right\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n", | |
"\n", | |
"mpl.extensions = [\"eps\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\"];\n", | |
"\n", | |
"mpl.default_extension = \"png\";var comm_websocket_adapter = function(comm) {\n", | |
" // Create a \"websocket\"-like object which calls the given IPython comm\n", | |
" // object with the appropriate methods. Currently this is a non binary\n", | |
" // socket, so there is still some room for performance tuning.\n", | |
" var ws = {};\n", | |
"\n", | |
" ws.close = function() {\n", | |
" comm.close()\n", | |
" };\n", | |
" ws.send = function(m) {\n", | |
" //console.log('sending', m);\n", | |
" comm.send(m);\n", | |
" };\n", | |
" // Register the callback with on_msg.\n", | |
" comm.on_msg(function(msg) {\n", | |
" //console.log('receiving', msg['content']['data'], msg);\n", | |
" // Pass the mpl event to the overriden (by mpl) onmessage function.\n", | |
" ws.onmessage(msg['content']['data'])\n", | |
" });\n", | |
" return ws;\n", | |
"}\n", | |
"\n", | |
"mpl.mpl_figure_comm = function(comm, msg) {\n", | |
" // This is the function which gets called when the mpl process\n", | |
" // starts-up an IPython Comm through the \"matplotlib\" channel.\n", | |
"\n", | |
" var id = msg.content.data.id;\n", | |
" // Get hold of the div created by the display call when the Comm\n", | |
" // socket was opened in Python.\n", | |
" var element = $(\"#\" + id);\n", | |
" var ws_proxy = comm_websocket_adapter(comm)\n", | |
"\n", | |
" function ondownload(figure, format) {\n", | |
" window.open(figure.imageObj.src);\n", | |
" }\n", | |
"\n", | |
" var fig = new mpl.figure(id, ws_proxy,\n", | |
" ondownload,\n", | |
" element.get(0));\n", | |
"\n", | |
" // Call onopen now - mpl needs it, as it is assuming we've passed it a real\n", | |
" // web socket which is closed, not our websocket->open comm proxy.\n", | |
" ws_proxy.onopen();\n", | |
"\n", | |
" fig.parent_element = element.get(0);\n", | |
" fig.cell_info = mpl.find_output_cell(\"<div id='\" + id + \"'></div>\");\n", | |
" if (!fig.cell_info) {\n", | |
" console.error(\"Failed to find cell for figure\", id, fig);\n", | |
" return;\n", | |
" }\n", | |
"\n", | |
" var output_index = fig.cell_info[2]\n", | |
" var cell = fig.cell_info[0];\n", | |
"\n", | |
"};\n", | |
"\n", | |
"mpl.figure.prototype.handle_close = function(fig, msg) {\n", | |
" fig.root.unbind('remove')\n", | |
"\n", | |
" // Update the output cell to use the data from the current canvas.\n", | |
" fig.push_to_output();\n", | |
" var dataURL = fig.canvas.toDataURL();\n", | |
" // Re-enable the keyboard manager in IPython - without this line, in FF,\n", | |
" // the notebook keyboard shortcuts fail.\n", | |
" IPython.keyboard_manager.enable()\n", | |
" $(fig.parent_element).html('<img src=\"' + dataURL + '\">');\n", | |
" fig.close_ws(fig, msg);\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.close_ws = function(fig, msg){\n", | |
" fig.send_message('closing', msg);\n", | |
" // fig.ws.close()\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.push_to_output = function(remove_interactive) {\n", | |
" // Turn the data on the canvas into data in the output cell.\n", | |
" var dataURL = this.canvas.toDataURL();\n", | |
" this.cell_info[1]['text/html'] = '<img src=\"' + dataURL + '\">';\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.updated_canvas_event = function() {\n", | |
" // Tell IPython that the notebook contents must change.\n", | |
" IPython.notebook.set_dirty(true);\n", | |
" this.send_message(\"ack\", {});\n", | |
" var fig = this;\n", | |
" // Wait a second, then push the new image to the DOM so\n", | |
" // that it is saved nicely (might be nice to debounce this).\n", | |
" setTimeout(function () { fig.push_to_output() }, 1000);\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._init_toolbar = function() {\n", | |
" var fig = this;\n", | |
"\n", | |
" var nav_element = $('<div/>')\n", | |
" nav_element.attr('style', 'width: 100%');\n", | |
" this.root.append(nav_element);\n", | |
"\n", | |
" // Define a callback function for later on.\n", | |
" function toolbar_event(event) {\n", | |
" return fig.toolbar_button_onclick(event['data']);\n", | |
" }\n", | |
" function toolbar_mouse_event(event) {\n", | |
" return fig.toolbar_button_onmouseover(event['data']);\n", | |
" }\n", | |
"\n", | |
" for(var toolbar_ind in mpl.toolbar_items){\n", | |
" var name = mpl.toolbar_items[toolbar_ind][0];\n", | |
" var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", | |
" var image = mpl.toolbar_items[toolbar_ind][2];\n", | |
" var method_name = mpl.toolbar_items[toolbar_ind][3];\n", | |
"\n", | |
" if (!name) { continue; };\n", | |
"\n", | |
" var button = $('<button class=\"btn btn-default\" href=\"#\" title=\"' + name + '\"><i class=\"fa ' + image + ' fa-lg\"></i></button>');\n", | |
" button.click(method_name, toolbar_event);\n", | |
" button.mouseover(tooltip, toolbar_mouse_event);\n", | |
" nav_element.append(button);\n", | |
" }\n", | |
"\n", | |
" // Add the status bar.\n", | |
" var status_bar = $('<span class=\"mpl-message\" style=\"text-align:right; float: right;\"/>');\n", | |
" nav_element.append(status_bar);\n", | |
" this.message = status_bar[0];\n", | |
"\n", | |
" // Add the close button to the window.\n", | |
" var buttongrp = $('<div class=\"btn-group inline pull-right\"></div>');\n", | |
" var button = $('<button class=\"btn btn-mini btn-primary\" href=\"#\" title=\"Stop Interaction\"><i class=\"fa fa-power-off icon-remove icon-large\"></i></button>');\n", | |
" button.click(function (evt) { fig.handle_close(fig, {}); } );\n", | |
" button.mouseover('Stop Interaction', toolbar_mouse_event);\n", | |
" buttongrp.append(button);\n", | |
" var titlebar = this.root.find($('.ui-dialog-titlebar'));\n", | |
" titlebar.prepend(buttongrp);\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._root_extra_style = function(el){\n", | |
" var fig = this\n", | |
" el.on(\"remove\", function(){\n", | |
"\tfig.close_ws(fig, {});\n", | |
" });\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._canvas_extra_style = function(el){\n", | |
" // this is important to make the div 'focusable\n", | |
" el.attr('tabindex', 0)\n", | |
" // reach out to IPython and tell the keyboard manager to turn it's self\n", | |
" // off when our div gets focus\n", | |
"\n", | |
" // location in version 3\n", | |
" if (IPython.notebook.keyboard_manager) {\n", | |
" IPython.notebook.keyboard_manager.register_events(el);\n", | |
" }\n", | |
" else {\n", | |
" // location in version 2\n", | |
" IPython.keyboard_manager.register_events(el);\n", | |
" }\n", | |
"\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._key_event_extra = function(event, name) {\n", | |
" var manager = IPython.notebook.keyboard_manager;\n", | |
" if (!manager)\n", | |
" manager = IPython.keyboard_manager;\n", | |
"\n", | |
" // Check for shift+enter\n", | |
" if (event.shiftKey && event.which == 13) {\n", | |
" this.canvas_div.blur();\n", | |
" // select the cell after this one\n", | |
" var index = IPython.notebook.find_cell_index(this.cell_info[0]);\n", | |
" IPython.notebook.select(index + 1);\n", | |
" }\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.handle_save = function(fig, msg) {\n", | |
" fig.ondownload(fig, null);\n", | |
"}\n", | |
"\n", | |
"\n", | |
"mpl.find_output_cell = function(html_output) {\n", | |
" // Return the cell and output element which can be found *uniquely* in the notebook.\n", | |
" // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n", | |
" // IPython event is triggered only after the cells have been serialised, which for\n", | |
" // our purposes (turning an active figure into a static one), is too late.\n", | |
" var cells = IPython.notebook.get_cells();\n", | |
" var ncells = cells.length;\n", | |
" for (var i=0; i<ncells; i++) {\n", | |
" var cell = cells[i];\n", | |
" if (cell.cell_type === 'code'){\n", | |
" for (var j=0; j<cell.output_area.outputs.length; j++) {\n", | |
" var data = cell.output_area.outputs[j];\n", | |
" if (data.data) {\n", | |
" // IPython >= 3 moved mimebundle to data attribute of output\n", | |
" data = data.data;\n", | |
" }\n", | |
" if (data['text/html'] == html_output) {\n", | |
" return [cell, data, j];\n", | |
" }\n", | |
" }\n", | |
" }\n", | |
" }\n", | |
"}\n", | |
"\n", | |
"// Register the function which deals with the matplotlib target/channel.\n", | |
"// The kernel may be null if the page has been refreshed.\n", | |
"if (IPython.notebook.kernel != null) {\n", | |
" IPython.notebook.kernel.comm_manager.register_target('matplotlib', mpl.mpl_figure_comm);\n", | |
"}\n" | |
], | |
"text/plain": [ | |
"<IPython.core.display.Javascript object>" | |
] | |
}, | |
"metadata": {}, | |
"output_type": "display_data" | |
}, | |
{ | |
"data": { | |
"text/html": [ | |
"<img src=\"data:image/png;base64,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\">" | |
], | |
"text/plain": [ | |
"<IPython.core.display.HTML object>" | |
] | |
}, | |
"metadata": {}, | |
"output_type": "display_data" | |
} | |
], | |
"source": [ | |
"sns.pointplot(x='month', y='num_tokens', data=grouped_msgs.reset_index(), hue='sender')\n", | |
"sns.plt.show()" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"## Multi-Index" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"When using *groupby* the *as_index* parameter specifies if the columns used for grouping should be used as indexes of the resulting dataframe, if set to *False* such columns will otherwise end up to be part of the other set of columns. So if you group by multiple values, and keep them as index, you will end up with a multi-index dataframe, like the one in our previous example (sender and month). \n", | |
"Understanding and being able to manage [multi-indexes](http://pandas.pydata.org/pandas-docs/stable/advanced.html) is a fundamental requirement when working with more complex dataset, so I will now quickly play with them for a multiplot scenario.\n", | |
"\n", | |
"Now that you have your resampled data, what do you want to plot?\n", | |
"We can reuse this dataframe for different possible plots/queries. We might be interested in the statistics for a specific sender, specific month, combination of the two, or just the overall values.\n", | |
"\n", | |
"With single index we can access my sender with *.loc['Donnie']*, now with a multi-index we use the *.xs* method instead." | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 11, | |
"metadata": { | |
"collapsed": false | |
}, | |
"outputs": [ | |
{ | |
"data": { | |
"application/javascript": [ | |
"/* Put everything inside the global mpl namespace */\n", | |
"window.mpl = {};\n", | |
"\n", | |
"mpl.get_websocket_type = function() {\n", | |
" if (typeof(WebSocket) !== 'undefined') {\n", | |
" return WebSocket;\n", | |
" } else if (typeof(MozWebSocket) !== 'undefined') {\n", | |
" return MozWebSocket;\n", | |
" } else {\n", | |
" alert('Your browser does not have WebSocket support.' +\n", | |
" 'Please try Chrome, Safari or Firefox ≥ 6. ' +\n", | |
" 'Firefox 4 and 5 are also supported but you ' +\n", | |
" 'have to enable WebSockets in about:config.');\n", | |
" };\n", | |
"}\n", | |
"\n", | |
"mpl.figure = function(figure_id, websocket, ondownload, parent_element) {\n", | |
" this.id = figure_id;\n", | |
"\n", | |
" this.ws = websocket;\n", | |
"\n", | |
" this.supports_binary = (this.ws.binaryType != undefined);\n", | |
"\n", | |
" if (!this.supports_binary) {\n", | |
" var warnings = document.getElementById(\"mpl-warnings\");\n", | |
" if (warnings) {\n", | |
" warnings.style.display = 'block';\n", | |
" warnings.textContent = (\n", | |
" \"This browser does not support binary websocket messages. \" +\n", | |
" \"Performance may be slow.\");\n", | |
" }\n", | |
" }\n", | |
"\n", | |
" this.imageObj = new Image();\n", | |
"\n", | |
" this.context = undefined;\n", | |
" this.message = undefined;\n", | |
" this.canvas = undefined;\n", | |
" this.rubberband_canvas = undefined;\n", | |
" this.rubberband_context = undefined;\n", | |
" this.format_dropdown = undefined;\n", | |
"\n", | |
" this.image_mode = 'full';\n", | |
"\n", | |
" this.root = $('<div/>');\n", | |
" this._root_extra_style(this.root)\n", | |
" this.root.attr('style', 'display: inline-block');\n", | |
"\n", | |
" $(parent_element).append(this.root);\n", | |
"\n", | |
" this._init_header(this);\n", | |
" this._init_canvas(this);\n", | |
" this._init_toolbar(this);\n", | |
"\n", | |
" var fig = this;\n", | |
"\n", | |
" this.waiting = false;\n", | |
"\n", | |
" this.ws.onopen = function () {\n", | |
" fig.send_message(\"supports_binary\", {value: fig.supports_binary});\n", | |
" fig.send_message(\"send_image_mode\", {});\n", | |
" fig.send_message(\"refresh\", {});\n", | |
" }\n", | |
"\n", | |
" this.imageObj.onload = function() {\n", | |
" if (fig.image_mode == 'full') {\n", | |
" // Full images could contain transparency (where diff images\n", | |
" // almost always do), so we need to clear the canvas so that\n", | |
" // there is no ghosting.\n", | |
" fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n", | |
" }\n", | |
" fig.context.drawImage(fig.imageObj, 0, 0);\n", | |
" };\n", | |
"\n", | |
" this.imageObj.onunload = function() {\n", | |
" this.ws.close();\n", | |
" }\n", | |
"\n", | |
" this.ws.onmessage = this._make_on_message_function(this);\n", | |
"\n", | |
" this.ondownload = ondownload;\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._init_header = function() {\n", | |
" var titlebar = $(\n", | |
" '<div class=\"ui-dialog-titlebar ui-widget-header ui-corner-all ' +\n", | |
" 'ui-helper-clearfix\"/>');\n", | |
" var titletext = $(\n", | |
" '<div class=\"ui-dialog-title\" style=\"width: 100%; ' +\n", | |
" 'text-align: center; padding: 3px;\"/>');\n", | |
" titlebar.append(titletext)\n", | |
" this.root.append(titlebar);\n", | |
" this.header = titletext[0];\n", | |
"}\n", | |
"\n", | |
"\n", | |
"\n", | |
"mpl.figure.prototype._canvas_extra_style = function(canvas_div) {\n", | |
"\n", | |
"}\n", | |
"\n", | |
"\n", | |
"mpl.figure.prototype._root_extra_style = function(canvas_div) {\n", | |
"\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._init_canvas = function() {\n", | |
" var fig = this;\n", | |
"\n", | |
" var canvas_div = $('<div/>');\n", | |
"\n", | |
" canvas_div.attr('style', 'position: relative; clear: both; outline: 0');\n", | |
"\n", | |
" function canvas_keyboard_event(event) {\n", | |
" return fig.key_event(event, event['data']);\n", | |
" }\n", | |
"\n", | |
" canvas_div.keydown('key_press', canvas_keyboard_event);\n", | |
" canvas_div.keyup('key_release', canvas_keyboard_event);\n", | |
" this.canvas_div = canvas_div\n", | |
" this._canvas_extra_style(canvas_div)\n", | |
" this.root.append(canvas_div);\n", | |
"\n", | |
" var canvas = $('<canvas/>');\n", | |
" canvas.addClass('mpl-canvas');\n", | |
" canvas.attr('style', \"left: 0; top: 0; z-index: 0; outline: 0\")\n", | |
"\n", | |
" this.canvas = canvas[0];\n", | |
" this.context = canvas[0].getContext(\"2d\");\n", | |
"\n", | |
" var rubberband = $('<canvas/>');\n", | |
" rubberband.attr('style', \"position: absolute; left: 0; top: 0; z-index: 1;\")\n", | |
"\n", | |
" var pass_mouse_events = true;\n", | |
"\n", | |
" canvas_div.resizable({\n", | |
" start: function(event, ui) {\n", | |
" pass_mouse_events = false;\n", | |
" },\n", | |
" resize: function(event, ui) {\n", | |
" fig.request_resize(ui.size.width, ui.size.height);\n", | |
" },\n", | |
" stop: function(event, ui) {\n", | |
" pass_mouse_events = true;\n", | |
" fig.request_resize(ui.size.width, ui.size.height);\n", | |
" },\n", | |
" });\n", | |
"\n", | |
" function mouse_event_fn(event) {\n", | |
" if (pass_mouse_events)\n", | |
" return fig.mouse_event(event, event['data']);\n", | |
" }\n", | |
"\n", | |
" rubberband.mousedown('button_press', mouse_event_fn);\n", | |
" rubberband.mouseup('button_release', mouse_event_fn);\n", | |
" // Throttle sequential mouse events to 1 every 20ms.\n", | |
" rubberband.mousemove('motion_notify', mouse_event_fn);\n", | |
"\n", | |
" rubberband.mouseenter('figure_enter', mouse_event_fn);\n", | |
" rubberband.mouseleave('figure_leave', mouse_event_fn);\n", | |
"\n", | |
" canvas_div.on(\"wheel\", function (event) {\n", | |
" event = event.originalEvent;\n", | |
" event['data'] = 'scroll'\n", | |
" if (event.deltaY < 0) {\n", | |
" event.step = 1;\n", | |
" } else {\n", | |
" event.step = -1;\n", | |
" }\n", | |
" mouse_event_fn(event);\n", | |
" });\n", | |
"\n", | |
" canvas_div.append(canvas);\n", | |
" canvas_div.append(rubberband);\n", | |
"\n", | |
" this.rubberband = rubberband;\n", | |
" this.rubberband_canvas = rubberband[0];\n", | |
" this.rubberband_context = rubberband[0].getContext(\"2d\");\n", | |
" this.rubberband_context.strokeStyle = \"#000000\";\n", | |
"\n", | |
" this._resize_canvas = function(width, height) {\n", | |
" // Keep the size of the canvas, canvas container, and rubber band\n", | |
" // canvas in synch.\n", | |
" canvas_div.css('width', width)\n", | |
" canvas_div.css('height', height)\n", | |
"\n", | |
" canvas.attr('width', width);\n", | |
" canvas.attr('height', height);\n", | |
"\n", | |
" rubberband.attr('width', width);\n", | |
" rubberband.attr('height', height);\n", | |
" }\n", | |
"\n", | |
" // Set the figure to an initial 600x600px, this will subsequently be updated\n", | |
" // upon first draw.\n", | |
" this._resize_canvas(600, 600);\n", | |
"\n", | |
" // Disable right mouse context menu.\n", | |
" $(this.rubberband_canvas).bind(\"contextmenu\",function(e){\n", | |
" return false;\n", | |
" });\n", | |
"\n", | |
" function set_focus () {\n", | |
" canvas.focus();\n", | |
" canvas_div.focus();\n", | |
" }\n", | |
"\n", | |
" window.setTimeout(set_focus, 100);\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._init_toolbar = function() {\n", | |
" var fig = this;\n", | |
"\n", | |
" var nav_element = $('<div/>')\n", | |
" nav_element.attr('style', 'width: 100%');\n", | |
" this.root.append(nav_element);\n", | |
"\n", | |
" // Define a callback function for later on.\n", | |
" function toolbar_event(event) {\n", | |
" return fig.toolbar_button_onclick(event['data']);\n", | |
" }\n", | |
" function toolbar_mouse_event(event) {\n", | |
" return fig.toolbar_button_onmouseover(event['data']);\n", | |
" }\n", | |
"\n", | |
" for(var toolbar_ind in mpl.toolbar_items) {\n", | |
" var name = mpl.toolbar_items[toolbar_ind][0];\n", | |
" var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", | |
" var image = mpl.toolbar_items[toolbar_ind][2];\n", | |
" var method_name = mpl.toolbar_items[toolbar_ind][3];\n", | |
"\n", | |
" if (!name) {\n", | |
" // put a spacer in here.\n", | |
" continue;\n", | |
" }\n", | |
" var button = $('<button/>');\n", | |
" button.addClass('ui-button ui-widget ui-state-default ui-corner-all ' +\n", | |
" 'ui-button-icon-only');\n", | |
" button.attr('role', 'button');\n", | |
" button.attr('aria-disabled', 'false');\n", | |
" button.click(method_name, toolbar_event);\n", | |
" button.mouseover(tooltip, toolbar_mouse_event);\n", | |
"\n", | |
" var icon_img = $('<span/>');\n", | |
" icon_img.addClass('ui-button-icon-primary ui-icon');\n", | |
" icon_img.addClass(image);\n", | |
" icon_img.addClass('ui-corner-all');\n", | |
"\n", | |
" var tooltip_span = $('<span/>');\n", | |
" tooltip_span.addClass('ui-button-text');\n", | |
" tooltip_span.html(tooltip);\n", | |
"\n", | |
" button.append(icon_img);\n", | |
" button.append(tooltip_span);\n", | |
"\n", | |
" nav_element.append(button);\n", | |
" }\n", | |
"\n", | |
" var fmt_picker_span = $('<span/>');\n", | |
"\n", | |
" var fmt_picker = $('<select/>');\n", | |
" fmt_picker.addClass('mpl-toolbar-option ui-widget ui-widget-content');\n", | |
" fmt_picker_span.append(fmt_picker);\n", | |
" nav_element.append(fmt_picker_span);\n", | |
" this.format_dropdown = fmt_picker[0];\n", | |
"\n", | |
" for (var ind in mpl.extensions) {\n", | |
" var fmt = mpl.extensions[ind];\n", | |
" var option = $(\n", | |
" '<option/>', {selected: fmt === mpl.default_extension}).html(fmt);\n", | |
" fmt_picker.append(option)\n", | |
" }\n", | |
"\n", | |
" // Add hover states to the ui-buttons\n", | |
" $( \".ui-button\" ).hover(\n", | |
" function() { $(this).addClass(\"ui-state-hover\");},\n", | |
" function() { $(this).removeClass(\"ui-state-hover\");}\n", | |
" );\n", | |
"\n", | |
" var status_bar = $('<span class=\"mpl-message\"/>');\n", | |
" nav_element.append(status_bar);\n", | |
" this.message = status_bar[0];\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.request_resize = function(x_pixels, y_pixels) {\n", | |
" // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n", | |
" // which will in turn request a refresh of the image.\n", | |
" this.send_message('resize', {'width': x_pixels, 'height': y_pixels});\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.send_message = function(type, properties) {\n", | |
" properties['type'] = type;\n", | |
" properties['figure_id'] = this.id;\n", | |
" this.ws.send(JSON.stringify(properties));\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.send_draw_message = function() {\n", | |
" if (!this.waiting) {\n", | |
" this.waiting = true;\n", | |
" this.ws.send(JSON.stringify({type: \"draw\", figure_id: this.id}));\n", | |
" }\n", | |
"}\n", | |
"\n", | |
"\n", | |
"mpl.figure.prototype.handle_save = function(fig, msg) {\n", | |
" var format_dropdown = fig.format_dropdown;\n", | |
" var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n", | |
" fig.ondownload(fig, format);\n", | |
"}\n", | |
"\n", | |
"\n", | |
"mpl.figure.prototype.handle_resize = function(fig, msg) {\n", | |
" var size = msg['size'];\n", | |
" if (size[0] != fig.canvas.width || size[1] != fig.canvas.height) {\n", | |
" fig._resize_canvas(size[0], size[1]);\n", | |
" fig.send_message(\"refresh\", {});\n", | |
" };\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.handle_rubberband = function(fig, msg) {\n", | |
" var x0 = msg['x0'];\n", | |
" var y0 = fig.canvas.height - msg['y0'];\n", | |
" var x1 = msg['x1'];\n", | |
" var y1 = fig.canvas.height - msg['y1'];\n", | |
" x0 = Math.floor(x0) + 0.5;\n", | |
" y0 = Math.floor(y0) + 0.5;\n", | |
" x1 = Math.floor(x1) + 0.5;\n", | |
" y1 = Math.floor(y1) + 0.5;\n", | |
" var min_x = Math.min(x0, x1);\n", | |
" var min_y = Math.min(y0, y1);\n", | |
" var width = Math.abs(x1 - x0);\n", | |
" var height = Math.abs(y1 - y0);\n", | |
"\n", | |
" fig.rubberband_context.clearRect(\n", | |
" 0, 0, fig.canvas.width, fig.canvas.height);\n", | |
"\n", | |
" fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.handle_figure_label = function(fig, msg) {\n", | |
" // Updates the figure title.\n", | |
" fig.header.textContent = msg['label'];\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.handle_cursor = function(fig, msg) {\n", | |
" var cursor = msg['cursor'];\n", | |
" switch(cursor)\n", | |
" {\n", | |
" case 0:\n", | |
" cursor = 'pointer';\n", | |
" break;\n", | |
" case 1:\n", | |
" cursor = 'default';\n", | |
" break;\n", | |
" case 2:\n", | |
" cursor = 'crosshair';\n", | |
" break;\n", | |
" case 3:\n", | |
" cursor = 'move';\n", | |
" break;\n", | |
" }\n", | |
" fig.rubberband_canvas.style.cursor = cursor;\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.handle_message = function(fig, msg) {\n", | |
" fig.message.textContent = msg['message'];\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.handle_draw = function(fig, msg) {\n", | |
" // Request the server to send over a new figure.\n", | |
" fig.send_draw_message();\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.handle_image_mode = function(fig, msg) {\n", | |
" fig.image_mode = msg['mode'];\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.updated_canvas_event = function() {\n", | |
" // Called whenever the canvas gets updated.\n", | |
" this.send_message(\"ack\", {});\n", | |
"}\n", | |
"\n", | |
"// A function to construct a web socket function for onmessage handling.\n", | |
"// Called in the figure constructor.\n", | |
"mpl.figure.prototype._make_on_message_function = function(fig) {\n", | |
" return function socket_on_message(evt) {\n", | |
" if (evt.data instanceof Blob) {\n", | |
" /* FIXME: We get \"Resource interpreted as Image but\n", | |
" * transferred with MIME type text/plain:\" errors on\n", | |
" * Chrome. But how to set the MIME type? It doesn't seem\n", | |
" * to be part of the websocket stream */\n", | |
" evt.data.type = \"image/png\";\n", | |
"\n", | |
" /* Free the memory for the previous frames */\n", | |
" if (fig.imageObj.src) {\n", | |
" (window.URL || window.webkitURL).revokeObjectURL(\n", | |
" fig.imageObj.src);\n", | |
" }\n", | |
"\n", | |
" fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n", | |
" evt.data);\n", | |
" fig.updated_canvas_event();\n", | |
" fig.waiting = false;\n", | |
" return;\n", | |
" }\n", | |
" else if (typeof evt.data === 'string' && evt.data.slice(0, 21) == \"data:image/png;base64\") {\n", | |
" fig.imageObj.src = evt.data;\n", | |
" fig.updated_canvas_event();\n", | |
" fig.waiting = false;\n", | |
" return;\n", | |
" }\n", | |
"\n", | |
" var msg = JSON.parse(evt.data);\n", | |
" var msg_type = msg['type'];\n", | |
"\n", | |
" // Call the \"handle_{type}\" callback, which takes\n", | |
" // the figure and JSON message as its only arguments.\n", | |
" try {\n", | |
" var callback = fig[\"handle_\" + msg_type];\n", | |
" } catch (e) {\n", | |
" console.log(\"No handler for the '\" + msg_type + \"' message type: \", msg);\n", | |
" return;\n", | |
" }\n", | |
"\n", | |
" if (callback) {\n", | |
" try {\n", | |
" // console.log(\"Handling '\" + msg_type + \"' message: \", msg);\n", | |
" callback(fig, msg);\n", | |
" } catch (e) {\n", | |
" console.log(\"Exception inside the 'handler_\" + msg_type + \"' callback:\", e, e.stack, msg);\n", | |
" }\n", | |
" }\n", | |
" };\n", | |
"}\n", | |
"\n", | |
"// from http://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\n", | |
"mpl.findpos = function(e) {\n", | |
" //this section is from http://www.quirksmode.org/js/events_properties.html\n", | |
" var targ;\n", | |
" if (!e)\n", | |
" e = window.event;\n", | |
" if (e.target)\n", | |
" targ = e.target;\n", | |
" else if (e.srcElement)\n", | |
" targ = e.srcElement;\n", | |
" if (targ.nodeType == 3) // defeat Safari bug\n", | |
" targ = targ.parentNode;\n", | |
"\n", | |
" // jQuery normalizes the pageX and pageY\n", | |
" // pageX,Y are the mouse positions relative to the document\n", | |
" // offset() returns the position of the element relative to the document\n", | |
" var x = e.pageX - $(targ).offset().left;\n", | |
" var y = e.pageY - $(targ).offset().top;\n", | |
"\n", | |
" return {\"x\": x, \"y\": y};\n", | |
"};\n", | |
"\n", | |
"/*\n", | |
" * return a copy of an object with only non-object keys\n", | |
" * we need this to avoid circular references\n", | |
" * http://stackoverflow.com/a/24161582/3208463\n", | |
" */\n", | |
"function simpleKeys (original) {\n", | |
" return Object.keys(original).reduce(function (obj, key) {\n", | |
" if (typeof original[key] !== 'object')\n", | |
" obj[key] = original[key]\n", | |
" return obj;\n", | |
" }, {});\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.mouse_event = function(event, name) {\n", | |
" var canvas_pos = mpl.findpos(event)\n", | |
"\n", | |
" if (name === 'button_press')\n", | |
" {\n", | |
" this.canvas.focus();\n", | |
" this.canvas_div.focus();\n", | |
" }\n", | |
"\n", | |
" var x = canvas_pos.x;\n", | |
" var y = canvas_pos.y;\n", | |
"\n", | |
" this.send_message(name, {x: x, y: y, button: event.button,\n", | |
" step: event.step,\n", | |
" guiEvent: simpleKeys(event)});\n", | |
"\n", | |
" /* This prevents the web browser from automatically changing to\n", | |
" * the text insertion cursor when the button is pressed. We want\n", | |
" * to control all of the cursor setting manually through the\n", | |
" * 'cursor' event from matplotlib */\n", | |
" event.preventDefault();\n", | |
" return false;\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._key_event_extra = function(event, name) {\n", | |
" // Handle any extra behaviour associated with a key event\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.key_event = function(event, name) {\n", | |
"\n", | |
" // Prevent repeat events\n", | |
" if (name == 'key_press')\n", | |
" {\n", | |
" if (event.which === this._key)\n", | |
" return;\n", | |
" else\n", | |
" this._key = event.which;\n", | |
" }\n", | |
" if (name == 'key_release')\n", | |
" this._key = null;\n", | |
"\n", | |
" var value = '';\n", | |
" if (event.ctrlKey && event.which != 17)\n", | |
" value += \"ctrl+\";\n", | |
" if (event.altKey && event.which != 18)\n", | |
" value += \"alt+\";\n", | |
" if (event.shiftKey && event.which != 16)\n", | |
" value += \"shift+\";\n", | |
"\n", | |
" value += 'k';\n", | |
" value += event.which.toString();\n", | |
"\n", | |
" this._key_event_extra(event, name);\n", | |
"\n", | |
" this.send_message(name, {key: value,\n", | |
" guiEvent: simpleKeys(event)});\n", | |
" return false;\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.toolbar_button_onclick = function(name) {\n", | |
" if (name == 'download') {\n", | |
" this.handle_save(this, null);\n", | |
" } else {\n", | |
" this.send_message(\"toolbar_button\", {name: name});\n", | |
" }\n", | |
"};\n", | |
"\n", | |
"mpl.figure.prototype.toolbar_button_onmouseover = function(tooltip) {\n", | |
" this.message.textContent = tooltip;\n", | |
"};\n", | |
"mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Pan axes with left mouse, zoom with right\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n", | |
"\n", | |
"mpl.extensions = [\"eps\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\"];\n", | |
"\n", | |
"mpl.default_extension = \"png\";var comm_websocket_adapter = function(comm) {\n", | |
" // Create a \"websocket\"-like object which calls the given IPython comm\n", | |
" // object with the appropriate methods. Currently this is a non binary\n", | |
" // socket, so there is still some room for performance tuning.\n", | |
" var ws = {};\n", | |
"\n", | |
" ws.close = function() {\n", | |
" comm.close()\n", | |
" };\n", | |
" ws.send = function(m) {\n", | |
" //console.log('sending', m);\n", | |
" comm.send(m);\n", | |
" };\n", | |
" // Register the callback with on_msg.\n", | |
" comm.on_msg(function(msg) {\n", | |
" //console.log('receiving', msg['content']['data'], msg);\n", | |
" // Pass the mpl event to the overriden (by mpl) onmessage function.\n", | |
" ws.onmessage(msg['content']['data'])\n", | |
" });\n", | |
" return ws;\n", | |
"}\n", | |
"\n", | |
"mpl.mpl_figure_comm = function(comm, msg) {\n", | |
" // This is the function which gets called when the mpl process\n", | |
" // starts-up an IPython Comm through the \"matplotlib\" channel.\n", | |
"\n", | |
" var id = msg.content.data.id;\n", | |
" // Get hold of the div created by the display call when the Comm\n", | |
" // socket was opened in Python.\n", | |
" var element = $(\"#\" + id);\n", | |
" var ws_proxy = comm_websocket_adapter(comm)\n", | |
"\n", | |
" function ondownload(figure, format) {\n", | |
" window.open(figure.imageObj.src);\n", | |
" }\n", | |
"\n", | |
" var fig = new mpl.figure(id, ws_proxy,\n", | |
" ondownload,\n", | |
" element.get(0));\n", | |
"\n", | |
" // Call onopen now - mpl needs it, as it is assuming we've passed it a real\n", | |
" // web socket which is closed, not our websocket->open comm proxy.\n", | |
" ws_proxy.onopen();\n", | |
"\n", | |
" fig.parent_element = element.get(0);\n", | |
" fig.cell_info = mpl.find_output_cell(\"<div id='\" + id + \"'></div>\");\n", | |
" if (!fig.cell_info) {\n", | |
" console.error(\"Failed to find cell for figure\", id, fig);\n", | |
" return;\n", | |
" }\n", | |
"\n", | |
" var output_index = fig.cell_info[2]\n", | |
" var cell = fig.cell_info[0];\n", | |
"\n", | |
"};\n", | |
"\n", | |
"mpl.figure.prototype.handle_close = function(fig, msg) {\n", | |
" fig.root.unbind('remove')\n", | |
"\n", | |
" // Update the output cell to use the data from the current canvas.\n", | |
" fig.push_to_output();\n", | |
" var dataURL = fig.canvas.toDataURL();\n", | |
" // Re-enable the keyboard manager in IPython - without this line, in FF,\n", | |
" // the notebook keyboard shortcuts fail.\n", | |
" IPython.keyboard_manager.enable()\n", | |
" $(fig.parent_element).html('<img src=\"' + dataURL + '\">');\n", | |
" fig.close_ws(fig, msg);\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.close_ws = function(fig, msg){\n", | |
" fig.send_message('closing', msg);\n", | |
" // fig.ws.close()\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.push_to_output = function(remove_interactive) {\n", | |
" // Turn the data on the canvas into data in the output cell.\n", | |
" var dataURL = this.canvas.toDataURL();\n", | |
" this.cell_info[1]['text/html'] = '<img src=\"' + dataURL + '\">';\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.updated_canvas_event = function() {\n", | |
" // Tell IPython that the notebook contents must change.\n", | |
" IPython.notebook.set_dirty(true);\n", | |
" this.send_message(\"ack\", {});\n", | |
" var fig = this;\n", | |
" // Wait a second, then push the new image to the DOM so\n", | |
" // that it is saved nicely (might be nice to debounce this).\n", | |
" setTimeout(function () { fig.push_to_output() }, 1000);\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._init_toolbar = function() {\n", | |
" var fig = this;\n", | |
"\n", | |
" var nav_element = $('<div/>')\n", | |
" nav_element.attr('style', 'width: 100%');\n", | |
" this.root.append(nav_element);\n", | |
"\n", | |
" // Define a callback function for later on.\n", | |
" function toolbar_event(event) {\n", | |
" return fig.toolbar_button_onclick(event['data']);\n", | |
" }\n", | |
" function toolbar_mouse_event(event) {\n", | |
" return fig.toolbar_button_onmouseover(event['data']);\n", | |
" }\n", | |
"\n", | |
" for(var toolbar_ind in mpl.toolbar_items){\n", | |
" var name = mpl.toolbar_items[toolbar_ind][0];\n", | |
" var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", | |
" var image = mpl.toolbar_items[toolbar_ind][2];\n", | |
" var method_name = mpl.toolbar_items[toolbar_ind][3];\n", | |
"\n", | |
" if (!name) { continue; };\n", | |
"\n", | |
" var button = $('<button class=\"btn btn-default\" href=\"#\" title=\"' + name + '\"><i class=\"fa ' + image + ' fa-lg\"></i></button>');\n", | |
" button.click(method_name, toolbar_event);\n", | |
" button.mouseover(tooltip, toolbar_mouse_event);\n", | |
" nav_element.append(button);\n", | |
" }\n", | |
"\n", | |
" // Add the status bar.\n", | |
" var status_bar = $('<span class=\"mpl-message\" style=\"text-align:right; float: right;\"/>');\n", | |
" nav_element.append(status_bar);\n", | |
" this.message = status_bar[0];\n", | |
"\n", | |
" // Add the close button to the window.\n", | |
" var buttongrp = $('<div class=\"btn-group inline pull-right\"></div>');\n", | |
" var button = $('<button class=\"btn btn-mini btn-primary\" href=\"#\" title=\"Stop Interaction\"><i class=\"fa fa-power-off icon-remove icon-large\"></i></button>');\n", | |
" button.click(function (evt) { fig.handle_close(fig, {}); } );\n", | |
" button.mouseover('Stop Interaction', toolbar_mouse_event);\n", | |
" buttongrp.append(button);\n", | |
" var titlebar = this.root.find($('.ui-dialog-titlebar'));\n", | |
" titlebar.prepend(buttongrp);\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._root_extra_style = function(el){\n", | |
" var fig = this\n", | |
" el.on(\"remove\", function(){\n", | |
"\tfig.close_ws(fig, {});\n", | |
" });\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._canvas_extra_style = function(el){\n", | |
" // this is important to make the div 'focusable\n", | |
" el.attr('tabindex', 0)\n", | |
" // reach out to IPython and tell the keyboard manager to turn it's self\n", | |
" // off when our div gets focus\n", | |
"\n", | |
" // location in version 3\n", | |
" if (IPython.notebook.keyboard_manager) {\n", | |
" IPython.notebook.keyboard_manager.register_events(el);\n", | |
" }\n", | |
" else {\n", | |
" // location in version 2\n", | |
" IPython.keyboard_manager.register_events(el);\n", | |
" }\n", | |
"\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._key_event_extra = function(event, name) {\n", | |
" var manager = IPython.notebook.keyboard_manager;\n", | |
" if (!manager)\n", | |
" manager = IPython.keyboard_manager;\n", | |
"\n", | |
" // Check for shift+enter\n", | |
" if (event.shiftKey && event.which == 13) {\n", | |
" this.canvas_div.blur();\n", | |
" // select the cell after this one\n", | |
" var index = IPython.notebook.find_cell_index(this.cell_info[0]);\n", | |
" IPython.notebook.select(index + 1);\n", | |
" }\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.handle_save = function(fig, msg) {\n", | |
" fig.ondownload(fig, null);\n", | |
"}\n", | |
"\n", | |
"\n", | |
"mpl.find_output_cell = function(html_output) {\n", | |
" // Return the cell and output element which can be found *uniquely* in the notebook.\n", | |
" // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n", | |
" // IPython event is triggered only after the cells have been serialised, which for\n", | |
" // our purposes (turning an active figure into a static one), is too late.\n", | |
" var cells = IPython.notebook.get_cells();\n", | |
" var ncells = cells.length;\n", | |
" for (var i=0; i<ncells; i++) {\n", | |
" var cell = cells[i];\n", | |
" if (cell.cell_type === 'code'){\n", | |
" for (var j=0; j<cell.output_area.outputs.length; j++) {\n", | |
" var data = cell.output_area.outputs[j];\n", | |
" if (data.data) {\n", | |
" // IPython >= 3 moved mimebundle to data attribute of output\n", | |
" data = data.data;\n", | |
" }\n", | |
" if (data['text/html'] == html_output) {\n", | |
" return [cell, data, j];\n", | |
" }\n", | |
" }\n", | |
" }\n", | |
" }\n", | |
"}\n", | |
"\n", | |
"// Register the function which deals with the matplotlib target/channel.\n", | |
"// The kernel may be null if the page has been refreshed.\n", | |
"if (IPython.notebook.kernel != null) {\n", | |
" IPython.notebook.kernel.comm_manager.register_target('matplotlib', mpl.mpl_figure_comm);\n", | |
"}\n" | |
], | |
"text/plain": [ | |
"<IPython.core.display.Javascript object>" | |
] | |
}, | |
"metadata": {}, | |
"output_type": "display_data" | |
}, | |
{ | |
"data": { | |
"text/html": [ | |
"<img src=\"data:image/png;base64,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DxAO78JCRIygd9eXiPeYICI+US+55LL6jDOW11dd9dX6jDOW16effmZ93nkfqN/ylrfVGzZM1Yce+sp6evqh+hvfuKf+oz96R/3pT3+2/uIXr6qnpx+qzz33ffW1166rjznm2Pq88z5Qn3feB+qTT35dfeed99avf/0bWz92764HCId3YSEiRtC760vEe0wQEZ+oN988Vd955731pZdeXr/pTW+pr756TT09/VB95ZVX1/fd92B9zDHH1tPTD9V33fWd+u1vP72+/PK/qL/whS/X09MP1eed94H62mvX1UceeXT94IP/VE9PP1Rfng3WUgAAIABJREFUdtmfPXbfto/du+sBwuFdWIiIEfTu+hLxHhNExCfql7701/Wb3/yWesWKlfUFF1xUv+51b6zf9a6V9emnL6//5V/+vV627FX19PTPNtg/+MF0feqpp9XnnPPe+qijjqmvu259fc01N9RveMOb6tNPX17/6Z9eygYbYFzxLixExAh6d32JeI8JImIEvbseIBzeFz0iYgS9u75EvMcEETGC3l0PEA7vix4RMYLeXV8i3mOCiBhB764HCIf3RY+IGEHvri8R7zFBRIygd9cDhMP7okdEjKB315eI95ggIkbQu+sBwuF90SMiRtC760vEe0wQMY6bNm2up6Y2zoubNm3eZtYVV3y+3n//l9bLlr2qPvLIo+uzzlqx3f9nW95334P1+ed/aM7/v3fXA4TDu/AQESPo3fUl4j0miBjHqamN9f7HnF0f/PqLdsj9jzm7nprauM2sK674fP2JT3z6sY8vvvjj9UUXXez2tXt3PUA4vAsPETGC3l1fIt5jgohxnJraWB/8+ovqY9/xFzvkwa+/6ElvsH/4w+n6yCOPrt/73vfXhx9+ZH300cvq2267o77rru/Uxx57fH3yya+rX/7yA+t1626d9bY3vOHN9fT0Q/UHPvAn9VFHHVMfe+zx9Z133vuEvnbvrgcIh3fhISJG0LvrS8R7TBAxjp4b7Onph+pf/uVfrt/0prfU09MP1XfeeW993HGvru+66zv1y152QD09/VD9t397fX3mmWfPetsb33hKfcstX6/f/Oa31tPTD9Xf+MY9jz3e9vTueoBweBceImIEvbu+RLzHBBHj6LnBfvDBf6qf85zn1Bdf/PHHbjvkkMPqu+66/7Fnpm+//c767W8//eeerd7y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\">" | |
], | |
"text/plain": [ | |
"<IPython.core.display.HTML object>" | |
] | |
}, | |
"metadata": {}, | |
"output_type": "display_data" | |
} | |
], | |
"source": [ | |
"# Create figure with two plots\n", | |
"fig, axes = sns.plt.subplots(2, 2, sharey=True)\n", | |
"\n", | |
"# Get messages for a specific sender and plot values\n", | |
"msgs_sender = grouped_msgs.xs('Donnie', level='sender')\n", | |
"axes[0,0].set_title('Word Count - Donnie')\n", | |
"sns.barplot(x='month', y='num_tokens', data=msgs_sender.reset_index(), ax=axes[0,0], color=\"steelblue\")\n", | |
"\n", | |
"#or we can plot the total, reusing groupby, on the index this time, and summing the results\n", | |
"msgs_total = grouped_msgs.groupby(level='month').sum()\n", | |
"axes[0,1].set_title('Word Count - Total')\n", | |
"sns.barplot(x='month', y='num_tokens', data=msgs_total.reset_index(), ax=axes[0,1], color=\"steelblue\")\n", | |
"\n", | |
"#or check for a single month\n", | |
"msgs_month = grouped_msgs.xs(3, level='month')\n", | |
"axes[1,0].set_title('Word Count - March')\n", | |
"sns.barplot(x='sender', y='num_tokens', data=msgs_month.reset_index(), ax=axes[1,0])\n", | |
"\n", | |
"#or finally plot all in one, withouth additional selection\n", | |
"axes[1,1].set_title('Word Count')\n", | |
"sns.barplot(x='month', y='num_tokens', hue='sender', data=grouped_msgs.reset_index(), ax=axes[1,1])\n", | |
"\n", | |
"sns.plt.show()" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"# Pivoting, Stacking and Melting" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"What is all this about? It's about swirling your dataframe data around in an ordered way, for friends: [reshaping](http://pandas.pydata.org/pandas-docs/stable/reshaping.html). \n", | |
"\n", | |
"With **pivot** you directly control which columns end up to form your index, which your columns, and which your values.\n", | |
"\n", | |
"**Stacking** operates a pivoting of columns (different columns become a single new attribute, duplicating the number of samples for each column label). \n", | |
"In the following lines we stack our messages statistics into a single *stats* column, that specifies which statistics we are considering, and has the associated value in the *val* column." | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 12, | |
"metadata": { | |
"collapsed": false | |
}, | |
"outputs": [ | |
{ | |
"data": { | |
"text/html": [ | |
"<div>\n", | |
"<table border=\"1\" class=\"dataframe\">\n", | |
" <thead>\n", | |
" <tr style=\"text-align: right;\">\n", | |
" <th></th>\n", | |
" <th></th>\n", | |
" <th></th>\n", | |
" <th>num_tokens</th>\n", | |
" <th>num_types</th>\n", | |
" <th>max_tokens</th>\n", | |
" <th>avg_tokens</th>\n", | |
" </tr>\n", | |
" <tr>\n", | |
" <th>sender</th>\n", | |
" <th>month</th>\n", | |
" <th>year</th>\n", | |
" <th></th>\n", | |
" <th></th>\n", | |
" <th></th>\n", | |
" <th></th>\n", | |
" </tr>\n", | |
" </thead>\n", | |
" <tbody>\n", | |
" <tr>\n", | |
" <th rowspan=\"5\" valign=\"top\">Donnie</th>\n", | |
" <th rowspan=\"2\" valign=\"top\">1</th>\n", | |
" <th>2016</th>\n", | |
" <td>124</td>\n", | |
" <td>117</td>\n", | |
" <td>124</td>\n", | |
" <td>124</td>\n", | |
" </tr>\n", | |
" <tr>\n", | |
" <th>2017</th>\n", | |
" <td>127</td>\n", | |
" <td>122</td>\n", | |
" <td>127</td>\n", | |
" <td>127</td>\n", | |
" </tr>\n", | |
" <tr>\n", | |
" <th rowspan=\"2\" valign=\"top\">2</th>\n", | |
" <th>2016</th>\n", | |
" <td>150</td>\n", | |
" <td>140</td>\n", | |
" <td>150</td>\n", | |
" <td>150</td>\n", | |
" </tr>\n", | |
" <tr>\n", | |
" <th>2017</th>\n", | |
" <td>130</td>\n", | |
" <td>125</td>\n", | |
" <td>130</td>\n", | |
" <td>130</td>\n", | |
" </tr>\n", | |
" <tr>\n", | |
" <th>3</th>\n", | |
" <th>2016</th>\n", | |
" <td>196</td>\n", | |
" <td>187</td>\n", | |
" <td>196</td>\n", | |
" <td>196</td>\n", | |
" </tr>\n", | |
" </tbody>\n", | |
"</table>\n", | |
"</div>" | |
], | |
"text/plain": [ | |
" num_tokens num_types max_tokens avg_tokens\n", | |
"sender month year \n", | |
"Donnie 1 2016 124 117 124 124\n", | |
" 2017 127 122 127 127\n", | |
" 2 2016 150 140 150 150\n", | |
" 2017 130 125 130 130\n", | |
" 3 2016 196 187 196 196" | |
] | |
}, | |
"execution_count": 12, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
], | |
"source": [ | |
"# First prepare data by extracting year info and group-by sum\n", | |
"msgs_stats['year'] = msgs_stats['datetime'].dt.year\n", | |
"yearly_msgs = msgs_stats.groupby(['sender','month','year']).sum()\n", | |
"yearly_msgs = yearly_msgs.drop('text_len', axis=1)\n", | |
"yearly_msgs.head()" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 13, | |
"metadata": { | |
"collapsed": false | |
}, | |
"outputs": [ | |
{ | |
"data": { | |
"text/html": [ | |
"<div>\n", | |
"<table border=\"1\" class=\"dataframe\">\n", | |
" <thead>\n", | |
" <tr style=\"text-align: right;\">\n", | |
" <th></th>\n", | |
" <th>sender</th>\n", | |
" <th>month</th>\n", | |
" <th>year</th>\n", | |
" <th>stat</th>\n", | |
" <th>val</th>\n", | |
" </tr>\n", | |
" </thead>\n", | |
" <tbody>\n", | |
" <tr>\n", | |
" <th>0</th>\n", | |
" <td>Donnie</td>\n", | |
" <td>1</td>\n", | |
" <td>2016</td>\n", | |
" <td>num_tokens</td>\n", | |
" <td>124</td>\n", | |
" </tr>\n", | |
" <tr>\n", | |
" <th>1</th>\n", | |
" <td>Donnie</td>\n", | |
" <td>1</td>\n", | |
" <td>2016</td>\n", | |
" <td>num_types</td>\n", | |
" <td>117</td>\n", | |
" </tr>\n", | |
" <tr>\n", | |
" <th>2</th>\n", | |
" <td>Donnie</td>\n", | |
" <td>1</td>\n", | |
" <td>2016</td>\n", | |
" <td>max_tokens</td>\n", | |
" <td>124</td>\n", | |
" </tr>\n", | |
" <tr>\n", | |
" <th>3</th>\n", | |
" <td>Donnie</td>\n", | |
" <td>1</td>\n", | |
" <td>2016</td>\n", | |
" <td>avg_tokens</td>\n", | |
" <td>124</td>\n", | |
" </tr>\n", | |
" <tr>\n", | |
" <th>4</th>\n", | |
" <td>Donnie</td>\n", | |
" <td>1</td>\n", | |
" <td>2017</td>\n", | |
" <td>num_tokens</td>\n", | |
" <td>127</td>\n", | |
" </tr>\n", | |
" </tbody>\n", | |
"</table>\n", | |
"</div>" | |
], | |
"text/plain": [ | |
" sender month year stat val\n", | |
"0 Donnie 1 2016 num_tokens 124\n", | |
"1 Donnie 1 2016 num_types 117\n", | |
"2 Donnie 1 2016 max_tokens 124\n", | |
"3 Donnie 1 2016 avg_tokens 124\n", | |
"4 Donnie 1 2017 num_tokens 127" | |
] | |
}, | |
"execution_count": 13, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
], | |
"source": [ | |
"# Stack our data and rename columns\n", | |
"yearly_msgs = yearly_msgs.stack().reset_index()\n", | |
"yearly_msgs.columns.values[3] = 'stat'\n", | |
"yearly_msgs.columns.values[4] = 'val'\n", | |
"yearly_msgs.head()" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"## Facet Grid" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"This format doesn't follow the [tidy data guidelines](http://vita.had.co.nz/papers/tidy-data.pdf), but can be useful for quick playing around and plotting, especially when using Seaborn [FacetGrid](http://seaborn.pydata.org/generated/seaborn.FacetGrid.html). We compose our plot specifying how subplots are built in terms of rows and columns, for then applying to each a basic plotting function.\n", | |
"\n", | |
"In the following example each column is a year, and different rows are different statistics about our messages. Each plot will then show the values along months, for each sender. Even if this explanation sounds convoluted, the actual plot is really intuitive and helpful when visualizing such kind of data. \n", | |
"\n", | |
"The **sharing of axis** (keep the same scale for the plots) is also an important part of the grid, that can help visualize how your values vary among different plots. At the same time some sharing might just be counterproductive, for example when comparing values with a big scale or range difference. We have one of such cases when comparing *text_len* with the other stats." | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 14, | |
"metadata": { | |
"collapsed": false | |
}, | |
"outputs": [ | |
{ | |
"data": { | |
"application/javascript": [ | |
"/* Put everything inside the global mpl namespace */\n", | |
"window.mpl = {};\n", | |
"\n", | |
"mpl.get_websocket_type = function() {\n", | |
" if (typeof(WebSocket) !== 'undefined') {\n", | |
" return WebSocket;\n", | |
" } else if (typeof(MozWebSocket) !== 'undefined') {\n", | |
" return MozWebSocket;\n", | |
" } else {\n", | |
" alert('Your browser does not have WebSocket support.' +\n", | |
" 'Please try Chrome, Safari or Firefox ≥ 6. ' +\n", | |
" 'Firefox 4 and 5 are also supported but you ' +\n", | |
" 'have to enable WebSockets in about:config.');\n", | |
" };\n", | |
"}\n", | |
"\n", | |
"mpl.figure = function(figure_id, websocket, ondownload, parent_element) {\n", | |
" this.id = figure_id;\n", | |
"\n", | |
" this.ws = websocket;\n", | |
"\n", | |
" this.supports_binary = (this.ws.binaryType != undefined);\n", | |
"\n", | |
" if (!this.supports_binary) {\n", | |
" var warnings = document.getElementById(\"mpl-warnings\");\n", | |
" if (warnings) {\n", | |
" warnings.style.display = 'block';\n", | |
" warnings.textContent = (\n", | |
" \"This browser does not support binary websocket messages. \" +\n", | |
" \"Performance may be slow.\");\n", | |
" }\n", | |
" }\n", | |
"\n", | |
" this.imageObj = new Image();\n", | |
"\n", | |
" this.context = undefined;\n", | |
" this.message = undefined;\n", | |
" this.canvas = undefined;\n", | |
" this.rubberband_canvas = undefined;\n", | |
" this.rubberband_context = undefined;\n", | |
" this.format_dropdown = undefined;\n", | |
"\n", | |
" this.image_mode = 'full';\n", | |
"\n", | |
" this.root = $('<div/>');\n", | |
" this._root_extra_style(this.root)\n", | |
" this.root.attr('style', 'display: inline-block');\n", | |
"\n", | |
" $(parent_element).append(this.root);\n", | |
"\n", | |
" this._init_header(this);\n", | |
" this._init_canvas(this);\n", | |
" this._init_toolbar(this);\n", | |
"\n", | |
" var fig = this;\n", | |
"\n", | |
" this.waiting = false;\n", | |
"\n", | |
" this.ws.onopen = function () {\n", | |
" fig.send_message(\"supports_binary\", {value: fig.supports_binary});\n", | |
" fig.send_message(\"send_image_mode\", {});\n", | |
" fig.send_message(\"refresh\", {});\n", | |
" }\n", | |
"\n", | |
" this.imageObj.onload = function() {\n", | |
" if (fig.image_mode == 'full') {\n", | |
" // Full images could contain transparency (where diff images\n", | |
" // almost always do), so we need to clear the canvas so that\n", | |
" // there is no ghosting.\n", | |
" fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n", | |
" }\n", | |
" fig.context.drawImage(fig.imageObj, 0, 0);\n", | |
" };\n", | |
"\n", | |
" this.imageObj.onunload = function() {\n", | |
" this.ws.close();\n", | |
" }\n", | |
"\n", | |
" this.ws.onmessage = this._make_on_message_function(this);\n", | |
"\n", | |
" this.ondownload = ondownload;\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._init_header = function() {\n", | |
" var titlebar = $(\n", | |
" '<div class=\"ui-dialog-titlebar ui-widget-header ui-corner-all ' +\n", | |
" 'ui-helper-clearfix\"/>');\n", | |
" var titletext = $(\n", | |
" '<div class=\"ui-dialog-title\" style=\"width: 100%; ' +\n", | |
" 'text-align: center; padding: 3px;\"/>');\n", | |
" titlebar.append(titletext)\n", | |
" this.root.append(titlebar);\n", | |
" this.header = titletext[0];\n", | |
"}\n", | |
"\n", | |
"\n", | |
"\n", | |
"mpl.figure.prototype._canvas_extra_style = function(canvas_div) {\n", | |
"\n", | |
"}\n", | |
"\n", | |
"\n", | |
"mpl.figure.prototype._root_extra_style = function(canvas_div) {\n", | |
"\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._init_canvas = function() {\n", | |
" var fig = this;\n", | |
"\n", | |
" var canvas_div = $('<div/>');\n", | |
"\n", | |
" canvas_div.attr('style', 'position: relative; clear: both; outline: 0');\n", | |
"\n", | |
" function canvas_keyboard_event(event) {\n", | |
" return fig.key_event(event, event['data']);\n", | |
" }\n", | |
"\n", | |
" canvas_div.keydown('key_press', canvas_keyboard_event);\n", | |
" canvas_div.keyup('key_release', canvas_keyboard_event);\n", | |
" this.canvas_div = canvas_div\n", | |
" this._canvas_extra_style(canvas_div)\n", | |
" this.root.append(canvas_div);\n", | |
"\n", | |
" var canvas = $('<canvas/>');\n", | |
" canvas.addClass('mpl-canvas');\n", | |
" canvas.attr('style', \"left: 0; top: 0; z-index: 0; outline: 0\")\n", | |
"\n", | |
" this.canvas = canvas[0];\n", | |
" this.context = canvas[0].getContext(\"2d\");\n", | |
"\n", | |
" var rubberband = $('<canvas/>');\n", | |
" rubberband.attr('style', \"position: absolute; left: 0; top: 0; z-index: 1;\")\n", | |
"\n", | |
" var pass_mouse_events = true;\n", | |
"\n", | |
" canvas_div.resizable({\n", | |
" start: function(event, ui) {\n", | |
" pass_mouse_events = false;\n", | |
" },\n", | |
" resize: function(event, ui) {\n", | |
" fig.request_resize(ui.size.width, ui.size.height);\n", | |
" },\n", | |
" stop: function(event, ui) {\n", | |
" pass_mouse_events = true;\n", | |
" fig.request_resize(ui.size.width, ui.size.height);\n", | |
" },\n", | |
" });\n", | |
"\n", | |
" function mouse_event_fn(event) {\n", | |
" if (pass_mouse_events)\n", | |
" return fig.mouse_event(event, event['data']);\n", | |
" }\n", | |
"\n", | |
" rubberband.mousedown('button_press', mouse_event_fn);\n", | |
" rubberband.mouseup('button_release', mouse_event_fn);\n", | |
" // Throttle sequential mouse events to 1 every 20ms.\n", | |
" rubberband.mousemove('motion_notify', mouse_event_fn);\n", | |
"\n", | |
" rubberband.mouseenter('figure_enter', mouse_event_fn);\n", | |
" rubberband.mouseleave('figure_leave', mouse_event_fn);\n", | |
"\n", | |
" canvas_div.on(\"wheel\", function (event) {\n", | |
" event = event.originalEvent;\n", | |
" event['data'] = 'scroll'\n", | |
" if (event.deltaY < 0) {\n", | |
" event.step = 1;\n", | |
" } else {\n", | |
" event.step = -1;\n", | |
" }\n", | |
" mouse_event_fn(event);\n", | |
" });\n", | |
"\n", | |
" canvas_div.append(canvas);\n", | |
" canvas_div.append(rubberband);\n", | |
"\n", | |
" this.rubberband = rubberband;\n", | |
" this.rubberband_canvas = rubberband[0];\n", | |
" this.rubberband_context = rubberband[0].getContext(\"2d\");\n", | |
" this.rubberband_context.strokeStyle = \"#000000\";\n", | |
"\n", | |
" this._resize_canvas = function(width, height) {\n", | |
" // Keep the size of the canvas, canvas container, and rubber band\n", | |
" // canvas in synch.\n", | |
" canvas_div.css('width', width)\n", | |
" canvas_div.css('height', height)\n", | |
"\n", | |
" canvas.attr('width', width);\n", | |
" canvas.attr('height', height);\n", | |
"\n", | |
" rubberband.attr('width', width);\n", | |
" rubberband.attr('height', height);\n", | |
" }\n", | |
"\n", | |
" // Set the figure to an initial 600x600px, this will subsequently be updated\n", | |
" // upon first draw.\n", | |
" this._resize_canvas(600, 600);\n", | |
"\n", | |
" // Disable right mouse context menu.\n", | |
" $(this.rubberband_canvas).bind(\"contextmenu\",function(e){\n", | |
" return false;\n", | |
" });\n", | |
"\n", | |
" function set_focus () {\n", | |
" canvas.focus();\n", | |
" canvas_div.focus();\n", | |
" }\n", | |
"\n", | |
" window.setTimeout(set_focus, 100);\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._init_toolbar = function() {\n", | |
" var fig = this;\n", | |
"\n", | |
" var nav_element = $('<div/>')\n", | |
" nav_element.attr('style', 'width: 100%');\n", | |
" this.root.append(nav_element);\n", | |
"\n", | |
" // Define a callback function for later on.\n", | |
" function toolbar_event(event) {\n", | |
" return fig.toolbar_button_onclick(event['data']);\n", | |
" }\n", | |
" function toolbar_mouse_event(event) {\n", | |
" return fig.toolbar_button_onmouseover(event['data']);\n", | |
" }\n", | |
"\n", | |
" for(var toolbar_ind in mpl.toolbar_items) {\n", | |
" var name = mpl.toolbar_items[toolbar_ind][0];\n", | |
" var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", | |
" var image = mpl.toolbar_items[toolbar_ind][2];\n", | |
" var method_name = mpl.toolbar_items[toolbar_ind][3];\n", | |
"\n", | |
" if (!name) {\n", | |
" // put a spacer in here.\n", | |
" continue;\n", | |
" }\n", | |
" var button = $('<button/>');\n", | |
" button.addClass('ui-button ui-widget ui-state-default ui-corner-all ' +\n", | |
" 'ui-button-icon-only');\n", | |
" button.attr('role', 'button');\n", | |
" button.attr('aria-disabled', 'false');\n", | |
" button.click(method_name, toolbar_event);\n", | |
" button.mouseover(tooltip, toolbar_mouse_event);\n", | |
"\n", | |
" var icon_img = $('<span/>');\n", | |
" icon_img.addClass('ui-button-icon-primary ui-icon');\n", | |
" icon_img.addClass(image);\n", | |
" icon_img.addClass('ui-corner-all');\n", | |
"\n", | |
" var tooltip_span = $('<span/>');\n", | |
" tooltip_span.addClass('ui-button-text');\n", | |
" tooltip_span.html(tooltip);\n", | |
"\n", | |
" button.append(icon_img);\n", | |
" button.append(tooltip_span);\n", | |
"\n", | |
" nav_element.append(button);\n", | |
" }\n", | |
"\n", | |
" var fmt_picker_span = $('<span/>');\n", | |
"\n", | |
" var fmt_picker = $('<select/>');\n", | |
" fmt_picker.addClass('mpl-toolbar-option ui-widget ui-widget-content');\n", | |
" fmt_picker_span.append(fmt_picker);\n", | |
" nav_element.append(fmt_picker_span);\n", | |
" this.format_dropdown = fmt_picker[0];\n", | |
"\n", | |
" for (var ind in mpl.extensions) {\n", | |
" var fmt = mpl.extensions[ind];\n", | |
" var option = $(\n", | |
" '<option/>', {selected: fmt === mpl.default_extension}).html(fmt);\n", | |
" fmt_picker.append(option)\n", | |
" }\n", | |
"\n", | |
" // Add hover states to the ui-buttons\n", | |
" $( \".ui-button\" ).hover(\n", | |
" function() { $(this).addClass(\"ui-state-hover\");},\n", | |
" function() { $(this).removeClass(\"ui-state-hover\");}\n", | |
" );\n", | |
"\n", | |
" var status_bar = $('<span class=\"mpl-message\"/>');\n", | |
" nav_element.append(status_bar);\n", | |
" this.message = status_bar[0];\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.request_resize = function(x_pixels, y_pixels) {\n", | |
" // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n", | |
" // which will in turn request a refresh of the image.\n", | |
" this.send_message('resize', {'width': x_pixels, 'height': y_pixels});\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.send_message = function(type, properties) {\n", | |
" properties['type'] = type;\n", | |
" properties['figure_id'] = this.id;\n", | |
" this.ws.send(JSON.stringify(properties));\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.send_draw_message = function() {\n", | |
" if (!this.waiting) {\n", | |
" this.waiting = true;\n", | |
" this.ws.send(JSON.stringify({type: \"draw\", figure_id: this.id}));\n", | |
" }\n", | |
"}\n", | |
"\n", | |
"\n", | |
"mpl.figure.prototype.handle_save = function(fig, msg) {\n", | |
" var format_dropdown = fig.format_dropdown;\n", | |
" var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n", | |
" fig.ondownload(fig, format);\n", | |
"}\n", | |
"\n", | |
"\n", | |
"mpl.figure.prototype.handle_resize = function(fig, msg) {\n", | |
" var size = msg['size'];\n", | |
" if (size[0] != fig.canvas.width || size[1] != fig.canvas.height) {\n", | |
" fig._resize_canvas(size[0], size[1]);\n", | |
" fig.send_message(\"refresh\", {});\n", | |
" };\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.handle_rubberband = function(fig, msg) {\n", | |
" var x0 = msg['x0'];\n", | |
" var y0 = fig.canvas.height - msg['y0'];\n", | |
" var x1 = msg['x1'];\n", | |
" var y1 = fig.canvas.height - msg['y1'];\n", | |
" x0 = Math.floor(x0) + 0.5;\n", | |
" y0 = Math.floor(y0) + 0.5;\n", | |
" x1 = Math.floor(x1) + 0.5;\n", | |
" y1 = Math.floor(y1) + 0.5;\n", | |
" var min_x = Math.min(x0, x1);\n", | |
" var min_y = Math.min(y0, y1);\n", | |
" var width = Math.abs(x1 - x0);\n", | |
" var height = Math.abs(y1 - y0);\n", | |
"\n", | |
" fig.rubberband_context.clearRect(\n", | |
" 0, 0, fig.canvas.width, fig.canvas.height);\n", | |
"\n", | |
" fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.handle_figure_label = function(fig, msg) {\n", | |
" // Updates the figure title.\n", | |
" fig.header.textContent = msg['label'];\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.handle_cursor = function(fig, msg) {\n", | |
" var cursor = msg['cursor'];\n", | |
" switch(cursor)\n", | |
" {\n", | |
" case 0:\n", | |
" cursor = 'pointer';\n", | |
" break;\n", | |
" case 1:\n", | |
" cursor = 'default';\n", | |
" break;\n", | |
" case 2:\n", | |
" cursor = 'crosshair';\n", | |
" break;\n", | |
" case 3:\n", | |
" cursor = 'move';\n", | |
" break;\n", | |
" }\n", | |
" fig.rubberband_canvas.style.cursor = cursor;\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.handle_message = function(fig, msg) {\n", | |
" fig.message.textContent = msg['message'];\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.handle_draw = function(fig, msg) {\n", | |
" // Request the server to send over a new figure.\n", | |
" fig.send_draw_message();\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.handle_image_mode = function(fig, msg) {\n", | |
" fig.image_mode = msg['mode'];\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.updated_canvas_event = function() {\n", | |
" // Called whenever the canvas gets updated.\n", | |
" this.send_message(\"ack\", {});\n", | |
"}\n", | |
"\n", | |
"// A function to construct a web socket function for onmessage handling.\n", | |
"// Called in the figure constructor.\n", | |
"mpl.figure.prototype._make_on_message_function = function(fig) {\n", | |
" return function socket_on_message(evt) {\n", | |
" if (evt.data instanceof Blob) {\n", | |
" /* FIXME: We get \"Resource interpreted as Image but\n", | |
" * transferred with MIME type text/plain:\" errors on\n", | |
" * Chrome. But how to set the MIME type? It doesn't seem\n", | |
" * to be part of the websocket stream */\n", | |
" evt.data.type = \"image/png\";\n", | |
"\n", | |
" /* Free the memory for the previous frames */\n", | |
" if (fig.imageObj.src) {\n", | |
" (window.URL || window.webkitURL).revokeObjectURL(\n", | |
" fig.imageObj.src);\n", | |
" }\n", | |
"\n", | |
" fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n", | |
" evt.data);\n", | |
" fig.updated_canvas_event();\n", | |
" fig.waiting = false;\n", | |
" return;\n", | |
" }\n", | |
" else if (typeof evt.data === 'string' && evt.data.slice(0, 21) == \"data:image/png;base64\") {\n", | |
" fig.imageObj.src = evt.data;\n", | |
" fig.updated_canvas_event();\n", | |
" fig.waiting = false;\n", | |
" return;\n", | |
" }\n", | |
"\n", | |
" var msg = JSON.parse(evt.data);\n", | |
" var msg_type = msg['type'];\n", | |
"\n", | |
" // Call the \"handle_{type}\" callback, which takes\n", | |
" // the figure and JSON message as its only arguments.\n", | |
" try {\n", | |
" var callback = fig[\"handle_\" + msg_type];\n", | |
" } catch (e) {\n", | |
" console.log(\"No handler for the '\" + msg_type + \"' message type: \", msg);\n", | |
" return;\n", | |
" }\n", | |
"\n", | |
" if (callback) {\n", | |
" try {\n", | |
" // console.log(\"Handling '\" + msg_type + \"' message: \", msg);\n", | |
" callback(fig, msg);\n", | |
" } catch (e) {\n", | |
" console.log(\"Exception inside the 'handler_\" + msg_type + \"' callback:\", e, e.stack, msg);\n", | |
" }\n", | |
" }\n", | |
" };\n", | |
"}\n", | |
"\n", | |
"// from http://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\n", | |
"mpl.findpos = function(e) {\n", | |
" //this section is from http://www.quirksmode.org/js/events_properties.html\n", | |
" var targ;\n", | |
" if (!e)\n", | |
" e = window.event;\n", | |
" if (e.target)\n", | |
" targ = e.target;\n", | |
" else if (e.srcElement)\n", | |
" targ = e.srcElement;\n", | |
" if (targ.nodeType == 3) // defeat Safari bug\n", | |
" targ = targ.parentNode;\n", | |
"\n", | |
" // jQuery normalizes the pageX and pageY\n", | |
" // pageX,Y are the mouse positions relative to the document\n", | |
" // offset() returns the position of the element relative to the document\n", | |
" var x = e.pageX - $(targ).offset().left;\n", | |
" var y = e.pageY - $(targ).offset().top;\n", | |
"\n", | |
" return {\"x\": x, \"y\": y};\n", | |
"};\n", | |
"\n", | |
"/*\n", | |
" * return a copy of an object with only non-object keys\n", | |
" * we need this to avoid circular references\n", | |
" * http://stackoverflow.com/a/24161582/3208463\n", | |
" */\n", | |
"function simpleKeys (original) {\n", | |
" return Object.keys(original).reduce(function (obj, key) {\n", | |
" if (typeof original[key] !== 'object')\n", | |
" obj[key] = original[key]\n", | |
" return obj;\n", | |
" }, {});\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.mouse_event = function(event, name) {\n", | |
" var canvas_pos = mpl.findpos(event)\n", | |
"\n", | |
" if (name === 'button_press')\n", | |
" {\n", | |
" this.canvas.focus();\n", | |
" this.canvas_div.focus();\n", | |
" }\n", | |
"\n", | |
" var x = canvas_pos.x;\n", | |
" var y = canvas_pos.y;\n", | |
"\n", | |
" this.send_message(name, {x: x, y: y, button: event.button,\n", | |
" step: event.step,\n", | |
" guiEvent: simpleKeys(event)});\n", | |
"\n", | |
" /* This prevents the web browser from automatically changing to\n", | |
" * the text insertion cursor when the button is pressed. We want\n", | |
" * to control all of the cursor setting manually through the\n", | |
" * 'cursor' event from matplotlib */\n", | |
" event.preventDefault();\n", | |
" return false;\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._key_event_extra = function(event, name) {\n", | |
" // Handle any extra behaviour associated with a key event\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.key_event = function(event, name) {\n", | |
"\n", | |
" // Prevent repeat events\n", | |
" if (name == 'key_press')\n", | |
" {\n", | |
" if (event.which === this._key)\n", | |
" return;\n", | |
" else\n", | |
" this._key = event.which;\n", | |
" }\n", | |
" if (name == 'key_release')\n", | |
" this._key = null;\n", | |
"\n", | |
" var value = '';\n", | |
" if (event.ctrlKey && event.which != 17)\n", | |
" value += \"ctrl+\";\n", | |
" if (event.altKey && event.which != 18)\n", | |
" value += \"alt+\";\n", | |
" if (event.shiftKey && event.which != 16)\n", | |
" value += \"shift+\";\n", | |
"\n", | |
" value += 'k';\n", | |
" value += event.which.toString();\n", | |
"\n", | |
" this._key_event_extra(event, name);\n", | |
"\n", | |
" this.send_message(name, {key: value,\n", | |
" guiEvent: simpleKeys(event)});\n", | |
" return false;\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.toolbar_button_onclick = function(name) {\n", | |
" if (name == 'download') {\n", | |
" this.handle_save(this, null);\n", | |
" } else {\n", | |
" this.send_message(\"toolbar_button\", {name: name});\n", | |
" }\n", | |
"};\n", | |
"\n", | |
"mpl.figure.prototype.toolbar_button_onmouseover = function(tooltip) {\n", | |
" this.message.textContent = tooltip;\n", | |
"};\n", | |
"mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Pan axes with left mouse, zoom with right\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n", | |
"\n", | |
"mpl.extensions = [\"eps\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\"];\n", | |
"\n", | |
"mpl.default_extension = \"png\";var comm_websocket_adapter = function(comm) {\n", | |
" // Create a \"websocket\"-like object which calls the given IPython comm\n", | |
" // object with the appropriate methods. Currently this is a non binary\n", | |
" // socket, so there is still some room for performance tuning.\n", | |
" var ws = {};\n", | |
"\n", | |
" ws.close = function() {\n", | |
" comm.close()\n", | |
" };\n", | |
" ws.send = function(m) {\n", | |
" //console.log('sending', m);\n", | |
" comm.send(m);\n", | |
" };\n", | |
" // Register the callback with on_msg.\n", | |
" comm.on_msg(function(msg) {\n", | |
" //console.log('receiving', msg['content']['data'], msg);\n", | |
" // Pass the mpl event to the overriden (by mpl) onmessage function.\n", | |
" ws.onmessage(msg['content']['data'])\n", | |
" });\n", | |
" return ws;\n", | |
"}\n", | |
"\n", | |
"mpl.mpl_figure_comm = function(comm, msg) {\n", | |
" // This is the function which gets called when the mpl process\n", | |
" // starts-up an IPython Comm through the \"matplotlib\" channel.\n", | |
"\n", | |
" var id = msg.content.data.id;\n", | |
" // Get hold of the div created by the display call when the Comm\n", | |
" // socket was opened in Python.\n", | |
" var element = $(\"#\" + id);\n", | |
" var ws_proxy = comm_websocket_adapter(comm)\n", | |
"\n", | |
" function ondownload(figure, format) {\n", | |
" window.open(figure.imageObj.src);\n", | |
" }\n", | |
"\n", | |
" var fig = new mpl.figure(id, ws_proxy,\n", | |
" ondownload,\n", | |
" element.get(0));\n", | |
"\n", | |
" // Call onopen now - mpl needs it, as it is assuming we've passed it a real\n", | |
" // web socket which is closed, not our websocket->open comm proxy.\n", | |
" ws_proxy.onopen();\n", | |
"\n", | |
" fig.parent_element = element.get(0);\n", | |
" fig.cell_info = mpl.find_output_cell(\"<div id='\" + id + \"'></div>\");\n", | |
" if (!fig.cell_info) {\n", | |
" console.error(\"Failed to find cell for figure\", id, fig);\n", | |
" return;\n", | |
" }\n", | |
"\n", | |
" var output_index = fig.cell_info[2]\n", | |
" var cell = fig.cell_info[0];\n", | |
"\n", | |
"};\n", | |
"\n", | |
"mpl.figure.prototype.handle_close = function(fig, msg) {\n", | |
" fig.root.unbind('remove')\n", | |
"\n", | |
" // Update the output cell to use the data from the current canvas.\n", | |
" fig.push_to_output();\n", | |
" var dataURL = fig.canvas.toDataURL();\n", | |
" // Re-enable the keyboard manager in IPython - without this line, in FF,\n", | |
" // the notebook keyboard shortcuts fail.\n", | |
" IPython.keyboard_manager.enable()\n", | |
" $(fig.parent_element).html('<img src=\"' + dataURL + '\">');\n", | |
" fig.close_ws(fig, msg);\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.close_ws = function(fig, msg){\n", | |
" fig.send_message('closing', msg);\n", | |
" // fig.ws.close()\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.push_to_output = function(remove_interactive) {\n", | |
" // Turn the data on the canvas into data in the output cell.\n", | |
" var dataURL = this.canvas.toDataURL();\n", | |
" this.cell_info[1]['text/html'] = '<img src=\"' + dataURL + '\">';\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.updated_canvas_event = function() {\n", | |
" // Tell IPython that the notebook contents must change.\n", | |
" IPython.notebook.set_dirty(true);\n", | |
" this.send_message(\"ack\", {});\n", | |
" var fig = this;\n", | |
" // Wait a second, then push the new image to the DOM so\n", | |
" // that it is saved nicely (might be nice to debounce this).\n", | |
" setTimeout(function () { fig.push_to_output() }, 1000);\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._init_toolbar = function() {\n", | |
" var fig = this;\n", | |
"\n", | |
" var nav_element = $('<div/>')\n", | |
" nav_element.attr('style', 'width: 100%');\n", | |
" this.root.append(nav_element);\n", | |
"\n", | |
" // Define a callback function for later on.\n", | |
" function toolbar_event(event) {\n", | |
" return fig.toolbar_button_onclick(event['data']);\n", | |
" }\n", | |
" function toolbar_mouse_event(event) {\n", | |
" return fig.toolbar_button_onmouseover(event['data']);\n", | |
" }\n", | |
"\n", | |
" for(var toolbar_ind in mpl.toolbar_items){\n", | |
" var name = mpl.toolbar_items[toolbar_ind][0];\n", | |
" var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", | |
" var image = mpl.toolbar_items[toolbar_ind][2];\n", | |
" var method_name = mpl.toolbar_items[toolbar_ind][3];\n", | |
"\n", | |
" if (!name) { continue; };\n", | |
"\n", | |
" var button = $('<button class=\"btn btn-default\" href=\"#\" title=\"' + name + '\"><i class=\"fa ' + image + ' fa-lg\"></i></button>');\n", | |
" button.click(method_name, toolbar_event);\n", | |
" button.mouseover(tooltip, toolbar_mouse_event);\n", | |
" nav_element.append(button);\n", | |
" }\n", | |
"\n", | |
" // Add the status bar.\n", | |
" var status_bar = $('<span class=\"mpl-message\" style=\"text-align:right; float: right;\"/>');\n", | |
" nav_element.append(status_bar);\n", | |
" this.message = status_bar[0];\n", | |
"\n", | |
" // Add the close button to the window.\n", | |
" var buttongrp = $('<div class=\"btn-group inline pull-right\"></div>');\n", | |
" var button = $('<button class=\"btn btn-mini btn-primary\" href=\"#\" title=\"Stop Interaction\"><i class=\"fa fa-power-off icon-remove icon-large\"></i></button>');\n", | |
" button.click(function (evt) { fig.handle_close(fig, {}); } );\n", | |
" button.mouseover('Stop Interaction', toolbar_mouse_event);\n", | |
" buttongrp.append(button);\n", | |
" var titlebar = this.root.find($('.ui-dialog-titlebar'));\n", | |
" titlebar.prepend(buttongrp);\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._root_extra_style = function(el){\n", | |
" var fig = this\n", | |
" el.on(\"remove\", function(){\n", | |
"\tfig.close_ws(fig, {});\n", | |
" });\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._canvas_extra_style = function(el){\n", | |
" // this is important to make the div 'focusable\n", | |
" el.attr('tabindex', 0)\n", | |
" // reach out to IPython and tell the keyboard manager to turn it's self\n", | |
" // off when our div gets focus\n", | |
"\n", | |
" // location in version 3\n", | |
" if (IPython.notebook.keyboard_manager) {\n", | |
" IPython.notebook.keyboard_manager.register_events(el);\n", | |
" }\n", | |
" else {\n", | |
" // location in version 2\n", | |
" IPython.keyboard_manager.register_events(el);\n", | |
" }\n", | |
"\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._key_event_extra = function(event, name) {\n", | |
" var manager = IPython.notebook.keyboard_manager;\n", | |
" if (!manager)\n", | |
" manager = IPython.keyboard_manager;\n", | |
"\n", | |
" // Check for shift+enter\n", | |
" if (event.shiftKey && event.which == 13) {\n", | |
" this.canvas_div.blur();\n", | |
" // select the cell after this one\n", | |
" var index = IPython.notebook.find_cell_index(this.cell_info[0]);\n", | |
" IPython.notebook.select(index + 1);\n", | |
" }\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.handle_save = function(fig, msg) {\n", | |
" fig.ondownload(fig, null);\n", | |
"}\n", | |
"\n", | |
"\n", | |
"mpl.find_output_cell = function(html_output) {\n", | |
" // Return the cell and output element which can be found *uniquely* in the notebook.\n", | |
" // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n", | |
" // IPython event is triggered only after the cells have been serialised, which for\n", | |
" // our purposes (turning an active figure into a static one), is too late.\n", | |
" var cells = IPython.notebook.get_cells();\n", | |
" var ncells = cells.length;\n", | |
" for (var i=0; i<ncells; i++) {\n", | |
" var cell = cells[i];\n", | |
" if (cell.cell_type === 'code'){\n", | |
" for (var j=0; j<cell.output_area.outputs.length; j++) {\n", | |
" var data = cell.output_area.outputs[j];\n", | |
" if (data.data) {\n", | |
" // IPython >= 3 moved mimebundle to data attribute of output\n", | |
" data = data.data;\n", | |
" }\n", | |
" if (data['text/html'] == html_output) {\n", | |
" return [cell, data, j];\n", | |
" }\n", | |
" }\n", | |
" }\n", | |
" }\n", | |
"}\n", | |
"\n", | |
"// Register the function which deals with the matplotlib target/channel.\n", | |
"// The kernel may be null if the page has been refreshed.\n", | |
"if (IPython.notebook.kernel != null) {\n", | |
" IPython.notebook.kernel.comm_manager.register_target('matplotlib', mpl.mpl_figure_comm);\n", | |
"}\n" | |
], | |
"text/plain": [ | |
"<IPython.core.display.Javascript object>" | |
] | |
}, | |
"metadata": {}, | |
"output_type": "display_data" | |
}, | |
{ | |
"data": { | |
"text/html": [ | |
"<img src=\"data:image/png;base64,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\">" | |
], | |
"text/plain": [ | |
"<IPython.core.display.HTML object>" | |
] | |
}, | |
"metadata": {}, | |
"output_type": "display_data" | |
} | |
], | |
"source": [ | |
"# Create grid specifying to use years for columns and stats for rows\n", | |
"grid = sns.FacetGrid(yearly_msgs, col='year', row='stat', margin_titles=True, sharey=True, legend_out=True)\n", | |
"# Map to each grid sub-plot a barplot\n", | |
"grid.map(sns.barplot, 'month', 'val', 'sender')\n", | |
"grid.axes[0][0].legend()\n", | |
"sns.plt.show()" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"# Filling time ranges" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"If you look closely at our messages occurrences, you can see that not all days are covered. If you index by date you will end up with something like the dataframe showed next.\n", | |
"\n", | |
"During the analysis part as operated until now this content might be okay and exactly what we need, but when plotting, missing entries are just misleading, showing a consecutive change for non-consecutive dates. What we might do is **fill our time index with all possible dates**, and associate with the new entry the most appropriate value. \n", | |
"Missing-value filling deserves an article itself; however, for our dataset, the most appropriate option is to simply assign to each nonpresent entry a value of 0, correctly representing that for such a day no messages have been sent.\n", | |
"\n", | |
"To build our complete list of dates we can rely on Pandas *date_range* method, then [reindex](http://pandas.pydata.org/pandas-docs/stable/generated/pandas.DataFrame.reindex.html) the dataframe and insert our new values. In the final plot, you will notice that all dates are now present and with associated value of 0." | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 15, | |
"metadata": { | |
"collapsed": false, | |
"scrolled": true | |
}, | |
"outputs": [ | |
{ | |
"data": { | |
"text/html": [ | |
"<div>\n", | |
"<table border=\"1\" class=\"dataframe\">\n", | |
" <thead>\n", | |
" <tr style=\"text-align: right;\">\n", | |
" <th></th>\n", | |
" <th>text_len</th>\n", | |
" <th>num_tokens</th>\n", | |
" <th>num_types</th>\n", | |
" <th>max_tokens</th>\n", | |
" <th>avg_tokens</th>\n", | |
" </tr>\n", | |
" <tr>\n", | |
" <th>date</th>\n", | |
" <th></th>\n", | |
" <th></th>\n", | |
" <th></th>\n", | |
" <th></th>\n", | |
" <th></th>\n", | |
" </tr>\n", | |
" </thead>\n", | |
" <tbody>\n", | |
" <tr>\n", | |
" <th>2016-01-23</th>\n", | |
" <td>12</td>\n", | |
" <td>2</td>\n", | |
" <td>2</td>\n", | |
" <td>2</td>\n", | |
" <td>2</td>\n", | |
" </tr>\n", | |
" <tr>\n", | |
" <th>2016-01-25</th>\n", | |
" <td>21</td>\n", | |
" <td>4</td>\n", | |
" <td>4</td>\n", | |
" <td>4</td>\n", | |
" <td>4</td>\n", | |
" </tr>\n", | |
" <tr>\n", | |
" <th>2016-01-26</th>\n", | |
" <td>67</td>\n", | |
" <td>13</td>\n", | |
" <td>11</td>\n", | |
" <td>13</td>\n", | |
" <td>13</td>\n", | |
" </tr>\n", | |
" <tr>\n", | |
" <th>2016-01-27</th>\n", | |
" <td>44</td>\n", | |
" <td>10</td>\n", | |
" <td>10</td>\n", | |
" <td>10</td>\n", | |
" <td>10</td>\n", | |
" </tr>\n", | |
" <tr>\n", | |
" <th>2016-01-29</th>\n", | |
" <td>17</td>\n", | |
" <td>4</td>\n", | |
" <td>4</td>\n", | |
" <td>4</td>\n", | |
" <td>4</td>\n", | |
" </tr>\n", | |
" <tr>\n", | |
" <th>2016-02-02</th>\n", | |
" <td>22</td>\n", | |
" <td>4</td>\n", | |
" <td>4</td>\n", | |
" <td>4</td>\n", | |
" <td>4</td>\n", | |
" </tr>\n", | |
" <tr>\n", | |
" <th>2016-02-04</th>\n", | |
" <td>28</td>\n", | |
" <td>6</td>\n", | |
" <td>6</td>\n", | |
" <td>6</td>\n", | |
" <td>6</td>\n", | |
" </tr>\n", | |
" <tr>\n", | |
" <th>2016-02-06</th>\n", | |
" <td>56</td>\n", | |
" <td>8</td>\n", | |
" <td>8</td>\n", | |
" <td>8</td>\n", | |
" <td>8</td>\n", | |
" </tr>\n", | |
" <tr>\n", | |
" <th>2016-02-07</th>\n", | |
" <td>95</td>\n", | |
" <td>19</td>\n", | |
" <td>19</td>\n", | |
" <td>19</td>\n", | |
" <td>19</td>\n", | |
" </tr>\n", | |
" <tr>\n", | |
" <th>2016-02-08</th>\n", | |
" <td>45</td>\n", | |
" <td>8</td>\n", | |
" <td>8</td>\n", | |
" <td>8</td>\n", | |
" <td>8</td>\n", | |
" </tr>\n", | |
" </tbody>\n", | |
"</table>\n", | |
"</div>" | |
], | |
"text/plain": [ | |
" text_len num_tokens num_types max_tokens avg_tokens\n", | |
"date \n", | |
"2016-01-23 12 2 2 2 2\n", | |
"2016-01-25 21 4 4 4 4\n", | |
"2016-01-26 67 13 11 13 13\n", | |
"2016-01-27 44 10 10 10 10\n", | |
"2016-01-29 17 4 4 4 4\n", | |
"2016-02-02 22 4 4 4 4\n", | |
"2016-02-04 28 6 6 6 6\n", | |
"2016-02-06 56 8 8 8 8\n", | |
"2016-02-07 95 19 19 19 19\n", | |
"2016-02-08 45 8 8 8 8" | |
] | |
}, | |
"execution_count": 15, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
], | |
"source": [ | |
"# Drop unused columns\n", | |
"msgs_stats.drop(['year', 'month'], axis=1, inplace=True)\n", | |
"# Extract date as new column\n", | |
"msgs_stats['date'] = msgs_stats['datetime'].dt.date\n", | |
"# Group-by date and sum, selecting just a subset of samples\n", | |
"fill_time_msgs = msgs_stats.groupby(['date']).sum().iloc[:20,:]\n", | |
"# Show example of discontinuous entries\n", | |
"fill_time_msgs[10:]" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 17, | |
"metadata": { | |
"collapsed": false | |
}, | |
"outputs": [ | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": [ | |
"[datetime.date(2016, 1, 11), datetime.date(2016, 1, 12), datetime.date(2016, 1, 13), datetime.date(2016, 1, 14), datetime.date(2016, 1, 15), datetime.date(2016, 1, 16), datetime.date(2016, 1, 17), datetime.date(2016, 1, 18), datetime.date(2016, 1, 19), datetime.date(2016, 1, 20), datetime.date(2016, 1, 21), datetime.date(2016, 1, 22), datetime.date(2016, 1, 23), datetime.date(2016, 1, 24), datetime.date(2016, 1, 25), datetime.date(2016, 1, 26), datetime.date(2016, 1, 27), datetime.date(2016, 1, 28), datetime.date(2016, 1, 29), datetime.date(2016, 1, 30), datetime.date(2016, 1, 31), datetime.date(2016, 2, 1), datetime.date(2016, 2, 2), datetime.date(2016, 2, 3), datetime.date(2016, 2, 4), datetime.date(2016, 2, 5), datetime.date(2016, 2, 6), datetime.date(2016, 2, 7), datetime.date(2016, 2, 8)]\n" | |
] | |
} | |
], | |
"source": [ | |
"# Generate our dates range using dates of first and last message\n", | |
"start_date = fill_time_msgs.index.values[0]\n", | |
"end_date = fill_time_msgs.index.values[-1]\n", | |
"dates = pd.date_range(start=start_date, end=end_date)\n", | |
"dates = [d.date() for d in dates]\n", | |
"print(dates)" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 18, | |
"metadata": { | |
"collapsed": false | |
}, | |
"outputs": [ | |
{ | |
"data": { | |
"application/javascript": [ | |
"/* Put everything inside the global mpl namespace */\n", | |
"window.mpl = {};\n", | |
"\n", | |
"mpl.get_websocket_type = function() {\n", | |
" if (typeof(WebSocket) !== 'undefined') {\n", | |
" return WebSocket;\n", | |
" } else if (typeof(MozWebSocket) !== 'undefined') {\n", | |
" return MozWebSocket;\n", | |
" } else {\n", | |
" alert('Your browser does not have WebSocket support.' +\n", | |
" 'Please try Chrome, Safari or Firefox ≥ 6. ' +\n", | |
" 'Firefox 4 and 5 are also supported but you ' +\n", | |
" 'have to enable WebSockets in about:config.');\n", | |
" };\n", | |
"}\n", | |
"\n", | |
"mpl.figure = function(figure_id, websocket, ondownload, parent_element) {\n", | |
" this.id = figure_id;\n", | |
"\n", | |
" this.ws = websocket;\n", | |
"\n", | |
" this.supports_binary = (this.ws.binaryType != undefined);\n", | |
"\n", | |
" if (!this.supports_binary) {\n", | |
" var warnings = document.getElementById(\"mpl-warnings\");\n", | |
" if (warnings) {\n", | |
" warnings.style.display = 'block';\n", | |
" warnings.textContent = (\n", | |
" \"This browser does not support binary websocket messages. \" +\n", | |
" \"Performance may be slow.\");\n", | |
" }\n", | |
" }\n", | |
"\n", | |
" this.imageObj = new Image();\n", | |
"\n", | |
" this.context = undefined;\n", | |
" this.message = undefined;\n", | |
" this.canvas = undefined;\n", | |
" this.rubberband_canvas = undefined;\n", | |
" this.rubberband_context = undefined;\n", | |
" this.format_dropdown = undefined;\n", | |
"\n", | |
" this.image_mode = 'full';\n", | |
"\n", | |
" this.root = $('<div/>');\n", | |
" this._root_extra_style(this.root)\n", | |
" this.root.attr('style', 'display: inline-block');\n", | |
"\n", | |
" $(parent_element).append(this.root);\n", | |
"\n", | |
" this._init_header(this);\n", | |
" this._init_canvas(this);\n", | |
" this._init_toolbar(this);\n", | |
"\n", | |
" var fig = this;\n", | |
"\n", | |
" this.waiting = false;\n", | |
"\n", | |
" this.ws.onopen = function () {\n", | |
" fig.send_message(\"supports_binary\", {value: fig.supports_binary});\n", | |
" fig.send_message(\"send_image_mode\", {});\n", | |
" fig.send_message(\"refresh\", {});\n", | |
" }\n", | |
"\n", | |
" this.imageObj.onload = function() {\n", | |
" if (fig.image_mode == 'full') {\n", | |
" // Full images could contain transparency (where diff images\n", | |
" // almost always do), so we need to clear the canvas so that\n", | |
" // there is no ghosting.\n", | |
" fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n", | |
" }\n", | |
" fig.context.drawImage(fig.imageObj, 0, 0);\n", | |
" };\n", | |
"\n", | |
" this.imageObj.onunload = function() {\n", | |
" this.ws.close();\n", | |
" }\n", | |
"\n", | |
" this.ws.onmessage = this._make_on_message_function(this);\n", | |
"\n", | |
" this.ondownload = ondownload;\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._init_header = function() {\n", | |
" var titlebar = $(\n", | |
" '<div class=\"ui-dialog-titlebar ui-widget-header ui-corner-all ' +\n", | |
" 'ui-helper-clearfix\"/>');\n", | |
" var titletext = $(\n", | |
" '<div class=\"ui-dialog-title\" style=\"width: 100%; ' +\n", | |
" 'text-align: center; padding: 3px;\"/>');\n", | |
" titlebar.append(titletext)\n", | |
" this.root.append(titlebar);\n", | |
" this.header = titletext[0];\n", | |
"}\n", | |
"\n", | |
"\n", | |
"\n", | |
"mpl.figure.prototype._canvas_extra_style = function(canvas_div) {\n", | |
"\n", | |
"}\n", | |
"\n", | |
"\n", | |
"mpl.figure.prototype._root_extra_style = function(canvas_div) {\n", | |
"\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._init_canvas = function() {\n", | |
" var fig = this;\n", | |
"\n", | |
" var canvas_div = $('<div/>');\n", | |
"\n", | |
" canvas_div.attr('style', 'position: relative; clear: both; outline: 0');\n", | |
"\n", | |
" function canvas_keyboard_event(event) {\n", | |
" return fig.key_event(event, event['data']);\n", | |
" }\n", | |
"\n", | |
" canvas_div.keydown('key_press', canvas_keyboard_event);\n", | |
" canvas_div.keyup('key_release', canvas_keyboard_event);\n", | |
" this.canvas_div = canvas_div\n", | |
" this._canvas_extra_style(canvas_div)\n", | |
" this.root.append(canvas_div);\n", | |
"\n", | |
" var canvas = $('<canvas/>');\n", | |
" canvas.addClass('mpl-canvas');\n", | |
" canvas.attr('style', \"left: 0; top: 0; z-index: 0; outline: 0\")\n", | |
"\n", | |
" this.canvas = canvas[0];\n", | |
" this.context = canvas[0].getContext(\"2d\");\n", | |
"\n", | |
" var rubberband = $('<canvas/>');\n", | |
" rubberband.attr('style', \"position: absolute; left: 0; top: 0; z-index: 1;\")\n", | |
"\n", | |
" var pass_mouse_events = true;\n", | |
"\n", | |
" canvas_div.resizable({\n", | |
" start: function(event, ui) {\n", | |
" pass_mouse_events = false;\n", | |
" },\n", | |
" resize: function(event, ui) {\n", | |
" fig.request_resize(ui.size.width, ui.size.height);\n", | |
" },\n", | |
" stop: function(event, ui) {\n", | |
" pass_mouse_events = true;\n", | |
" fig.request_resize(ui.size.width, ui.size.height);\n", | |
" },\n", | |
" });\n", | |
"\n", | |
" function mouse_event_fn(event) {\n", | |
" if (pass_mouse_events)\n", | |
" return fig.mouse_event(event, event['data']);\n", | |
" }\n", | |
"\n", | |
" rubberband.mousedown('button_press', mouse_event_fn);\n", | |
" rubberband.mouseup('button_release', mouse_event_fn);\n", | |
" // Throttle sequential mouse events to 1 every 20ms.\n", | |
" rubberband.mousemove('motion_notify', mouse_event_fn);\n", | |
"\n", | |
" rubberband.mouseenter('figure_enter', mouse_event_fn);\n", | |
" rubberband.mouseleave('figure_leave', mouse_event_fn);\n", | |
"\n", | |
" canvas_div.on(\"wheel\", function (event) {\n", | |
" event = event.originalEvent;\n", | |
" event['data'] = 'scroll'\n", | |
" if (event.deltaY < 0) {\n", | |
" event.step = 1;\n", | |
" } else {\n", | |
" event.step = -1;\n", | |
" }\n", | |
" mouse_event_fn(event);\n", | |
" });\n", | |
"\n", | |
" canvas_div.append(canvas);\n", | |
" canvas_div.append(rubberband);\n", | |
"\n", | |
" this.rubberband = rubberband;\n", | |
" this.rubberband_canvas = rubberband[0];\n", | |
" this.rubberband_context = rubberband[0].getContext(\"2d\");\n", | |
" this.rubberband_context.strokeStyle = \"#000000\";\n", | |
"\n", | |
" this._resize_canvas = function(width, height) {\n", | |
" // Keep the size of the canvas, canvas container, and rubber band\n", | |
" // canvas in synch.\n", | |
" canvas_div.css('width', width)\n", | |
" canvas_div.css('height', height)\n", | |
"\n", | |
" canvas.attr('width', width);\n", | |
" canvas.attr('height', height);\n", | |
"\n", | |
" rubberband.attr('width', width);\n", | |
" rubberband.attr('height', height);\n", | |
" }\n", | |
"\n", | |
" // Set the figure to an initial 600x600px, this will subsequently be updated\n", | |
" // upon first draw.\n", | |
" this._resize_canvas(600, 600);\n", | |
"\n", | |
" // Disable right mouse context menu.\n", | |
" $(this.rubberband_canvas).bind(\"contextmenu\",function(e){\n", | |
" return false;\n", | |
" });\n", | |
"\n", | |
" function set_focus () {\n", | |
" canvas.focus();\n", | |
" canvas_div.focus();\n", | |
" }\n", | |
"\n", | |
" window.setTimeout(set_focus, 100);\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._init_toolbar = function() {\n", | |
" var fig = this;\n", | |
"\n", | |
" var nav_element = $('<div/>')\n", | |
" nav_element.attr('style', 'width: 100%');\n", | |
" this.root.append(nav_element);\n", | |
"\n", | |
" // Define a callback function for later on.\n", | |
" function toolbar_event(event) {\n", | |
" return fig.toolbar_button_onclick(event['data']);\n", | |
" }\n", | |
" function toolbar_mouse_event(event) {\n", | |
" return fig.toolbar_button_onmouseover(event['data']);\n", | |
" }\n", | |
"\n", | |
" for(var toolbar_ind in mpl.toolbar_items) {\n", | |
" var name = mpl.toolbar_items[toolbar_ind][0];\n", | |
" var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", | |
" var image = mpl.toolbar_items[toolbar_ind][2];\n", | |
" var method_name = mpl.toolbar_items[toolbar_ind][3];\n", | |
"\n", | |
" if (!name) {\n", | |
" // put a spacer in here.\n", | |
" continue;\n", | |
" }\n", | |
" var button = $('<button/>');\n", | |
" button.addClass('ui-button ui-widget ui-state-default ui-corner-all ' +\n", | |
" 'ui-button-icon-only');\n", | |
" button.attr('role', 'button');\n", | |
" button.attr('aria-disabled', 'false');\n", | |
" button.click(method_name, toolbar_event);\n", | |
" button.mouseover(tooltip, toolbar_mouse_event);\n", | |
"\n", | |
" var icon_img = $('<span/>');\n", | |
" icon_img.addClass('ui-button-icon-primary ui-icon');\n", | |
" icon_img.addClass(image);\n", | |
" icon_img.addClass('ui-corner-all');\n", | |
"\n", | |
" var tooltip_span = $('<span/>');\n", | |
" tooltip_span.addClass('ui-button-text');\n", | |
" tooltip_span.html(tooltip);\n", | |
"\n", | |
" button.append(icon_img);\n", | |
" button.append(tooltip_span);\n", | |
"\n", | |
" nav_element.append(button);\n", | |
" }\n", | |
"\n", | |
" var fmt_picker_span = $('<span/>');\n", | |
"\n", | |
" var fmt_picker = $('<select/>');\n", | |
" fmt_picker.addClass('mpl-toolbar-option ui-widget ui-widget-content');\n", | |
" fmt_picker_span.append(fmt_picker);\n", | |
" nav_element.append(fmt_picker_span);\n", | |
" this.format_dropdown = fmt_picker[0];\n", | |
"\n", | |
" for (var ind in mpl.extensions) {\n", | |
" var fmt = mpl.extensions[ind];\n", | |
" var option = $(\n", | |
" '<option/>', {selected: fmt === mpl.default_extension}).html(fmt);\n", | |
" fmt_picker.append(option)\n", | |
" }\n", | |
"\n", | |
" // Add hover states to the ui-buttons\n", | |
" $( \".ui-button\" ).hover(\n", | |
" function() { $(this).addClass(\"ui-state-hover\");},\n", | |
" function() { $(this).removeClass(\"ui-state-hover\");}\n", | |
" );\n", | |
"\n", | |
" var status_bar = $('<span class=\"mpl-message\"/>');\n", | |
" nav_element.append(status_bar);\n", | |
" this.message = status_bar[0];\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.request_resize = function(x_pixels, y_pixels) {\n", | |
" // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n", | |
" // which will in turn request a refresh of the image.\n", | |
" this.send_message('resize', {'width': x_pixels, 'height': y_pixels});\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.send_message = function(type, properties) {\n", | |
" properties['type'] = type;\n", | |
" properties['figure_id'] = this.id;\n", | |
" this.ws.send(JSON.stringify(properties));\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.send_draw_message = function() {\n", | |
" if (!this.waiting) {\n", | |
" this.waiting = true;\n", | |
" this.ws.send(JSON.stringify({type: \"draw\", figure_id: this.id}));\n", | |
" }\n", | |
"}\n", | |
"\n", | |
"\n", | |
"mpl.figure.prototype.handle_save = function(fig, msg) {\n", | |
" var format_dropdown = fig.format_dropdown;\n", | |
" var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n", | |
" fig.ondownload(fig, format);\n", | |
"}\n", | |
"\n", | |
"\n", | |
"mpl.figure.prototype.handle_resize = function(fig, msg) {\n", | |
" var size = msg['size'];\n", | |
" if (size[0] != fig.canvas.width || size[1] != fig.canvas.height) {\n", | |
" fig._resize_canvas(size[0], size[1]);\n", | |
" fig.send_message(\"refresh\", {});\n", | |
" };\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.handle_rubberband = function(fig, msg) {\n", | |
" var x0 = msg['x0'];\n", | |
" var y0 = fig.canvas.height - msg['y0'];\n", | |
" var x1 = msg['x1'];\n", | |
" var y1 = fig.canvas.height - msg['y1'];\n", | |
" x0 = Math.floor(x0) + 0.5;\n", | |
" y0 = Math.floor(y0) + 0.5;\n", | |
" x1 = Math.floor(x1) + 0.5;\n", | |
" y1 = Math.floor(y1) + 0.5;\n", | |
" var min_x = Math.min(x0, x1);\n", | |
" var min_y = Math.min(y0, y1);\n", | |
" var width = Math.abs(x1 - x0);\n", | |
" var height = Math.abs(y1 - y0);\n", | |
"\n", | |
" fig.rubberband_context.clearRect(\n", | |
" 0, 0, fig.canvas.width, fig.canvas.height);\n", | |
"\n", | |
" fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.handle_figure_label = function(fig, msg) {\n", | |
" // Updates the figure title.\n", | |
" fig.header.textContent = msg['label'];\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.handle_cursor = function(fig, msg) {\n", | |
" var cursor = msg['cursor'];\n", | |
" switch(cursor)\n", | |
" {\n", | |
" case 0:\n", | |
" cursor = 'pointer';\n", | |
" break;\n", | |
" case 1:\n", | |
" cursor = 'default';\n", | |
" break;\n", | |
" case 2:\n", | |
" cursor = 'crosshair';\n", | |
" break;\n", | |
" case 3:\n", | |
" cursor = 'move';\n", | |
" break;\n", | |
" }\n", | |
" fig.rubberband_canvas.style.cursor = cursor;\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.handle_message = function(fig, msg) {\n", | |
" fig.message.textContent = msg['message'];\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.handle_draw = function(fig, msg) {\n", | |
" // Request the server to send over a new figure.\n", | |
" fig.send_draw_message();\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.handle_image_mode = function(fig, msg) {\n", | |
" fig.image_mode = msg['mode'];\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.updated_canvas_event = function() {\n", | |
" // Called whenever the canvas gets updated.\n", | |
" this.send_message(\"ack\", {});\n", | |
"}\n", | |
"\n", | |
"// A function to construct a web socket function for onmessage handling.\n", | |
"// Called in the figure constructor.\n", | |
"mpl.figure.prototype._make_on_message_function = function(fig) {\n", | |
" return function socket_on_message(evt) {\n", | |
" if (evt.data instanceof Blob) {\n", | |
" /* FIXME: We get \"Resource interpreted as Image but\n", | |
" * transferred with MIME type text/plain:\" errors on\n", | |
" * Chrome. But how to set the MIME type? It doesn't seem\n", | |
" * to be part of the websocket stream */\n", | |
" evt.data.type = \"image/png\";\n", | |
"\n", | |
" /* Free the memory for the previous frames */\n", | |
" if (fig.imageObj.src) {\n", | |
" (window.URL || window.webkitURL).revokeObjectURL(\n", | |
" fig.imageObj.src);\n", | |
" }\n", | |
"\n", | |
" fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n", | |
" evt.data);\n", | |
" fig.updated_canvas_event();\n", | |
" fig.waiting = false;\n", | |
" return;\n", | |
" }\n", | |
" else if (typeof evt.data === 'string' && evt.data.slice(0, 21) == \"data:image/png;base64\") {\n", | |
" fig.imageObj.src = evt.data;\n", | |
" fig.updated_canvas_event();\n", | |
" fig.waiting = false;\n", | |
" return;\n", | |
" }\n", | |
"\n", | |
" var msg = JSON.parse(evt.data);\n", | |
" var msg_type = msg['type'];\n", | |
"\n", | |
" // Call the \"handle_{type}\" callback, which takes\n", | |
" // the figure and JSON message as its only arguments.\n", | |
" try {\n", | |
" var callback = fig[\"handle_\" + msg_type];\n", | |
" } catch (e) {\n", | |
" console.log(\"No handler for the '\" + msg_type + \"' message type: \", msg);\n", | |
" return;\n", | |
" }\n", | |
"\n", | |
" if (callback) {\n", | |
" try {\n", | |
" // console.log(\"Handling '\" + msg_type + \"' message: \", msg);\n", | |
" callback(fig, msg);\n", | |
" } catch (e) {\n", | |
" console.log(\"Exception inside the 'handler_\" + msg_type + \"' callback:\", e, e.stack, msg);\n", | |
" }\n", | |
" }\n", | |
" };\n", | |
"}\n", | |
"\n", | |
"// from http://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\n", | |
"mpl.findpos = function(e) {\n", | |
" //this section is from http://www.quirksmode.org/js/events_properties.html\n", | |
" var targ;\n", | |
" if (!e)\n", | |
" e = window.event;\n", | |
" if (e.target)\n", | |
" targ = e.target;\n", | |
" else if (e.srcElement)\n", | |
" targ = e.srcElement;\n", | |
" if (targ.nodeType == 3) // defeat Safari bug\n", | |
" targ = targ.parentNode;\n", | |
"\n", | |
" // jQuery normalizes the pageX and pageY\n", | |
" // pageX,Y are the mouse positions relative to the document\n", | |
" // offset() returns the position of the element relative to the document\n", | |
" var x = e.pageX - $(targ).offset().left;\n", | |
" var y = e.pageY - $(targ).offset().top;\n", | |
"\n", | |
" return {\"x\": x, \"y\": y};\n", | |
"};\n", | |
"\n", | |
"/*\n", | |
" * return a copy of an object with only non-object keys\n", | |
" * we need this to avoid circular references\n", | |
" * http://stackoverflow.com/a/24161582/3208463\n", | |
" */\n", | |
"function simpleKeys (original) {\n", | |
" return Object.keys(original).reduce(function (obj, key) {\n", | |
" if (typeof original[key] !== 'object')\n", | |
" obj[key] = original[key]\n", | |
" return obj;\n", | |
" }, {});\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.mouse_event = function(event, name) {\n", | |
" var canvas_pos = mpl.findpos(event)\n", | |
"\n", | |
" if (name === 'button_press')\n", | |
" {\n", | |
" this.canvas.focus();\n", | |
" this.canvas_div.focus();\n", | |
" }\n", | |
"\n", | |
" var x = canvas_pos.x;\n", | |
" var y = canvas_pos.y;\n", | |
"\n", | |
" this.send_message(name, {x: x, y: y, button: event.button,\n", | |
" step: event.step,\n", | |
" guiEvent: simpleKeys(event)});\n", | |
"\n", | |
" /* This prevents the web browser from automatically changing to\n", | |
" * the text insertion cursor when the button is pressed. We want\n", | |
" * to control all of the cursor setting manually through the\n", | |
" * 'cursor' event from matplotlib */\n", | |
" event.preventDefault();\n", | |
" return false;\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._key_event_extra = function(event, name) {\n", | |
" // Handle any extra behaviour associated with a key event\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.key_event = function(event, name) {\n", | |
"\n", | |
" // Prevent repeat events\n", | |
" if (name == 'key_press')\n", | |
" {\n", | |
" if (event.which === this._key)\n", | |
" return;\n", | |
" else\n", | |
" this._key = event.which;\n", | |
" }\n", | |
" if (name == 'key_release')\n", | |
" this._key = null;\n", | |
"\n", | |
" var value = '';\n", | |
" if (event.ctrlKey && event.which != 17)\n", | |
" value += \"ctrl+\";\n", | |
" if (event.altKey && event.which != 18)\n", | |
" value += \"alt+\";\n", | |
" if (event.shiftKey && event.which != 16)\n", | |
" value += \"shift+\";\n", | |
"\n", | |
" value += 'k';\n", | |
" value += event.which.toString();\n", | |
"\n", | |
" this._key_event_extra(event, name);\n", | |
"\n", | |
" this.send_message(name, {key: value,\n", | |
" guiEvent: simpleKeys(event)});\n", | |
" return false;\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.toolbar_button_onclick = function(name) {\n", | |
" if (name == 'download') {\n", | |
" this.handle_save(this, null);\n", | |
" } else {\n", | |
" this.send_message(\"toolbar_button\", {name: name});\n", | |
" }\n", | |
"};\n", | |
"\n", | |
"mpl.figure.prototype.toolbar_button_onmouseover = function(tooltip) {\n", | |
" this.message.textContent = tooltip;\n", | |
"};\n", | |
"mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Pan axes with left mouse, zoom with right\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n", | |
"\n", | |
"mpl.extensions = [\"eps\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\"];\n", | |
"\n", | |
"mpl.default_extension = \"png\";var comm_websocket_adapter = function(comm) {\n", | |
" // Create a \"websocket\"-like object which calls the given IPython comm\n", | |
" // object with the appropriate methods. Currently this is a non binary\n", | |
" // socket, so there is still some room for performance tuning.\n", | |
" var ws = {};\n", | |
"\n", | |
" ws.close = function() {\n", | |
" comm.close()\n", | |
" };\n", | |
" ws.send = function(m) {\n", | |
" //console.log('sending', m);\n", | |
" comm.send(m);\n", | |
" };\n", | |
" // Register the callback with on_msg.\n", | |
" comm.on_msg(function(msg) {\n", | |
" //console.log('receiving', msg['content']['data'], msg);\n", | |
" // Pass the mpl event to the overriden (by mpl) onmessage function.\n", | |
" ws.onmessage(msg['content']['data'])\n", | |
" });\n", | |
" return ws;\n", | |
"}\n", | |
"\n", | |
"mpl.mpl_figure_comm = function(comm, msg) {\n", | |
" // This is the function which gets called when the mpl process\n", | |
" // starts-up an IPython Comm through the \"matplotlib\" channel.\n", | |
"\n", | |
" var id = msg.content.data.id;\n", | |
" // Get hold of the div created by the display call when the Comm\n", | |
" // socket was opened in Python.\n", | |
" var element = $(\"#\" + id);\n", | |
" var ws_proxy = comm_websocket_adapter(comm)\n", | |
"\n", | |
" function ondownload(figure, format) {\n", | |
" window.open(figure.imageObj.src);\n", | |
" }\n", | |
"\n", | |
" var fig = new mpl.figure(id, ws_proxy,\n", | |
" ondownload,\n", | |
" element.get(0));\n", | |
"\n", | |
" // Call onopen now - mpl needs it, as it is assuming we've passed it a real\n", | |
" // web socket which is closed, not our websocket->open comm proxy.\n", | |
" ws_proxy.onopen();\n", | |
"\n", | |
" fig.parent_element = element.get(0);\n", | |
" fig.cell_info = mpl.find_output_cell(\"<div id='\" + id + \"'></div>\");\n", | |
" if (!fig.cell_info) {\n", | |
" console.error(\"Failed to find cell for figure\", id, fig);\n", | |
" return;\n", | |
" }\n", | |
"\n", | |
" var output_index = fig.cell_info[2]\n", | |
" var cell = fig.cell_info[0];\n", | |
"\n", | |
"};\n", | |
"\n", | |
"mpl.figure.prototype.handle_close = function(fig, msg) {\n", | |
" fig.root.unbind('remove')\n", | |
"\n", | |
" // Update the output cell to use the data from the current canvas.\n", | |
" fig.push_to_output();\n", | |
" var dataURL = fig.canvas.toDataURL();\n", | |
" // Re-enable the keyboard manager in IPython - without this line, in FF,\n", | |
" // the notebook keyboard shortcuts fail.\n", | |
" IPython.keyboard_manager.enable()\n", | |
" $(fig.parent_element).html('<img src=\"' + dataURL + '\">');\n", | |
" fig.close_ws(fig, msg);\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.close_ws = function(fig, msg){\n", | |
" fig.send_message('closing', msg);\n", | |
" // fig.ws.close()\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.push_to_output = function(remove_interactive) {\n", | |
" // Turn the data on the canvas into data in the output cell.\n", | |
" var dataURL = this.canvas.toDataURL();\n", | |
" this.cell_info[1]['text/html'] = '<img src=\"' + dataURL + '\">';\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.updated_canvas_event = function() {\n", | |
" // Tell IPython that the notebook contents must change.\n", | |
" IPython.notebook.set_dirty(true);\n", | |
" this.send_message(\"ack\", {});\n", | |
" var fig = this;\n", | |
" // Wait a second, then push the new image to the DOM so\n", | |
" // that it is saved nicely (might be nice to debounce this).\n", | |
" setTimeout(function () { fig.push_to_output() }, 1000);\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._init_toolbar = function() {\n", | |
" var fig = this;\n", | |
"\n", | |
" var nav_element = $('<div/>')\n", | |
" nav_element.attr('style', 'width: 100%');\n", | |
" this.root.append(nav_element);\n", | |
"\n", | |
" // Define a callback function for later on.\n", | |
" function toolbar_event(event) {\n", | |
" return fig.toolbar_button_onclick(event['data']);\n", | |
" }\n", | |
" function toolbar_mouse_event(event) {\n", | |
" return fig.toolbar_button_onmouseover(event['data']);\n", | |
" }\n", | |
"\n", | |
" for(var toolbar_ind in mpl.toolbar_items){\n", | |
" var name = mpl.toolbar_items[toolbar_ind][0];\n", | |
" var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", | |
" var image = mpl.toolbar_items[toolbar_ind][2];\n", | |
" var method_name = mpl.toolbar_items[toolbar_ind][3];\n", | |
"\n", | |
" if (!name) { continue; };\n", | |
"\n", | |
" var button = $('<button class=\"btn btn-default\" href=\"#\" title=\"' + name + '\"><i class=\"fa ' + image + ' fa-lg\"></i></button>');\n", | |
" button.click(method_name, toolbar_event);\n", | |
" button.mouseover(tooltip, toolbar_mouse_event);\n", | |
" nav_element.append(button);\n", | |
" }\n", | |
"\n", | |
" // Add the status bar.\n", | |
" var status_bar = $('<span class=\"mpl-message\" style=\"text-align:right; float: right;\"/>');\n", | |
" nav_element.append(status_bar);\n", | |
" this.message = status_bar[0];\n", | |
"\n", | |
" // Add the close button to the window.\n", | |
" var buttongrp = $('<div class=\"btn-group inline pull-right\"></div>');\n", | |
" var button = $('<button class=\"btn btn-mini btn-primary\" href=\"#\" title=\"Stop Interaction\"><i class=\"fa fa-power-off icon-remove icon-large\"></i></button>');\n", | |
" button.click(function (evt) { fig.handle_close(fig, {}); } );\n", | |
" button.mouseover('Stop Interaction', toolbar_mouse_event);\n", | |
" buttongrp.append(button);\n", | |
" var titlebar = this.root.find($('.ui-dialog-titlebar'));\n", | |
" titlebar.prepend(buttongrp);\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._root_extra_style = function(el){\n", | |
" var fig = this\n", | |
" el.on(\"remove\", function(){\n", | |
"\tfig.close_ws(fig, {});\n", | |
" });\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._canvas_extra_style = function(el){\n", | |
" // this is important to make the div 'focusable\n", | |
" el.attr('tabindex', 0)\n", | |
" // reach out to IPython and tell the keyboard manager to turn it's self\n", | |
" // off when our div gets focus\n", | |
"\n", | |
" // location in version 3\n", | |
" if (IPython.notebook.keyboard_manager) {\n", | |
" IPython.notebook.keyboard_manager.register_events(el);\n", | |
" }\n", | |
" else {\n", | |
" // location in version 2\n", | |
" IPython.keyboard_manager.register_events(el);\n", | |
" }\n", | |
"\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype._key_event_extra = function(event, name) {\n", | |
" var manager = IPython.notebook.keyboard_manager;\n", | |
" if (!manager)\n", | |
" manager = IPython.keyboard_manager;\n", | |
"\n", | |
" // Check for shift+enter\n", | |
" if (event.shiftKey && event.which == 13) {\n", | |
" this.canvas_div.blur();\n", | |
" // select the cell after this one\n", | |
" var index = IPython.notebook.find_cell_index(this.cell_info[0]);\n", | |
" IPython.notebook.select(index + 1);\n", | |
" }\n", | |
"}\n", | |
"\n", | |
"mpl.figure.prototype.handle_save = function(fig, msg) {\n", | |
" fig.ondownload(fig, null);\n", | |
"}\n", | |
"\n", | |
"\n", | |
"mpl.find_output_cell = function(html_output) {\n", | |
" // Return the cell and output element which can be found *uniquely* in the notebook.\n", | |
" // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n", | |
" // IPython event is triggered only after the cells have been serialised, which for\n", | |
" // our purposes (turning an active figure into a static one), is too late.\n", | |
" var cells = IPython.notebook.get_cells();\n", | |
" var ncells = cells.length;\n", | |
" for (var i=0; i<ncells; i++) {\n", | |
" var cell = cells[i];\n", | |
" if (cell.cell_type === 'code'){\n", | |
" for (var j=0; j<cell.output_area.outputs.length; j++) {\n", | |
" var data = cell.output_area.outputs[j];\n", | |
" if (data.data) {\n", | |
" // IPython >= 3 moved mimebundle to data attribute of output\n", | |
" data = data.data;\n", | |
" }\n", | |
" if (data['text/html'] == html_output) {\n", | |
" return [cell, data, j];\n", | |
" }\n", | |
" }\n", | |
" }\n", | |
" }\n", | |
"}\n", | |
"\n", | |
"// Register the function which deals with the matplotlib target/channel.\n", | |
"// The kernel may be null if the page has been refreshed.\n", | |
"if (IPython.notebook.kernel != null) {\n", | |
" IPython.notebook.kernel.comm_manager.register_target('matplotlib', mpl.mpl_figure_comm);\n", | |
"}\n" | |
], | |
"text/plain": [ | |
"<IPython.core.display.Javascript object>" | |
] | |
}, | |
"metadata": {}, | |
"output_type": "display_data" | |
}, | |
{ | |
"data": { | |
"text/html": [ | |
"<img src=\"data:image/png;base64,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\">" | |
], | |
"text/plain": [ | |
"<IPython.core.display.HTML object>" | |
] | |
}, | |
"metadata": {}, | |
"output_type": "display_data" | |
} | |
], | |
"source": [ | |
"# Reindex using dates, specify as filling value 0, as previously discussed\n", | |
"fill_time_msgs = fill_time_msgs.reindex(dates, fill_value=0)\n", | |
"fill_time_msgs.index.name = 'date'\n", | |
"# Plot data\n", | |
"sns.pointplot(x='date', y='text_len', data=fill_time_msgs.reset_index())\n", | |
"sns.plt.gcf().autofmt_xdate()\n", | |
"sns.plt.show()" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"# Notes" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"I'm using this notebook as a personal \"wiki\" and practice field, as a mean to clarify my understanding and knowledge of the topic. I chose to share cause you know, you tend to do better when you go public, but also I'm hoping for possible feedback, comments or critiques. In the future I might explore other important operations here missing (take joining and merging for example), or go into more specific details on things already explored, also based on eventual feedback.\n", | |
"\n", | |
"**Pandas and Seaborn are pretty cool**, both for personal or work related projects, but I'm also open to possible new alternatives or additions to use for the task of data analysis and visualization, so feel free to give me your feedback on this too." | |
] | |
} | |
], | |
"metadata": { | |
"anaconda-cloud": {}, | |
"kernelspec": { | |
"display_name": "Python [conda env:kaggle]", | |
"language": "python", | |
"name": "conda-env-kaggle-py" | |
}, | |
"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.5.2" | |
} | |
}, | |
"nbformat": 4, | |
"nbformat_minor": 0 | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment