Skip to content

Instantly share code, notes, and snippets.

@douglasstarnes
Last active May 30, 2018 21:59
Show Gist options
  • Save douglasstarnes/8526722e5dd7fea848d07a44b055c359 to your computer and use it in GitHub Desktop.
Save douglasstarnes/8526722e5dd7fea848d07a44b055c359 to your computer and use it in GitHub Desktop.
Display the source blob
Display the rendered blob
Raw
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Matrix Multiplication"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Given a matrix $X$ with $n$ rows and $m$ columns,\n",
"\n",
"And a matrix $Y$ with $m$ rows and $p$ columns,\n",
"\n",
"The matrix $Z$ with $n$ rows and $p$ columns is a product of $X$ and $Y$ \n",
"\n",
"if\n",
"\n",
"$\\forall{i} \\in 1\\dots n, \\forall{j} \\in 1\\dots p$\n",
"\n",
"$Z_{ij} = \\displaystyle\\sum_{k=1}^{m} X_{ik}Y_{kj}$"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
"left = [[1, 2, 3], [4, 5, 6]] # 2 rows and 3 columns\n",
"right = [[1, 2], [3, 4], [5, 6]] # 3 rows and 2 columns"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [],
"source": [
"def get_row(m, i):\n",
" return m[i]\n",
"\n",
"def get_col(m, i):\n",
" return [row[i] for row in m]\n",
"\n",
"def dot_vector(l, r):\n",
" return sum([t[0] * t[1] for t in list(zip(l, r))])\n",
"\n",
"def zero_m(h, w):\n",
" m = []\n",
" for r in range(h):\n",
" m.append([0] * w)\n",
" return m\n",
" \n",
"\n",
"def shape_m(m):\n",
" return (len(m), len(m[0])) # (rows, cols)\n",
"\n",
"def dot_matrix(l, r):\n",
" rows_l, cols_l = shape_m(l)\n",
" rows_r, cols_r = shape_m(r)\n",
" \n",
" if not cols_l == rows_r:\n",
" raise ValueError('columns on left must equal rows on right')\n",
" \n",
" zeroes = zero_m(rows_l, cols_r)\n",
" \n",
" for row in range(rows_l):\n",
" for col in range(cols_r): \n",
" zeroes[row][col] = dot_vector(get_row(l, row), get_col(r, col)) # c_ij\n",
" \n",
" return zeroes\n",
" \n",
" "
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[[22, 28], [49, 64]]"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"dot_matrix(left, right)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## `numpy`\n",
"\n",
"The `numpy` library provides a more efficient implementation."
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [],
"source": [
"import numpy as np"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [],
"source": [
"p = np.array(left)\n",
"q = np.array(right)"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"array([[22, 28],\n",
" [49, 64]])"
]
},
"execution_count": 7,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"p.dot(q)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"A useful concept in Jupyter Notebook is the *magic command* which is similar to a macro. Magic commands are always prefixed with a percent sign (*%*)\n",
"\n",
"The `timeit` magic command will run the code in the rest of the line over and over and return the average run time."
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"8.71 µs ± 288 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)\n"
]
}
],
"source": [
"%timeit dot_matrix(left, right)"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"762 ns ± 11 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)\n"
]
}
],
"source": [
"%timeit p.dot(q)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"This shows that the brute force implementation of matrix multiplication is a lot slower than the `numpy` implementation which is written in C."
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {},
"outputs": [],
"source": [
"data = [[1, 2, 3], [2, 3, 4], [3, 4, 5]]"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[6, 9, 12]"
]
},
"execution_count": 11,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"def sum_vector(*args):\n",
" length = len(args[0])\n",
" for v in args:\n",
" if not len(v) == length:\n",
" raise ValueError('length of all vectors must be the same')\n",
" return [sum(t) for t in list(zip(*args))]\n",
"\n",
"sum_vector(*data)"
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"array([ 6, 9, 12])"
]
},
"execution_count": 12,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"np.array(data[0]) + np.array(data[1]) + np.array(data[2])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## `pandas`\n",
"\n",
"The `pandas` library is for statistical analysis"
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {},
"outputs": [],
"source": [
"import pandas as pd"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Data can be loaded from a csv file into a data frame. This is the fundamental data structure in pandas. It is similar to the data frame in R."
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {},
"outputs": [],
"source": [
"df = pd.read_csv('dow_jones_index/dow_jones_index.csv')"
]
},
{
"cell_type": "code",
"execution_count": 15,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>quarter</th>\n",
" <th>stock</th>\n",
" <th>date</th>\n",
" <th>open</th>\n",
" <th>high</th>\n",
" <th>low</th>\n",
" <th>close</th>\n",
" <th>volume</th>\n",
" <th>percent_change_price</th>\n",
" <th>percent_change_volume_over_last_wk</th>\n",
" <th>previous_weeks_volume</th>\n",
" <th>next_weeks_open</th>\n",
" <th>next_weeks_close</th>\n",
" <th>percent_change_next_weeks_price</th>\n",
" <th>days_to_next_dividend</th>\n",
" <th>percent_return_next_dividend</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>0</th>\n",
" <td>1</td>\n",
" <td>AA</td>\n",
" <td>1/7/2011</td>\n",
" <td>$15.82</td>\n",
" <td>$16.72</td>\n",
" <td>$15.78</td>\n",
" <td>$16.42</td>\n",
" <td>239655616</td>\n",
" <td>3.79267</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>$16.71</td>\n",
" <td>$15.97</td>\n",
" <td>-4.428490</td>\n",
" <td>26</td>\n",
" <td>0.182704</td>\n",
" </tr>\n",
" <tr>\n",
" <th>1</th>\n",
" <td>1</td>\n",
" <td>AA</td>\n",
" <td>1/14/2011</td>\n",
" <td>$16.71</td>\n",
" <td>$16.71</td>\n",
" <td>$15.64</td>\n",
" <td>$15.97</td>\n",
" <td>242963398</td>\n",
" <td>-4.42849</td>\n",
" <td>1.380223</td>\n",
" <td>239655616.0</td>\n",
" <td>$16.19</td>\n",
" <td>$15.79</td>\n",
" <td>-2.470660</td>\n",
" <td>19</td>\n",
" <td>0.187852</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2</th>\n",
" <td>1</td>\n",
" <td>AA</td>\n",
" <td>1/21/2011</td>\n",
" <td>$16.19</td>\n",
" <td>$16.38</td>\n",
" <td>$15.60</td>\n",
" <td>$15.79</td>\n",
" <td>138428495</td>\n",
" <td>-2.47066</td>\n",
" <td>-43.024959</td>\n",
" <td>242963398.0</td>\n",
" <td>$15.87</td>\n",
" <td>$16.13</td>\n",
" <td>1.638310</td>\n",
" <td>12</td>\n",
" <td>0.189994</td>\n",
" </tr>\n",
" <tr>\n",
" <th>3</th>\n",
" <td>1</td>\n",
" <td>AA</td>\n",
" <td>1/28/2011</td>\n",
" <td>$15.87</td>\n",
" <td>$16.63</td>\n",
" <td>$15.82</td>\n",
" <td>$16.13</td>\n",
" <td>151379173</td>\n",
" <td>1.63831</td>\n",
" <td>9.355500</td>\n",
" <td>138428495.0</td>\n",
" <td>$16.18</td>\n",
" <td>$17.14</td>\n",
" <td>5.933250</td>\n",
" <td>5</td>\n",
" <td>0.185989</td>\n",
" </tr>\n",
" <tr>\n",
" <th>4</th>\n",
" <td>1</td>\n",
" <td>AA</td>\n",
" <td>2/4/2011</td>\n",
" <td>$16.18</td>\n",
" <td>$17.39</td>\n",
" <td>$16.18</td>\n",
" <td>$17.14</td>\n",
" <td>154387761</td>\n",
" <td>5.93325</td>\n",
" <td>1.987452</td>\n",
" <td>151379173.0</td>\n",
" <td>$17.33</td>\n",
" <td>$17.37</td>\n",
" <td>0.230814</td>\n",
" <td>97</td>\n",
" <td>0.175029</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" quarter stock date open high low close volume \\\n",
"0 1 AA 1/7/2011 $15.82 $16.72 $15.78 $16.42 239655616 \n",
"1 1 AA 1/14/2011 $16.71 $16.71 $15.64 $15.97 242963398 \n",
"2 1 AA 1/21/2011 $16.19 $16.38 $15.60 $15.79 138428495 \n",
"3 1 AA 1/28/2011 $15.87 $16.63 $15.82 $16.13 151379173 \n",
"4 1 AA 2/4/2011 $16.18 $17.39 $16.18 $17.14 154387761 \n",
"\n",
" percent_change_price percent_change_volume_over_last_wk \\\n",
"0 3.79267 NaN \n",
"1 -4.42849 1.380223 \n",
"2 -2.47066 -43.024959 \n",
"3 1.63831 9.355500 \n",
"4 5.93325 1.987452 \n",
"\n",
" previous_weeks_volume next_weeks_open next_weeks_close \\\n",
"0 NaN $16.71 $15.97 \n",
"1 239655616.0 $16.19 $15.79 \n",
"2 242963398.0 $15.87 $16.13 \n",
"3 138428495.0 $16.18 $17.14 \n",
"4 151379173.0 $17.33 $17.37 \n",
"\n",
" percent_change_next_weeks_price days_to_next_dividend \\\n",
"0 -4.428490 26 \n",
"1 -2.470660 19 \n",
"2 1.638310 12 \n",
"3 5.933250 5 \n",
"4 0.230814 97 \n",
"\n",
" percent_return_next_dividend \n",
"0 0.182704 \n",
"1 0.187852 \n",
"2 0.189994 \n",
"3 0.185989 \n",
"4 0.175029 "
]
},
"execution_count": 15,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"df.head()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Take a subset of the columns and copy them to suppress a warning."
]
},
{
"cell_type": "code",
"execution_count": 16,
"metadata": {},
"outputs": [],
"source": [
"v = df[df.columns[1:8]].copy()"
]
},
{
"cell_type": "code",
"execution_count": 17,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>stock</th>\n",
" <th>date</th>\n",
" <th>open</th>\n",
" <th>high</th>\n",
" <th>low</th>\n",
" <th>close</th>\n",
" <th>volume</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>0</th>\n",
" <td>AA</td>\n",
" <td>1/7/2011</td>\n",
" <td>$15.82</td>\n",
" <td>$16.72</td>\n",
" <td>$15.78</td>\n",
" <td>$16.42</td>\n",
" <td>239655616</td>\n",
" </tr>\n",
" <tr>\n",
" <th>1</th>\n",
" <td>AA</td>\n",
" <td>1/14/2011</td>\n",
" <td>$16.71</td>\n",
" <td>$16.71</td>\n",
" <td>$15.64</td>\n",
" <td>$15.97</td>\n",
" <td>242963398</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2</th>\n",
" <td>AA</td>\n",
" <td>1/21/2011</td>\n",
" <td>$16.19</td>\n",
" <td>$16.38</td>\n",
" <td>$15.60</td>\n",
" <td>$15.79</td>\n",
" <td>138428495</td>\n",
" </tr>\n",
" <tr>\n",
" <th>3</th>\n",
" <td>AA</td>\n",
" <td>1/28/2011</td>\n",
" <td>$15.87</td>\n",
" <td>$16.63</td>\n",
" <td>$15.82</td>\n",
" <td>$16.13</td>\n",
" <td>151379173</td>\n",
" </tr>\n",
" <tr>\n",
" <th>4</th>\n",
" <td>AA</td>\n",
" <td>2/4/2011</td>\n",
" <td>$16.18</td>\n",
" <td>$17.39</td>\n",
" <td>$16.18</td>\n",
" <td>$17.14</td>\n",
" <td>154387761</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" stock date open high low close volume\n",
"0 AA 1/7/2011 $15.82 $16.72 $15.78 $16.42 239655616\n",
"1 AA 1/14/2011 $16.71 $16.71 $15.64 $15.97 242963398\n",
"2 AA 1/21/2011 $16.19 $16.38 $15.60 $15.79 138428495\n",
"3 AA 1/28/2011 $15.87 $16.63 $15.82 $16.13 151379173\n",
"4 AA 2/4/2011 $16.18 $17.39 $16.18 $17.14 154387761"
]
},
"execution_count": 17,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"v.head()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The values in the columns with a dollar sign are currently of type `str`. However, we'd need to work with them as `float`. This is easy in `pandas` converting 3000 values with 2 lines of code. The `lambda` is an anonymous function that slices the value thus omitting the dollar sign and then converting it to a `float`. This function is applied to every value in a column and then assigned back to that column in the data frame."
]
},
{
"cell_type": "code",
"execution_count": 18,
"metadata": {},
"outputs": [],
"source": [
"for column in v.columns[2:6]:\n",
" v[column] = v[column].apply(lambda x: float(x[1:]), 1)"
]
},
{
"cell_type": "code",
"execution_count": 19,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>stock</th>\n",
" <th>date</th>\n",
" <th>open</th>\n",
" <th>high</th>\n",
" <th>low</th>\n",
" <th>close</th>\n",
" <th>volume</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>0</th>\n",
" <td>AA</td>\n",
" <td>1/7/2011</td>\n",
" <td>15.82</td>\n",
" <td>16.72</td>\n",
" <td>15.78</td>\n",
" <td>16.42</td>\n",
" <td>239655616</td>\n",
" </tr>\n",
" <tr>\n",
" <th>1</th>\n",
" <td>AA</td>\n",
" <td>1/14/2011</td>\n",
" <td>16.71</td>\n",
" <td>16.71</td>\n",
" <td>15.64</td>\n",
" <td>15.97</td>\n",
" <td>242963398</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2</th>\n",
" <td>AA</td>\n",
" <td>1/21/2011</td>\n",
" <td>16.19</td>\n",
" <td>16.38</td>\n",
" <td>15.60</td>\n",
" <td>15.79</td>\n",
" <td>138428495</td>\n",
" </tr>\n",
" <tr>\n",
" <th>3</th>\n",
" <td>AA</td>\n",
" <td>1/28/2011</td>\n",
" <td>15.87</td>\n",
" <td>16.63</td>\n",
" <td>15.82</td>\n",
" <td>16.13</td>\n",
" <td>151379173</td>\n",
" </tr>\n",
" <tr>\n",
" <th>4</th>\n",
" <td>AA</td>\n",
" <td>2/4/2011</td>\n",
" <td>16.18</td>\n",
" <td>17.39</td>\n",
" <td>16.18</td>\n",
" <td>17.14</td>\n",
" <td>154387761</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" stock date open high low close volume\n",
"0 AA 1/7/2011 15.82 16.72 15.78 16.42 239655616\n",
"1 AA 1/14/2011 16.71 16.71 15.64 15.97 242963398\n",
"2 AA 1/21/2011 16.19 16.38 15.60 15.79 138428495\n",
"3 AA 1/28/2011 15.87 16.63 15.82 16.13 151379173\n",
"4 AA 2/4/2011 16.18 17.39 16.18 17.14 154387761"
]
},
"execution_count": 19,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"v.head()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"A data frame can be filtered with boolean expressions by one or more columns."
]
},
{
"cell_type": "code",
"execution_count": 20,
"metadata": {},
"outputs": [],
"source": [
"listing = v[v['stock'] == 'HD']"
]
},
{
"cell_type": "code",
"execution_count": 21,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>stock</th>\n",
" <th>date</th>\n",
" <th>open</th>\n",
" <th>high</th>\n",
" <th>low</th>\n",
" <th>close</th>\n",
" <th>volume</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>120</th>\n",
" <td>HD</td>\n",
" <td>1/7/2011</td>\n",
" <td>35.20</td>\n",
" <td>35.57</td>\n",
" <td>34.18</td>\n",
" <td>34.38</td>\n",
" <td>56576860</td>\n",
" </tr>\n",
" <tr>\n",
" <th>121</th>\n",
" <td>HD</td>\n",
" <td>1/14/2011</td>\n",
" <td>34.16</td>\n",
" <td>35.89</td>\n",
" <td>34.07</td>\n",
" <td>35.89</td>\n",
" <td>41918450</td>\n",
" </tr>\n",
" <tr>\n",
" <th>122</th>\n",
" <td>HD</td>\n",
" <td>1/21/2011</td>\n",
" <td>35.97</td>\n",
" <td>36.99</td>\n",
" <td>35.48</td>\n",
" <td>36.51</td>\n",
" <td>45249920</td>\n",
" </tr>\n",
" <tr>\n",
" <th>123</th>\n",
" <td>HD</td>\n",
" <td>1/28/2011</td>\n",
" <td>36.46</td>\n",
" <td>38.12</td>\n",
" <td>36.30</td>\n",
" <td>36.70</td>\n",
" <td>63397367</td>\n",
" </tr>\n",
" <tr>\n",
" <th>124</th>\n",
" <td>HD</td>\n",
" <td>2/4/2011</td>\n",
" <td>37.13</td>\n",
" <td>37.18</td>\n",
" <td>36.38</td>\n",
" <td>36.80</td>\n",
" <td>41844858</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" stock date open high low close volume\n",
"120 HD 1/7/2011 35.20 35.57 34.18 34.38 56576860\n",
"121 HD 1/14/2011 34.16 35.89 34.07 35.89 41918450\n",
"122 HD 1/21/2011 35.97 36.99 35.48 36.51 45249920\n",
"123 HD 1/28/2011 36.46 38.12 36.30 36.70 63397367\n",
"124 HD 2/4/2011 37.13 37.18 36.38 36.80 41844858"
]
},
"execution_count": 21,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"listing.head()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## `matplotlib`\n",
"\n",
"The `matplotlib` library is for visualization of data."
]
},
{
"cell_type": "code",
"execution_count": 22,
"metadata": {},
"outputs": [],
"source": [
"import matplotlib.pyplot as plt\n",
"%matplotlib inline"
]
},
{
"cell_type": "code",
"execution_count": 23,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[<matplotlib.lines.Line2D at 0x1162afd30>]"
]
},
"execution_count": 23,
"metadata": {},
"output_type": "execute_result"
},
{
"data": {
"image/png": "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\n",
"text/plain": [
"<Figure size 432x288 with 2 Axes>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"y = listing.close\n",
"x = list(range(len(y)))\n",
"\n",
"plt.xticks(np.arange(len(x)), listing.date, rotation='vertical')\n",
"\n",
"y2 = listing.volume\n",
"plt.bar(x, y2, color='#cccccc')\n",
"ax2 = plt.twinx()\n",
"ax2.plot(x, y, color='k')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Dashboard"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Using `ipywidgets` will create interactive JavaScript controls which can be used to manipulate data with a friendly interface instead of code"
]
},
{
"cell_type": "code",
"execution_count": 24,
"metadata": {},
"outputs": [],
"source": [
"import ipywidgets as widgets\n",
"from IPython.core.display import display"
]
},
{
"cell_type": "code",
"execution_count": 25,
"metadata": {},
"outputs": [
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "7f7020e019a543e9bd968126cb4ab4b7",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"SelectionRangeSlider(description='Dates', index=(0, 24), layout=Layout(width='500px'), options=('1/7/2011', '1…"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "b24cf5fbfe7b4d04b61c773ec7528636",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"Checkbox(value=False, description='Show Volume')"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "2845bfe18ded4c349a188caeb226e012",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"Dropdown(options=('AA', 'AXP', 'BA', 'BAC', 'CAT', 'CSCO', 'CVX', 'DD', 'DIS', 'GE', 'HD', 'HPQ', 'IBM', 'INTC…"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "4784e67daf2b4a68a917b240332be34a",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"Button(description='Show Graph', style=ButtonStyle())"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"stock_dict = {}\n",
"\n",
"for stock in v.stock.unique():\n",
" stock_dict[stock] = v[v.stock == stock]\n",
"\n",
"date_slider = widgets.SelectionRangeSlider(options=list(stock_dict['AA'].date), \n",
" index=(0, 24), \n",
" description='Dates', \n",
" layout=widgets.Layout(width='500px'))\n",
"show_vol2 = widgets.Checkbox(description='Show Volume')\n",
"dd_select3 = widgets.Dropdown(options=sorted(list(stock_dict.keys())))\n",
"button = widgets.Button(description='Show Graph')\n",
"\n",
"def button_click(b):\n",
" stock_row = stock_dict[dd_select3.value]\n",
" start_index = date_slider.index[0]\n",
" end_index = date_slider.index[1]\n",
" stock_row = stock_row[start_index:(end_index+1)]\n",
" \n",
" y = stock_row.close\n",
" x = list(range(len(y)))\n",
" \n",
" plt.xticks(np.arange(len(x)), stock_row.date, rotation='vertical')\n",
" \n",
" if show_vol2.value == False:\n",
" plt.plot(x, y, color='k')\n",
" else:\n",
" y2 = stock_row.volume\n",
" plt.bar(x, y2, color='#cccccc')\n",
" ax2 = plt.twinx()\n",
" ax2.plot(x, y, color='k')\n",
"\n",
"button.on_click(button_click)\n",
"\n",
"display(date_slider)\n",
"display(show_vol2)\n",
"display(dd_select3)\n",
"display(button)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.6.5"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment