├── 01-ipywidgets.ipynb ├── 02-ipywidgets-takes-on-the-datasaurus.ipynb ├── 03-bqplot-takes-on-the-datasaurus.ipynb ├── 04-plotly-and-dash-take-on-the-datasaurus.ipynb ├── 05-altair-takes-on-the-datasaurus.ipynb ├── 4fun-altair.ipynb ├── 4fun-bqplot.ipynb ├── 4fun-ipywidgets.ipynb ├── README.md ├── data ├── DatasaurusDozen.tsv └── latimes-agency-totals.csv ├── exercises ├── altaircars.py ├── altairowidgetasaurus.py ├── ipywidgets1.py ├── ipywidgets2.py ├── plotlysaurus.py └── widgetosaurus.py └── requirements.txt /01-ipywidgets.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# ipywidgets" 8 | ] 9 | }, 10 | { 11 | "cell_type": "markdown", 12 | "metadata": {}, 13 | "source": [ 14 | "The interact function (ipywidgets.interact) automatically creates user interface (UI) controls for exploring code and data interactively." 15 | ] 16 | }, 17 | { 18 | "cell_type": "code", 19 | "execution_count": null, 20 | "metadata": {}, 21 | "outputs": [], 22 | "source": [ 23 | "import ipywidgets" 24 | ] 25 | }, 26 | { 27 | "cell_type": "code", 28 | "execution_count": null, 29 | "metadata": {}, 30 | "outputs": [], 31 | "source": [ 32 | "def f(x):\n", 33 | " return x" 34 | ] 35 | }, 36 | { 37 | "cell_type": "code", 38 | "execution_count": null, 39 | "metadata": {}, 40 | "outputs": [], 41 | "source": [ 42 | "f(10)" 43 | ] 44 | }, 45 | { 46 | "cell_type": "code", 47 | "execution_count": null, 48 | "metadata": {}, 49 | "outputs": [], 50 | "source": [ 51 | "f('a string')" 52 | ] 53 | }, 54 | { 55 | "cell_type": "code", 56 | "execution_count": null, 57 | "metadata": {}, 58 | "outputs": [], 59 | "source": [ 60 | "ipywidgets.interact(f, x=10);" 61 | ] 62 | }, 63 | { 64 | "cell_type": "code", 65 | "execution_count": null, 66 | "metadata": {}, 67 | "outputs": [], 68 | "source": [ 69 | "ipywidgets.interact(f, x=True);" 70 | ] 71 | }, 72 | { 73 | "cell_type": "code", 74 | "execution_count": null, 75 | "metadata": {}, 76 | "outputs": [], 77 | "source": [ 78 | "ipywidgets.interact(f, x=10.6);" 79 | ] 80 | }, 81 | { 82 | "cell_type": "code", 83 | "execution_count": null, 84 | "metadata": {}, 85 | "outputs": [], 86 | "source": [ 87 | "def h(p, q):\n", 88 | " return (p, q)" 89 | ] 90 | }, 91 | { 92 | "cell_type": "code", 93 | "execution_count": null, 94 | "metadata": {}, 95 | "outputs": [], 96 | "source": [ 97 | "ipywidgets.interact(h, p=6, q=5);" 98 | ] 99 | }, 100 | { 101 | "cell_type": "code", 102 | "execution_count": null, 103 | "metadata": {}, 104 | "outputs": [], 105 | "source": [ 106 | "ipywidgets.interact(h,\n", 107 | " p=6,\n", 108 | " q=ipywidgets.IntSlider(min=0,max=100,step=5,value=10));" 109 | ] 110 | }, 111 | { 112 | "cell_type": "code", 113 | "execution_count": null, 114 | "metadata": {}, 115 | "outputs": [], 116 | "source": [ 117 | "ipywidgets.interact(h, p=6, q=(0,100,5));" 118 | ] 119 | }, 120 | { 121 | "cell_type": "code", 122 | "execution_count": null, 123 | "metadata": {}, 124 | "outputs": [], 125 | "source": [ 126 | "ipywidgets.interact(f, x=['apples','oranges']);" 127 | ] 128 | }, 129 | { 130 | "cell_type": "code", 131 | "execution_count": null, 132 | "metadata": {}, 133 | "outputs": [], 134 | "source": [ 135 | "ipywidgets.interact(f, x=[('apples',10),('oranges',20)]);" 136 | ] 137 | }, 138 | { 139 | "cell_type": "code", 140 | "execution_count": null, 141 | "metadata": {}, 142 | "outputs": [], 143 | "source": [ 144 | "from IPython.display import display\n", 145 | "def f(a, b):\n", 146 | " display(a + b)\n", 147 | " return a+b" 148 | ] 149 | }, 150 | { 151 | "cell_type": "code", 152 | "execution_count": null, 153 | "metadata": {}, 154 | "outputs": [], 155 | "source": [ 156 | "w = ipywidgets.interactive(f, a=10, b=20)" 157 | ] 158 | }, 159 | { 160 | "cell_type": "code", 161 | "execution_count": null, 162 | "metadata": {}, 163 | "outputs": [], 164 | "source": [ 165 | "w" 166 | ] 167 | }, 168 | { 169 | "cell_type": "code", 170 | "execution_count": null, 171 | "metadata": {}, 172 | "outputs": [], 173 | "source": [ 174 | "type(w)" 175 | ] 176 | }, 177 | { 178 | "cell_type": "code", 179 | "execution_count": null, 180 | "metadata": {}, 181 | "outputs": [], 182 | "source": [ 183 | "w.children" 184 | ] 185 | }, 186 | { 187 | "cell_type": "code", 188 | "execution_count": null, 189 | "metadata": {}, 190 | "outputs": [], 191 | "source": [ 192 | "w.kwargs" 193 | ] 194 | }, 195 | { 196 | "cell_type": "code", 197 | "execution_count": null, 198 | "metadata": {}, 199 | "outputs": [], 200 | "source": [ 201 | "w.result" 202 | ] 203 | }, 204 | { 205 | "cell_type": "code", 206 | "execution_count": null, 207 | "metadata": {}, 208 | "outputs": [], 209 | "source": [ 210 | "def slow_function(i):\n", 211 | " print(int(i),list(x for x in range(int(i)) if\n", 212 | " str(x)==str(x)[::-1] and\n", 213 | " str(x**2)==str(x**2)[::-1]))\n", 214 | " return" 215 | ] 216 | }, 217 | { 218 | "cell_type": "code", 219 | "execution_count": null, 220 | "metadata": {}, 221 | "outputs": [], 222 | "source": [ 223 | "ipywidgets.interact(slow_function,i=ipywidgets.FloatSlider(min=1e5, max=1e7, step=1e5));" 224 | ] 225 | }, 226 | { 227 | "cell_type": "code", 228 | "execution_count": null, 229 | "metadata": {}, 230 | "outputs": [], 231 | "source": [ 232 | "ipywidgets.interact_manual(slow_function,i=ipywidgets.FloatSlider(min=1e5, max=1e7, step=1e5));" 233 | ] 234 | }, 235 | { 236 | "cell_type": "code", 237 | "execution_count": null, 238 | "metadata": {}, 239 | "outputs": [], 240 | "source": [ 241 | "ipywidgets.interact(slow_function,i=ipywidgets.FloatSlider(min=1e5, max=1e7, step=1e5,continuous_update=False));" 242 | ] 243 | }, 244 | { 245 | "cell_type": "code", 246 | "execution_count": null, 247 | "metadata": {}, 248 | "outputs": [], 249 | "source": [ 250 | "#\n", 251 | "# Exercise: \n", 252 | "# Execute this cell to see what the function does.\n", 253 | "# Then use interact to make an interactive control for this function\n", 254 | "#\n", 255 | "\n", 256 | "def reverse(x):\n", 257 | " return x[::-1]\n", 258 | "\n", 259 | "reverse('I am printed backwards.')\n" 260 | ] 261 | }, 262 | { 263 | "cell_type": "code", 264 | "execution_count": null, 265 | "metadata": {}, 266 | "outputs": [], 267 | "source": [ 268 | "# Execute this cell to see an example solution\n", 269 | "%load 'exercises/ipywidgets1.py'" 270 | ] 271 | }, 272 | { 273 | "cell_type": "markdown", 274 | "metadata": {}, 275 | "source": [ 276 | "# Usefulness in exploring data and visualization" 277 | ] 278 | }, 279 | { 280 | "cell_type": "markdown", 281 | "metadata": {}, 282 | "source": [ 283 | "## Let's take the Mean and Standard Deviation\n", 284 | "\n", 285 | "How can we conceptually grasp these concepts?" 286 | ] 287 | }, 288 | { 289 | "cell_type": "markdown", 290 | "metadata": {}, 291 | "source": [ 292 | "Gaussian distribution function:\n", 293 | "\n", 294 | "$$y(x) = \\frac{1}{\\sigma\\sqrt{2\\pi}}\\text{exp}\\left(-\\frac{1}{2}\\frac{(x-\\mu)^2}{\\sigma^2}\\right)$$\n", 295 | "\n", 296 | "* $\\mu$ is the mean\n", 297 | "* $\\sigma$ is the standard deviation" 298 | ] 299 | }, 300 | { 301 | "cell_type": "code", 302 | "execution_count": null, 303 | "metadata": {}, 304 | "outputs": [], 305 | "source": [ 306 | "import matplotlib.pyplot as plt\n", 307 | "import numpy as np" 308 | ] 309 | }, 310 | { 311 | "cell_type": "code", 312 | "execution_count": null, 313 | "metadata": {}, 314 | "outputs": [], 315 | "source": [ 316 | "mu = 0\n", 317 | "sigma = 1\n", 318 | "\n", 319 | "x = np.linspace(-10,10,200)\n", 320 | "y = 1/sigma/np.sqrt(2*np.pi) * np.exp(-0.5*((x-mu)**2/sigma**2))\n", 321 | "\n", 322 | "plt.figure(figsize=(8,4))\n", 323 | "plt.plot(x,y,'k-')\n", 324 | "plt.grid()\n", 325 | "plt.ylim([-0.01,0.5])\n", 326 | "plt.show()" 327 | ] 328 | }, 329 | { 330 | "cell_type": "code", 331 | "execution_count": null, 332 | "metadata": {}, 333 | "outputs": [], 334 | "source": [ 335 | "def gaussian(mu=0,sigma=1):\n", 336 | " x = np.linspace(-10,10,200)\n", 337 | " y = 1/sigma/np.sqrt(2*np.pi) * np.exp(-0.5*((x-mu)**2/sigma**2))\n", 338 | "\n", 339 | " plt.figure(figsize=(8,4))\n", 340 | " plt.plot(x,y,'k-')\n", 341 | " plt.grid()\n", 342 | " plt.ylim([-0.01,0.5])\n", 343 | " plt.show()" 344 | ] 345 | }, 346 | { 347 | "cell_type": "code", 348 | "execution_count": null, 349 | "metadata": {}, 350 | "outputs": [], 351 | "source": [ 352 | "gaussian(0,1)" 353 | ] 354 | }, 355 | { 356 | "cell_type": "code", 357 | "execution_count": null, 358 | "metadata": {}, 359 | "outputs": [], 360 | "source": [ 361 | "ipywidgets.interactive(gaussian,mu=(-10,10),sigma=(0.1,10))" 362 | ] 363 | }, 364 | { 365 | "cell_type": "code", 366 | "execution_count": null, 367 | "metadata": {}, 368 | "outputs": [], 369 | "source": [ 370 | "def plotgaus(mu=0,sigma=1):\n", 371 | "\n", 372 | " x = np.linspace(-5,5,100)\n", 373 | " y = 1 / (sigma*np.sqrt(2*np.pi)) * np.exp(-1/2 * (x-mu)**2 / sigma**2)\n", 374 | "\n", 375 | " plt.figure(figsize=(6,5))\n", 376 | " plt.plot(x,y,lw=2)\n", 377 | " plt.ylim([0,0.5])\n", 378 | " plt.xlim([-5,5])\n", 379 | " plt.xlabel('x',fontsize=16)\n", 380 | " plt.ylabel('y',fontsize=16)\n", 381 | " plt.xticks(np.linspace(-5,5,11))\n", 382 | "\n", 383 | " plt.text(6,0.45,f'$\\mu = {mu}$',fontsize=16,color='red')\n", 384 | " plt.plot([mu,mu],\n", 385 | " [0,1 / (sigma*np.sqrt(2*np.pi)) * np.exp(-1/2 * (mu-mu)**2 / sigma**2)],\n", 386 | " 'r--')\n", 387 | " plt.plot([mu,mu+sigma*np.sqrt(2*np.log(2))],\n", 388 | " [1 / (sigma*np.sqrt(2*np.pi)) / 2, 1 / (sigma*np.sqrt(2*np.pi)) / 2],\n", 389 | " 'g--')\n", 390 | " plt.text(6,0.4,f'$\\sigma = {sigma}$',fontsize=16)\n", 391 | " plt.text(6,0.35,'{:s}{:.2f}'.format('half-width half max = $\\sigma\\sqrt{2\\ln2} = $',\n", 392 | " sigma*np.sqrt(2*np.log(2))),color='green',fontsize=16)\n", 393 | " plt.show()\n", 394 | " \n", 395 | "ipywidgets.interactive(plotgaus,mu=(-5,5),sigma=(0.1,3))" 396 | ] 397 | }, 398 | { 399 | "cell_type": "code", 400 | "execution_count": null, 401 | "metadata": {}, 402 | "outputs": [], 403 | "source": [ 404 | "#\n", 405 | "# Exercise: Here is a function that plots sin(k*x + p)\n", 406 | "# Execute the cell to see an example plot\n", 407 | "# Then use interact to make sliders for the parameters $k$ and $p$, \n", 408 | "# where 0.5 <= k <= 2 and 0 <= p <= 2*pi (hint: use `np.pi` for pi).\n", 409 | "#\n", 410 | "#\n", 411 | "def plot_f(k, p):\n", 412 | " x = np.linspace(0, 4 * np.pi)\n", 413 | " y = np.sin(k*x + p)\n", 414 | " plt.plot(x, y)\n", 415 | " \n", 416 | "plot_f(1.3, 3)" 417 | ] 418 | }, 419 | { 420 | "cell_type": "code", 421 | "execution_count": null, 422 | "metadata": {}, 423 | "outputs": [], 424 | "source": [ 425 | "# Execute this cell to see an example solution\n", 426 | "%load 'exercises/ipywidgets2.py'" 427 | ] 428 | }, 429 | { 430 | "cell_type": "code", 431 | "execution_count": null, 432 | "metadata": {}, 433 | "outputs": [], 434 | "source": [] 435 | } 436 | ], 437 | "metadata": { 438 | "kernelspec": { 439 | "display_name": "Python 3", 440 | "language": "python", 441 | "name": "python3" 442 | }, 443 | "language_info": { 444 | "codemirror_mode": { 445 | "name": "ipython", 446 | "version": 3 447 | }, 448 | "file_extension": ".py", 449 | "mimetype": "text/x-python", 450 | "name": "python", 451 | "nbconvert_exporter": "python", 452 | "pygments_lexer": "ipython3", 453 | "version": "3.8.8" 454 | }, 455 | "toc": { 456 | "base_numbering": 1, 457 | "nav_menu": {}, 458 | "number_sections": true, 459 | "sideBar": true, 460 | "skip_h1_title": false, 461 | "title_cell": "Table of Contents", 462 | "title_sidebar": "Contents", 463 | "toc_cell": false, 464 | "toc_position": {}, 465 | "toc_section_display": true, 466 | "toc_window_display": false 467 | } 468 | }, 469 | "nbformat": 4, 470 | "nbformat_minor": 2 471 | } 472 | -------------------------------------------------------------------------------- /02-ipywidgets-takes-on-the-datasaurus.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "This dataset is taken from a research study called The Datasaurus Dozen by Autodesk research and the original Datasaurus provided by Alberto Cairo." 8 | ] 9 | }, 10 | { 11 | "cell_type": "code", 12 | "execution_count": null, 13 | "metadata": {}, 14 | "outputs": [], 15 | "source": [ 16 | "import pandas as pd\n", 17 | "import matplotlib.pyplot as plt\n", 18 | "import ipywidgets" 19 | ] 20 | }, 21 | { 22 | "cell_type": "code", 23 | "execution_count": null, 24 | "metadata": {}, 25 | "outputs": [], 26 | "source": [ 27 | "dinodf = pd.read_csv('data/DatasaurusDozen.tsv', delimiter='\\t')" 28 | ] 29 | }, 30 | { 31 | "cell_type": "code", 32 | "execution_count": null, 33 | "metadata": {}, 34 | "outputs": [], 35 | "source": [ 36 | "dinodf" 37 | ] 38 | }, 39 | { 40 | "cell_type": "code", 41 | "execution_count": null, 42 | "metadata": {}, 43 | "outputs": [], 44 | "source": [ 45 | "fig,ax = plt.subplots(2,2,figsize=(8,8))\n", 46 | "\n", 47 | "x = dinodf[dinodf.dataset=='dino'].x\n", 48 | "y = dinodf[dinodf.dataset=='dino'].y\n", 49 | "\n", 50 | "ax[1,0].scatter(x, y)\n", 51 | "\n", 52 | "ax[0,0].hist(x, bins=10, rwidth=0.9)\n", 53 | "ax[1,1].hist(y, bins=10, rwidth=0.9, orientation='horizontal')\n", 54 | "\n", 55 | "ax[0,1].text(0.2,0.8,'x_mean = {:.2f}'.format(x.mean()))\n", 56 | "ax[0,1].text(0.2,0.7,'x_stddev = {:.2f}'.format(x.std()))\n", 57 | "ax[0,1].text(0.2,0.6,'y_mean = {:.2f}'.format(y.mean()))\n", 58 | "ax[0,1].text(0.2,0.5,'y_stddev = {:.2f}'.format(y.std()))\n", 59 | "ax[0,1].text(0.2,0.4,'corr = {:.2f}'.format(x.corr(y)))\n", 60 | "\n", 61 | "fig.show()" 62 | ] 63 | }, 64 | { 65 | "cell_type": "code", 66 | "execution_count": null, 67 | "metadata": {}, 68 | "outputs": [], 69 | "source": [ 70 | "#\n", 71 | "# Exercise: use ipywidgets to make a menu for cycling through all of the dataset values\n", 72 | "# \n", 73 | "\n" 74 | ] 75 | }, 76 | { 77 | "cell_type": "code", 78 | "execution_count": null, 79 | "metadata": {}, 80 | "outputs": [], 81 | "source": [ 82 | "# Execute this cell to see one possible solution\n", 83 | "%load 'exercises/widgetosaurus.py'" 84 | ] 85 | } 86 | ], 87 | "metadata": { 88 | "kernelspec": { 89 | "display_name": "Python 3", 90 | "language": "python", 91 | "name": "python3" 92 | }, 93 | "language_info": { 94 | "codemirror_mode": { 95 | "name": "ipython", 96 | "version": 3 97 | }, 98 | "file_extension": ".py", 99 | "mimetype": "text/x-python", 100 | "name": "python", 101 | "nbconvert_exporter": "python", 102 | "pygments_lexer": "ipython3", 103 | "version": "3.8.8" 104 | }, 105 | "toc": { 106 | "base_numbering": 1, 107 | "nav_menu": {}, 108 | "number_sections": true, 109 | "sideBar": true, 110 | "skip_h1_title": false, 111 | "title_cell": "Table of Contents", 112 | "title_sidebar": "Contents", 113 | "toc_cell": false, 114 | "toc_position": {}, 115 | "toc_section_display": true, 116 | "toc_window_display": false 117 | } 118 | }, 119 | "nbformat": 4, 120 | "nbformat_minor": 2 121 | } 122 | -------------------------------------------------------------------------------- /03-bqplot-takes-on-the-datasaurus.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "Note: This notebook is taken from Chakri Cherukuri's [GitHub repo](https://github.com/ChakriCherukuri/mlviz) showing a variety of examples on using bqplot to visualize theoretical and applied machine learning algorithms/models.\n", 8 | "*****" 9 | ] 10 | }, 11 | { 12 | "cell_type": "markdown", 13 | "metadata": {}, 14 | "source": [ 15 | "In this notebook we'll look at how data visualization can help us understand the relationship between two features(x and y). This is based on a research study called The Datasaurus Dozen by Autodesk research and the original Datasaurus provided by Alberto Cairo.\n", 16 | "\n", 17 | "Takeaway: Never trust summary statistics alone; always visualize your data\n", 18 | "\n", 19 | "Use the dropdown to select different datasets. Note that the basic stats (first, second moments and correlation) are almost the same for all the datasets eventhough though the relationships between `x` and `y` are quite different (as evident from the scatter plot and histograms)" 20 | ] 21 | }, 22 | { 23 | "cell_type": "code", 24 | "execution_count": null, 25 | "metadata": {}, 26 | "outputs": [], 27 | "source": [ 28 | "import pandas as pd\n", 29 | "\n", 30 | "import ipywidgets as widgets\n", 31 | "import bqplot.pyplot as plt" 32 | ] 33 | }, 34 | { 35 | "cell_type": "code", 36 | "execution_count": null, 37 | "metadata": {}, 38 | "outputs": [], 39 | "source": [ 40 | "pd.options.display.float_format = '{:.2f}'.format" 41 | ] 42 | }, 43 | { 44 | "cell_type": "code", 45 | "execution_count": null, 46 | "metadata": {}, 47 | "outputs": [], 48 | "source": [ 49 | "datasaurus_data = pd.read_csv('data/DatasaurusDozen.tsv', delimiter='\\t')\n", 50 | "\n", 51 | "# group by dataset and compute first two moments and corr\n", 52 | "dataset_gby = datasaurus_data.groupby('dataset')\n", 53 | "\n", 54 | "# basic stats for all datasets: mean and std\n", 55 | "stats = dataset_gby.agg(['mean', 'std'])\n", 56 | "\n", 57 | "# correlation between x and y for all datasets\n", 58 | "corr = dataset_gby.apply(lambda g: g['x'].corr(g['y']))\n", 59 | "\n", 60 | "# stats for all datasets\n", 61 | "stats_df = pd.concat([stats, corr], axis=1)\n", 62 | "stats_df.columns = ['x_mean', 'x_std', 'y_mean', 'y_std', 'corr']" 63 | ] 64 | }, 65 | { 66 | "cell_type": "code", 67 | "execution_count": null, 68 | "metadata": {}, 69 | "outputs": [], 70 | "source": [ 71 | "type_dropdown = widgets.Dropdown(description='Dataset', options=list(dataset_gby.groups.keys()))\n", 72 | "stats_table_placeholder = widgets.Box()" 73 | ] 74 | }, 75 | { 76 | "cell_type": "code", 77 | "execution_count": null, 78 | "metadata": { 79 | "scrolled": true 80 | }, 81 | "outputs": [], 82 | "source": [ 83 | "scat_fig = plt.figure(animation_duration=1000, preserve_aspect=True)\n", 84 | "scat_fig.layout.width = '800px'\n", 85 | "scat_fig.layout.height = '650px'\n", 86 | "scat = plt.scatter([], [], colors=['deepskyblue'], default_size=40, stroke='black')\n", 87 | "plt.xlabel('X')\n", 88 | "plt.ylabel('Y')\n", 89 | "\n", 90 | "# historgrams of X and Y\n", 91 | "hist_layout = widgets.Layout(height='320px', width='400px')\n", 92 | "hist_title_tmpl = 'Histogram of {dataset}[{var}]'\n", 93 | "x_hist_fig = plt.figure(layout=hist_layout)\n", 94 | "x_hist = plt.hist([], colors=['orangered'], bins=30)\n", 95 | "\n", 96 | "y_hist_fig = plt.figure(layout=hist_layout)\n", 97 | "y_hist = plt.hist([], colors=['lightgreen'], bins=30)\n", 98 | "\n", 99 | "for axis in x_hist_fig.axes:\n", 100 | " axis.grid_lines = 'none'\n", 101 | "\n", 102 | "for axis in y_hist_fig.axes:\n", 103 | " axis.grid_lines = 'none'\n", 104 | " \n", 105 | "# create a callback to update the scatter and the stats table\n", 106 | "def update(*args):\n", 107 | " dataset = type_dropdown.value\n", 108 | " scat_fig.title = dataset\n", 109 | " with scat.hold_sync():\n", 110 | " x, y = (dataset_gby\n", 111 | " .get_group(dataset)[['x', 'y']]\n", 112 | " .values.T)\n", 113 | " scat.x, scat.y = x, y\n", 114 | " \n", 115 | " x_hist.sample = x\n", 116 | " x_hist_fig.title = hist_title_tmpl.format(dataset=dataset,\n", 117 | " var='x')\n", 118 | " y_hist.sample = y\n", 119 | " y_hist_fig.title = hist_title_tmpl.format(dataset=dataset,\n", 120 | " var='y')\n", 121 | "\n", 122 | " out = widgets.Output()\n", 123 | " with out:\n", 124 | " display(stats_df.loc[dataset].to_frame())\n", 125 | " stats_table_placeholder.children = [out]\n", 126 | "\n", 127 | "type_dropdown.observe(update, 'value')\n", 128 | "\n", 129 | "# invoke the callback on startup\n", 130 | "update(None)\n", 131 | "\n", 132 | "histograms = widgets.VBox([x_hist_fig, y_hist_fig])\n", 133 | "widgets.VBox([type_dropdown, \n", 134 | " widgets.HBox([scat_fig, \n", 135 | " histograms, \n", 136 | " stats_table_placeholder])])" 137 | ] 138 | } 139 | ], 140 | "metadata": { 141 | "kernelspec": { 142 | "display_name": "Python 3", 143 | "language": "python", 144 | "name": "python3" 145 | }, 146 | "language_info": { 147 | "codemirror_mode": { 148 | "name": "ipython", 149 | "version": 3 150 | }, 151 | "file_extension": ".py", 152 | "mimetype": "text/x-python", 153 | "name": "python", 154 | "nbconvert_exporter": "python", 155 | "pygments_lexer": "ipython3", 156 | "version": "3.8.8" 157 | }, 158 | "toc": { 159 | "base_numbering": 1, 160 | "nav_menu": {}, 161 | "number_sections": true, 162 | "sideBar": true, 163 | "skip_h1_title": false, 164 | "title_cell": "Table of Contents", 165 | "title_sidebar": "Contents", 166 | "toc_cell": false, 167 | "toc_position": {}, 168 | "toc_section_display": true, 169 | "toc_window_display": false 170 | } 171 | }, 172 | "nbformat": 4, 173 | "nbformat_minor": 2 174 | } 175 | -------------------------------------------------------------------------------- /04-plotly-and-dash-take-on-the-datasaurus.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "id": "varied-syndicate", 7 | "metadata": { 8 | "jupyter": { 9 | "outputs_hidden": true 10 | } 11 | }, 12 | "outputs": [], 13 | "source": [ 14 | "# need to install jupyter-dash before running this notebook\n", 15 | "# !pip install jupyter-dash" 16 | ] 17 | }, 18 | { 19 | "cell_type": "markdown", 20 | "id": "dated-stamp", 21 | "metadata": {}, 22 | "source": [ 23 | "# Plotly and Dash" 24 | ] 25 | }, 26 | { 27 | "cell_type": "code", 28 | "execution_count": null, 29 | "id": "annual-chamber", 30 | "metadata": {}, 31 | "outputs": [], 32 | "source": [ 33 | "import pandas as pd\n", 34 | "import plotly.express as px" 35 | ] 36 | }, 37 | { 38 | "cell_type": "code", 39 | "execution_count": null, 40 | "id": "activated-worthy", 41 | "metadata": {}, 42 | "outputs": [], 43 | "source": [ 44 | "dinodf = pd.read_csv('data/DatasaurusDozen.tsv',sep='\\t')" 45 | ] 46 | }, 47 | { 48 | "cell_type": "code", 49 | "execution_count": null, 50 | "id": "animated-engineer", 51 | "metadata": {}, 52 | "outputs": [], 53 | "source": [ 54 | "px.scatter(dinodf[dinodf.dataset=='dino'], x=\"x\", y=\"y\")" 55 | ] 56 | }, 57 | { 58 | "cell_type": "code", 59 | "execution_count": null, 60 | "id": "asian-kingston", 61 | "metadata": {}, 62 | "outputs": [], 63 | "source": [ 64 | "px.scatter(dinodf[dinodf.dataset=='dino'], x=\"x\", y=\"y\",\n", 65 | " width=500, height=500)" 66 | ] 67 | }, 68 | { 69 | "cell_type": "code", 70 | "execution_count": null, 71 | "id": "matched-myrtle", 72 | "metadata": {}, 73 | "outputs": [], 74 | "source": [ 75 | "dinodf" 76 | ] 77 | }, 78 | { 79 | "cell_type": "code", 80 | "execution_count": null, 81 | "id": "informed-channel", 82 | "metadata": {}, 83 | "outputs": [], 84 | "source": [ 85 | "import numpy as np" 86 | ] 87 | }, 88 | { 89 | "cell_type": "code", 90 | "execution_count": null, 91 | "id": "infinite-screen", 92 | "metadata": {}, 93 | "outputs": [], 94 | "source": [ 95 | "dinodf['arbitrary_z'] = 0.\n", 96 | "for i in dinodf.index:\n", 97 | " dinodf.loc[i,'arbitrary_z'] = np.where(dinodf.dataset.unique()==dinodf.loc[i,'dataset'])[0][0]" 98 | ] 99 | }, 100 | { 101 | "cell_type": "code", 102 | "execution_count": null, 103 | "id": "respiratory-paragraph", 104 | "metadata": {}, 105 | "outputs": [], 106 | "source": [ 107 | "px.scatter_3d(dinodf, x=\"x\", y=\"y\", z='arbitrary_z', color='dataset',\n", 108 | " width=1000, height=1000)" 109 | ] 110 | }, 111 | { 112 | "cell_type": "code", 113 | "execution_count": null, 114 | "id": "tamil-department", 115 | "metadata": {}, 116 | "outputs": [], 117 | "source": [ 118 | "import plotly.graph_objects as go" 119 | ] 120 | }, 121 | { 122 | "cell_type": "code", 123 | "execution_count": null, 124 | "id": "intended-prairie", 125 | "metadata": {}, 126 | "outputs": [], 127 | "source": [ 128 | "fig = go.Figure()\n", 129 | "\n", 130 | "localdf = dinodf[dinodf.dataset=='dino']\n", 131 | "\n", 132 | "fig.add_trace(go.Scatter(\n", 133 | " x=localdf.x,\n", 134 | " y=localdf.y\n", 135 | "))" 136 | ] 137 | }, 138 | { 139 | "cell_type": "code", 140 | "execution_count": null, 141 | "id": "nutritional-respondent", 142 | "metadata": {}, 143 | "outputs": [], 144 | "source": [ 145 | "fig = go.Figure()\n", 146 | "\n", 147 | "localdf = dinodf[dinodf.dataset=='dino']\n", 148 | "\n", 149 | "fig.add_trace(go.Scatter(\n", 150 | " x=localdf.x,\n", 151 | " y=localdf.y,\n", 152 | " mode='markers',\n", 153 | " marker=dict(\n", 154 | " size=16,\n", 155 | " color=np.random.randn(500), #set color equal to a variable\n", 156 | " colorscale='rainbow', # one of plotly colorscales\n", 157 | " showscale=True\n", 158 | " )\n", 159 | "))" 160 | ] 161 | }, 162 | { 163 | "cell_type": "code", 164 | "execution_count": null, 165 | "id": "spectacular-yugoslavia", 166 | "metadata": {}, 167 | "outputs": [], 168 | "source": [ 169 | "#\n", 170 | "# Exercise: Use the ipywidget library to make a dropdown menu that can plot the above for different dataset values\n", 171 | "#\n", 172 | "\n" 173 | ] 174 | }, 175 | { 176 | "cell_type": "code", 177 | "execution_count": null, 178 | "id": "familiar-relaxation", 179 | "metadata": {}, 180 | "outputs": [], 181 | "source": [ 182 | "# For one solution, execute this cell\n", 183 | "%load 'exercises/plotlysaurus.py'" 184 | ] 185 | }, 186 | { 187 | "cell_type": "markdown", 188 | "id": "latter-chicken", 189 | "metadata": {}, 190 | "source": [ 191 | "# JupyterDash\n", 192 | "The `jupyter-dash` package makes it easy to develop Plotly Dash apps from the Jupyter Notebook and JupyterLab.\n", 193 | "\n", 194 | "Just replace the standard `dash.Dash` class with the `jupyter_dash.JupyterDash` subclass." 195 | ] 196 | }, 197 | { 198 | "cell_type": "code", 199 | "execution_count": null, 200 | "id": "nominated-chain", 201 | "metadata": {}, 202 | "outputs": [], 203 | "source": [ 204 | "from jupyter_dash import JupyterDash\n", 205 | "import dash_core_components as dcc\n", 206 | "import dash_html_components as html\n", 207 | "from dash.dependencies import Input, Output" 208 | ] 209 | }, 210 | { 211 | "cell_type": "markdown", 212 | "id": "institutional-malpractice", 213 | "metadata": {}, 214 | "source": [ 215 | "When running in JupyterHub or Binder, call the `infer_jupyter_config` function to detect the proxy configuration." 216 | ] 217 | }, 218 | { 219 | "cell_type": "code", 220 | "execution_count": null, 221 | "id": "curious-plane", 222 | "metadata": {}, 223 | "outputs": [], 224 | "source": [ 225 | "# Get proxy to run server within this JupyterHub environment\n", 226 | "JupyterDash.infer_jupyter_proxy_config()" 227 | ] 228 | }, 229 | { 230 | "cell_type": "code", 231 | "execution_count": null, 232 | "id": "domestic-nursery", 233 | "metadata": {}, 234 | "outputs": [], 235 | "source": [ 236 | "# Build App\n", 237 | "app = JupyterDash(__name__)\n", 238 | "\n", 239 | "# Create server variable with Flask server object for use with gunicorn\n", 240 | "server = app.server\n", 241 | "\n", 242 | "app.layout = html.Div([\n", 243 | " html.H1(\"JupyterDash for you Demo\"),\n", 244 | " html.Label([\n", 245 | " \"colorscale\",\n", 246 | " dcc.Dropdown(\n", 247 | " id='colorscale-dropdown', clearable=False,\n", 248 | " value='dino', options=[\n", 249 | " {'label': c, 'value': c}\n", 250 | " for c in dinodf.dataset.unique()\n", 251 | " ])\n", 252 | " ]),\n", 253 | " dcc.Graph(id='graph'),\n", 254 | "])\n", 255 | "\n", 256 | "# Define callback to update graph\n", 257 | "@app.callback(\n", 258 | " Output('graph', 'figure'),\n", 259 | " [Input(\"colorscale-dropdown\", \"value\")]\n", 260 | ")\n", 261 | "\n", 262 | "def update_figure(dinoshape):\n", 263 | " return px.scatter(\n", 264 | " dinodf[dinodf.dataset==dinoshape], x=\"x\", y=\"y\",\n", 265 | " marginal_y=\"histogram\",marginal_x=\"histogram\",\n", 266 | " width=500, height=500\n", 267 | " )" 268 | ] 269 | }, 270 | { 271 | "cell_type": "code", 272 | "execution_count": null, 273 | "id": "progressive-clarity", 274 | "metadata": {}, 275 | "outputs": [], 276 | "source": [ 277 | "# Run app and display result inline in the notebook\n", 278 | "app.run_server(mode='inline')" 279 | ] 280 | }, 281 | { 282 | "cell_type": "code", 283 | "execution_count": null, 284 | "id": "attractive-footwear", 285 | "metadata": {}, 286 | "outputs": [], 287 | "source": [ 288 | "app.run_server()" 289 | ] 290 | }, 291 | { 292 | "cell_type": "markdown", 293 | "id": "physical-guarantee", 294 | "metadata": {}, 295 | "source": [ 296 | "# An example taken from the JupyterDash repo\n", 297 | "\n", 298 | "https://github.com/plotly/jupyter-dash" 299 | ] 300 | }, 301 | { 302 | "cell_type": "code", 303 | "execution_count": null, 304 | "id": "economic-haven", 305 | "metadata": {}, 306 | "outputs": [], 307 | "source": [ 308 | "import dash" 309 | ] 310 | }, 311 | { 312 | "cell_type": "code", 313 | "execution_count": null, 314 | "id": "retained-group", 315 | "metadata": {}, 316 | "outputs": [], 317 | "source": [ 318 | "df = pd.read_csv('https://plotly.github.io/datasets/country_indicators.csv')\n", 319 | "available_indicators = df['Indicator Name'].unique()" 320 | ] 321 | }, 322 | { 323 | "cell_type": "markdown", 324 | "id": "running-program", 325 | "metadata": {}, 326 | "source": [ 327 | "Construct the app and callbacks" 328 | ] 329 | }, 330 | { 331 | "cell_type": "code", 332 | "execution_count": null, 333 | "id": "floppy-cigarette", 334 | "metadata": {}, 335 | "outputs": [], 336 | "source": [ 337 | "external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']\n", 338 | "\n", 339 | "app = JupyterDash(__name__, external_stylesheets=external_stylesheets)\n", 340 | "\n", 341 | "# Create server variable with Flask server object for use with gunicorn\n", 342 | "server = app.server\n", 343 | "\n", 344 | "app.layout = html.Div([\n", 345 | " html.Div([\n", 346 | "\n", 347 | " html.Div([\n", 348 | " dcc.Dropdown(\n", 349 | " id='crossfilter-xaxis-column',\n", 350 | " options=[{'label': i, 'value': i} for i in available_indicators],\n", 351 | " value='Agriculture, value added (% of GDP)'\n", 352 | " ),\n", 353 | " dcc.RadioItems(\n", 354 | " id='crossfilter-xaxis-type',\n", 355 | " options=[{'label': i, 'value': i} for i in ['Linear', 'Log']],\n", 356 | " value='Linear',\n", 357 | " labelStyle={'display': 'inline-block'}\n", 358 | " )\n", 359 | " ],\n", 360 | " style={'width': '49%', 'display': 'inline-block'}),\n", 361 | "\n", 362 | " html.Div([\n", 363 | " dcc.Dropdown(\n", 364 | " id='crossfilter-yaxis-column',\n", 365 | " options=[{'label': i, 'value': i} for i in available_indicators],\n", 366 | " value='Life expectancy at birth, total (years)'\n", 367 | " ),\n", 368 | " dcc.RadioItems(\n", 369 | " id='crossfilter-yaxis-type',\n", 370 | " options=[{'label': i, 'value': i} for i in ['Linear', 'Log']],\n", 371 | " value='Linear',\n", 372 | " labelStyle={'display': 'inline-block'}\n", 373 | " )\n", 374 | " ], style={'width': '49%', 'float': 'right', 'display': 'inline-block'})\n", 375 | " ], style={\n", 376 | " 'borderBottom': 'thin lightgrey solid',\n", 377 | " 'backgroundColor': 'rgb(250, 250, 250)',\n", 378 | " 'padding': '10px 5px'\n", 379 | " }),\n", 380 | "\n", 381 | " html.Div([\n", 382 | " dcc.Graph(\n", 383 | " id='crossfilter-indicator-scatter',\n", 384 | " hoverData={'points': [{'customdata': 'Japan'}]}\n", 385 | " )\n", 386 | " ], style={'width': '49%', 'display': 'inline-block', 'padding': '0 20'}),\n", 387 | " html.Div([\n", 388 | " dcc.Graph(id='x-time-series'),\n", 389 | " dcc.Graph(id='y-time-series'),\n", 390 | " ], style={'display': 'inline-block', 'width': '49%'}),\n", 391 | "\n", 392 | " html.Div(dcc.Slider(\n", 393 | " id='crossfilter-year--slider',\n", 394 | " min=df['Year'].min(),\n", 395 | " max=df['Year'].max(),\n", 396 | " value=df['Year'].max(),\n", 397 | " marks={str(year): str(year) for year in df['Year'].unique()},\n", 398 | " step=None\n", 399 | " ), style={'width': '49%', 'padding': '0px 20px 20px 20px'})\n", 400 | "])\n", 401 | "\n", 402 | "\n", 403 | "@app.callback(\n", 404 | " dash.dependencies.Output('crossfilter-indicator-scatter', 'figure'),\n", 405 | " [dash.dependencies.Input('crossfilter-xaxis-column', 'value'),\n", 406 | " dash.dependencies.Input('crossfilter-yaxis-column', 'value'),\n", 407 | " dash.dependencies.Input('crossfilter-xaxis-type', 'value'),\n", 408 | " dash.dependencies.Input('crossfilter-yaxis-type', 'value'),\n", 409 | " dash.dependencies.Input('crossfilter-year--slider', 'value')])\n", 410 | "def update_graph(xaxis_column_name, yaxis_column_name,\n", 411 | " xaxis_type, yaxis_type,\n", 412 | " year_value):\n", 413 | " dff = df[df['Year'] == year_value]\n", 414 | "\n", 415 | " return {\n", 416 | " 'data': [dict(\n", 417 | " x=dff[dff['Indicator Name'] == xaxis_column_name]['Value'],\n", 418 | " y=dff[dff['Indicator Name'] == yaxis_column_name]['Value'],\n", 419 | " text=dff[dff['Indicator Name'] == yaxis_column_name]['Country Name'],\n", 420 | " customdata=dff[dff['Indicator Name'] == yaxis_column_name]['Country Name'],\n", 421 | " mode='markers',\n", 422 | " marker={\n", 423 | " 'size': 25,\n", 424 | " 'opacity': 0.7,\n", 425 | " 'color': 'orange',\n", 426 | " 'line': {'width': 2, 'color': 'purple'}\n", 427 | " }\n", 428 | " )],\n", 429 | " 'layout': dict(\n", 430 | " xaxis={\n", 431 | " 'title': xaxis_column_name,\n", 432 | " 'type': 'linear' if xaxis_type == 'Linear' else 'log'\n", 433 | " },\n", 434 | " yaxis={\n", 435 | " 'title': yaxis_column_name,\n", 436 | " 'type': 'linear' if yaxis_type == 'Linear' else 'log'\n", 437 | " },\n", 438 | " margin={'l': 40, 'b': 30, 't': 10, 'r': 0},\n", 439 | " height=450,\n", 440 | " hovermode='closest'\n", 441 | " )\n", 442 | " }\n", 443 | "\n", 444 | "\n", 445 | "def create_time_series(dff, axis_type, title):\n", 446 | " return {\n", 447 | " 'data': [dict(\n", 448 | " x=dff['Year'],\n", 449 | " y=dff['Value'],\n", 450 | " mode='lines+markers'\n", 451 | " )],\n", 452 | " 'layout': {\n", 453 | " 'height': 225,\n", 454 | " 'margin': {'l': 20, 'b': 30, 'r': 10, 't': 10},\n", 455 | " 'annotations': [{\n", 456 | " 'x': 0, 'y': 0.85, 'xanchor': 'left', 'yanchor': 'bottom',\n", 457 | " 'xref': 'paper', 'yref': 'paper', 'showarrow': False,\n", 458 | " 'align': 'left', 'bgcolor': 'rgba(255, 255, 255, 0.5)',\n", 459 | " 'text': title\n", 460 | " }],\n", 461 | " 'yaxis': {'type': 'linear' if axis_type == 'Linear' else 'log'},\n", 462 | " 'xaxis': {'showgrid': False}\n", 463 | " }\n", 464 | " }\n", 465 | "\n", 466 | "\n", 467 | "@app.callback(\n", 468 | " dash.dependencies.Output('x-time-series', 'figure'),\n", 469 | " [dash.dependencies.Input('crossfilter-indicator-scatter', 'hoverData'),\n", 470 | " dash.dependencies.Input('crossfilter-xaxis-column', 'value'),\n", 471 | " dash.dependencies.Input('crossfilter-xaxis-type', 'value')])\n", 472 | "def update_y_timeseries(hoverData, xaxis_column_name, axis_type):\n", 473 | " country_name = hoverData['points'][0]['customdata']\n", 474 | " dff = df[df['Country Name'] == country_name]\n", 475 | " dff = dff[dff['Indicator Name'] == xaxis_column_name]\n", 476 | " title = '{}
{}'.format(country_name, xaxis_column_name)\n", 477 | " return create_time_series(dff, axis_type, title)\n", 478 | "\n", 479 | "\n", 480 | "@app.callback(\n", 481 | " dash.dependencies.Output('y-time-series', 'figure'),\n", 482 | " [dash.dependencies.Input('crossfilter-indicator-scatter', 'hoverData'),\n", 483 | " dash.dependencies.Input('crossfilter-yaxis-column', 'value'),\n", 484 | " dash.dependencies.Input('crossfilter-yaxis-type', 'value')])\n", 485 | "def update_x_timeseries(hoverData, yaxis_column_name, axis_type):\n", 486 | " dff = df[df['Country Name'] == hoverData['points'][0]['customdata']]\n", 487 | " dff = dff[dff['Indicator Name'] == yaxis_column_name]\n", 488 | " return create_time_series(dff, axis_type, yaxis_column_name)" 489 | ] 490 | }, 491 | { 492 | "cell_type": "markdown", 493 | "id": "worst-speaker", 494 | "metadata": {}, 495 | "source": [ 496 | "Serve the app using `run_server`. Unlike the standard `Dash.run_server` method, the `JupyterDash.run_server` method doesn't block execution of the notebook. It serves the app in a background thread, making it possible to run other notebook calculations while the app is running.\n", 497 | "\n", 498 | "This makes it possible to iterativly update the app without rerunning the potentially expensive data processing steps." 499 | ] 500 | }, 501 | { 502 | "cell_type": "code", 503 | "execution_count": null, 504 | "id": "backed-advance", 505 | "metadata": {}, 506 | "outputs": [], 507 | "source": [ 508 | "app.run_server()\n", 509 | "# app.run_server(mode=\"inline\")" 510 | ] 511 | }, 512 | { 513 | "cell_type": "markdown", 514 | "id": "according-restriction", 515 | "metadata": {}, 516 | "source": [ 517 | "By default, `run_server` displays a URL that you can click on to open the app in a browser tab. The `mode` argument to `run_server` can be used to change this behavior. Setting `mode=\"inline\"` will display the app directly in the notebook output cell." 518 | ] 519 | }, 520 | { 521 | "cell_type": "code", 522 | "execution_count": null, 523 | "id": "alpha-commodity", 524 | "metadata": {}, 525 | "outputs": [], 526 | "source": [ 527 | "#app.run_server()\n", 528 | "app.run_server(mode=\"inline\")" 529 | ] 530 | }, 531 | { 532 | "cell_type": "markdown", 533 | "id": "figured-singapore", 534 | "metadata": {}, 535 | "source": [ 536 | "When running in JupyterLab, with the `jupyterlab-dash` extension, setting `mode=\"jupyterlab\"` will open the app in a tab in JupyterLab.\n", 537 | "\n", 538 | "```python\n", 539 | "app.run_server(mode=\"jupyterlab\")\n", 540 | "```" 541 | ] 542 | }, 543 | { 544 | "cell_type": "code", 545 | "execution_count": null, 546 | "id": "connected-livestock", 547 | "metadata": {}, 548 | "outputs": [], 549 | "source": [ 550 | "app.run_server(mode=\"jupyterlab\")" 551 | ] 552 | }, 553 | { 554 | "cell_type": "code", 555 | "execution_count": null, 556 | "id": "trying-scottish", 557 | "metadata": {}, 558 | "outputs": [], 559 | "source": [] 560 | } 561 | ], 562 | "metadata": { 563 | "kernelspec": { 564 | "display_name": "Python 3", 565 | "language": "python", 566 | "name": "python3" 567 | }, 568 | "language_info": { 569 | "codemirror_mode": { 570 | "name": "ipython", 571 | "version": 3 572 | }, 573 | "file_extension": ".py", 574 | "mimetype": "text/x-python", 575 | "name": "python", 576 | "nbconvert_exporter": "python", 577 | "pygments_lexer": "ipython3", 578 | "version": "3.8.8" 579 | }, 580 | "toc": { 581 | "base_numbering": 1, 582 | "nav_menu": {}, 583 | "number_sections": true, 584 | "sideBar": true, 585 | "skip_h1_title": false, 586 | "title_cell": "Table of Contents", 587 | "title_sidebar": "Contents", 588 | "toc_cell": false, 589 | "toc_position": {}, 590 | "toc_section_display": true, 591 | "toc_window_display": false 592 | } 593 | }, 594 | "nbformat": 4, 595 | "nbformat_minor": 5 596 | } 597 | -------------------------------------------------------------------------------- /05-altair-takes-on-the-datasaurus.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "id": "beautiful-premium", 7 | "metadata": {}, 8 | "outputs": [], 9 | "source": [ 10 | "import altair as alt\n", 11 | "import pandas as pd\n", 12 | "import ipywidgets as widgets" 13 | ] 14 | }, 15 | { 16 | "cell_type": "code", 17 | "execution_count": null, 18 | "id": "marine-anchor", 19 | "metadata": {}, 20 | "outputs": [], 21 | "source": [ 22 | "dinodf = pd.read_csv('data/DatasaurusDozen.tsv',sep='\\t')" 23 | ] 24 | }, 25 | { 26 | "cell_type": "code", 27 | "execution_count": null, 28 | "id": "theoretical-allen", 29 | "metadata": {}, 30 | "outputs": [], 31 | "source": [ 32 | "dinodf = dinodf[dinodf.dataset=='dino']" 33 | ] 34 | }, 35 | { 36 | "cell_type": "code", 37 | "execution_count": null, 38 | "id": "cellular-portrait", 39 | "metadata": {}, 40 | "outputs": [], 41 | "source": [ 42 | "dinodf" 43 | ] 44 | }, 45 | { 46 | "cell_type": "code", 47 | "execution_count": null, 48 | "id": "historical-portsmouth", 49 | "metadata": {}, 50 | "outputs": [], 51 | "source": [ 52 | "points = alt.Chart(dinodf).mark_point().encode(\n", 53 | " x='x',\n", 54 | " y='y'\n", 55 | ")\n", 56 | "points" 57 | ] 58 | }, 59 | { 60 | "cell_type": "code", 61 | "execution_count": null, 62 | "id": "laden-individual", 63 | "metadata": {}, 64 | "outputs": [], 65 | "source": [ 66 | "bars = alt.Chart(dinodf).mark_bar().encode(\n", 67 | " x='count(x)',\n", 68 | " y='x'\n", 69 | ")\n", 70 | "bars" 71 | ] 72 | }, 73 | { 74 | "cell_type": "code", 75 | "execution_count": null, 76 | "id": "twelve-jewelry", 77 | "metadata": {}, 78 | "outputs": [], 79 | "source": [ 80 | "barsX = alt.Chart(dinodf).mark_bar().encode(\n", 81 | " alt.X('x',bin=True),\n", 82 | " y='count()'\n", 83 | ")\n", 84 | "barsX" 85 | ] 86 | }, 87 | { 88 | "cell_type": "code", 89 | "execution_count": null, 90 | "id": "empty-pollution", 91 | "metadata": {}, 92 | "outputs": [], 93 | "source": [ 94 | "bars = alt.Chart(dinodf).mark_bar().encode(\n", 95 | " alt.X('y',bin=True),\n", 96 | " y='count()'\n", 97 | ")\n", 98 | "bars" 99 | ] 100 | }, 101 | { 102 | "cell_type": "code", 103 | "execution_count": null, 104 | "id": "obvious-globe", 105 | "metadata": {}, 106 | "outputs": [], 107 | "source": [ 108 | "barsY = alt.Chart(dinodf).mark_bar().encode(\n", 109 | " alt.Y('y',bin=True),\n", 110 | " x='count()'\n", 111 | ")\n", 112 | "barsY" 113 | ] 114 | }, 115 | { 116 | "cell_type": "code", 117 | "execution_count": null, 118 | "id": "conditional-canadian", 119 | "metadata": {}, 120 | "outputs": [], 121 | "source": [ 122 | "barsX & barsY" 123 | ] 124 | }, 125 | { 126 | "cell_type": "code", 127 | "execution_count": null, 128 | "id": "psychological-fossil", 129 | "metadata": {}, 130 | "outputs": [], 131 | "source": [ 132 | "alt.vconcat(barsX,\n", 133 | " alt.hconcat(points,barsY))" 134 | ] 135 | }, 136 | { 137 | "cell_type": "code", 138 | "execution_count": null, 139 | "id": "missing-lewis", 140 | "metadata": {}, 141 | "outputs": [], 142 | "source": [ 143 | "my_si = alt.selection_interval()" 144 | ] 145 | }, 146 | { 147 | "cell_type": "code", 148 | "execution_count": null, 149 | "id": "sorted-learning", 150 | "metadata": {}, 151 | "outputs": [], 152 | "source": [ 153 | "points = alt.Chart(dinodf).mark_point().encode(\n", 154 | " x='x',\n", 155 | " y='y'\n", 156 | ").add_selection(\n", 157 | " my_si\n", 158 | ")\n", 159 | "\n", 160 | "barsX = alt.Chart(dinodf).mark_bar().encode(\n", 161 | " alt.X('x',bin=True),\n", 162 | " y='count()'\n", 163 | ").transform_filter(\n", 164 | " my_si\n", 165 | ")\n", 166 | "\n", 167 | "barsY = alt.Chart(dinodf).mark_bar().encode(\n", 168 | " alt.Y('y',bin=True),\n", 169 | " x='count()'\n", 170 | ").transform_filter(\n", 171 | " my_si\n", 172 | ")\n", 173 | "\n", 174 | "chart = alt.vconcat(barsX,\n", 175 | " alt.hconcat(points,barsY))" 176 | ] 177 | }, 178 | { 179 | "cell_type": "code", 180 | "execution_count": null, 181 | "id": "monthly-alloy", 182 | "metadata": {}, 183 | "outputs": [], 184 | "source": [ 185 | "chart" 186 | ] 187 | }, 188 | { 189 | "cell_type": "code", 190 | "execution_count": null, 191 | "id": "embedded-farmer", 192 | "metadata": {}, 193 | "outputs": [], 194 | "source": [ 195 | "points = alt.Chart(dinodf).mark_point().encode(\n", 196 | " x='x',\n", 197 | " y='y'\n", 198 | ").add_selection(\n", 199 | " my_si\n", 200 | ")\n", 201 | "\n", 202 | "barsX = alt.Chart(dinodf).mark_bar().encode(\n", 203 | " alt.X('x',bin=True,scale=alt.Scale(domain=[0, 100])),\n", 204 | " y='count()'\n", 205 | ").transform_filter(\n", 206 | " my_si\n", 207 | ")\n", 208 | "\n", 209 | "barsY = alt.Chart(dinodf).mark_bar().encode(\n", 210 | " alt.Y('y',bin=True,scale=alt.Scale(domain=[0, 100])),\n", 211 | " x='count()'\n", 212 | ").transform_filter(\n", 213 | " my_si\n", 214 | ")\n", 215 | "\n", 216 | "chart = alt.vconcat(barsX,\n", 217 | " alt.hconcat(points,barsY))\n", 218 | "\n", 219 | "chart" 220 | ] 221 | }, 222 | { 223 | "cell_type": "code", 224 | "execution_count": null, 225 | "id": "underlying-expense", 226 | "metadata": {}, 227 | "outputs": [], 228 | "source": [ 229 | "chart.save('altairasaurus_chart.html')" 230 | ] 231 | }, 232 | { 233 | "cell_type": "code", 234 | "execution_count": null, 235 | "id": "neutral-granny", 236 | "metadata": {}, 237 | "outputs": [], 238 | "source": [ 239 | "#\n", 240 | "# Exercise: Use ipywidgets to enable this chart for any dataset in the datasaurus dataframe\n", 241 | "#\n", 242 | "\n" 243 | ] 244 | }, 245 | { 246 | "cell_type": "code", 247 | "execution_count": null, 248 | "id": "sticky-junction", 249 | "metadata": { 250 | "scrolled": false 251 | }, 252 | "outputs": [], 253 | "source": [ 254 | "# Execute this cell to see a solution\n", 255 | "%load 'exercises/altairowidgetasaurus.py'" 256 | ] 257 | }, 258 | { 259 | "cell_type": "code", 260 | "execution_count": null, 261 | "id": "heavy-harvey", 262 | "metadata": {}, 263 | "outputs": [], 264 | "source": [ 265 | "#\n", 266 | "# Exercise: If you have ipywidgets down, try this:\n", 267 | "# 1. Execute this cell to see the plots\n", 268 | "# 2. Link the two plots with a selection interval like the above\n", 269 | "#\n", 270 | "\n", 271 | "from vega_datasets import data\n", 272 | "cars = data.cars()\n", 273 | "\n", 274 | "points = alt.Chart(cars).mark_point().encode(\n", 275 | " x='Horsepower',\n", 276 | " y='Miles_per_Gallon',\n", 277 | " color='Origin'\n", 278 | ")\n", 279 | "\n", 280 | "bars = alt.Chart(cars).mark_bar().encode(\n", 281 | " x='count(Origin)',\n", 282 | " y='Origin',\n", 283 | " color='Origin'\n", 284 | ")\n", 285 | "\n", 286 | "points & bars" 287 | ] 288 | }, 289 | { 290 | "cell_type": "code", 291 | "execution_count": null, 292 | "id": "expired-people", 293 | "metadata": {}, 294 | "outputs": [], 295 | "source": [ 296 | "# Execute this cell to see a solution\n", 297 | "%load 'exercises/altaircars.py'" 298 | ] 299 | }, 300 | { 301 | "cell_type": "code", 302 | "execution_count": null, 303 | "id": "small-queensland", 304 | "metadata": {}, 305 | "outputs": [], 306 | "source": [] 307 | } 308 | ], 309 | "metadata": { 310 | "kernelspec": { 311 | "display_name": "Python 3", 312 | "language": "python", 313 | "name": "python3" 314 | }, 315 | "language_info": { 316 | "codemirror_mode": { 317 | "name": "ipython", 318 | "version": 3 319 | }, 320 | "file_extension": ".py", 321 | "mimetype": "text/x-python", 322 | "name": "python", 323 | "nbconvert_exporter": "python", 324 | "pygments_lexer": "ipython3", 325 | "version": "3.8.8" 326 | }, 327 | "toc": { 328 | "base_numbering": 1, 329 | "nav_menu": {}, 330 | "number_sections": true, 331 | "sideBar": true, 332 | "skip_h1_title": false, 333 | "title_cell": "Table of Contents", 334 | "title_sidebar": "Contents", 335 | "toc_cell": false, 336 | "toc_position": {}, 337 | "toc_section_display": true, 338 | "toc_window_display": false 339 | } 340 | }, 341 | "nbformat": 4, 342 | "nbformat_minor": 5 343 | } 344 | -------------------------------------------------------------------------------- /4fun-altair.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# NOTE: This notebook is taken from the GitHub repo of the LA Times datadesk:\n", 8 | "\n", 9 | "Original Title: california-coronavirus-data examples\n", 10 | "\n", 11 | "By [Ben Welsh](https://palewi.re/who-is-ben-welsh)\n", 12 | "\n", 13 | "A demonstration of how to use Python to work with the Los Angeles Times' independent tally of coronavirus cases in California published on GitHub at [datadesk/california-coronavirus-data](https://github.com/datadesk/california-coronavirus-data#state-cdph-totalscsv). To run this notebook immediately in the cloud, click the [Binder](https://mybinder.org/) launcher below.\n", 14 | "\n", 15 | "[![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/datadesk/california-coronavirus-data/master?urlpath=lab/tree/notebooks/examples.ipynb)\n", 16 | "\n", 17 | "## Subnotes: \n", 18 | "\n", 19 | "* Notebook and data were retrieved on April 23, 2021\n", 20 | "* Modifications for the IDRE workshop are made at the very bottom below the header \"IDRE Workshop Additions\"\n", 21 | "* The Binder link for interacting with the IDRE workshop materials is [Here](https://mybinder.org/v2/gh/benjum/idre-spring21-python-data-viz-2/HEAD)" 22 | ] 23 | }, 24 | { 25 | "cell_type": "code", 26 | "execution_count": null, 27 | "metadata": {}, 28 | "outputs": [], 29 | "source": [ 30 | "# commented out for IDRE class\n", 31 | "# %load_ext lab_black" 32 | ] 33 | }, 34 | { 35 | "cell_type": "markdown", 36 | "metadata": {}, 37 | "source": [ 38 | "## Import Python tools" 39 | ] 40 | }, 41 | { 42 | "cell_type": "markdown", 43 | "metadata": {}, 44 | "source": [ 45 | "Our data analysis and plotting tools" 46 | ] 47 | }, 48 | { 49 | "cell_type": "code", 50 | "execution_count": null, 51 | "metadata": {}, 52 | "outputs": [], 53 | "source": [ 54 | "import pandas as pd\n", 55 | "import altair as alt" 56 | ] 57 | }, 58 | { 59 | "cell_type": "markdown", 60 | "metadata": {}, 61 | "source": [ 62 | "Customizations to the Altair theme" 63 | ] 64 | }, 65 | { 66 | "cell_type": "code", 67 | "execution_count": null, 68 | "metadata": {}, 69 | "outputs": [], 70 | "source": [ 71 | "import altair_latimes as lat" 72 | ] 73 | }, 74 | { 75 | "cell_type": "code", 76 | "execution_count": null, 77 | "metadata": {}, 78 | "outputs": [], 79 | "source": [ 80 | "alt.themes.register(\"latimes\", lat.theme)\n", 81 | "alt.themes.enable(\"latimes\")" 82 | ] 83 | }, 84 | { 85 | "cell_type": "code", 86 | "execution_count": null, 87 | "metadata": {}, 88 | "outputs": [], 89 | "source": [ 90 | "alt.data_transformers.disable_max_rows()" 91 | ] 92 | }, 93 | { 94 | "cell_type": "markdown", 95 | "metadata": {}, 96 | "source": [ 97 | "## Import data" 98 | ] 99 | }, 100 | { 101 | "cell_type": "markdown", 102 | "metadata": {}, 103 | "source": [ 104 | "Read in the agency totals" 105 | ] 106 | }, 107 | { 108 | "cell_type": "code", 109 | "execution_count": null, 110 | "metadata": {}, 111 | "outputs": [], 112 | "source": [ 113 | "agency_df = pd.read_csv(\"data/latimes-agency-totals.csv\", parse_dates=[\"date\"])" 114 | ] 115 | }, 116 | { 117 | "cell_type": "code", 118 | "execution_count": null, 119 | "metadata": {}, 120 | "outputs": [], 121 | "source": [ 122 | "agency_df.head()" 123 | ] 124 | }, 125 | { 126 | "cell_type": "code", 127 | "execution_count": null, 128 | "metadata": {}, 129 | "outputs": [], 130 | "source": [ 131 | "agency_df.info()" 132 | ] 133 | }, 134 | { 135 | "cell_type": "markdown", 136 | "metadata": {}, 137 | "source": [ 138 | "## Aggregate data" 139 | ] 140 | }, 141 | { 142 | "cell_type": "markdown", 143 | "metadata": {}, 144 | "source": [ 145 | "### By state" 146 | ] 147 | }, 148 | { 149 | "cell_type": "markdown", 150 | "metadata": {}, 151 | "source": [ 152 | "Lump all the agencies together and you get the statewide totals." 153 | ] 154 | }, 155 | { 156 | "cell_type": "code", 157 | "execution_count": null, 158 | "metadata": {}, 159 | "outputs": [], 160 | "source": [ 161 | "state_df = (\n", 162 | " agency_df.groupby([\"date\"])\n", 163 | " .agg({\"confirmed_cases\": \"sum\", \"deaths\": \"sum\"})\n", 164 | " .reset_index()\n", 165 | ")" 166 | ] 167 | }, 168 | { 169 | "cell_type": "code", 170 | "execution_count": null, 171 | "metadata": {}, 172 | "outputs": [], 173 | "source": [ 174 | "state_df.head()" 175 | ] 176 | }, 177 | { 178 | "cell_type": "code", 179 | "execution_count": null, 180 | "metadata": {}, 181 | "outputs": [], 182 | "source": [ 183 | "state_df.info()" 184 | ] 185 | }, 186 | { 187 | "cell_type": "markdown", 188 | "metadata": {}, 189 | "source": [ 190 | "### By county" 191 | ] 192 | }, 193 | { 194 | "cell_type": "markdown", 195 | "metadata": {}, 196 | "source": [ 197 | "Three cities — Berkeley, Long Beach and Pasadena — run independent public health departments. Calculating county-level totals requires grouping them with their local peers." 198 | ] 199 | }, 200 | { 201 | "cell_type": "code", 202 | "execution_count": null, 203 | "metadata": {}, 204 | "outputs": [], 205 | "source": [ 206 | "county_df = (\n", 207 | " agency_df.groupby([\"date\", \"county\"])\n", 208 | " .agg({\"confirmed_cases\": \"sum\", \"deaths\": \"sum\"})\n", 209 | " .reset_index()\n", 210 | ")" 211 | ] 212 | }, 213 | { 214 | "cell_type": "code", 215 | "execution_count": null, 216 | "metadata": {}, 217 | "outputs": [], 218 | "source": [ 219 | "county_df.head()" 220 | ] 221 | }, 222 | { 223 | "cell_type": "code", 224 | "execution_count": null, 225 | "metadata": {}, 226 | "outputs": [], 227 | "source": [ 228 | "county_df.info()" 229 | ] 230 | }, 231 | { 232 | "cell_type": "markdown", 233 | "metadata": {}, 234 | "source": [ 235 | "## Chart the statewide totals over time" 236 | ] 237 | }, 238 | { 239 | "cell_type": "code", 240 | "execution_count": null, 241 | "metadata": {}, 242 | "outputs": [], 243 | "source": [ 244 | "# Create a base chart with the common x-axis\n", 245 | "chart = alt.Chart(state_df).encode(x=alt.X(\"date:T\", title=None))\n", 246 | "\n", 247 | "# Create the cases line\n", 248 | "cases = chart.mark_line(color=lat.palette[\"default\"]).encode(\n", 249 | " y=alt.Y(\"confirmed_cases:Q\", title=\"Confirmed cases\")\n", 250 | ")\n", 251 | "\n", 252 | "# Create the deaths line\n", 253 | "deaths = chart.mark_line(color=lat.palette[\"schemes\"][\"ice-7\"][3]).encode(\n", 254 | " y=alt.Y(\"deaths:Q\", title=\"Deaths\")\n", 255 | ")\n", 256 | "\n", 257 | "# Combine them into a single chart\n", 258 | "(cases & deaths).properties(title=\"Statewide cumulative totals\")" 259 | ] 260 | }, 261 | { 262 | "cell_type": "markdown", 263 | "metadata": {}, 264 | "source": [ 265 | "## Chart the county totals" 266 | ] 267 | }, 268 | { 269 | "cell_type": "markdown", 270 | "metadata": {}, 271 | "source": [ 272 | "First on a linear scale" 273 | ] 274 | }, 275 | { 276 | "cell_type": "code", 277 | "execution_count": null, 278 | "metadata": {}, 279 | "outputs": [], 280 | "source": [ 281 | "# Create the base chart\n", 282 | "chart = (\n", 283 | " alt.Chart(county_df)\n", 284 | " .mark_line()\n", 285 | " .encode(\n", 286 | " x=alt.X(\"date:T\", title=None),\n", 287 | " color=alt.Color(\"county:N\", title=\"County\", legend=None),\n", 288 | " )\n", 289 | ")\n", 290 | "\n", 291 | "# The cases line\n", 292 | "cases = chart.encode(y=alt.Y(\"confirmed_cases:Q\", title=\"Confirmed cases\"),)\n", 293 | "\n", 294 | "# The deaths line\n", 295 | "deaths = chart.mark_line().encode(y=alt.Y(\"deaths:Q\", title=\"Deaths\"),)\n", 296 | "\n", 297 | "# Combined into a chart\n", 298 | "(cases & deaths).properties(title=\"Cumulative totals by county\")" 299 | ] 300 | }, 301 | { 302 | "cell_type": "markdown", 303 | "metadata": {}, 304 | "source": [ 305 | "Again on a logarithmic scale" 306 | ] 307 | }, 308 | { 309 | "cell_type": "code", 310 | "execution_count": null, 311 | "metadata": {}, 312 | "outputs": [], 313 | "source": [ 314 | "# Make a base chart\n", 315 | "chart = (\n", 316 | " alt.Chart(county_df)\n", 317 | " .mark_line()\n", 318 | " .encode(\n", 319 | " x=alt.X(\"date:T\", title=None),\n", 320 | " color=alt.Color(\"county:N\", title=\"County\", legend=None),\n", 321 | " )\n", 322 | ")\n", 323 | "\n", 324 | "# The cases lines\n", 325 | "cases = chart.transform_filter(alt.datum.confirmed_cases > 0).encode(\n", 326 | " y=alt.Y(\"confirmed_cases:Q\", scale=alt.Scale(type=\"log\"), title=\"Confirmed cases\"),\n", 327 | ")\n", 328 | "\n", 329 | "# The deaths lines\n", 330 | "deaths = chart.transform_filter(alt.datum.deaths > 0).encode(\n", 331 | " y=alt.Y(\"deaths:Q\", scale=alt.Scale(type=\"log\"), title=\"Deaths\"),\n", 332 | ")\n", 333 | "\n", 334 | "# Slapping them together\n", 335 | "(cases & deaths).properties(title=\"Cumulative totals by county\")" 336 | ] 337 | }, 338 | { 339 | "cell_type": "markdown", 340 | "metadata": {}, 341 | "source": [ 342 | "A common technique for clarifying these charts to begin each line on the day the county hit a minimum number. Let's try it with 10." 343 | ] 344 | }, 345 | { 346 | "cell_type": "code", 347 | "execution_count": null, 348 | "metadata": {}, 349 | "outputs": [], 350 | "source": [ 351 | "day_10_df = (\n", 352 | " county_df[\n", 353 | " # Filter down to only days with 10 or more cumulative cases\n", 354 | " county_df.confirmed_cases\n", 355 | " >= 10\n", 356 | " ]\n", 357 | " .groupby(\n", 358 | " # And then get the minimum date for each county\n", 359 | " \"county\"\n", 360 | " )\n", 361 | " .date.min()\n", 362 | " .reset_index()\n", 363 | ")" 364 | ] 365 | }, 366 | { 367 | "cell_type": "markdown", 368 | "metadata": {}, 369 | "source": [ 370 | "Merge that date to each row in the data." 371 | ] 372 | }, 373 | { 374 | "cell_type": "code", 375 | "execution_count": null, 376 | "metadata": {}, 377 | "outputs": [], 378 | "source": [ 379 | "county_date_diff_df = county_df.merge(\n", 380 | " day_10_df, how=\"inner\", on=\"county\", suffixes=[\"\", \"_gte_10_cases\"]\n", 381 | ")" 382 | ] 383 | }, 384 | { 385 | "cell_type": "markdown", 386 | "metadata": {}, 387 | "source": [ 388 | "Calculate each day's distance from its tenth day." 389 | ] 390 | }, 391 | { 392 | "cell_type": "code", 393 | "execution_count": null, 394 | "metadata": {}, 395 | "outputs": [], 396 | "source": [ 397 | "county_date_diff_df[\"days_since_10\"] = (\n", 398 | " county_date_diff_df.date - county_date_diff_df.date_gte_10_cases\n", 399 | ").dt.days" 400 | ] 401 | }, 402 | { 403 | "cell_type": "markdown", 404 | "metadata": {}, 405 | "source": [ 406 | "Chart it." 407 | ] 408 | }, 409 | { 410 | "cell_type": "code", 411 | "execution_count": null, 412 | "metadata": {}, 413 | "outputs": [], 414 | "source": [ 415 | "alt.Chart(county_date_diff_df).transform_filter(\n", 416 | " # Only keep everything once they hit 10 cases\n", 417 | " alt.datum.days_since_10\n", 418 | " >= 0\n", 419 | ").mark_line().encode(\n", 420 | " x=alt.X(\"days_since_10:O\", title=\"Days since 10th case\"),\n", 421 | " y=alt.Y(\"confirmed_cases:Q\", scale=alt.Scale(type=\"log\"), title=\"Confirmed cases\"),\n", 422 | " color=alt.Color(\"county:N\", title=\"County\", legend=None),\n", 423 | ").properties(\n", 424 | " title=\"Cumulative totals by county\"\n", 425 | ")" 426 | ] 427 | }, 428 | { 429 | "cell_type": "markdown", 430 | "metadata": {}, 431 | "source": [ 432 | "## County trends on a linear 'Pez' plot" 433 | ] 434 | }, 435 | { 436 | "cell_type": "markdown", 437 | "metadata": {}, 438 | "source": [ 439 | "Fill in any date gaps so that every county has a row for every date." 440 | ] 441 | }, 442 | { 443 | "cell_type": "code", 444 | "execution_count": null, 445 | "metadata": {}, 446 | "outputs": [], 447 | "source": [ 448 | "backfilled_county_df = (\n", 449 | " county_df.set_index([\"county\", \"date\"])\n", 450 | " .unstack(\"county\")\n", 451 | " .fillna(0)\n", 452 | " .stack(\"county\")\n", 453 | " .reset_index()\n", 454 | ")" 455 | ] 456 | }, 457 | { 458 | "cell_type": "markdown", 459 | "metadata": {}, 460 | "source": [ 461 | "Calculate the rolling change in each county." 462 | ] 463 | }, 464 | { 465 | "cell_type": "code", 466 | "execution_count": null, 467 | "metadata": {}, 468 | "outputs": [], 469 | "source": [ 470 | "chronological_county_df = backfilled_county_df.sort_values([\"county\", \"date\"])" 471 | ] 472 | }, 473 | { 474 | "cell_type": "markdown", 475 | "metadata": {}, 476 | "source": [ 477 | "Calculate the daily change in each county." 478 | ] 479 | }, 480 | { 481 | "cell_type": "code", 482 | "execution_count": null, 483 | "metadata": {}, 484 | "outputs": [], 485 | "source": [ 486 | "chronological_county_df[\"new_confirmed_cases\"] = chronological_county_df.groupby(\n", 487 | " \"county\"\n", 488 | ").confirmed_cases.diff()" 489 | ] 490 | }, 491 | { 492 | "cell_type": "markdown", 493 | "metadata": {}, 494 | "source": [ 495 | "Let's chill that out as a seven-day average." 496 | ] 497 | }, 498 | { 499 | "cell_type": "code", 500 | "execution_count": null, 501 | "metadata": {}, 502 | "outputs": [], 503 | "source": [ 504 | "chronological_county_df[\"new_confirmed_cases_rolling_average\"] = (\n", 505 | " chronological_county_df.groupby(\"county\")\n", 506 | " .new_confirmed_cases.rolling(7)\n", 507 | " .mean()\n", 508 | " .droplevel(0)\n", 509 | ")" 510 | ] 511 | }, 512 | { 513 | "cell_type": "markdown", 514 | "metadata": {}, 515 | "source": [ 516 | "Make the chart." 517 | ] 518 | }, 519 | { 520 | "cell_type": "code", 521 | "execution_count": null, 522 | "metadata": {}, 523 | "outputs": [], 524 | "source": [ 525 | "alt.Chart(chronological_county_df, title=\"New cases by day\").mark_rect(\n", 526 | " stroke=None\n", 527 | ").encode(\n", 528 | " x=alt.X(\n", 529 | " \"date:O\", axis=alt.Axis(ticks=False, grid=False, labels=False,), title=None\n", 530 | " ),\n", 531 | " y=alt.Y(\n", 532 | " \"county:N\",\n", 533 | " title=\"County\",\n", 534 | " axis=alt.Axis(ticks=False, grid=False, labelPadding=5),\n", 535 | " ),\n", 536 | " color=alt.Color(\n", 537 | " \"new_confirmed_cases_rolling_average:Q\",\n", 538 | " scale=alt.Scale(\n", 539 | " type=\"threshold\", domain=[0, 3, 10, 25, 50, 100, 500], scheme=\"blues\"\n", 540 | " ),\n", 541 | " title=\"New cases (7-day average)\",\n", 542 | " ),\n", 543 | ").properties(\n", 544 | " height=800\n", 545 | ")" 546 | ] 547 | }, 548 | { 549 | "cell_type": "markdown", 550 | "metadata": {}, 551 | "source": [ 552 | "## Chart new cases and deaths" 553 | ] 554 | }, 555 | { 556 | "cell_type": "markdown", 557 | "metadata": {}, 558 | "source": [ 559 | "Calculate the number of new cases each day using panda's [diff](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.diff.html) method." 560 | ] 561 | }, 562 | { 563 | "cell_type": "code", 564 | "execution_count": null, 565 | "metadata": {}, 566 | "outputs": [], 567 | "source": [ 568 | "state_df[\"new_confirmed_cases\"] = state_df.confirmed_cases.diff()" 569 | ] 570 | }, 571 | { 572 | "cell_type": "markdown", 573 | "metadata": {}, 574 | "source": [ 575 | "Do the same for deaths" 576 | ] 577 | }, 578 | { 579 | "cell_type": "code", 580 | "execution_count": null, 581 | "metadata": {}, 582 | "outputs": [], 583 | "source": [ 584 | "state_df[\"new_deaths\"] = state_df.deaths.diff()" 585 | ] 586 | }, 587 | { 588 | "cell_type": "markdown", 589 | "metadata": {}, 590 | "source": [ 591 | "Now calculate the moving seven-day average of each using panda's [rolling](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.rolling.html) method." 592 | ] 593 | }, 594 | { 595 | "cell_type": "code", 596 | "execution_count": null, 597 | "metadata": {}, 598 | "outputs": [], 599 | "source": [ 600 | "state_df[\"new_confirmed_cases_rolling_average\"] = state_df.new_confirmed_cases.rolling(\n", 601 | " 7\n", 602 | ").mean()" 603 | ] 604 | }, 605 | { 606 | "cell_type": "code", 607 | "execution_count": null, 608 | "metadata": {}, 609 | "outputs": [], 610 | "source": [ 611 | "state_df[\"new_deaths_rolling_average\"] = state_df.new_deaths.rolling(7).mean()" 612 | ] 613 | }, 614 | { 615 | "cell_type": "markdown", 616 | "metadata": {}, 617 | "source": [ 618 | "Put it all together on the chart " 619 | ] 620 | }, 621 | { 622 | "cell_type": "code", 623 | "execution_count": null, 624 | "metadata": {}, 625 | "outputs": [], 626 | "source": [ 627 | "# One base chart object with the data they all share\n", 628 | "chart = alt.Chart(state_df).encode(x=alt.X(\"date:T\", title=None),)\n", 629 | "\n", 630 | "# The new cases bars\n", 631 | "cases_bars = chart.mark_bar(color=lat.palette[\"default\"]).encode(\n", 632 | " y=alt.Y(\"new_confirmed_cases:Q\", title=\"New confirmed cases\")\n", 633 | ")\n", 634 | "\n", 635 | "# The cases rolling average\n", 636 | "cases_line = chart.mark_line(color=lat.palette[\"accent\"]).encode(\n", 637 | " y=alt.Y(\"new_confirmed_cases_rolling_average:Q\", title=\"7-day average\")\n", 638 | ")\n", 639 | "\n", 640 | "# The new deaths bars\n", 641 | "deaths_bars = chart.mark_bar(color=lat.palette[\"schemes\"][\"ice-7\"][3]).encode(\n", 642 | " y=alt.Y(\"new_deaths:Q\", title=\"New deaths\")\n", 643 | ")\n", 644 | "\n", 645 | "# The deaths rolling average\n", 646 | "deaths_line = chart.mark_line(color=lat.palette[\"schemes\"][\"ice-7\"][6]).encode(\n", 647 | " y=alt.Y(\"new_deaths_rolling_average:Q\", title=\"7-day average\")\n", 648 | ")\n", 649 | "\n", 650 | "# Combine it all together into one paired chart\n", 651 | "((cases_bars + cases_line) & (deaths_bars + deaths_line)).properties(\n", 652 | " title=\"New case and deaths statewide by day\"\n", 653 | ")" 654 | ] 655 | }, 656 | { 657 | "cell_type": "markdown", 658 | "metadata": {}, 659 | "source": [ 660 | "Now do it by county" 661 | ] 662 | }, 663 | { 664 | "cell_type": "code", 665 | "execution_count": null, 666 | "metadata": {}, 667 | "outputs": [], 668 | "source": [ 669 | "chronological_county_df.head()" 670 | ] 671 | }, 672 | { 673 | "cell_type": "markdown", 674 | "metadata": {}, 675 | "source": [ 676 | "Try it by county" 677 | ] 678 | }, 679 | { 680 | "cell_type": "code", 681 | "execution_count": null, 682 | "metadata": {}, 683 | "outputs": [], 684 | "source": [ 685 | "alt.Chart(chronological_county_df, title=\"New cases by day\").mark_line().encode(\n", 686 | " x=alt.X(\"date:O\", axis=alt.Axis(ticks=False, grid=False, labels=False), title=None),\n", 687 | " y=alt.Y(\"new_confirmed_cases_rolling_average:Q\", title=\"7-day average\"),\n", 688 | " color=alt.Color(\"county:N\", title=\"County\", legend=None),\n", 689 | ")" 690 | ] 691 | }, 692 | { 693 | "cell_type": "markdown", 694 | "metadata": {}, 695 | "source": [ 696 | "Create a statistic to measure recent changes in new cases" 697 | ] 698 | }, 699 | { 700 | "cell_type": "code", 701 | "execution_count": null, 702 | "metadata": {}, 703 | "outputs": [], 704 | "source": [ 705 | "chronological_county_df.tail(14)" 706 | ] 707 | }, 708 | { 709 | "cell_type": "code", 710 | "execution_count": null, 711 | "metadata": {}, 712 | "outputs": [], 713 | "source": [ 714 | "chronological_county_df[\n", 715 | " \"new_confirmed_cases_rolling_average_two_week_pct_change\"\n", 716 | "] = chronological_county_df.groupby(\n", 717 | " \"county\"\n", 718 | ").new_confirmed_cases_rolling_average.pct_change(\n", 719 | " 14\n", 720 | ")" 721 | ] 722 | }, 723 | { 724 | "cell_type": "code", 725 | "execution_count": null, 726 | "metadata": {}, 727 | "outputs": [], 728 | "source": [ 729 | "latest_county_df = chronological_county_df[\n", 730 | " chronological_county_df.date == chronological_county_df.date.max()\n", 731 | "]" 732 | ] 733 | }, 734 | { 735 | "cell_type": "code", 736 | "execution_count": null, 737 | "metadata": {}, 738 | "outputs": [], 739 | "source": [ 740 | "biggest_county_jumps = latest_county_df[\n", 741 | " latest_county_df.new_confirmed_cases_rolling_average >= 25\n", 742 | "].sort_values(\n", 743 | " \"new_confirmed_cases_rolling_average_two_week_pct_change\", ascending=False\n", 744 | ")" 745 | ] 746 | }, 747 | { 748 | "cell_type": "code", 749 | "execution_count": null, 750 | "metadata": {}, 751 | "outputs": [], 752 | "source": [ 753 | "def facet_wrap(subplts, plots_per_row):\n", 754 | " rows = [\n", 755 | " subplts[i : i + plots_per_row] for i in range(0, len(subplts), plots_per_row)\n", 756 | " ]\n", 757 | " compound_chart = alt.hconcat()\n", 758 | " for r in rows:\n", 759 | " rowplot = alt.vconcat() # start a new row\n", 760 | " for item in r:\n", 761 | " rowplot |= item # add suplot to current row as a new column\n", 762 | " compound_chart &= rowplot # add the entire row of plots as a new row\n", 763 | " return compound_chart" 764 | ] 765 | }, 766 | { 767 | "cell_type": "code", 768 | "execution_count": null, 769 | "metadata": {}, 770 | "outputs": [], 771 | "source": [] 772 | }, 773 | { 774 | "cell_type": "code", 775 | "execution_count": null, 776 | "metadata": {}, 777 | "outputs": [], 778 | "source": [ 779 | "chart_list = []\n", 780 | "for county in list(biggest_county_jumps.head(12).county):\n", 781 | " this_df = chronological_county_df[chronological_county_df.county == county]\n", 782 | " chart = alt.Chart(this_df, title=county).encode(\n", 783 | " x=alt.X(\"date:T\", title=None, axis=None),\n", 784 | " )\n", 785 | " lines = chart.mark_line(color=lat.palette[\"accent\"]).encode(\n", 786 | " y=alt.Y(\"new_confirmed_cases_rolling_average:Q\", title=None,),\n", 787 | " )\n", 788 | " bars = chart.mark_bar(color=lat.palette[\"default\"], opacity=0.33).encode(\n", 789 | " y=alt.Y(\"new_confirmed_cases:Q\", title=\"New confirmed cases\",),\n", 790 | " )\n", 791 | " chart_list.append((bars + lines).properties(height=200, width=250))\n", 792 | "facet_wrap(chart_list, plots_per_row=4)" 793 | ] 794 | }, 795 | { 796 | "cell_type": "code", 797 | "execution_count": null, 798 | "metadata": {}, 799 | "outputs": [], 800 | "source": [ 801 | "chart_list = []\n", 802 | "for county in list(biggest_county_jumps.tail(12).county):\n", 803 | " this_df = chronological_county_df[chronological_county_df.county == county]\n", 804 | " chart = alt.Chart(this_df, title=county).encode(\n", 805 | " x=alt.X(\"date:T\", title=None, axis=None),\n", 806 | " )\n", 807 | " lines = chart.mark_line(color=lat.palette[\"accent\"]).encode(\n", 808 | " y=alt.Y(\"new_confirmed_cases_rolling_average:Q\", title=None,),\n", 809 | " )\n", 810 | " bars = chart.mark_bar(color=lat.palette[\"default\"], opacity=0.33).encode(\n", 811 | " y=alt.Y(\"new_confirmed_cases:Q\", title=\"New confirmed cases\",),\n", 812 | " )\n", 813 | " chart_list.append((bars + lines).properties(height=200, width=250))\n", 814 | "facet_wrap(chart_list, plots_per_row=4)" 815 | ] 816 | }, 817 | { 818 | "cell_type": "code", 819 | "execution_count": null, 820 | "metadata": {}, 821 | "outputs": [], 822 | "source": [ 823 | "biggest_county_jumps.new_confirmed_cases_rolling_average_two_week_pct_change.describe()" 824 | ] 825 | }, 826 | { 827 | "cell_type": "code", 828 | "execution_count": null, 829 | "metadata": {}, 830 | "outputs": [], 831 | "source": [ 832 | "biggest_county_jumps[\n", 833 | " biggest_county_jumps.new_confirmed_cases_rolling_average_two_week_pct_change < 0\n", 834 | "]" 835 | ] 836 | }, 837 | { 838 | "cell_type": "markdown", 839 | "metadata": {}, 840 | "source": [ 841 | "# IDRE Workshop Additions" 842 | ] 843 | }, 844 | { 845 | "cell_type": "code", 846 | "execution_count": null, 847 | "metadata": {}, 848 | "outputs": [], 849 | "source": [ 850 | "alt.Chart(chronological_county_df, title=\"New cases by day\").mark_line().encode(\n", 851 | " x=alt.X(\"date:O\", axis=alt.Axis(ticks=False, grid=False, labels=False), title=None),\n", 852 | " y=alt.Y(\"new_confirmed_cases_rolling_average:Q\", title=\"7-day average\"),\n", 853 | " color=alt.Color(\"county:N\", title=\"County\", legend=None),\n", 854 | ")" 855 | ] 856 | }, 857 | { 858 | "cell_type": "code", 859 | "execution_count": null, 860 | "metadata": {}, 861 | "outputs": [], 862 | "source": [ 863 | "selopac = alt.selection_single(fields=['county'],bind='legend')\n", 864 | "how_to_select = 'CLICK ON COUNTY IN LEGEND TO SELECT'\n", 865 | "chronological_county_df_minusLA = chronological_county_df[chronological_county_df['county']!='Los Angeles']\n", 866 | "\n", 867 | "alt.Chart(chronological_county_df_minusLA, title=\"New cases by day\"+how_to_select).mark_line().encode(\n", 868 | " x=alt.X(\"date:O\", axis=alt.Axis(ticks=False, grid=False, labels=False), title=None),\n", 869 | " y=alt.Y(\"new_confirmed_cases_rolling_average:Q\", title=\"7-day average\"),\n", 870 | " color=alt.Color(\"county:N\", title=\"County\"),\n", 871 | " opacity=alt.condition(selopac, alt.value(1), alt.value(0.1))\n", 872 | ").add_selection(selopac)" 873 | ] 874 | }, 875 | { 876 | "cell_type": "code", 877 | "execution_count": null, 878 | "metadata": {}, 879 | "outputs": [], 880 | "source": [ 881 | "# top 20 counties in terms of mean new confirmed cases\n", 882 | "top20=list(chronological_county_df.groupby('county')['new_confirmed_cases'].mean().sort_values(ascending=False)[:20].index)" 883 | ] 884 | }, 885 | { 886 | "cell_type": "code", 887 | "execution_count": null, 888 | "metadata": {}, 889 | "outputs": [], 890 | "source": [ 891 | "selopac = alt.selection_single(on='mouseover',fields=['county'],bind='legend') \n", 892 | "how_to_select = 'MOVE MOUSE OVER LINE TO SELECT LINE'\n", 893 | "chronological_county_df_top20 = chronological_county_df[chronological_county_df['county'].isin(top20)]\n", 894 | "\n", 895 | "alt.Chart(chronological_county_df_top20, title=\"New cases by day\"+how_to_select).mark_line().encode(\n", 896 | " x=alt.X(\"date:O\", axis=alt.Axis(ticks=False, grid=False, labels=False), title=None),\n", 897 | " y=alt.Y(\"new_confirmed_cases_rolling_average:Q\", title=\"7-day average\"),\n", 898 | " color=alt.Color(\"county:N\", title=\"County\"),\n", 899 | " opacity=alt.condition(selopac, alt.value(1), alt.value(0.1))\n", 900 | ").add_selection(selopac)" 901 | ] 902 | }, 903 | { 904 | "cell_type": "code", 905 | "execution_count": null, 906 | "metadata": {}, 907 | "outputs": [], 908 | "source": [ 909 | "selopac = alt.selection_single(fields=['county'],bind='legend') \n", 910 | "how_to_select = 'CLICK ON COUNTY IN LEGEND TO SELECT'\n", 911 | "chronological_county_df_top20 = chronological_county_df[chronological_county_df['county'].isin(top20)]\n", 912 | "chronological_county_df_top20 = chronological_county_df_top20[chronological_county_df_top20.date > '2021-03-01']\n", 913 | "\n", 914 | "alt.Chart(chronological_county_df_top20, title=\"New cases by day\"+how_to_select).mark_line().encode(\n", 915 | " x=alt.X(\"date:O\", axis=alt.Axis(ticks=False, grid=False, labels=False), title=None),\n", 916 | " y=alt.Y(\"new_confirmed_cases_rolling_average:Q\", title=\"7-day average\"),\n", 917 | " color=alt.Color(\"county:N\", title=\"County\"),\n", 918 | " opacity=alt.condition(selopac, alt.value(1), alt.value(0.1))\n", 919 | ").add_selection(selopac)" 920 | ] 921 | }, 922 | { 923 | "cell_type": "code", 924 | "execution_count": null, 925 | "metadata": {}, 926 | "outputs": [], 927 | "source": [ 928 | "chart_list = []\n", 929 | "for county in list(biggest_county_jumps.tail(12).county):\n", 930 | " this_df = chronological_county_df[chronological_county_df.county == county]\n", 931 | " chart = alt.Chart(this_df, title=county).encode(\n", 932 | " x=alt.X(\"date:T\", title=None, axis=None),\n", 933 | " )\n", 934 | " lines = chart.mark_line(color=lat.palette[\"accent\"]).encode(\n", 935 | " y=alt.Y(\"new_confirmed_cases_rolling_average:Q\", title=None,),\n", 936 | " )\n", 937 | " bars = chart.mark_bar(color=lat.palette[\"default\"], opacity=0.33).encode(\n", 938 | " y=alt.Y(\"new_confirmed_cases:Q\", title=\"New confirmed cases\",),\n", 939 | " )\n", 940 | " chart_list.append((bars + lines).properties(height=200, width=250))\n", 941 | "facet_wrap(chart_list, plots_per_row=4)" 942 | ] 943 | }, 944 | { 945 | "cell_type": "code", 946 | "execution_count": null, 947 | "metadata": {}, 948 | "outputs": [], 949 | "source": [ 950 | "import ipywidgets" 951 | ] 952 | }, 953 | { 954 | "cell_type": "code", 955 | "execution_count": null, 956 | "metadata": {}, 957 | "outputs": [], 958 | "source": [ 959 | "# for county in list(biggest_county_jumps.tail(12).county):\n", 960 | "def countyplot(county='Los Angeles'):\n", 961 | " this_df = chronological_county_df[chronological_county_df.county == county]\n", 962 | " chart = alt.Chart(this_df, title=county).encode(\n", 963 | " x=alt.X(\"date:T\", title=None, axis=None),\n", 964 | " )\n", 965 | " lines = chart.mark_line(color=lat.palette[\"accent\"]).encode(\n", 966 | " y=alt.Y(\"new_confirmed_cases_rolling_average:Q\", title=None,),\n", 967 | " )\n", 968 | " bars = chart.mark_bar(color=lat.palette[\"default\"], opacity=0.33).encode(\n", 969 | " y=alt.Y(\"new_confirmed_cases:Q\", title=\"New confirmed cases\",),\n", 970 | " )\n", 971 | " #chart_list.append((bars + lines).properties(height=200, width=250))\n", 972 | " return (bars + lines).properties(height=200, width=250)\n", 973 | " \n", 974 | "#facet_wrap(chart_list, plots_per_row=4)\n", 975 | "ipywidgets.interact(countyplot,county=biggest_county_jumps.county.unique());" 976 | ] 977 | }, 978 | { 979 | "cell_type": "code", 980 | "execution_count": null, 981 | "metadata": {}, 982 | "outputs": [], 983 | "source": [] 984 | } 985 | ], 986 | "metadata": { 987 | "kernelspec": { 988 | "display_name": "Python 3", 989 | "language": "python", 990 | "name": "python3" 991 | }, 992 | "language_info": { 993 | "codemirror_mode": { 994 | "name": "ipython", 995 | "version": 3 996 | }, 997 | "file_extension": ".py", 998 | "mimetype": "text/x-python", 999 | "name": "python", 1000 | "nbconvert_exporter": "python", 1001 | "pygments_lexer": "ipython3", 1002 | "version": "3.8.8" 1003 | }, 1004 | "toc": { 1005 | "base_numbering": 1, 1006 | "nav_menu": {}, 1007 | "number_sections": true, 1008 | "sideBar": true, 1009 | "skip_h1_title": false, 1010 | "title_cell": "Table of Contents", 1011 | "title_sidebar": "Contents", 1012 | "toc_cell": false, 1013 | "toc_position": {}, 1014 | "toc_section_display": true, 1015 | "toc_window_display": false 1016 | } 1017 | }, 1018 | "nbformat": 4, 1019 | "nbformat_minor": 4 1020 | } 1021 | -------------------------------------------------------------------------------- /4fun-bqplot.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "Note: This notebook is taken from Chakri Cherukuri's [GitHub repo](https://github.com/ChakriCherukuri/mlviz) showing a variety of examples on using bqplot to visualize theoretical and applied machine learning algorithms/models.\n", 8 | "*****" 9 | ] 10 | }, 11 | { 12 | "cell_type": "markdown", 13 | "metadata": {}, 14 | "source": [ 15 | "

Multi-variate Gaussian Distribution

\n", 16 | "\n", 17 | "The multivariate normal distribution of a k-dimensional random vector $ \\large X =(X_{1},\\ldots ,X_{k})^{T}$ is given by:
\n", 18 | "$ \\large X \\sim N \\boldsymbol (\\mu, \\boldsymbol \\Sigma) $\n", 19 | "with k-dimensional mean vector
\n", 20 | "$ \\large \\boldsymbol \\mu = E[X] = [E[X_{1}], E[X_{2}],\\ldots , E[X_{k}]]^{T} $\n", 21 | "\n", 22 | "and $ \\large k \\times k $ covariance matrix $\\large \\Sigma $\n", 23 | "\n", 24 | "

Gaussian Process

\n", 25 | "* GP is Gaussian distribution over functions. We are learning a *distribution* from empirical data\n", 26 | "* Start with prior $\\large f_{prior} \\sim N (\\boldsymbol 0, \\boldsymbol K) $\n", 27 | "* Compute the posterior from training samples by doing a bayesian update
\n", 28 | "$ \\large f_{posterior} \\sim N \\boldsymbol (\\mu_{*}, \\Sigma_{*}) $ where
\n", 29 | "$ \\large \\mu_* = K_* K^{-1} y $
\n", 30 | "$ \\large \\Sigma_* = K_{∗∗} − K_∗ K^{−1} K^T $\n" 31 | ] 32 | }, 33 | { 34 | "cell_type": "code", 35 | "execution_count": null, 36 | "metadata": {}, 37 | "outputs": [], 38 | "source": [ 39 | "import numpy as np\n", 40 | "\n", 41 | "import ipywidgets as w\n", 42 | "import bqplot.pyplot as plt\n", 43 | "from bqplot import *" 44 | ] 45 | }, 46 | { 47 | "cell_type": "code", 48 | "execution_count": null, 49 | "metadata": {}, 50 | "outputs": [], 51 | "source": [ 52 | "def squared_exponential(x1, x2, sigma=1., l=1.):\n", 53 | " z = (x1 - x2[:, np.newaxis]) / l\n", 54 | " return sigma**2 * np.exp(-.5 * z ** 2)" 55 | ] 56 | }, 57 | { 58 | "cell_type": "code", 59 | "execution_count": null, 60 | "metadata": {}, 61 | "outputs": [], 62 | "source": [ 63 | "def gp_regression(X_train, y_train, X_test,\n", 64 | " kernel=squared_exponential,\n", 65 | " sigma_noise=.1,\n", 66 | " params=dict(sigma=1., l=1.)):\n", 67 | " # compute the kernel matrices for train, train_test, test combinations\n", 68 | " K = kernel(X_train, X_train, **params)\n", 69 | " K_s = kernel(X_train, X_test, **params)\n", 70 | " K_ss = kernel(X_test, X_test, **params)\n", 71 | " \n", 72 | " n, p = len(X_train), len(X_test)\n", 73 | " \n", 74 | " # compute the posterior mean and cov\n", 75 | " mu_s = np.dot(K_s, np.linalg.solve(K + sigma_noise**2 * np.eye(n), y_train))\n", 76 | " cov_s = K_ss - np.dot(K_s, np.linalg.solve(K + sigma_noise**2 * np.eye(n), K_s.T))\n", 77 | " \n", 78 | " # prior and posterior moments\n", 79 | " mu_prior, cov_prior = np.zeros(p), K_ss\n", 80 | " mu_post, cov_post = mu_s, cov_s + sigma_noise**2\n", 81 | " \n", 82 | " return dict(prior=(mu_prior, cov_prior), \n", 83 | " posterior=(mu_post, cov_post))" 84 | ] 85 | }, 86 | { 87 | "cell_type": "code", 88 | "execution_count": null, 89 | "metadata": {}, 90 | "outputs": [], 91 | "source": [ 92 | "kernel = squared_exponential\n", 93 | "params = dict(sigma=1., l=1.)\n", 94 | "\n", 95 | "X_test = np.arange(-5, 5, .05)\n", 96 | "p = len(X_test)\n", 97 | "K_ss = kernel(X_test, X_test, **params)\n", 98 | "mu_prior, cov_prior = np.zeros(p), K_ss\n", 99 | "\n", 100 | "N = 5\n", 101 | "f_priors = np.random.multivariate_normal(mu_prior, cov_prior, N)" 102 | ] 103 | }, 104 | { 105 | "cell_type": "code", 106 | "execution_count": null, 107 | "metadata": { 108 | "scrolled": true 109 | }, 110 | "outputs": [], 111 | "source": [ 112 | "fig_margin=dict(top=60, bottom=40, left=50, right=0)\n", 113 | "\n", 114 | "fig = plt.figure(title='Gaussian Process Regression', \n", 115 | " layout=w.Layout(width='1000px', height='600px'),\n", 116 | " animation_duration=750,\n", 117 | " fig_margin=fig_margin)\n", 118 | "\n", 119 | "plt.scales(scales={'x': LinearScale(min=-5, max=5),\n", 120 | " 'y': LinearScale(min=-5, max=5)})\n", 121 | "\n", 122 | "train_scat = plt.scatter([], [], colors=['magenta'], \n", 123 | " enable_move=True,\n", 124 | " interactions={'click': 'add'},\n", 125 | " marker_size=1, marker='square')\n", 126 | "\n", 127 | "prior_lines = plt.plot(X_test, f_priors, stroke_width=1, colors=['#ccc'])\n", 128 | "posterior_lines = plt.plot(X_test, [], stroke_width=1)\n", 129 | "\n", 130 | "mean_line = plt.plot(X_test, [], 'm')\n", 131 | "std_bands = plt.plot(X_test, [],\n", 132 | " fill='between',\n", 133 | " fill_colors=['yellow'],\n", 134 | " fill_opacities=[.2], stroke_width=0)\n", 135 | "plt.xlabel('X')\n", 136 | "plt.ylabel('Y')\n", 137 | "\n", 138 | "# reset btn\n", 139 | "reset_button = w.Button(description='Reset Points', button_style='success')\n", 140 | "reset_button.layout.margin = '20px 0px 0px 70px'\n", 141 | "\n", 142 | "# controls for the plot\n", 143 | "f_priors_cb = w.Checkbox(description='Display 5 Priors?')\n", 144 | "f_posteriors_cb = w.Checkbox(description='Display 5 Posteriors?')\n", 145 | "std_bands_cb = w.Checkbox(description='Display Std Bands?')\n", 146 | "check_boxes = [f_priors_cb, f_posteriors_cb, std_bands_cb]\n", 147 | "\n", 148 | "label = w.Label('*Click on the figure to add training samples')\n", 149 | "controls = w.VBox(check_boxes + [reset_button, label])\n", 150 | "\n", 151 | "# link widgets\n", 152 | "_ = w.jslink((f_priors_cb, 'value'), (prior_lines, 'visible'))\n", 153 | "_ = w.jslink((f_posteriors_cb, 'value'), (posterior_lines, 'visible'))\n", 154 | "_ = w.jslink((std_bands_cb, 'value'), (std_bands, 'visible'))\n", 155 | "\n", 156 | "def update_reg_line(change):\n", 157 | " global mu_post, sig_post\n", 158 | " \n", 159 | " fig.animation_duration = 0\n", 160 | " X_train = train_scat.x\n", 161 | " y_train = train_scat.y\n", 162 | "\n", 163 | " gp_res = gp_regression(X_train, y_train, X_test, sigma_noise=0.04)\n", 164 | " mu_post, cov_post = gp_res['posterior']\n", 165 | " \n", 166 | " # simulate N samples from the posterior distribution\n", 167 | " posterior_lines.y = np.random.multivariate_normal(mu_post, cov_post, N)\n", 168 | " sig_post = np.sqrt(np.diag(cov_post))\n", 169 | "\n", 170 | " # update the regression line to the mean of the posterior distribution\n", 171 | " mean_line.y = mu_post\n", 172 | " \n", 173 | " # update the std bands to +/- 2 sigmas from the posterior mean\n", 174 | " std_bands.y = [mu_post - 2 * sig_post, mu_post + 2 * sig_post]\n", 175 | "\n", 176 | "train_scat.observe(update_reg_line, names=['x', 'y'])\n", 177 | "\n", 178 | "def reset_points(*args):\n", 179 | " with train_scat.hold_trait_notifications():\n", 180 | " train_scat.x = []\n", 181 | " train_scat.y = []\n", 182 | "reset_button.on_click(lambda btn: reset_points())\n", 183 | "\n", 184 | "fig.on_displayed(update_reg_line)\n", 185 | "w.HBox([fig, controls])" 186 | ] 187 | }, 188 | { 189 | "cell_type": "code", 190 | "execution_count": null, 191 | "metadata": {}, 192 | "outputs": [], 193 | "source": [] 194 | } 195 | ], 196 | "metadata": { 197 | "kernelspec": { 198 | "display_name": "Python 3", 199 | "language": "python", 200 | "name": "python3" 201 | }, 202 | "language_info": { 203 | "codemirror_mode": { 204 | "name": "ipython", 205 | "version": 3 206 | }, 207 | "file_extension": ".py", 208 | "mimetype": "text/x-python", 209 | "name": "python", 210 | "nbconvert_exporter": "python", 211 | "pygments_lexer": "ipython3", 212 | "version": "3.8.8" 213 | }, 214 | "toc": { 215 | "base_numbering": 1, 216 | "nav_menu": {}, 217 | "number_sections": true, 218 | "sideBar": true, 219 | "skip_h1_title": false, 220 | "title_cell": "Table of Contents", 221 | "title_sidebar": "Contents", 222 | "toc_cell": false, 223 | "toc_position": {}, 224 | "toc_section_display": true, 225 | "toc_window_display": false 226 | } 227 | }, 228 | "nbformat": 4, 229 | "nbformat_minor": 2 230 | } 231 | -------------------------------------------------------------------------------- /4fun-ipywidgets.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Gradient Descent\n", 8 | "# This is too much for our short workshop; instead take this as an illustration of what you might like to have fun with later\n", 9 | "\n", 10 | "Gradient descent is an iterative optimization algorithm for determining the local minimum of a differentiable function.\n", 11 | "\n", 12 | "For a function _f_, the gradient of _f_ with respect to the independent variables gives the direction in which _f_ increases the fastest. By taking steps opposite to the gradient, one can iteratively move towards lower and lower values until one reaches the minimum of _f_ (ideally)." 13 | ] 14 | }, 15 | { 16 | "cell_type": "code", 17 | "execution_count": null, 18 | "metadata": {}, 19 | "outputs": [], 20 | "source": [ 21 | "from IPython.display import Image\n", 22 | "Image(url='https://www.ahmednasr.at/wp-content/uploads/2017/05/gradientDescent.jpg') " 23 | ] 24 | }, 25 | { 26 | "cell_type": "markdown", 27 | "metadata": {}, 28 | "source": [ 29 | "For an _f(x)_ that depends on only one variable:\n", 30 | "* choose a starting point, $x_0$\n", 31 | "* take a step towards a new $x$:\n", 32 | " * $x_{n+1} = x_{n} - \\eta*df/dx$\n", 33 | " * here, $\\eta$ is a user-chosen step size and $df/dx$ is the 1D gradient of f (i.e., the derivative)\n", 34 | "* continue until the value of _f(x)_ stops changing within some low tolerance" 35 | ] 36 | }, 37 | { 38 | "cell_type": "code", 39 | "execution_count": null, 40 | "metadata": {}, 41 | "outputs": [], 42 | "source": [ 43 | "import numpy as np\n", 44 | "from ipywidgets import interactive, interact, interactive_output\n", 45 | "from ipywidgets import fixed, VBox, HBox, jslink, Play, FloatSlider, IntSlider\n", 46 | "import matplotlib.pyplot as plt" 47 | ] 48 | }, 49 | { 50 | "cell_type": "code", 51 | "execution_count": null, 52 | "metadata": {}, 53 | "outputs": [], 54 | "source": [ 55 | "def f(x):\n", 56 | " return np.exp(-x) * np.sin(5 * x)\n", 57 | "def df(x):\n", 58 | " return -np.exp(-x) * np.sin(5 * x) + 5 * np.cos(5 *x) * np.exp(-x)\n", 59 | "\n", 60 | "def gradient_descent_2(x0, eta=.1, tol=1e-6, num_iters=10):\n", 61 | " xtheory = np.linspace(0.5, 3, 300)\n", 62 | " ytheory = f(xtheory)\n", 63 | " x = [x0]\n", 64 | " i = 0\n", 65 | "\n", 66 | " fig = plt.figure(figsize=(12,7))\n", 67 | " curve = plt.plot(xtheory, ytheory, 'b-')\n", 68 | " plt.plot(x[-1],f(x[-1]),'ko',markersize=10)\n", 69 | " while i < num_iters:\n", 70 | " x_prev = x[-1]\n", 71 | " grad = df(x_prev)\n", 72 | " x_curr = x_prev - eta * grad\n", 73 | " x.append(x_curr)\n", 74 | " if f(x_curr) > 10:\n", 75 | " break\n", 76 | " plt.plot(x_curr,f(x_curr),'ko',markersize=10)\n", 77 | " plt.plot([x_prev,x_curr],[f(x_prev),f(x_curr)],'k--')\n", 78 | " \n", 79 | " if np.abs(x_curr - x_prev) < tol:\n", 80 | " break\n", 81 | " i += 1\n", 82 | " \n", 83 | " fig.show()\n", 84 | " \n", 85 | "a = FloatSlider(value=1.3,min=0.5,max=3,description='x0')\n", 86 | "b = IntSlider(value=0,min=0,max=50,description='num_iters')\n", 87 | "c = FloatSlider(value=0.2,min=0.01,max=2.0,step=0.01,description='eta')\n", 88 | "controls = VBox([a,b,c])\n", 89 | "plotwidget = interactive_output(gradient_descent_2, {\n", 90 | " 'x0':a,\n", 91 | " 'num_iters':b,\n", 92 | " 'eta':c,\n", 93 | " 'tol':fixed(1e-6)});\n", 94 | "def achanged(change):\n", 95 | " b.value = 0\n", 96 | "a.observe(achanged,'value')\n", 97 | "\n", 98 | "def cchanged(change):\n", 99 | " b.value = 0\n", 100 | "c.observe(cchanged,'value')\n", 101 | "\n", 102 | "play = Play(value=0,min=0,max=1000,step=1,interval=1000,description=\"Press play\",disabled=False)\n", 103 | "jslink((play, 'value'), (b, 'value'))\n", 104 | "gradmov = VBox([controls,plotwidget,play])\n", 105 | "\n", 106 | "display(gradmov)" 107 | ] 108 | }, 109 | { 110 | "cell_type": "markdown", 111 | "metadata": {}, 112 | "source": [ 113 | "# The below are some more managably understandable coding steps to work up to the above code" 114 | ] 115 | }, 116 | { 117 | "cell_type": "code", 118 | "execution_count": null, 119 | "metadata": {}, 120 | "outputs": [], 121 | "source": [ 122 | "def f(x):\n", 123 | " return np.exp(-x) * np.sin(5 * x)\n", 124 | "def df(x):\n", 125 | " return -np.exp(-x) * np.sin(5 * x) + 5 * np.cos(5 *x) * np.exp(-x)" 126 | ] 127 | }, 128 | { 129 | "cell_type": "code", 130 | "execution_count": null, 131 | "metadata": {}, 132 | "outputs": [], 133 | "source": [ 134 | "x = np.linspace(0.5, 5.5, 500)\n", 135 | "y = f(x)" 136 | ] 137 | }, 138 | { 139 | "cell_type": "code", 140 | "execution_count": null, 141 | "metadata": {}, 142 | "outputs": [], 143 | "source": [ 144 | "plt.plot(x,y);" 145 | ] 146 | }, 147 | { 148 | "cell_type": "code", 149 | "execution_count": null, 150 | "metadata": {}, 151 | "outputs": [], 152 | "source": [ 153 | "fig = plt.figure(figsize=(12,7))\n", 154 | "curve = plt.plot(x, y, 'b-')\n", 155 | "points = plt.plot(x[100],y[100],'ko',markersize=10)" 156 | ] 157 | }, 158 | { 159 | "cell_type": "code", 160 | "execution_count": null, 161 | "metadata": {}, 162 | "outputs": [], 163 | "source": [ 164 | "def gradient_descent(x0, eta=.1, tol=1e-6, num_iters=10):\n", 165 | " xtheory = np.linspace(0.5, 5.5, 500)\n", 166 | " ytheory = f(xtheory)\n", 167 | " x = [x0]\n", 168 | " i = 0\n", 169 | "\n", 170 | " fig = plt.figure(figsize=(12,7))\n", 171 | " curve = plt.plot(xtheory, ytheory, 'b-')\n", 172 | " plt.plot(x[-1],f(x[-1]),'ko',markersize=10)\n", 173 | " while i < num_iters:\n", 174 | " x_prev = x[-1]\n", 175 | " grad = df(x_prev)\n", 176 | " x_curr = x_prev - eta * grad\n", 177 | " x.append(x_curr)\n", 178 | " if f(x_curr) > 10:\n", 179 | " break\n", 180 | " plt.plot(x_curr,f(x_curr),'ko',markersize=10)\n", 181 | " \n", 182 | " if np.abs(x_curr - x_prev) < tol:\n", 183 | " break\n", 184 | " i += 1\n", 185 | " \n", 186 | " fig.show()" 187 | ] 188 | }, 189 | { 190 | "cell_type": "code", 191 | "execution_count": null, 192 | "metadata": {}, 193 | "outputs": [], 194 | "source": [ 195 | "gradient_descent(2.5, eta=.1, tol=1e-6, num_iters=10)" 196 | ] 197 | }, 198 | { 199 | "cell_type": "code", 200 | "execution_count": null, 201 | "metadata": {}, 202 | "outputs": [], 203 | "source": [ 204 | "interactive(gradient_descent,x0=(0.5,5.5),num_iters=(0,20))" 205 | ] 206 | }, 207 | { 208 | "cell_type": "code", 209 | "execution_count": null, 210 | "metadata": {}, 211 | "outputs": [], 212 | "source": [ 213 | "interactive(gradient_descent,\n", 214 | " x0=FloatSlider(value=2,min=0.5,max=5.5),\n", 215 | " num_iters=IntSlider(value=0,min=0,max=20),\n", 216 | " eta=FloatSlider(value=0.1,min=0.01,max=1.0,step=0.01),\n", 217 | " tol=fixed(1e-6))" 218 | ] 219 | }, 220 | { 221 | "cell_type": "code", 222 | "execution_count": null, 223 | "metadata": {}, 224 | "outputs": [], 225 | "source": [ 226 | "a = FloatSlider(value=2,min=0.5,max=5.5)\n", 227 | "b = IntSlider(value=0,min=0,max=50)\n", 228 | "c = FloatSlider(value=0.1,min=0.01,max=2.0,step=0.01)\n", 229 | "interact(gradient_descent,\n", 230 | " x0 = a,\n", 231 | " num_iters = b,\n", 232 | " eta = c,\n", 233 | " tol=fixed(1e-6))\n", 234 | "def achanged(change):\n", 235 | " b.value = 0\n", 236 | "a.observe(achanged,'value')\n", 237 | "\n", 238 | "def cchanged(change):\n", 239 | " b.value = 0\n", 240 | "c.observe(cchanged,'value')" 241 | ] 242 | }, 243 | { 244 | "cell_type": "code", 245 | "execution_count": null, 246 | "metadata": {}, 247 | "outputs": [], 248 | "source": [ 249 | "def gradient_descent_2(x0, eta=.1, tol=1e-6, num_iters=10):\n", 250 | " xtheory = np.linspace(0.5, 3, 300)\n", 251 | " ytheory = f(xtheory)\n", 252 | " x = [x0]\n", 253 | " i = 0\n", 254 | "\n", 255 | " fig = plt.figure(figsize=(12,7))\n", 256 | " curve = plt.plot(xtheory, ytheory, 'b-')\n", 257 | " plt.plot(x[-1],f(x[-1]),'ko',markersize=10)\n", 258 | " while i < num_iters:\n", 259 | " x_prev = x[-1]\n", 260 | " grad = df(x_prev)\n", 261 | " x_curr = x_prev - eta * grad\n", 262 | " x.append(x_curr)\n", 263 | " if f(x_curr) > 10:\n", 264 | " break\n", 265 | " plt.plot(x_curr,f(x_curr),'ko',markersize=10)\n", 266 | " plt.plot([x_prev,x_curr],[f(x_prev),f(x_curr)],'k--')\n", 267 | " \n", 268 | " if np.abs(x_curr - x_prev) < tol:\n", 269 | " break\n", 270 | " i += 1\n", 271 | " \n", 272 | " fig.show()" 273 | ] 274 | }, 275 | { 276 | "cell_type": "code", 277 | "execution_count": null, 278 | "metadata": {}, 279 | "outputs": [], 280 | "source": [ 281 | "a = FloatSlider(value=1.3,min=0.5,max=3,description='x0')\n", 282 | "b = IntSlider(value=0,min=0,max=50,description='num_iters')\n", 283 | "c = FloatSlider(value=0.2,min=0.01,max=2.0,step=0.01,description='eta')\n", 284 | "controls = VBox([a,b,c])\n", 285 | "plotwidget = interactive_output(gradient_descent_2, {\n", 286 | " 'x0':a,\n", 287 | " 'num_iters':b,\n", 288 | " 'eta':c,\n", 289 | " 'tol':fixed(1e-6)});\n", 290 | "def achanged(change):\n", 291 | " b.value = 0\n", 292 | "a.observe(achanged,'value')\n", 293 | "\n", 294 | "def cchanged(change):\n", 295 | " b.value = 0\n", 296 | "c.observe(cchanged,'value')\n", 297 | "\n", 298 | "play = Play(value=0,min=0,max=1000,step=1,interval=1000,description=\"Press play\",disabled=False)\n", 299 | "jslink((play, 'value'), (b, 'value'))\n", 300 | "gradmov = VBox([controls,plotwidget,play])\n", 301 | "\n", 302 | "display(gradmov)" 303 | ] 304 | }, 305 | { 306 | "cell_type": "code", 307 | "execution_count": null, 308 | "metadata": {}, 309 | "outputs": [], 310 | "source": [] 311 | } 312 | ], 313 | "metadata": { 314 | "kernelspec": { 315 | "display_name": "Python 3", 316 | "language": "python", 317 | "name": "python3" 318 | }, 319 | "language_info": { 320 | "codemirror_mode": { 321 | "name": "ipython", 322 | "version": 3 323 | }, 324 | "file_extension": ".py", 325 | "mimetype": "text/x-python", 326 | "name": "python", 327 | "nbconvert_exporter": "python", 328 | "pygments_lexer": "ipython3", 329 | "version": "3.8.8" 330 | }, 331 | "toc": { 332 | "base_numbering": 1, 333 | "nav_menu": {}, 334 | "number_sections": true, 335 | "sideBar": true, 336 | "skip_h1_title": false, 337 | "title_cell": "Table of Contents", 338 | "title_sidebar": "Contents", 339 | "toc_cell": false, 340 | "toc_position": {}, 341 | "toc_section_display": true, 342 | "toc_window_display": false 343 | } 344 | }, 345 | "nbformat": 4, 346 | "nbformat_minor": 1 347 | } 348 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Spring 2021 IDRE Data Visualization with Python II: Making Interactive Plots and Widgets 2 | 3 | This repository is maintained by Ben Winjum for use in UCLA IDRE's introductory training class. 4 | 5 | ## Interacting with JupyterHub for class 6 | 7 | * UCLA participants (multi-factor authentication required): 8 | 9 | * Launch into JupyterHub (choose UCLA) 10 | 11 | * Non UCLA participants (or anyone who has trouble with the above): 12 | * [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/benjum/idre-spring21-python-data-viz-2/HEAD) 13 | 14 | 15 | -------------------------------------------------------------------------------- /data/DatasaurusDozen.tsv: -------------------------------------------------------------------------------- 1 | dataset x y 2 | dino 55.3846 97.1795 3 | dino 51.5385 96.0256 4 | dino 46.1538 94.4872 5 | dino 42.8205 91.4103 6 | dino 40.7692 88.3333 7 | dino 38.7179 84.8718 8 | dino 35.641 79.8718 9 | dino 33.0769 77.5641 10 | dino 28.9744 74.4872 11 | dino 26.1538 71.4103 12 | dino 23.0769 66.4103 13 | dino 22.3077 61.7949 14 | dino 22.3077 57.1795 15 | dino 23.3333 52.9487 16 | dino 25.8974 51.0256 17 | dino 29.4872 51.0256 18 | dino 32.8205 51.0256 19 | dino 35.3846 51.4103 20 | dino 40.2564 51.4103 21 | dino 44.1026 52.9487 22 | dino 46.6667 54.1026 23 | dino 50.0 55.2564 24 | dino 53.0769 55.641 25 | dino 56.6667 56.0256 26 | dino 59.2308 57.9487 27 | dino 61.2821 62.1795 28 | dino 61.5385 66.4103 29 | dino 61.7949 69.1026 30 | dino 57.4359 55.2564 31 | dino 54.8718 49.8718 32 | dino 52.5641 46.0256 33 | dino 48.2051 38.3333 34 | dino 49.4872 42.1795 35 | dino 51.0256 44.1026 36 | dino 45.3846 36.4103 37 | dino 42.8205 32.5641 38 | dino 38.7179 31.4103 39 | dino 35.1282 30.2564 40 | dino 32.5641 32.1795 41 | dino 30.0 36.7949 42 | dino 33.5897 41.4103 43 | dino 36.6667 45.641 44 | dino 38.2051 49.1026 45 | dino 29.7436 36.0256 46 | dino 29.7436 32.1795 47 | dino 30.0 29.1026 48 | dino 32.0513 26.7949 49 | dino 35.8974 25.2564 50 | dino 41.0256 25.2564 51 | dino 44.1026 25.641 52 | dino 47.1795 28.718 53 | dino 49.4872 31.4103 54 | dino 51.5385 34.8718 55 | dino 53.5897 37.5641 56 | dino 55.1282 40.641 57 | dino 56.6667 42.1795 58 | dino 59.2308 44.4872 59 | dino 62.3077 46.0256 60 | dino 64.8718 46.7949 61 | dino 67.9487 47.9487 62 | dino 70.5128 53.718 63 | dino 71.5385 60.641 64 | dino 71.5385 64.4872 65 | dino 69.4872 69.4872 66 | dino 46.9231 79.8718 67 | dino 48.2051 84.1026 68 | dino 50.0 85.2564 69 | dino 53.0769 85.2564 70 | dino 55.3846 86.0256 71 | dino 56.6667 86.0256 72 | dino 56.1538 82.9487 73 | dino 53.8462 80.641 74 | dino 51.2821 78.718 75 | dino 50.0 78.718 76 | dino 47.9487 77.5641 77 | dino 29.7436 59.8718 78 | dino 29.7436 62.1795 79 | dino 31.2821 62.5641 80 | dino 57.9487 99.4872 81 | dino 61.7949 99.1026 82 | dino 64.8718 97.5641 83 | dino 68.4615 94.1026 84 | dino 70.7692 91.0256 85 | dino 72.0513 86.4103 86 | dino 73.8462 83.3333 87 | dino 75.1282 79.1026 88 | dino 76.6667 75.2564 89 | dino 77.6923 71.4103 90 | dino 79.7436 66.7949 91 | dino 81.7949 60.2564 92 | dino 83.3333 55.2564 93 | dino 85.1282 51.4103 94 | dino 86.4103 47.5641 95 | dino 87.9487 46.0256 96 | dino 89.4872 42.5641 97 | dino 93.3333 39.8718 98 | dino 95.3846 36.7949 99 | dino 98.2051 33.718 100 | dino 56.6667 40.641 101 | dino 59.2308 38.3333 102 | dino 60.7692 33.718 103 | dino 63.0769 29.1026 104 | dino 64.1026 25.2564 105 | dino 64.359 24.1026 106 | dino 74.359 22.9487 107 | dino 71.2821 22.9487 108 | dino 67.9487 22.1795 109 | dino 65.8974 20.2564 110 | dino 63.0769 19.1026 111 | dino 61.2821 19.1026 112 | dino 58.7179 18.3333 113 | dino 55.1282 18.3333 114 | dino 52.3077 18.3333 115 | dino 49.7436 17.5641 116 | dino 47.4359 16.0256 117 | dino 44.8718 13.718 118 | dino 48.7179 14.8718 119 | dino 51.2821 14.8718 120 | dino 54.1026 14.8718 121 | dino 56.1538 14.1026 122 | dino 52.0513 12.5641 123 | dino 48.7179 11.0256 124 | dino 47.1795 9.8718 125 | dino 46.1538 6.0256 126 | dino 50.5128 9.4872 127 | dino 53.8462 10.2564 128 | dino 57.4359 10.2564 129 | dino 60.0 10.641 130 | dino 64.1026 10.641 131 | dino 66.9231 10.641 132 | dino 71.2821 10.641 133 | dino 74.359 10.641 134 | dino 78.2051 10.641 135 | dino 67.9487 8.718 136 | dino 68.4615 5.2564 137 | dino 68.2051 2.9487 138 | dino 37.6923 25.7692 139 | dino 39.4872 25.3846 140 | dino 91.2821 41.5385 141 | dino 50.0 95.7692 142 | dino 47.9487 95.0 143 | dino 44.1026 92.6923 144 | away 32.3311102266 61.411101248 145 | away 53.4214628807 26.1868803879 146 | away 63.92020226 30.8321939163 147 | away 70.2895057187 82.5336485877 148 | away 34.1188302357 45.7345513203 149 | away 67.6707164012 37.110947969 150 | away 53.2591294055 97.4757710964 151 | away 63.5149808671 25.1000785788 152 | away 67.9805388133 80.9571652197 153 | away 67.3724659005 29.720400203 154 | away 15.5607495229 80.0656402858 155 | away 71.7907676942 71.0654666627 156 | away 70.2425464362 24.1095975542 157 | away 64.9374355444 81.5542049945 158 | away 62.2135245453 21.4758389969 159 | away 67.2694004772 18.7089683725 160 | away 40.5701970446 79.3729634752 161 | away 74.7411813341 21.1016372041 162 | away 71.7683189223 20.0110618423 163 | away 76.1669198143 75.9361704048 164 | away 65.6236574431 15.5828033531 165 | away 50.8506336394 13.9876016304 166 | away 33.0240700249 24.4678303872 167 | away 39.7063261674 84.2752871038 168 | away 45.5964849542 9.76334884943 169 | away 42.9680469104 17.9454583961 170 | away 52.4944067819 16.0511142003 171 | away 46.0822757831 23.1104578154 172 | away 74.2477082092 20.314187812 173 | away 64.5682641863 83.6396338956 174 | away 74.0216939058 76.1282745076 175 | away 62.3911805626 5.62307076073 176 | away 74.189036683 68.1335832223 177 | away 28.2367819396 56.1395964513 178 | away 75.7719387944 69.8292300322 179 | away 75.8552294691 62.5170442862 180 | away 65.9708570175 72.7448559954 181 | away 21.7780404779 6.61662530728 182 | away 67.7597962473 72.4212015285 183 | away 78.6171953363 52.5752573142 184 | away 68.5077081898 15.4569189652 185 | away 74.8850211598 25.4166063231 186 | away 66.4549036599 19.8366286542 187 | away 77.3178020985 48.3983464352 188 | away 58.9124603193 75.6677562173 189 | away 57.617447817 8.19480060319 190 | away 76.0882257967 59.6799300235 191 | away 57.4660505497 1.50441817488 192 | away 79.4283834934 45.2107942872 193 | away 76.3565221496 10.4182411281 194 | away 64.4050752632 78.5841760758 195 | away 40.6350418091 73.3947503698 196 | away 43.9498645857 75.9587156671 197 | away 30.9962205791 71.694404938 198 | away 68.2307689907 80.8725016628 199 | away 72.0463894612 12.9180067349 200 | away 46.5927679682 84.9723827774 201 | away 49.2572183396 81.8814032306 202 | away 42.7817612539 12.9911884302 203 | away 65.475952195 14.2745856444 204 | away 71.9650826544 17.7102359443 205 | away 32.1464623358 43.4817094425 206 | away 31.8384976954 71.8121653901 207 | away 31.0052582572 40.682503007 208 | away 80.4708943189 49.5021483467 209 | away 71.9641671122 41.8742826668 210 | away 78.0794214417 93.1333167652 211 | away 41.6775957748 30.2012640846 212 | away 65.953595185 31.1474060835 213 | away 62.9344593731 31.9163906992 214 | away 64.3737979844 28.8625834061 215 | away 72.5093283599 39.5401302526 216 | away 30.0522898741 96.6175423534 217 | away 28.0033242354 46.6721919544 218 | away 75.4012268619 88.6390766207 219 | away 38.9800154218 87.322160691 220 | away 65.2199135479 84.6829549336 221 | away 73.0539899616 29.3808085571 222 | away 34.3983616372 59.5444469033 223 | away 43.4904501336 40.782542065 224 | away 55.138737967 30.7257603575 225 | away 43.6843934333 32.8230098696 226 | away 35.9036097344 91.1118630801 227 | away 45.3780188805 29.1692166544 228 | away 39.7774828713 43.75581895 229 | away 38.6644611569 33.3172384774 230 | away 39.0440366877 84.6760108316 231 | away 91.6399614428 79.4066030605 232 | away 47.4881326771 85.3899333808 233 | away 44.5902125769 22.0340116412 234 | away 39.0896145478 70.4661940802 235 | away 42.2293783752 19.9140684075 236 | away 37.0003871448 60.264279248 237 | away 39.0520864793 70.6525028457 238 | away 37.4884147432 60.8144048511 239 | away 69.3595594592 65.5213545959 240 | away 43.542775926 62.4603112824 241 | away 39.8112302539 65.3348328092 242 | away 70.0689259404 7.59346560899 243 | away 70.0405435824 77.1438066024 244 | away 39.505789079 74.8516272173 245 | away 62.5168908529 66.4847322418 246 | away 72.1399254065 0.0151193251552 247 | away 45.2515760666 70.0034213192 248 | away 42.0633045627 2.33519661206 249 | away 36.3556951539 6.0058486497 250 | away 30.3918276596 42.75961287 251 | away 36.4490038543 50.5462690659 252 | away 40.467576002 60.0275120878 253 | away 81.7246168002 6.03754484635 254 | away 48.8231974964 76.6353305783 255 | away 35.6205617651 57.2860155789 256 | away 50.5839631148 71.8066161014 257 | away 61.8564651063 71.7927431642 258 | away 39.3237560262 59.3008196656 259 | away 42.1856791429 66.0348978235 260 | away 30.8469189898 37.3416401041 261 | away 29.3462004281 42.1487418312 262 | away 82.1105579783 1.21055166293 263 | away 38.3020058088 60.0177857932 264 | away 56.5841530218 70.512514809 265 | away 33.3393742865 0.5091067352 266 | away 78.7742390407 35.4841012146 267 | away 27.9226442446 25.9868781844 268 | away 71.6978651182 10.8681445111 269 | away 74.1383313856 49.1739189791 270 | away 32.579020066 1.80811559665 271 | away 59.83218542 69.1525081443 272 | away 35.0306285457 12.5366493416 273 | away 74.3001198284 42.4770945921 274 | away 63.2501970628 65.9524861966 275 | away 34.1730737648 25.6936743092 276 | away 40.9138319319 38.5590195509 277 | away 62.8332930874 62.5108942269 278 | away 42.4767923803 56.7312899691 279 | away 52.0334562787 64.5666620298 280 | away 48.9070429644 74.2877488252 281 | away 59.8518383854 72.9583909677 282 | away 48.9604602016 72.6295257275 283 | away 46.8448551673 36.7917136918 284 | away 39.9630215796 42.9449148487 285 | away 66.704943997 32.0150954299 286 | h_lines 53.366566866 90.2080300059 287 | h_lines 52.8019793617 90.0880645063 288 | h_lines 47.0541298828 90.458936026 289 | h_lines 42.4484337816 89.5077001153 290 | h_lines 42.7040363241 90.4426288607 291 | h_lines 32.3789386326 90.144142631 292 | h_lines 32.5307027362 70.1573965085 293 | h_lines 33.3652644305 70.4593354392 294 | h_lines 32.6560991775 70.0510704992 295 | h_lines 22.9550932697 70.4268951523 296 | h_lines 27.1498436318 70.2108058428 297 | h_lines 26.1669092779 70.5003151212 298 | h_lines 26.3314570795 50.494556525 299 | h_lines 22.0037091415 50.4994401238 300 | h_lines 23.2847479782 50.4743528321 301 | h_lines 27.6340383923 50.5036668574 302 | h_lines 32.0371080007 50.5000661229 303 | h_lines 29.3365106528 50.461893969 304 | h_lines 40.816953133 50.4877601957 305 | h_lines 42.7390751173 50.483155992 306 | h_lines 51.8678937691 50.4932732621 307 | h_lines 43.3710018924 50.5008373234 308 | h_lines 53.0427983006 50.4875038691 309 | h_lines 56.6189102967 50.5033740178 310 | h_lines 70.568289819 50.4962361243 311 | h_lines 67.870887292 70.0680261345 312 | h_lines 58.8215465891 70.4557817483 313 | h_lines 60.1869278445 70.4835512458 314 | h_lines 59.4355174849 50.4206326073 315 | h_lines 49.5858434795 50.4349813825 316 | h_lines 41.7797417848 50.4706422808 317 | h_lines 46.1276863394 30.4494107792 318 | h_lines 38.0337837705 50.4908995189 319 | h_lines 44.5468412844 50.4159133758 320 | h_lines 46.2381175975 30.4956129408 321 | h_lines 49.7397453317 30.4727248868 322 | h_lines 39.1102518936 30.4786951789 323 | h_lines 39.1428377913 30.7928559394 324 | h_lines 34.6254702671 30.4997410297 325 | h_lines 34.7542074707 30.4958650002 326 | h_lines 36.7537002386 50.5293043035 327 | h_lines 37.0193512929 50.4926666972 328 | h_lines 41.4171152753 50.4697734181 329 | h_lines 22.8171074784 30.4360403387 330 | h_lines 34.800701463 30.4979313851 331 | h_lines 27.8918897953 30.4849702595 332 | h_lines 32.8706391587 30.5023417488 333 | h_lines 37.7940494081 30.4926383364 334 | h_lines 35.6673163042 30.522035054 335 | h_lines 48.4422995026 30.5017227753 336 | h_lines 58.6693732631 30.5068378948 337 | h_lines 47.9169554375 30.4867257545 338 | h_lines 51.8941663939 30.494755684 339 | h_lines 51.7490891643 30.4729933399 340 | h_lines 58.2465530013 50.4428744949 341 | h_lines 57.3106968674 50.4825132895 342 | h_lines 67.9680481127 50.4521589097 343 | h_lines 65.9493192681 50.494240794 344 | h_lines 55.7470928102 50.4789748003 345 | h_lines 68.4030100436 50.4986272318 346 | h_lines 76.696465628 50.4938305609 347 | h_lines 67.2148371159 70.2600284356 348 | h_lines 72.3020057291 70.2204373812 349 | h_lines 66.7632573944 70.4738617113 350 | h_lines 51.6803004439 89.9970275485 351 | h_lines 53.0965573571 89.540243712 352 | h_lines 42.2422929389 89.7085785936 353 | h_lines 56.2338756577 89.635308157 354 | h_lines 54.6809865825 89.9645994589 355 | h_lines 57.9026866088 89.6184017294 356 | h_lines 51.8202625467 89.8341303987 357 | h_lines 56.8283953362 89.5836647251 358 | h_lines 58.2722785867 69.9002729578 359 | h_lines 51.8281274487 70.3526492053 360 | h_lines 44.8499842618 70.3952029719 361 | h_lines 24.2703823928 50.4855676918 362 | h_lines 34.7424714403 70.3399348879 363 | h_lines 37.7993149267 70.4695358763 364 | h_lines 49.7869410649 89.6580469945 365 | h_lines 59.751497414 89.7319325216 366 | h_lines 64.7747992444 90.132316736 367 | h_lines 70.8444476004 89.9242090159 368 | h_lines 70.6059455116 90.042061963 369 | h_lines 78.6424883907 89.5512032307 370 | h_lines 70.810692254 89.5926540118 371 | h_lines 66.7699466213 70.4233752108 372 | h_lines 73.3363681612 70.3169271705 373 | h_lines 73.1051481627 70.300511881 374 | h_lines 74.6782541432 70.4453141903 375 | h_lines 80.240250951 70.3543089613 376 | h_lines 70.9468490729 50.4727017878 377 | h_lines 88.6626638086 50.4553260208 378 | h_lines 84.405952832 50.4948658866 379 | h_lines 75.9871047068 50.4919669557 380 | h_lines 91.1120854191 50.489389393 381 | h_lines 98.2881232748 30.6039193719 382 | h_lines 95.0652748396 30.5004028652 383 | h_lines 95.2492339568 30.4594542144 384 | h_lines 49.6561262881 50.4313198677 385 | h_lines 63.0186009279 30.4925819262 386 | h_lines 70.5382006004 30.4835582526 387 | h_lines 70.0868697658 30.5100448173 388 | h_lines 62.6856904424 30.5464245575 389 | h_lines 69.239417959 30.5060052447 390 | h_lines 80.4002336791 30.4850063746 391 | h_lines 74.6478782284 30.6738628584 392 | h_lines 65.9952727194 30.4750241915 393 | h_lines 67.0885590036 30.4945239508 394 | h_lines 59.8525770283 10.5431877763 395 | h_lines 53.7489900744 10.5225745323 396 | h_lines 58.2390955164 10.4948990284 397 | h_lines 54.810300495 10.7249292845 398 | h_lines 52.9767123211 10.859490276 399 | h_lines 52.2038939272 10.4956369212 400 | h_lines 50.7451622429 10.5249055723 401 | h_lines 42.7131936166 10.4875472428 402 | h_lines 45.3172255098 10.4958733944 403 | h_lines 53.116915041 10.5068605973 404 | h_lines 57.049801823 10.5081369414 405 | h_lines 54.8546309465 10.4639151924 406 | h_lines 54.9248425638 10.498331942 407 | h_lines 63.3095540306 10.4888142392 408 | h_lines 49.7494984129 10.4858029097 409 | h_lines 41.1693104232 10.4973002433 410 | h_lines 46.6883404976 10.5075321353 411 | h_lines 52.4189219215 10.4959063596 412 | h_lines 50.8784883796 10.5030898746 413 | h_lines 57.0533061394 10.5140692392 414 | h_lines 70.7864511597 10.5136950409 415 | h_lines 56.7721495656 10.5032723508 416 | h_lines 75.4772220231 10.5185680355 417 | h_lines 76.7659121498 10.496999064 418 | h_lines 77.621049129 10.4958742318 419 | h_lines 65.4144870523 10.6818401251 420 | h_lines 69.9647805002 10.5072329326 421 | h_lines 64.9697382623 10.4821595117 422 | h_lines 38.0502407785 30.4079460343 423 | h_lines 42.2241202189 30.4980371582 424 | h_lines 87.6984642949 50.4899365296 425 | h_lines 52.3603310701 89.7043170068 426 | h_lines 51.4067867898 89.7121385499 427 | h_lines 43.588468468 89.7455365536 428 | v_lines 50.4815081703 93.2227013657 429 | v_lines 50.2824056687 97.6099835723 430 | v_lines 50.1867033389 99.6946801425 431 | v_lines 50.3269108629 90.0220534916 432 | v_lines 50.4562073315 89.9874101286 433 | v_lines 30.4648469308 82.0892320845 434 | v_lines 30.5015964737 82.3071263462 435 | v_lines 30.489569227 77.7298801366 436 | v_lines 30.45024833 79.8348944011 437 | v_lines 30.5183947985 68.2525829388 438 | v_lines 30.5116612138 66.0937181831 439 | v_lines 30.4980777147 62.3849850338 440 | v_lines 30.6202277519 60.8600821544 441 | v_lines 30.4921854231 55.8789168923 442 | v_lines 30.474634333 52.3221596388 443 | v_lines 30.500998079 48.0696996767 444 | v_lines 30.4944213623 45.7734755346 445 | v_lines 30.4995445502 49.3820997506 446 | v_lines 50.144392979 47.0142836417 447 | v_lines 49.9128985877 55.5878389121 448 | v_lines 50.3374274859 57.9553142818 449 | v_lines 50.4189791992 60.5249117346 450 | v_lines 50.2052589714 57.9242171676 451 | v_lines 50.4353151633 56.4393224734 452 | v_lines 69.5057889042 59.2849248279 453 | v_lines 69.5340709833 64.8929773346 454 | v_lines 69.5101069302 61.3296244282 455 | v_lines 69.5263228876 56.9952791254 456 | v_lines 49.9472399619 48.2295767261 457 | v_lines 50.0631041291 58.085037556 458 | v_lines 50.489902373 39.2054119682 459 | v_lines 49.6888638379 42.018514414 460 | v_lines 50.3334811097 43.588671479 461 | v_lines 50.4467514196 57.2305889423 462 | v_lines 50.4209091963 43.3479568967 463 | v_lines 50.3740520508 32.9182406167 464 | v_lines 30.4768299051 39.4246055913 465 | v_lines 30.5034743497 38.3485561381 466 | v_lines 30.5048349283 28.7714879939 467 | v_lines 30.4622184647 32.7504720275 468 | v_lines 30.491302622 43.5613740753 469 | v_lines 30.449653838 44.9951230008 470 | v_lines 30.4859942157 47.3479464653 471 | v_lines 30.5020767935 36.4412129198 472 | v_lines 30.4780588886 28.020580489 473 | v_lines 30.5011608787 38.3141385582 474 | v_lines 30.5047730174 26.4166256707 475 | v_lines 30.509115326 22.8255471196 476 | v_lines 50.1156321728 16.6940065479 477 | v_lines 50.4483013392 21.6402564599 478 | v_lines 50.17317743 29.177864393 479 | v_lines 49.9227419194 38.4404729124 480 | v_lines 50.3112993746 26.8604882737 481 | v_lines 50.3994096114 38.9638389234 482 | v_lines 50.3373626448 40.913386144 483 | v_lines 50.2023180965 47.2129480045 484 | v_lines 50.2749326091 49.6234993439 485 | v_lines 69.4943803061 52.6124107059 486 | v_lines 69.5193397556 47.384533707 487 | v_lines 69.5291826079 57.8412666804 488 | v_lines 69.495755496 58.785073162 489 | v_lines 69.199057706 60.4189573202 490 | v_lines 69.5510914402 58.4010982686 491 | v_lines 69.5066843151 68.0844667893 492 | v_lines 50.4441175504 75.0132995059 493 | v_lines 50.4672293846 90.281464505 494 | v_lines 50.210881979 80.9417717084 495 | v_lines 50.205406955 90.6405359009 496 | v_lines 50.4382906648 92.3547602661 497 | v_lines 50.3735188462 82.2192057218 498 | v_lines 50.253683689 81.7438287242 499 | v_lines 50.2504708592 71.9513852104 500 | v_lines 50.0870583609 73.9518736268 501 | v_lines 50.2310718829 80.6473870602 502 | v_lines 50.2077411201 73.1145035177 503 | v_lines 30.4995061735 53.6125276703 504 | v_lines 30.5203612407 65.1004090686 505 | v_lines 30.4549558873 63.5017787814 506 | v_lines 50.4164772292 97.2696919618 507 | v_lines 69.5250693831 93.5980502089 508 | v_lines 69.5202566002 97.1938760167 509 | v_lines 69.4989700668 92.1204899292 510 | v_lines 69.5286156637 91.6220882409 511 | v_lines 69.4997669273 84.47968329 512 | v_lines 69.5038013697 81.8154056166 513 | v_lines 69.4875893963 81.8359875567 514 | v_lines 69.5191157281 71.1159672213 515 | v_lines 69.5043762947 70.4934438205 516 | v_lines 69.5125730109 61.497563947 517 | v_lines 89.4942005622 62.2343269676 518 | v_lines 89.5001240511 53.2163306165 519 | v_lines 89.490246254 48.890796551 520 | v_lines 89.49778562 46.6299066746 521 | v_lines 89.5048511306 48.4234081578 522 | v_lines 89.5010526463 43.3473436951 523 | v_lines 89.4888412549 40.6250912209 524 | v_lines 89.5016235276 45.8151791904 525 | v_lines 89.497529662 33.901372671 526 | v_lines 50.4390920216 37.670771798 527 | v_lines 50.179850128 36.8075593352 528 | v_lines 69.5050706711 33.7401525035 529 | v_lines 69.5798670452 21.9352367455 530 | v_lines 69.5064719492 22.7286628748 531 | v_lines 69.4994788728 16.3374699821 532 | v_lines 69.500264751 27.1360239942 533 | v_lines 69.5041565157 23.6689181006 534 | v_lines 69.5322795847 21.8509008151 535 | v_lines 69.5001114942 11.7916260054 536 | v_lines 69.5048154243 15.6143582625 537 | v_lines 69.5034745234 17.1690897689 538 | v_lines 50.3774019862 16.5060150613 539 | v_lines 50.3394254813 18.7234506861 540 | v_lines 50.0936501016 11.6077134241 541 | v_lines 49.9341271714 22.2561397401 542 | v_lines 50.2888186122 13.436410466 543 | v_lines 50.2879944267 12.8722822086 544 | v_lines 50.2347207 20.4044729196 545 | v_lines 50.2797016367 15.608737571 546 | v_lines 50.2530067222 11.3020842468 547 | v_lines 50.4741411162 12.4619966676 548 | v_lines 50.1084399725 18.2590443442 549 | v_lines 49.919058249 3.7168538855 550 | v_lines 50.0836645085 10.2358819024 551 | v_lines 50.3522608355 12.4686005273 552 | v_lines 50.4975722411 7.40962523419 553 | v_lines 50.0163058346 16.2536847911 554 | v_lines 50.4682212165 13.3567024555 555 | v_lines 69.5506789842 9.03298756904 556 | v_lines 69.5186727475 15.1804864298 557 | v_lines 69.5034574098 16.8783132661 558 | v_lines 69.4994868291 10.2078266798 559 | v_lines 69.5275296829 9.99229666551 560 | v_lines 69.5190952617 11.4226857018 561 | v_lines 69.5618994268 15.9998037858 562 | v_lines 69.5010445197 5.95583353945 563 | v_lines 69.524635976 2.73476016988 564 | v_lines 30.4995937106 26.0855615691 565 | v_lines 30.487391709 19.7794703733 566 | v_lines 89.5001804719 31.9789174357 567 | v_lines 50.4102716314 98.6283694405 568 | v_lines 50.3259243927 94.9946308528 569 | v_lines 50.1040315041 95.0885380179 570 | x_shape 38.3377571839 92.472719051 571 | x_shape 35.7518707905 94.1167680276 572 | x_shape 32.7672179591 88.5182945794 573 | x_shape 33.7296067755 88.622265789 574 | x_shape 37.238249327 83.7249284144 575 | x_shape 36.0271982243 82.0407806505 576 | x_shape 39.2392807768 79.2637238398 577 | x_shape 39.7845249391 82.2605658962 578 | x_shape 35.1660293896 84.1564919839 579 | x_shape 40.622115916 78.5421042089 580 | x_shape 39.181907086 79.8190371976 581 | x_shape 42.4308889944 75.1336346406 582 | x_shape 43.0812558617 75.3467016413 583 | x_shape 44.3607194344 70.4742070546 584 | x_shape 44.3247788435 71.0459044019 585 | x_shape 47.552160202 66.4604037217 586 | x_shape 48.6649246215 64.9610049438 587 | x_shape 46.280631958 68.0284231525 588 | x_shape 50.5632444849 62.6238279659 589 | x_shape 52.6309642318 59.9639357915 590 | x_shape 54.6362064446 57.4938424367 591 | x_shape 51.346027139 61.6005530364 592 | x_shape 57.134855354 53.8779658045 593 | x_shape 54.1712415737 58.0598078952 594 | x_shape 50.9423823071 62.0973939351 595 | x_shape 66.512924456 59.0712818478 596 | x_shape 64.3979095023 56.1748754008 597 | x_shape 68.686593294 62.2373439902 598 | x_shape 65.0277937057 57.0962573525 599 | x_shape 53.3045031712 40.883844549 600 | x_shape 52.9451780881 40.3846256942 601 | x_shape 45.225738199 30.5552259226 602 | x_shape 50.8433046202 37.7446280981 603 | x_shape 49.6592939398 36.273442735 604 | x_shape 47.1427889348 32.0250641734 605 | x_shape 41.2700565259 21.7218171465 606 | x_shape 47.1301271555 31.3099029222 607 | x_shape 40.9159715783 23.5415923929 608 | x_shape 38.6601639737 21.6895496369 609 | x_shape 42.6125508262 24.195053774 610 | x_shape 41.2090395008 24.1287091891 611 | x_shape 50.6492211499 37.4837907435 612 | x_shape 52.1319019406 39.3646020815 613 | x_shape 39.7972712705 16.083549649 614 | x_shape 43.5498814807 24.3072109395 615 | x_shape 36.7146633371 17.3818098572 616 | x_shape 37.2246101278 17.9800644828 617 | x_shape 40.5124478537 22.9622145725 618 | x_shape 40.2220627884 20.6146217044 619 | x_shape 44.1249612057 23.3662452858 620 | x_shape 42.3462616039 24.5583269437 621 | x_shape 44.1890843708 28.772625473 622 | x_shape 45.8142453101 31.056979672 623 | x_shape 46.6431192691 31.4072190618 624 | x_shape 52.1898883047 39.3677964938 625 | x_shape 51.1003173587 38.095699984 626 | x_shape 65.4008341477 42.6140320392 627 | x_shape 64.8520398267 43.1337348404 628 | x_shape 63.4038060118 44.847493688 629 | x_shape 65.3715488709 42.6959713164 630 | x_shape 61.9080397046 53.1903520694 631 | x_shape 71.8266529973 65.6862303604 632 | x_shape 72.6546231621 66.8782776113 633 | x_shape 76.8885781632 72.1654720777 634 | x_shape 43.7446196844 71.2679610751 635 | x_shape 39.0542484428 83.1190207043 636 | x_shape 39.9316746322 79.9391880033 637 | x_shape 39.762826107 76.4059674459 638 | x_shape 41.323383287 80.2118685969 639 | x_shape 41.7873232275 83.9880779935 640 | x_shape 39.7556490651 79.6570966361 641 | x_shape 40.7498793985 77.204657286 642 | x_shape 43.8568984983 77.6098436482 643 | x_shape 42.3678224956 73.6382718294 644 | x_shape 39.4629598364 78.459973898 645 | x_shape 44.5173339492 70.3693498252 646 | x_shape 41.6360929963 76.2492229205 647 | x_shape 45.4229321817 69.0955016709 648 | x_shape 42.0028562742 97.8376147235 649 | x_shape 76.1056290282 95.3049805754 650 | x_shape 79.2025626772 92.4072156264 651 | x_shape 84.8482440854 95.4248045304 652 | x_shape 81.5644753207 83.7929072262 653 | x_shape 85.4461864009 83.0782938448 654 | x_shape 80.3124913784 76.5162389518 655 | x_shape 80.7662322969 79.8960656796 656 | x_shape 82.6701124763 81.711479845 657 | x_shape 77.1636254828 73.5237189623 658 | x_shape 74.0707000309 68.4795642895 659 | x_shape 74.1142308687 71.9263635185 660 | x_shape 68.942075458 62.635150577 661 | x_shape 73.2420023309 67.3532711288 662 | x_shape 71.8670131631 34.3291575887 663 | x_shape 71.5691888532 34.699218607 664 | x_shape 71.4829096934 34.6805169926 665 | x_shape 78.1285220512 23.139231835 666 | x_shape 78.789825254 25.5971933466 667 | x_shape 77.7815467962 23.4472144745 668 | x_shape 48.5306293681 34.1524500077 669 | x_shape 65.5307814724 42.2810847015 670 | x_shape 70.1180341426 36.4481847905 671 | x_shape 70.4722626065 36.0880530553 672 | x_shape 76.7911300701 28.0720081685 673 | x_shape 72.4093545935 33.0392569664 674 | x_shape 76.271299815 25.5695441452 675 | x_shape 76.9532591384 27.7706107405 676 | x_shape 75.1077300904 30.0169834157 677 | x_shape 75.9692852004 29.0388653753 678 | x_shape 75.2838870131 24.8747531721 679 | x_shape 77.0522406385 27.9056390894 680 | x_shape 42.8267563374 25.5569594065 681 | x_shape 42.0806606852 24.9465264454 682 | x_shape 39.8783046498 18.3249292157 683 | x_shape 36.4754991726 17.7189477884 684 | x_shape 37.1030736419 15.4456116477 685 | x_shape 34.1489682077 13.5238698677 686 | x_shape 37.5794219113 15.0158901333 687 | x_shape 41.0643057894 12.2144689321 688 | x_shape 42.195266147 20.9193721857 689 | x_shape 36.6055782383 15.748875885 690 | x_shape 39.4311612098 19.3238407541 691 | x_shape 37.5620763361 11.9284457044 692 | x_shape 34.2381100444 13.7131582305 693 | x_shape 34.1409410386 4.57766135024 694 | x_shape 36.6592676972 17.6819644553 695 | x_shape 40.6437192795 20.2408613716 696 | x_shape 36.7924815919 13.3921347304 697 | x_shape 75.9483006427 22.7447959146 698 | x_shape 76.8406924717 20.9185472197 699 | x_shape 81.9504776395 19.8745582085 700 | x_shape 81.6437056853 16.5917599845 701 | x_shape 84.6102197758 14.2477619017 702 | x_shape 84.1028794336 8.98065579006 703 | x_shape 80.18002545 19.0888441297 704 | x_shape 80.5732729943 8.36563890168 705 | x_shape 79.0043268649 10.6281977654 706 | x_shape 40.0481864651 24.2614879334 707 | x_shape 34.7945935378 13.9696834611 708 | x_shape 79.221764441 22.0945914837 709 | x_shape 36.0308797708 93.1217332374 710 | x_shape 34.4995583102 86.6099850511 711 | x_shape 31.1068665551 89.4616352367 712 | star 58.2136082599 91.881891513 713 | star 58.1960536923 92.2149886482 714 | star 58.7182307185 90.310532087 715 | star 57.27837287 89.9076067166 716 | star 58.082020494 92.0081450101 717 | star 57.4894477748 88.0852855629 718 | star 28.088741319 63.5107944263 719 | star 28.0854682136 63.5901969481 720 | star 28.087273053 63.1232828116 721 | star 27.5780252176 62.8210386566 722 | star 27.7799191139 63.518147517 723 | star 28.5889998149 63.0240805653 724 | star 28.7391414969 62.7208638859 725 | star 27.0246032407 62.9018588649 726 | star 28.8013366963 63.389040388 727 | star 27.186463838 63.5587296478 728 | star 29.2851466002 63.3836058254 729 | star 39.4029453026 51.1508571967 730 | star 28.8113284396 61.3578540576 731 | star 34.3039579069 56.5421259093 732 | star 29.6027609801 60.157346722 733 | star 49.1161568603 63.6600006211 734 | star 39.6175458263 62.9251879574 735 | star 43.23308466 63.1652187223 736 | star 64.8927879422 65.8141767574 737 | star 62.4901493154 74.5842896072 738 | star 68.9880844294 63.232147305 739 | star 62.1056186306 75.9908707599 740 | star 32.4618467439 62.8819029189 741 | star 41.3272006535 49.0702512739 742 | star 44.0071499323 46.4496737846 743 | star 44.0740606888 34.5532038906 744 | star 44.0013152386 33.9042073544 745 | star 45.0063004454 38.2990195507 746 | star 44.4438406096 36.0190833012 747 | star 42.1787133986 26.492119482 748 | star 44.0445656189 35.6622382764 749 | star 41.6404540171 27.0930954213 750 | star 41.9383300069 24.9915229793 751 | star 44.0539275103 33.5563924949 752 | star 39.2067193308 51.5337156971 753 | star 28.7044492315 61.7775254006 754 | star 31.7086628996 58.837754374 755 | star 42.8117114739 30.0204484197 756 | star 43.300614891 31.5264261979 757 | star 40.3986329069 16.3470083822 758 | star 40.4356915763 20.2326706762 759 | star 40.9365466658 16.9130048364 760 | star 39.6615736653 15.609355577 761 | star 40.899259175 20.7985289466 762 | star 41.9686168283 26.4970725985 763 | star 40.3834058238 21.3912255247 764 | star 56.5381264538 32.4442454697 765 | star 52.9706912828 29.0401966941 766 | star 54.6209525885 30.3445244537 767 | star 65.0990443941 27.2415575588 768 | star 63.0559909076 29.7090956748 769 | star 70.9601362323 41.2595012867 770 | star 69.8958192404 43.4537592655 771 | star 70.5958928563 41.9647438672 772 | star 69.6470214273 44.044445022 773 | star 77.392982494 63.3714590552 774 | star 64.4007871926 67.4487184472 775 | star 63.8689598271 70.2137388333 776 | star 56.5944213157 86.9270062202 777 | star 56.5313372853 87.4998110713 778 | star 59.6521583697 87.8094615921 779 | star 56.6365087005 85.637495556 780 | star 58.6722880025 90.077160307 781 | star 58.2216127264 90.4110187715 782 | star 57.9146644768 89.9538027677 783 | star 55.3155090581 80.2518606944 784 | star 54.5757285877 77.5362884724 785 | star 54.4130936504 78.2290865878 786 | star 55.0745059041 79.8175464166 787 | star 29.4329605156 60.8017765444 788 | star 29.4226860665 63.0684648229 789 | star 29.0056141576 63.3907513334 790 | star 58.4618385916 90.2653263904 791 | star 57.997804739 92.1599086113 792 | star 57.5494740761 90.7489065581 793 | star 59.5299284566 88.3272741451 794 | star 58.2493910631 92.1296814778 795 | star 58.0245140126 91.6944211685 796 | star 58.3821244904 90.5534760692 797 | star 62.5667590405 77.7439347591 798 | star 72.1758243064 63.1289294176 799 | star 79.4727615693 63.4086861199 800 | star 80.3577008812 63.2954375404 801 | star 78.7572361375 53.3326200111 802 | star 82.5402395934 56.5410522935 803 | star 86.4358971909 59.7927618087 804 | star 79.4886844186 53.6516742641 805 | star 81.5304203188 56.0253645659 806 | star 79.1867885665 53.2347918468 807 | star 77.8990579454 51.8224583343 808 | star 75.1307142075 23.3724419733 809 | star 76.0580137544 16.3837496882 810 | star 57.6146743876 33.8224476502 811 | star 56.1713975295 32.1179887719 812 | star 66.2878905956 26.1171097453 813 | star 67.8817196169 24.2360184109 814 | star 64.0280813016 27.6726855125 815 | star 77.4966517463 14.9485235577 816 | star 77.6346517616 14.461853927 817 | star 77.8637264289 14.6106776491 818 | star 77.3381581659 15.890054658 819 | star 76.1804165329 15.9125737458 820 | star 77.2526510945 15.1515170246 821 | star 77.4133752817 15.2219279762 822 | star 76.7318493982 16.2168461441 823 | star 49.4711054118 25.0630193062 824 | star 42.4765399431 18.3384735636 825 | star 43.5951158622 19.9942009773 826 | star 50.3399696652 26.4713966117 827 | star 40.7489802561 16.1821416622 828 | star 38.3865255803 14.5802151464 829 | star 38.4040152085 14.4519484496 830 | star 38.7642788913 14.3655904744 831 | star 41.4701423265 17.2780334445 832 | star 47.1554048146 22.3779325323 833 | star 39.5825667453 17.6484528361 834 | star 41.7402438167 17.8293243101 835 | star 39.3118718889 15.6407169743 836 | star 41.6798476925 17.7459190133 837 | star 39.0874644519 15.1223039378 838 | star 41.4815028637 18.0474374446 839 | star 77.6060865492 15.1628725383 840 | star 75.9826615205 16.3069223786 841 | star 76.9457572424 15.858478334 842 | star 77.5437200743 15.253949149 843 | star 77.5847398447 15.8300393854 844 | star 76.8223042562 15.5951653195 845 | star 77.3485716647 15.7745292395 846 | star 77.5731526867 14.7806458332 847 | star 77.9726106776 14.9556987461 848 | star 41.5289197586 24.9164251896 849 | star 43.7225508034 19.0773278036 850 | star 79.3260781751 52.9003912897 851 | star 56.6639740815 87.9401250067 852 | star 57.8217892319 90.6931665489 853 | star 58.2431718991 92.1043278652 854 | high_lines 57.6132335477 83.9051707998 855 | high_lines 51.2743918205 82.8179829379 856 | high_lines 50.7538984114 76.7541289533 857 | high_lines 37.0211840116 81.954469997 858 | high_lines 42.8817550897 80.1847700514 859 | high_lines 37.1557104777 84.9541114129 860 | high_lines 38.7318581261 83.7893564281 861 | high_lines 31.0005254108 82.5794805962 862 | high_lines 25.9854996226 74.3493875478 863 | high_lines 23.7145662909 75.0980490792 864 | high_lines 23.077319507 71.7205396747 865 | high_lines 24.9391368744 72.5802858084 866 | high_lines 17.8934987136 71.1096873431 867 | high_lines 23.6730818572 64.8708391385 868 | high_lines 23.7490719056 63.557171895 869 | high_lines 32.2151786343 65.6009580191 870 | high_lines 29.4368412489 67.5545281141 871 | high_lines 40.0500951983 71.8890335708 872 | high_lines 37.5747950188 69.9887444412 873 | high_lines 49.3104686263 67.1687603206 874 | high_lines 47.3480185524 72.5916115408 875 | high_lines 50.157414434 71.1617258246 876 | high_lines 58.2100082697 68.0445380719 877 | high_lines 56.6089076561 70.4089460888 878 | high_lines 52.8338205303 72.5518862725 879 | high_lines 61.8185854576 76.1128240228 880 | high_lines 61.7276334143 77.0692384058 881 | high_lines 62.1922520815 76.1568192103 882 | high_lines 58.1187658587 73.1189614684 883 | high_lines 58.2704123843 35.5968950425 884 | high_lines 59.7665303129 32.6212516009 885 | high_lines 52.5355347207 27.1741229419 886 | high_lines 50.8925215809 29.1452839382 887 | high_lines 49.9675511775 28.4602033343 888 | high_lines 51.3478832298 27.3642602259 889 | high_lines 37.5551162883 24.5376638386 890 | high_lines 39.1995941778 23.6804956588 891 | high_lines 36.3827221955 25.2380052325 892 | high_lines 41.1006943822 26.5526132161 893 | high_lines 28.2178118248 28.2802924847 894 | high_lines 33.7970720433 28.6997507433 895 | high_lines 43.9193873055 27.7978445882 896 | high_lines 44.4164011084 28.9476923345 897 | high_lines 28.0376027706 26.8038243613 898 | high_lines 31.6895761296 26.6664002089 899 | high_lines 27.2796128523 19.2572809685 900 | high_lines 31.2218783581 26.4408557812 901 | high_lines 33.5458051034 20.0807081014 902 | high_lines 39.7363306065 23.4948069772 903 | high_lines 42.8418426525 20.3339004396 904 | high_lines 47.0937298879 26.9934706312 905 | high_lines 51.4536583539 26.0229417521 906 | high_lines 49.3677068167 22.005580703 907 | high_lines 48.2534809736 29.2708555331 908 | high_lines 61.0029597643 30.0153144324 909 | high_lines 60.8065608049 27.8683134861 910 | high_lines 59.0674270249 26.7882840217 911 | high_lines 61.4146337175 31.0393750583 912 | high_lines 68.9343694692 31.6198930866 913 | high_lines 63.0652438592 34.2357465852 914 | high_lines 72.2725228706 67.6444437851 915 | high_lines 75.3598819664 75.4216145799 916 | high_lines 72.8336258709 72.1146938095 917 | high_lines 70.8104148392 79.3504618327 918 | high_lines 45.8019347617 81.6582677456 919 | high_lines 45.835020171 82.7425919919 920 | high_lines 40.0788064496 80.2471852579 921 | high_lines 51.9829247003 82.2043834675 922 | high_lines 57.337906053 84.9995188696 923 | high_lines 62.3362327624 83.2465678988 924 | high_lines 58.5059100905 81.7455863409 925 | high_lines 56.2479351377 83.513949493 926 | high_lines 55.4065208295 80.2666719669 927 | high_lines 53.2839688423 84.2635992213 928 | high_lines 44.6767615455 80.2382855483 929 | high_lines 30.0112642472 68.1467563743 930 | high_lines 29.7452539206 74.4045268347 931 | high_lines 37.3099483549 72.5861552003 932 | high_lines 61.1359469093 82.5375693646 933 | high_lines 61.7206191907 85.7056934492 934 | high_lines 63.6093798555 83.3600288362 935 | high_lines 70.7237162322 87.1522075165 936 | high_lines 75.0063926418 82.4258351563 937 | high_lines 75.0925497814 83.4373907092 938 | high_lines 70.3279291834 82.0293546968 939 | high_lines 70.8633308324 79.4081747457 940 | high_lines 75.7799048195 79.8861994199 941 | high_lines 78.0936590733 77.759558184 942 | high_lines 76.7457612243 71.9733600919 943 | high_lines 78.4646034437 69.7493008209 944 | high_lines 76.7456848486 70.0440572588 945 | high_lines 85.161682187 65.5124047088 946 | high_lines 91.8083525691 34.9655944844 947 | high_lines 91.8874485924 34.7692538555 948 | high_lines 92.2484016622 32.3771544295 949 | high_lines 91.782532741 34.3842946521 950 | high_lines 96.0805193672 28.0536005018 951 | high_lines 92.2478988278 28.0707514904 952 | high_lines 57.662276619 24.0606079328 953 | high_lines 59.8469248931 26.1777124879 954 | high_lines 64.0670962137 27.7765005568 955 | high_lines 60.9815013975 23.9342981582 956 | high_lines 59.9670579092 26.8025832185 957 | high_lines 62.7074541119 21.9955085787 958 | high_lines 69.1952352018 22.709792166 959 | high_lines 65.5923626471 20.6941731019 960 | high_lines 64.6170985343 22.4431455531 961 | high_lines 59.191159132 17.767901807 962 | high_lines 55.8396313724 21.6185696663 963 | high_lines 59.0007586238 21.1710074885 964 | high_lines 56.4593711284 25.1445642082 965 | high_lines 52.3581053558 19.4170541222 966 | high_lines 51.8116637782 17.0334945954 967 | high_lines 54.222845621 19.503665544 968 | high_lines 49.0187553996 23.4892740197 969 | high_lines 35.6535812004 17.5145080595 970 | high_lines 46.965785757 16.7892016247 971 | high_lines 49.5842392555 16.7579160147 972 | high_lines 51.8562999655 21.969084007 973 | high_lines 55.9528886329 16.7897576048 974 | high_lines 51.6637862607 19.9251251977 975 | high_lines 45.8596693166 17.1289036754 976 | high_lines 39.7403795202 18.7410514146 977 | high_lines 52.8112216438 17.8549881176 978 | high_lines 56.7891043639 17.3061155207 979 | high_lines 54.1145317806 17.6735343896 980 | high_lines 55.571119732 17.369574581 981 | high_lines 65.3064624975 18.1973548277 982 | high_lines 66.9907491546 17.2228706509 983 | high_lines 72.1567189391 16.8142747914 984 | high_lines 70.9805804021 17.15575228 985 | high_lines 72.2220822073 19.3692816955 986 | high_lines 79.7001583481 20.740218365 987 | high_lines 64.2491606461 14.9139624607 988 | high_lines 66.9432914334 20.1891325034 989 | high_lines 66.9416766634 18.2435093914 990 | high_lines 39.6482666468 22.7313663718 991 | high_lines 37.9497836858 26.5217492344 992 | high_lines 86.5043905611 34.8940252874 993 | high_lines 50.8154853559 78.950838805 994 | high_lines 51.1834666638 85.1288546497 995 | high_lines 40.8638552293 82.9261452983 996 | dots 51.1479167122 90.8674123313 997 | dots 50.5171258092 89.1023945866 998 | dots 50.2074801993 85.4600473951 999 | dots 50.0694819181 83.0576695262 1000 | dots 50.5628463386 82.9378217764 1001 | dots 50.288527801 82.9752535659 1002 | dots 25.5834750822 82.9148911324 1003 | dots 25.4835833896 82.929084979 1004 | dots 25.4435257049 82.8742004973 1005 | dots 25.5651134237 82.9240977704 1006 | dots 25.9288442731 82.8211841087 1007 | dots 27.5514782612 51.4873865306 1008 | dots 27.5304663693 51.4148465629 1009 | dots 27.0955703605 52.0767994399 1010 | dots 27.4392496133 51.7120790527 1011 | dots 27.8782642594 50.70890793 1012 | dots 27.3388689181 51.6530467544 1013 | dots 27.6784029741 51.1819891691 1014 | dots 52.6356576791 51.4185522584 1015 | dots 52.0252141055 52.1230110467 1016 | dots 52.8811647904 50.6215547554 1017 | dots 52.9526073133 50.0747390118 1018 | dots 52.5205524936 51.5024421035 1019 | dots 52.3428220582 51.8619520908 1020 | dots 51.9275902141 52.2577906065 1021 | dots 52.7137744894 51.1979443188 1022 | dots 50.4438027888 82.9418288173 1023 | dots 50.2166950326 83.7523429676 1024 | dots 52.1841801076 51.9752506714 1025 | dots 52.7920973459 51.0733956509 1026 | dots 52.5897198565 51.338090196 1027 | dots 52.0288486671 52.1768375002 1028 | dots 52.729246581 51.2017650475 1029 | dots 52.8843132864 50.4414354501 1030 | dots 52.5093008862 51.4162051508 1031 | dots 50.8626843321 17.1456310914 1032 | dots 50.8914922458 17.1413237277 1033 | dots 25.8551275976 17.0819086886 1034 | dots 26.0256445518 16.9250135319 1035 | dots 27.8931727187 50.6619634101 1036 | dots 27.6399679354 51.3990974838 1037 | dots 27.892658895 50.7952815163 1038 | dots 52.7977329399 50.6860370901 1039 | dots 27.5806388053 51.5247612607 1040 | dots 26.4913985349 17.4053909738 1041 | dots 25.9853178159 17.203722132 1042 | dots 26.2014192753 17.0938239128 1043 | dots 25.8575694746 17.113842659 1044 | dots 50.7046843629 17.0237445436 1045 | dots 50.8119753465 17.1149252621 1046 | dots 50.5648455577 17.0777773216 1047 | dots 50.9393039127 16.981021883 1048 | dots 50.4588548392 17.0385789686 1049 | dots 52.9013640729 50.6905627231 1050 | dots 52.6849534438 51.2944692191 1051 | dots 52.500088943 51.594356165 1052 | dots 51.8356372562 52.3357655339 1053 | dots 76.9954121013 52.0455286534 1054 | dots 77.310600475 51.7467300374 1055 | dots 77.9260443352 50.3186604237 1056 | dots 77.2543883433 51.4618248152 1057 | dots 76.2431578028 52.1236898503 1058 | dots 77.0844843669 51.9671367003 1059 | dots 75.2280531954 82.9856620201 1060 | dots 50.6583547714 83.1144793358 1061 | dots 50.2033658114 82.9826568621 1062 | dots 50.9295476993 82.8460411252 1063 | dots 50.1786718465 83.1846223323 1064 | dots 50.422698058 82.9099014671 1065 | dots 50.4642248341 82.9353284131 1066 | dots 50.4492703306 83.9699203811 1067 | dots 49.9283802752 82.9936654888 1068 | dots 50.4880136436 83.0995191232 1069 | dots 49.9649053793 83.7083177013 1070 | dots 50.7521082559 82.9019500961 1071 | dots 27.4224292143 51.4388762325 1072 | dots 27.6740833991 51.3041121452 1073 | dots 27.5373953151 51.5936540762 1074 | dots 52.2633473764 94.2493278274 1075 | dots 51.7372816586 92.9791175339 1076 | dots 75.8709636929 88.3864417377 1077 | dots 75.2443262052 83.9034973788 1078 | dots 75.1982952889 83.4623033365 1079 | dots 75.7010415321 82.9194588559 1080 | dots 75.4793396586 82.8840513891 1081 | dots 75.1945668739 82.9321157846 1082 | dots 74.8202539551 82.9623887856 1083 | dots 75.1643404933 83.0349971693 1084 | dots 75.2633555474 82.9452793018 1085 | dots 77.7564189331 51.1517703255 1086 | dots 77.9544350549 50.4755789715 1087 | dots 77.0833377719 52.1577992662 1088 | dots 76.0635502513 52.1046520613 1089 | dots 77.6820163247 51.1656378096 1090 | dots 76.8780819814 51.8675622958 1091 | dots 76.9485027211 51.9075165439 1092 | dots 77.8640547124 49.6625455341 1093 | dots 75.7714500936 17.1112512057 1094 | dots 52.3315691301 51.8788603463 1095 | dots 52.5928183721 51.3915915185 1096 | dots 50.4770477213 17.0482894119 1097 | dots 75.2964750909 17.0156531888 1098 | dots 75.573954129 17.0621921415 1099 | dots 75.4005271583 17.0411068868 1100 | dots 75.8709908356 17.1348939075 1101 | dots 75.6058847579 17.0677230553 1102 | dots 75.8955770466 17.1699497051 1103 | dots 75.7465632023 17.1057165115 1104 | dots 75.1423414811 16.7549238932 1105 | dots 50.6617795563 17.0781405153 1106 | dots 50.6998506392 17.0851843836 1107 | dots 50.9189408654 17.1476047571 1108 | dots 50.7252585405 16.9074698083 1109 | dots 51.2638712274 17.1623497098 1110 | dots 51.2509196468 17.2404558616 1111 | dots 50.7851572052 17.1801964842 1112 | dots 50.5013965822 17.1057707188 1113 | dots 50.7336745372 16.9929634144 1114 | dots 50.7113785398 17.0883158538 1115 | dots 50.8127448984 16.5727180506 1116 | dots 51.014232955 17.2210955293 1117 | dots 50.3535214052 17.0647430794 1118 | dots 50.4355295722 17.0651685025 1119 | dots 50.6309819633 17.0765223454 1120 | dots 51.0668072036 17.2088597095 1121 | dots 50.7923547263 17.2042143387 1122 | dots 50.5512780561 17.0846551833 1123 | dots 50.5597580562 17.0938837744 1124 | dots 75.3259785471 15.7718919896 1125 | dots 75.04472578 17.0042622562 1126 | dots 75.2870877232 16.1749349097 1127 | dots 75.2399699777 17.0318474866 1128 | dots 75.1524592023 17.0049424015 1129 | dots 75.9618400861 16.6948422289 1130 | dots 75.4480625064 17.0451494125 1131 | dots 75.7593838168 16.9429296545 1132 | dots 50.378262298 16.9462798113 1133 | dots 50.5336350094 17.0195813691 1134 | dots 77.5009073165 50.1669859475 1135 | dots 50.6911241884 87.5139604241 1136 | dots 49.9903949511 83.997356924 1137 | dots 50.1271820272 82.990749996 1138 | circle 55.9930301513 79.2772636977 1139 | circle 50.0322537946 79.013071199 1140 | circle 51.2884586593 82.4359398425 1141 | circle 51.1705368873 79.1652941091 1142 | circle 44.3779145287 78.1646280036 1143 | circle 45.0102700721 77.8808631153 1144 | circle 48.559816719 78.7883706035 1145 | circle 42.1422682922 76.8806345544 1146 | circle 41.0269715735 76.4095884613 1147 | circle 34.5753090115 72.7248411845 1148 | circle 31.1686007236 69.2454213193 1149 | circle 32.6442540714 70.7325550334 1150 | circle 26.6666456539 62.9809712052 1151 | circle 26.4592623274 62.6346733709 1152 | circle 25.5380076465 60.6302361093 1153 | circle 26.3207111933 62.3962811186 1154 | circle 26.1633874648 62.0268148985 1155 | circle 25.2137904611 60.2733986018 1156 | circle 26.8595295604 63.1418346727 1157 | circle 31.7606691716 69.7976553936 1158 | circle 39.7932905744 75.8079642168 1159 | circle 45.6174391854 78.1199702277 1160 | circle 53.7714606827 79.3029316435 1161 | circle 56.4514336733 79.2471070208 1162 | circle 66.0937201989 77.0178182606 1163 | circle 56.9258394194 79.2063186226 1164 | circle 58.9882538467 78.9172589554 1165 | circle 57.9022660533 79.0808678187 1166 | circle 64.8128669568 77.4869389235 1167 | circle 60.3497935166 78.7294458673 1168 | circle 48.890555756 16.8065435154 1169 | circle 47.549506348 17.0714045245 1170 | circle 30.8894308257 26.3290372348 1171 | circle 28.972835724 29.0504689185 1172 | circle 35.1613607523 22.7868496473 1173 | circle 45.8703691685 17.4893933655 1174 | circle 32.8314206076 24.7975381459 1175 | circle 39.4855747899 20.0330500489 1176 | circle 33.4461829314 24.1906434075 1177 | circle 21.8635812756 46.0525933457 1178 | circle 25.1658708449 35.7666139539 1179 | circle 22.9594789762 51.2877067934 1180 | circle 25.5860552415 60.8465833585 1181 | circle 26.3143100672 33.2971681038 1182 | circle 33.037301251 24.5644624921 1183 | circle 36.4688894772 21.8761144725 1184 | circle 26.5135531009 32.9187498178 1185 | circle 36.0270912125 22.1954385395 1186 | circle 42.6810404129 18.5425361977 1187 | circle 45.0410771273 17.7345177303 1188 | circle 47.3524164557 17.1347454264 1189 | circle 53.8461940322 16.3873180961 1190 | circle 55.1296393835 16.3532468711 1191 | circle 52.1881199617 16.4182174652 1192 | circle 58.6726630845 16.6633724322 1193 | circle 82.1443116037 33.1952218669 1194 | circle 75.6865711383 24.7415103908 1195 | circle 85.344964503 52.886419866 1196 | circle 83.8446535127 58.6160602305 1197 | circle 85.66476066 45.5427527692 1198 | circle 77.931004934 68.6900618808 1199 | circle 73.0318330209 73.12057473 1200 | circle 70.1028044999 75.6832068542 1201 | circle 57.1446946889 79.1941025922 1202 | circle 46.9624619942 78.6665589963 1203 | circle 50.5649338128 79.0772985529 1204 | circle 53.7196765696 79.306936167 1205 | circle 54.2002323226 79.3250023354 1206 | circle 47.875198343 78.6758534295 1207 | circle 51.4734585149 79.1598669963 1208 | circle 52.7512222162 79.2936599344 1209 | circle 48.5622230047 78.7823835275 1210 | circle 53.0812416908 79.2972507654 1211 | circle 56.475383374 79.1678400979 1212 | circle 49.570911405 79.0381238831 1213 | circle 37.6277032991 74.5575043418 1214 | circle 32.2637000863 70.3166389012 1215 | circle 32.542380336 70.7024172063 1216 | circle 52.8250171923 85.5781338795 1217 | circle 58.5282895407 78.9787042541 1218 | circle 56.265834263 79.2529394854 1219 | circle 57.0758924193 79.1939225382 1220 | circle 54.2564023808 79.3138648504 1221 | circle 62.6856712475 78.1747432719 1222 | circle 68.0856058616 76.1123175001 1223 | circle 65.1181219632 77.3991884543 1224 | circle 68.7456312971 75.7900554548 1225 | circle 63.0902251412 78.041409495 1226 | circle 77.2045893204 69.3121497658 1227 | circle 82.0339084758 62.6818697859 1228 | circle 81.5779459183 63.8949255005 1229 | circle 85.1801432805 53.7816766607 1230 | circle 84.2868249936 57.2561680896 1231 | circle 85.6224867126 45.0241658646 1232 | circle 85.6044990156 50.7623379055 1233 | circle 84.4247858276 38.761410164 1234 | circle 85.570985078 44.5600964397 1235 | circle 84.6267242006 39.4870775084 1236 | circle 73.3961549252 22.8162479426 1237 | circle 73.5074477338 22.8569887644 1238 | circle 63.3564881135 17.7240688424 1239 | circle 72.9590468722 22.5114998325 1240 | circle 64.0208416469 17.8835812521 1241 | circle 61.124463241 17.1163533592 1242 | circle 75.6742073297 24.7825009657 1243 | circle 69.5135831574 20.2793578927 1244 | circle 58.5721354669 16.642703808 1245 | circle 70.9819409865 21.1458881039 1246 | circle 65.2230303973 18.3971951223 1247 | circle 65.3150439093 18.3337482986 1248 | circle 64.0102406778 17.9244781912 1249 | circle 57.1409359294 16.5986105437 1250 | circle 56.3651532552 16.3265463742 1251 | circle 43.046183241 18.4025593948 1252 | circle 52.0817555582 16.4538745312 1253 | circle 42.8790365154 18.4894110252 1254 | circle 51.5669948027 16.4857136372 1255 | circle 58.8391251216 16.6376352154 1256 | circle 60.7557891374 17.0332315064 1257 | circle 54.7611561181 16.3661890954 1258 | circle 53.5699050003 16.3397125506 1259 | circle 48.9146182416 16.7986370826 1260 | circle 52.6461373069 16.3848363836 1261 | circle 49.8384929905 16.6763302777 1262 | circle 52.6840664009 16.4079627826 1263 | circle 65.8072190346 18.1407649473 1264 | circle 60.6739209233 17.0518023644 1265 | circle 63.394712866 17.7005351354 1266 | circle 64.8020094467 18.2168786335 1267 | circle 65.0259398087 18.2878245002 1268 | circle 65.7555515404 18.5162158303 1269 | circle 69.9677341245 20.5389887758 1270 | circle 68.8927840241 20.0324488429 1271 | circle 61.8265465579 17.2696554452 1272 | circle 60.7879700808 17.0462048583 1273 | circle 61.9156096372 17.2821912228 1274 | circle 45.0289975331 17.7244473049 1275 | circle 39.9213628084 19.7018495281 1276 | circle 84.7942778751 55.5686503735 1277 | circle 55.6629587449 83.3564796243 1278 | circle 50.4922475132 78.997532065 1279 | circle 51.4671011416 79.2018445773 1280 | bullseye 51.2038911373 83.3397766053 1281 | bullseye 58.9744699018 85.499817612 1282 | bullseye 51.8720726696 85.8297376348 1283 | bullseye 48.1799307865 85.0451167372 1284 | bullseye 41.6832004024 84.0179406019 1285 | bullseye 37.8904155015 82.5674929978 1286 | bullseye 39.5489736947 80.812601774 1287 | bullseye 39.6495738769 82.6645338713 1288 | bullseye 34.7505970472 80.0110909897 1289 | bullseye 27.560835291 72.8478255927 1290 | bullseye 24.6355399754 71.6107148256 1291 | bullseye 20.9594648125 66.0414983826 1292 | bullseye 20.6891490538 62.7213052064 1293 | bullseye 19.2882047369 62.0630593582 1294 | bullseye 20.0245005746 61.3426238695 1295 | bullseye 35.4695229971 43.1158849517 1296 | bullseye 36.8943276507 47.7065559687 1297 | bullseye 39.0555497751 55.5469737131 1298 | bullseye 46.9570801494 65.2404073935 1299 | bullseye 37.3104527352 45.2587508999 1300 | bullseye 40.0096720019 60.9865825077 1301 | bullseye 48.0143866846 65.7128195934 1302 | bullseye 53.7037759268 66.3894820363 1303 | bullseye 63.0674998921 64.0350004581 1304 | bullseye 62.0480325076 63.8458632494 1305 | bullseye 59.8399667124 64.4767644437 1306 | bullseye 55.1609418175 65.2373081694 1307 | bullseye 61.2797865792 65.766402504 1308 | bullseye 60.8349175348 64.6037697063 1309 | bullseye 61.5205906458 64.7918550418 1310 | bullseye 36.916543863 41.0952474383 1311 | bullseye 38.5021996714 41.5671556174 1312 | bullseye 48.6643707344 30.6806668519 1313 | bullseye 50.2852524042 30.3379221051 1314 | bullseye 42.276332666 34.5276361236 1315 | bullseye 54.0317756204 29.6723483149 1316 | bullseye 37.3293552552 39.6020423074 1317 | bullseye 41.389522551 37.2960562328 1318 | bullseye 40.0746666572 34.6236852027 1319 | bullseye 35.3496806211 47.1410731332 1320 | bullseye 34.7637004189 47.6247999245 1321 | bullseye 37.0266294472 44.4622930512 1322 | bullseye 36.4555695327 40.791843032 1323 | bullseye 35.5376642131 48.7293868661 1324 | bullseye 20.4089478892 32.2030304205 1325 | bullseye 23.4957104659 25.3224681542 1326 | bullseye 29.5575433634 21.3647774591 1327 | bullseye 33.0082339121 15.9850714584 1328 | bullseye 53.9803991821 29.3509867092 1329 | bullseye 52.2343086004 29.7116729865 1330 | bullseye 59.5030766063 30.6696739448 1331 | bullseye 41.1637810689 34.3157582514 1332 | bullseye 48.9930401201 32.0303588363 1333 | bullseye 59.2692803248 29.6407017654 1334 | bullseye 45.4691770035 33.8311927323 1335 | bullseye 62.6912665406 30.2903738312 1336 | bullseye 73.4286708654 48.5778551303 1337 | bullseye 70.846426107 52.282253331 1338 | bullseye 71.5390198495 45.5218061588 1339 | bullseye 67.6208658884 38.0065584658 1340 | bullseye 72.4709525633 51.1221348241 1341 | bullseye 64.8122375621 62.8109155873 1342 | bullseye 60.8536798727 65.4991470327 1343 | bullseye 67.7894961571 61.3637015467 1344 | bullseye 41.6095572691 83.8486865559 1345 | bullseye 53.0030253202 84.6747986012 1346 | bullseye 54.7141710591 84.0431280734 1347 | bullseye 44.2916687157 82.9094412144 1348 | bullseye 49.1917219555 85.8762291224 1349 | bullseye 53.1013817819 84.5476586881 1350 | bullseye 51.599848153 84.8198214898 1351 | bullseye 54.3797219484 84.2403555494 1352 | bullseye 46.4807681047 83.518211666 1353 | bullseye 53.174656268 84.2605679876 1354 | bullseye 45.272002941 85.2370794195 1355 | bullseye 36.03340215 53.371687755 1356 | bullseye 28.2711941653 72.840231257 1357 | bullseye 25.0548060847 71.5485979221 1358 | bullseye 64.7588870014 82.3152236405 1359 | bullseye 63.1445274767 85.2366963264 1360 | bullseye 50.4246786898 85.1747475891 1361 | bullseye 70.6449962629 82.4309187567 1362 | bullseye 63.1490490834 83.9468553485 1363 | bullseye 62.8240245172 84.9661859507 1364 | bullseye 70.236869515 82.1711510621 1365 | bullseye 70.0427352449 80.3850213547 1366 | bullseye 72.5706234458 80.9712184346 1367 | bullseye 75.1307160373 79.9840931403 1368 | bullseye 83.2939057345 70.7784317876 1369 | bullseye 79.6642622775 73.9323097157 1370 | bullseye 88.4321025254 64.6242470023 1371 | bullseye 89.1155590082 64.0015066388 1372 | bullseye 89.0921976305 57.768193047 1373 | bullseye 91.7260057727 52.6233532573 1374 | bullseye 91.7355387598 48.9702108877 1375 | bullseye 91.5078881747 53.3126520851 1376 | bullseye 88.239001904 31.4774348786 1377 | bullseye 88.5305192041 30.4760310098 1378 | bullseye 55.3651603364 30.4458502777 1379 | bullseye 62.5602588694 30.4471356661 1380 | bullseye 58.0066691227 30.2537212987 1381 | bullseye 55.0671179917 29.0115351997 1382 | bullseye 61.6147759612 29.9943911942 1383 | bullseye 68.5431435378 35.6578321706 1384 | bullseye 77.7061096487 20.3042601852 1385 | bullseye 68.4530459999 13.0355285908 1386 | bullseye 68.2572064402 12.3846391542 1387 | bullseye 70.2554746739 13.2503849689 1388 | bullseye 65.0443252778 11.0008414785 1389 | bullseye 60.0922466078 11.8721117093 1390 | bullseye 52.9920289667 9.90666848335 1391 | bullseye 50.1446289773 12.2115430946 1392 | bullseye 46.5086141932 11.2071344881 1393 | bullseye 43.8070319612 11.3189448865 1394 | bullseye 57.8178546865 10.9451424306 1395 | bullseye 50.9404926595 9.69154713447 1396 | bullseye 63.4973230835 11.9140691707 1397 | bullseye 50.0164829541 11.9338520864 1398 | bullseye 58.6367650809 11.9747210652 1399 | bullseye 54.7302890863 11.4128826732 1400 | bullseye 65.8755478023 11.7324363627 1401 | bullseye 57.0609827131 9.92056085444 1402 | bullseye 46.819907946 10.4946526804 1403 | bullseye 38.3593948725 13.431322624 1404 | bullseye 47.3154157773 12.853451784 1405 | bullseye 55.0519165396 11.9499886181 1406 | bullseye 50.5159602579 9.76559162187 1407 | bullseye 49.6774146514 10.3831325083 1408 | bullseye 67.2806595188 14.1286515323 1409 | bullseye 66.1730182606 12.0379170217 1410 | bullseye 61.0885441374 10.0845344144 1411 | bullseye 66.0530857731 13.3802260073 1412 | bullseye 72.6699892655 15.2342259409 1413 | bullseye 61.503472503 10.8284144754 1414 | bullseye 68.995028632 13.9943105293 1415 | bullseye 78.2499161658 17.8832409068 1416 | bullseye 36.4819805699 15.1627600944 1417 | bullseye 50.9677483784 29.6797742875 1418 | bullseye 91.1910536059 46.6743428399 1419 | bullseye 55.8637684939 85.3364867573 1420 | bullseye 49.2805947987 84.0488228306 1421 | bullseye 43.3685015447 84.3321772039 1422 | slant_up 47.6952008891 95.2411869282 1423 | slant_up 44.6099759113 93.075835032 1424 | slant_up 43.8563814822 94.0858717713 1425 | slant_up 41.5789293984 90.3035665387 1426 | slant_up 49.1774185559 96.6105324538 1427 | slant_up 42.6522509813 90.5606397307 1428 | slant_up 30.1233318217 81.1442953304 1429 | slant_up 31.915519986 79.7904134869 1430 | slant_up 27.8877193574 75.4455759934 1431 | slant_up 27.5482344526 75.1647590544 1432 | slant_up 20.2097781637 67.5166420988 1433 | slant_up 24.4434528292 54.4736551228 1434 | slant_up 24.6308301331 53.3957388998 1435 | slant_up 21.0379688969 49.829299645 1436 | slant_up 27.106242932 54.7195531488 1437 | slant_up 28.0896176553 60.512212549 1438 | slant_up 31.1357349152 61.4319127116 1439 | slant_up 37.700837521 68.4184052804 1440 | slant_up 43.3926884016 43.5975940692 1441 | slant_up 51.2623974761 49.6665051258 1442 | slant_up 43.2867129203 43.0568603208 1443 | slant_up 55.9059162338 54.2526725342 1444 | slant_up 50.198958949 52.2250295948 1445 | slant_up 55.761587372 54.0305172396 1446 | slant_up 58.3231396505 60.0310591646 1447 | slant_up 70.171457528 70.2822212385 1448 | slant_up 60.600276471 59.7533088802 1449 | slant_up 63.7602750188 61.1856411696 1450 | slant_up 54.3894952619 53.811901225 1451 | slant_up 55.2412969947 52.5935746267 1452 | slant_up 49.0701267502 48.8969446811 1453 | slant_up 46.0181190216 46.2577233718 1454 | slant_up 44.7595874356 41.9746066134 1455 | slant_up 51.6006241332 50.0324668853 1456 | slant_up 36.4658820182 35.5241876075 1457 | slant_up 33.3947987327 35.0168013145 1458 | slant_up 37.5800715749 38.8315722007 1459 | slant_up 36.619722059 36.8114141094 1460 | slant_up 36.1634664356 33.819314199 1461 | slant_up 33.1677570349 31.2477411672 1462 | slant_up 42.6449829318 40.1543216386 1463 | slant_up 47.2250252695 44.5756466185 1464 | slant_up 48.3923630032 50.3315285645 1465 | slant_up 27.2451932001 29.8195074488 1466 | slant_up 30.4043143388 29.0795399241 1467 | slant_up 30.0807992865 27.5880255173 1468 | slant_up 33.9806339675 35.0330581489 1469 | slant_up 34.2506117649 34.4415865654 1470 | slant_up 51.1411835714 20.6183237784 1471 | slant_up 49.899592066 19.5640835356 1472 | slant_up 55.5898773424 26.2992451143 1473 | slant_up 55.4820563581 22.8274662445 1474 | slant_up 63.5729393821 32.9386176053 1475 | slant_up 59.0250770848 28.3220858801 1476 | slant_up 62.4237785929 33.4865952868 1477 | slant_up 47.849907703 48.556624645 1478 | slant_up 52.2685616825 52.8052401961 1479 | slant_up 68.8510215758 39.573434273 1480 | slant_up 65.6763960251 35.1093154301 1481 | slant_up 77.7468851148 47.3718241139 1482 | slant_up 80.352934115 47.8002988852 1483 | slant_up 60.3375864858 63.0349593272 1484 | slant_up 61.6587233282 64.1000234779 1485 | slant_up 71.5450389858 72.657716331 1486 | slant_up 49.0409566406 77.3625832678 1487 | slant_up 51.8868880222 80.1136076892 1488 | slant_up 57.968981435 85.3560457326 1489 | slant_up 54.855664646 83.7833976656 1490 | slant_up 54.1331954613 83.7939960824 1491 | slant_up 51.3088252568 80.1363911553 1492 | slant_up 61.1203943844 88.7040529069 1493 | slant_up 55.5638650304 85.8103751441 1494 | slant_up 55.044123361 83.5414106324 1495 | slant_up 50.4441853247 78.1958883115 1496 | slant_up 43.8277089981 71.0470522123 1497 | slant_up 37.7914323802 66.8546410275 1498 | slant_up 29.292783111 59.79199693 1499 | slant_up 32.1920969515 60.2006509147 1500 | slant_up 51.4339355233 99.2568672885 1501 | slant_up 67.4641398118 95.999963905 1502 | slant_up 65.1507052757 92.5026337368 1503 | slant_up 65.5834276962 93.5170609365 1504 | slant_up 69.2889731377 99.5795911254 1505 | slant_up 81.2694405511 80.635944933 1506 | slant_up 78.5757448361 76.8157030942 1507 | slant_up 74.1867734997 74.3442041467 1508 | slant_up 72.622209414 74.5269656511 1509 | slant_up 75.1415195918 73.8196684648 1510 | slant_up 75.168667839 73.9004448762 1511 | slant_up 85.3242028911 55.3885788252 1512 | slant_up 84.090507598 56.301968006 1513 | slant_up 86.2562238205 55.8309303793 1514 | slant_up 92.5487933153 42.9019079116 1515 | slant_up 90.3426345089 37.7803861004 1516 | slant_up 85.8191629042 37.5713861819 1517 | slant_up 90.4418181137 40.1331134204 1518 | slant_up 92.0175590822 39.9179311324 1519 | slant_up 95.2605278366 46.008830272 1520 | slant_up 41.2902998033 42.3115524113 1521 | slant_up 66.4313243161 36.5361097353 1522 | slant_up 63.9863468858 32.7134316129 1523 | slant_up 61.2696820905 29.3857240482 1524 | slant_up 60.7493891909 28.0517363383 1525 | slant_up 64.6552305634 16.6122765086 1526 | slant_up 68.6382807138 18.5080128963 1527 | slant_up 65.7766651568 16.7876560655 1528 | slant_up 71.1341263421 19.3859575313 1529 | slant_up 63.7231835039 14.5214211957 1530 | slant_up 73.0014940701 24.0738125371 1531 | slant_up 72.436657497 20.5783451136 1532 | slant_up 68.4127671225 19.9131265443 1533 | slant_up 52.8583150204 20.2350610035 1534 | slant_up 53.5299526734 24.2419155845 1535 | slant_up 50.7597410528 18.0893946004 1536 | slant_up 42.53480844 10.1698684594 1537 | slant_up 41.5303978512 13.3962907452 1538 | slant_up 46.7145179915 15.6132744651 1539 | slant_up 48.4506176492 16.2122782224 1540 | slant_up 59.6326101247 7.05803719901 1541 | slant_up 61.6185682473 13.4108227184 1542 | slant_up 48.8339891275 17.7259558674 1543 | slant_up 45.3426531889 13.9088123935 1544 | slant_up 42.4150122932 11.6937810961 1545 | slant_up 58.9347650647 9.36920121911 1546 | slant_up 60.6836260407 10.6326415569 1547 | slant_up 54.8117983683 6.56964870325 1548 | slant_up 61.5868424253 11.0275858356 1549 | slant_up 59.7160077628 11.5190968617 1550 | slant_up 58.4163840114 5.64577748328 1551 | slant_up 59.243809528 10.5401420943 1552 | slant_up 56.5943646385 8.91734404523 1553 | slant_up 68.4925132247 17.9873634289 1554 | slant_up 65.0059431717 15.9126686464 1555 | slant_up 63.9210367078 15.6209610332 1556 | slant_up 53.5129705438 5.87510952836 1557 | slant_up 57.8328062743 9.22238462333 1558 | slant_up 31.0329174395 28.9281418896 1559 | slant_up 31.3332440932 32.5385694785 1560 | slant_up 86.4015498466 38.7469331743 1561 | slant_up 47.4421116354 98.1843018589 1562 | slant_up 46.2647413368 94.1161921613 1563 | slant_up 40.1638157437 87.4486723469 1564 | slant_down 52.8720214902 97.3432226575 1565 | slant_down 59.0141444945 93.5748748733 1566 | slant_down 56.3751090389 96.30514763 1567 | slant_down 37.8391996844 94.359443503 1568 | slant_down 39.8853734176 90.6346620817 1569 | slant_down 44.0774051326 84.1258533023 1570 | slant_down 31.4970254888 67.9125621568 1571 | slant_down 25.9525965456 73.0331803541 1572 | slant_down 36.4347239148 62.2940454639 1573 | slant_down 24.997482691 75.4415616626 1574 | slant_down 32.2462751352 70.1105868383 1575 | slant_down 18.1094722949 53.7081103324 1576 | slant_down 21.7960992263 49.4995295248 1577 | slant_down 24.150491069 47.1885335266 1578 | slant_down 28.6031956015 44.2237227 1579 | slant_down 20.9319996761 51.6462354868 1580 | slant_down 35.4355325251 34.9610381566 1581 | slant_down 44.3773828236 57.6847369215 1582 | slant_down 46.313692406 55.6855917703 1583 | slant_down 46.3860739231 51.0021645381 1584 | slant_down 42.4954400876 56.9036815315 1585 | slant_down 44.3005150328 58.1329592781 1586 | slant_down 46.9882833458 55.4206367313 1587 | slant_down 52.4215231623 47.867005488 1588 | slant_down 64.090998189 68.6509850107 1589 | slant_down 63.0168717563 68.2252589114 1590 | slant_down 67.3025779226 64.4134627173 1591 | slant_down 67.2259568904 62.4764105301 1592 | slant_down 50.309988715 47.2678818553 1593 | slant_down 45.4126389249 53.8707976895 1594 | slant_down 54.0206071815 47.2698972057 1595 | slant_down 49.8420222483 50.5765522936 1596 | slant_down 50.7132995657 48.7722867825 1597 | slant_down 55.0455549106 45.9862727459 1598 | slant_down 37.5437012597 32.8840244657 1599 | slant_down 39.7958978066 28.4432191156 1600 | slant_down 28.5233910215 39.7966662481 1601 | slant_down 31.3996460012 39.2652632229 1602 | slant_down 39.2415652162 33.3614166846 1603 | slant_down 27.7140565712 42.2500516253 1604 | slant_down 32.9607035213 35.8231112903 1605 | slant_down 35.4047362397 31.9391621952 1606 | slant_down 51.6422264225 50.8383406788 1607 | slant_down 34.207620843 33.5938054385 1608 | slant_down 36.5234892198 35.6710483891 1609 | slant_down 28.4192021281 21.3157482481 1610 | slant_down 26.1604969417 23.0422314726 1611 | slant_down 40.435327397 27.6400711421 1612 | slant_down 44.544955314 27.3622426221 1613 | slant_down 40.1237272008 30.0731619402 1614 | slant_down 42.1848064503 28.5736747331 1615 | slant_down 40.0026592892 27.4875100027 1616 | slant_down 57.1252310509 42.7390191388 1617 | slant_down 60.375744968 41.3564579961 1618 | slant_down 55.1058961252 46.1490510561 1619 | slant_down 61.7372710245 38.5848440052 1620 | slant_down 55.7412443246 46.6535769763 1621 | slant_down 58.2964084158 41.5831977346 1622 | slant_down 57.0425482213 42.418757739 1623 | slant_down 76.7756112073 55.2545987009 1624 | slant_down 71.8870772602 59.2669926152 1625 | slant_down 73.7883730663 58.6208121164 1626 | slant_down 75.1396847343 54.2445521781 1627 | slant_down 64.5963189058 65.952512925 1628 | slant_down 46.9095386323 85.6022611586 1629 | slant_down 46.9051031994 81.5656424136 1630 | slant_down 49.8069877356 82.085025592 1631 | slant_down 48.8632418189 83.1583574877 1632 | slant_down 66.7481899024 84.0048341677 1633 | slant_down 62.3597662841 90.2436599196 1634 | slant_down 47.3748964954 83.4715555182 1635 | slant_down 47.4464667165 82.622124014 1636 | slant_down 53.385280634 78.8711362558 1637 | slant_down 49.4527077733 79.9306028004 1638 | slant_down 54.8026825363 75.3639849893 1639 | slant_down 37.0882451339 60.857227495 1640 | slant_down 32.3759619238 68.5108204552 1641 | slant_down 37.4770138847 63.7886855744 1642 | slant_down 52.4066488967 96.4254150057 1643 | slant_down 50.2970432655 99.6441791736 1644 | slant_down 61.5633243835 89.8641388605 1645 | slant_down 61.0865603919 89.2243556475 1646 | slant_down 59.2364904011 91.4811005766 1647 | slant_down 66.1540050863 84.1119370252 1648 | slant_down 67.2313347767 85.055640808 1649 | slant_down 73.7099068702 78.2706108976 1650 | slant_down 76.5804513576 72.8915336827 1651 | slant_down 78.8765811639 71.5625276804 1652 | slant_down 77.4490021363 72.4473094857 1653 | slant_down 86.8537073476 65.5433240194 1654 | slant_down 79.806531301 52.7231797883 1655 | slant_down 76.9882734745 54.2027848022 1656 | slant_down 79.4793575017 49.1410154455 1657 | slant_down 82.3041595613 46.9909036215 1658 | slant_down 90.700691247 41.4667030063 1659 | slant_down 95.4434878077 36.1897016148 1660 | slant_down 94.9974880471 35.0953011066 1661 | slant_down 91.517847246 36.8902631263 1662 | slant_down 62.2926421901 40.0295333141 1663 | slant_down 59.2034756073 41.2706776667 1664 | slant_down 65.2563927704 32.8541041237 1665 | slant_down 62.4447424979 35.4149992261 1666 | slant_down 72.4624062907 27.6242638254 1667 | slant_down 69.9194262552 29.3110927191 1668 | slant_down 77.7628590782 23.4913735562 1669 | slant_down 72.7784101997 26.9005353695 1670 | slant_down 65.198316667 33.0271717859 1671 | slant_down 69.4598588916 30.1970315053 1672 | slant_down 59.3150555205 12.3543136666 1673 | slant_down 51.487713355 20.8436106875 1674 | slant_down 57.1124475509 15.3217851726 1675 | slant_down 50.2244178157 19.1709698793 1676 | slant_down 50.4338611907 18.5348708515 1677 | slant_down 56.3803054046 14.7884597101 1678 | slant_down 54.8635045999 14.009611915 1679 | slant_down 45.4998576493 3.98542629287 1680 | slant_down 49.9367453057 21.5900969196 1681 | slant_down 53.8098729164 17.7664144512 1682 | slant_down 59.8526782804 9.03994992133 1683 | slant_down 48.1756567942 19.5212153543 1684 | slant_down 50.7417190667 17.8384056087 1685 | slant_down 58.4597612975 14.1486628546 1686 | slant_down 50.1145975193 1.74146171252 1687 | slant_down 45.4203820755 5.37240899542 1688 | slant_down 51.3532681954 0.30387242055 1689 | slant_down 53.091704424 19.2900304453 1690 | slant_down 58.9431665444 11.6128777918 1691 | slant_down 59.3109904219 10.5721363739 1692 | slant_down 68.2221986595 1.13388036584 1693 | slant_down 62.5117719862 7.49123248279 1694 | slant_down 69.3800563617 1.48813233299 1695 | slant_down 82.8002478704 18.5293762368 1696 | slant_down 85.1603321777 15.0183695439 1697 | slant_down 67.095847697 0.601490941965 1698 | slant_down 62.307750573 5.74997471945 1699 | slant_down 59.9704150458 7.9133104735 1700 | slant_down 39.9116351739 32.1440226145 1701 | slant_down 39.5965009783 32.1361317349 1702 | slant_down 95.5934163965 33.2341293953 1703 | slant_down 52.772288113 99.6134716833 1704 | slant_down 53.1786144535 98.6038611855 1705 | slant_down 37.6270534612 93.060672952 1706 | wide_lines 65.8155400946 95.5883740582 1707 | wide_lines 65.6722651618 91.9334018119 1708 | wide_lines 39.002716451 92.26183816 1709 | wide_lines 37.7953028026 93.5324553607 1710 | wide_lines 35.5139005857 89.5991901138 1711 | wide_lines 39.2194529062 83.5434818364 1712 | wide_lines 31.5882013862 77.0280524208 1713 | wide_lines 32.6577782482 80.1138612983 1714 | wide_lines 31.6415992679 77.6108331874 1715 | wide_lines 30.9871426099 63.9440280336 1716 | wide_lines 27.4396322054 65.7444629564 1717 | wide_lines 30.5783699449 65.5219454667 1718 | wide_lines 28.809401637 59.6210044666 1719 | wide_lines 27.9395185786 50.9850621313 1720 | wide_lines 33.8499497887 46.2844744465 1721 | wide_lines 32.628463049 54.0637204929 1722 | wide_lines 32.4386500903 49.9030194916 1723 | wide_lines 35.8382920833 53.3514883396 1724 | wide_lines 37.1420429198 49.7419879519 1725 | wide_lines 34.6445467746 54.4441077764 1726 | wide_lines 35.675404696 49.6256433471 1727 | wide_lines 64.99458547 49.4621728312 1728 | wide_lines 66.1291498342 61.6437178853 1729 | wide_lines 65.790651882 55.9453737171 1730 | wide_lines 67.5017806138 60.8502274659 1731 | wide_lines 67.6295142176 61.4291060701 1732 | wide_lines 63.9100640599 62.9723349305 1733 | wide_lines 66.5493579687 67.7255886599 1734 | wide_lines 64.0515417112 48.8200939835 1735 | wide_lines 68.1932895833 46.5435408096 1736 | wide_lines 65.2783891952 44.8203529292 1737 | wide_lines 34.9976613883 28.6329939351 1738 | wide_lines 33.9237661692 53.5586885227 1739 | wide_lines 66.0044543688 52.6846691452 1740 | wide_lines 37.0232562464 32.1184816794 1741 | wide_lines 34.6982700852 36.4753541349 1742 | wide_lines 34.8634441715 30.9392745218 1743 | wide_lines 31.0900650789 32.3837547196 1744 | wide_lines 34.9213061465 35.4184380529 1745 | wide_lines 30.343361183 32.5245631742 1746 | wide_lines 35.4833288685 44.4916349303 1747 | wide_lines 33.2349104271 38.502483721 1748 | wide_lines 35.4399436182 47.7975176939 1749 | wide_lines 33.8735095227 37.056028453 1750 | wide_lines 28.5641377966 34.6263997171 1751 | wide_lines 31.3195486706 31.6639905316 1752 | wide_lines 30.796174379 23.5190343602 1753 | wide_lines 34.2333554347 22.4959116015 1754 | wide_lines 36.6307877065 31.4768312621 1755 | wide_lines 34.5486191018 26.1110878654 1756 | wide_lines 37.5196356 29.2676058936 1757 | wide_lines 62.4256973267 34.0442062707 1758 | wide_lines 64.320383794 31.7856294846 1759 | wide_lines 65.4455325117 43.2734991936 1760 | wide_lines 64.3256315887 41.6159763773 1761 | wide_lines 65.264338195 35.7175447772 1762 | wide_lines 66.388704175 38.8447443366 1763 | wide_lines 68.1914228192 45.1694864796 1764 | wide_lines 68.5406624515 48.4385263389 1765 | wide_lines 67.6614312657 51.0685845873 1766 | wide_lines 68.6755160534 50.465234811 1767 | wide_lines 69.0817099788 55.4740530987 1768 | wide_lines 69.048716493 66.3660457477 1769 | wide_lines 67.3082233775 71.5902965807 1770 | wide_lines 38.714899408 76.3969778172 1771 | wide_lines 35.5480948795 89.3049758407 1772 | wide_lines 36.7106729424 86.0749670003 1773 | wide_lines 64.9126624396 87.6457960566 1774 | wide_lines 66.0641357377 89.2086685815 1775 | wide_lines 64.9359294757 87.0721381174 1776 | wide_lines 67.0265507063 76.2225153324 1777 | wide_lines 65.3140322257 77.9387440331 1778 | wide_lines 64.2952177764 84.1258572133 1779 | wide_lines 38.7980121423 74.6106304299 1780 | wide_lines 34.6757573144 79.6375987743 1781 | wide_lines 30.7192619427 58.812060082 1782 | wide_lines 34.1051509268 64.1291972267 1783 | wide_lines 33.4233718511 67.0957433613 1784 | wide_lines 66.3138096741 98.9310270371 1785 | wide_lines 65.3096314498 98.1651568396 1786 | wide_lines 65.7140760682 99.2837639519 1787 | wide_lines 67.5934531877 90.9519098246 1788 | wide_lines 66.3880173202 89.2812304744 1789 | wide_lines 67.8061881135 91.6881773501 1790 | wide_lines 70.9626103129 86.5181213517 1791 | wide_lines 71.9936371714 78.8209480695 1792 | wide_lines 72.4798343312 80.8006830564 1793 | wide_lines 74.0784292286 72.9515308522 1794 | wide_lines 72.0362880189 69.7160484161 1795 | wide_lines 72.9981862717 59.6656447092 1796 | wide_lines 74.8121312339 57.5898837631 1797 | wide_lines 73.0273050243 46.2741878725 1798 | wide_lines 77.067111934 51.4869181896 1799 | wide_lines 75.0171368727 46.6224426489 1800 | wide_lines 76.6653077326 38.440250966 1801 | wide_lines 77.9158742329 45.9268433826 1802 | wide_lines 73.7420460836 39.1209853231 1803 | wide_lines 75.3298157891 32.8303519164 1804 | wide_lines 63.4104355141 38.377735614 1805 | wide_lines 68.856486669 43.084147214 1806 | wide_lines 66.3377850519 33.3065100022 1807 | wide_lines 64.2037185014 26.6441143003 1808 | wide_lines 64.4986348346 22.863501327 1809 | wide_lines 68.8909906096 27.2962056672 1810 | wide_lines 72.371523338 21.9616397473 1811 | wide_lines 69.7654249704 19.999850454 1812 | wide_lines 68.6213124429 18.9156764428 1813 | wide_lines 64.2977371716 20.4287496884 1814 | wide_lines 66.6992650902 18.5910853404 1815 | wide_lines 67.5445276811 16.4479381344 1816 | wide_lines 63.9469519836 18.6928454476 1817 | wide_lines 64.3881920705 15.7728122808 1818 | wide_lines 65.570045691 23.7657582226 1819 | wide_lines 38.4028368753 19.0468586722 1820 | wide_lines 37.8323600164 14.4694894463 1821 | wide_lines 36.9041611715 13.5838157511 1822 | wide_lines 36.286143052 17.1057707266 1823 | wide_lines 62.7866325947 13.9189931024 1824 | wide_lines 66.8176792234 11.4124971575 1825 | wide_lines 66.755021412 18.0853051378 1826 | wide_lines 65.4155283864 10.4635122068 1827 | wide_lines 36.9463314417 13.5143774996 1828 | wide_lines 37.8254347391 9.60103429534 1829 | wide_lines 36.722837943 9.33330210686 1830 | wide_lines 67.0733217806 6.04921458162 1831 | wide_lines 64.601818107 12.0019169643 1832 | wide_lines 65.4372821806 15.5453860785 1833 | wide_lines 67.0040223792 15.3458266393 1834 | wide_lines 66.7241920702 5.24980548075 1835 | wide_lines 68.3076187632 13.2809165227 1836 | wide_lines 68.7680482759 13.5214565521 1837 | wide_lines 74.1672717461 5.34988087254 1838 | wide_lines 64.9003579699 16.2452583689 1839 | wide_lines 68.7634335463 8.70057294385 1840 | wide_lines 66.8169139163 12.2732943361 1841 | wide_lines 67.3093466467 0.217006270014 1842 | wide_lines 34.7318293093 19.6017950724 1843 | wide_lines 33.6744421497 26.09049021 1844 | wide_lines 75.6272550236 37.128751949 1845 | wide_lines 40.6101254225 89.1362398699 1846 | wide_lines 39.1143664549 96.4817513202 1847 | wide_lines 34.5838289299 89.5889019877 1848 | -------------------------------------------------------------------------------- /exercises/altaircars.py: -------------------------------------------------------------------------------- 1 | from vega_datasets import data 2 | cars = data.cars() 3 | 4 | my_si = alt.selection_interval() 5 | 6 | points = alt.Chart(cars).mark_point().encode( 7 | x='Horsepower', 8 | y='Miles_per_Gallon', 9 | color='Origin' 10 | ).add_selection( 11 | my_si 12 | ) 13 | 14 | bars = alt.Chart(cars).mark_bar().encode( 15 | x='count(Origin)', 16 | y='Origin', 17 | color='Origin' 18 | ).transform_filter( 19 | my_si 20 | ) 21 | 22 | points & bars -------------------------------------------------------------------------------- /exercises/altairowidgetasaurus.py: -------------------------------------------------------------------------------- 1 | import ipywidgets 2 | dinodf = pd.read_csv('data/DatasaurusDozen.tsv',sep='\t') 3 | 4 | def chartosaurus(dataset='dino'): 5 | localdf = dinodf[dinodf.dataset==dataset] 6 | points = alt.Chart(localdf).mark_point().encode( 7 | x='x', 8 | y='y' 9 | ).add_selection( 10 | my_si 11 | ) 12 | 13 | barsX = alt.Chart(localdf).mark_bar().encode( 14 | alt.X('x',bin=True, scale=alt.Scale(domain=[0, 100])), 15 | y='count()' 16 | ).transform_filter( 17 | my_si 18 | ) 19 | 20 | barsY = alt.Chart(localdf).mark_bar().encode( 21 | alt.Y('y',bin=True, scale=alt.Scale(domain=[0, 100])), 22 | x='count()' 23 | ).transform_filter( 24 | my_si 25 | ) 26 | 27 | chart = alt.vconcat(barsX, 28 | alt.hconcat(points,barsY)) 29 | 30 | return chart 31 | 32 | ipywidgets.interact(chartosaurus,dataset=dinodf.dataset.unique()); -------------------------------------------------------------------------------- /exercises/ipywidgets1.py: -------------------------------------------------------------------------------- 1 | def reverse(x): 2 | return x[::-1] 3 | 4 | ipywidgets.interact(reverse, x='Hello'); -------------------------------------------------------------------------------- /exercises/ipywidgets2.py: -------------------------------------------------------------------------------- 1 | def plot_f(k, p): 2 | x = np.linspace(0, 4 * np.pi) 3 | y = np.sin(k*x + p) 4 | plt.plot(x, y) 5 | 6 | ipywidgets.interact(plot_f,k=(0.5,2),p=(0,2*np.pi)); -------------------------------------------------------------------------------- /exercises/plotlysaurus.py: -------------------------------------------------------------------------------- 1 | import ipywidgets 2 | 3 | def plotlysaurus(dataset='dino'): 4 | fig = go.Figure() 5 | 6 | localdf = dinodf[dinodf.dataset==dataset] 7 | 8 | fig.add_trace(go.Scatter( 9 | x=localdf.x, 10 | y=localdf.y, 11 | mode='markers', 12 | marker=dict( 13 | size=16, 14 | color=np.random.randn(500), #set color equal to a variable 15 | colorscale='rainbow', # one of plotly colorscales 16 | showscale=True 17 | ) 18 | )) 19 | 20 | return fig 21 | 22 | ipywidgets.interact(plotlysaurus,dataset=dinodf.dataset.unique()); -------------------------------------------------------------------------------- /exercises/widgetosaurus.py: -------------------------------------------------------------------------------- 1 | def widgetosaurus(dataset='dino'): 2 | fig,ax = plt.subplots(2,2,figsize=(8,8)) 3 | 4 | x = dinodf[dinodf.dataset==dataset].x 5 | y = dinodf[dinodf.dataset==dataset].y 6 | 7 | ax[1,0].scatter(x, y) 8 | 9 | ax[0,0].hist(x, bins=10, rwidth=0.9) 10 | ax[1,1].hist(y, bins=10, rwidth=0.9, orientation='horizontal') 11 | 12 | ax[0,1].text(0.2,0.8,'x_mean = {:.2f}'.format(x.mean())) 13 | ax[0,1].text(0.2,0.7,'x_stddev = {:.2f}'.format(x.std())) 14 | ax[0,1].text(0.2,0.6,'y_mean = {:.2f}'.format(y.mean())) 15 | ax[0,1].text(0.2,0.5,'y_stddev = {:.2f}'.format(y.std())) 16 | ax[0,1].text(0.2,0.4,'corr = {:.2f}'.format(x.corr(y))) 17 | 18 | fig.show() 19 | 20 | ipywidgets.interact(widgetosaurus, dataset=dinodf.dataset.unique()); -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | numpy 2 | scipy 3 | matplotlib 4 | ipywidgets 5 | pandas 6 | seaborn 7 | plotly 8 | bqplot 9 | voila 10 | dash 11 | jupyter-dash 12 | altair 13 | altair_latimes 14 | vega_datasets 15 | --------------------------------------------------------------------------------