Created
December 27, 2017 01:16
-
-
Save jwhendy/8542f9f330796814d98cf94a3c0caf6d to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"cells": [ | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "### imports and functions\n\nThis code formalizes [this sketch](https://community.plot.ly/t/use-plotly-offline-to-save-chart-as-image-file/408/25) laid out on the `plotly` community forum by user `broken_symlink`. It uses `selenium` to click the download button. This is an attempt to programmatically save out plot files via `plotly.offline` (do not require authentication with `plotly` servers, and do not open a web page).\n\nRequests for this are very common:\n\n- requests/mentions via `plotly` github: [351](https://github.com/plotly/plotly.py/issues/351), [352](https://github.com/plotly/plotly.py/issues/352), [483](https://github.com/plotly/plotly.py/issues/483), [564](https://github.com/plotly/plotly.py/issues/564), [596](https://github.com/plotly/plotly.py/issues/596), [856](https://github.com/plotly/plotly.py/issues/856), [880](https://github.com/plotly/plotly.py/issues/880)\n- this [question](https://community.plot.ly/t/use-plotly-offline-to-save-chart-as-image-file/408) at the `plotly` community forums\n- [this](https://stackoverflow.com/questions/40243446/how-to-save-plotly-offline-graph-in-format-png) and [this](https://stackoverflow.com/questions/34957790/use-plotly-offline-to-generate-graphs-as-images) on StackOverflow\n\nI couldn't get this to work with `phantomjs`, which was my aim since the original code uses the `firefox` webdriver and actually opens a browser instance. After much googling, it turns out downloading isn't super straihtforward in `phantomjs`.\n\n- [this SO post](https://stackoverflow.com/a/25756960/495990) suggests `phantomjs` doesn't really support downloads\n- another [SO answer](https://stackoverflow.com/a/27911585/495990) that combines `phantomjs` with `urllib` to download\n- [an issue](https://github.com/ariya/phantomjs/issues/10052) going back to 2011 about downloads!\n- [a PR](https://github.com/ariya/phantomjs/pull/14225) that *looks* like downloads was recently merged into `phantomjs`, but I admit I don't know how to use it\n- this [SO answer](https://stackoverflow.com/a/38655474/495990) features using `chromedriver` which is how I ultimiately did this\n\nIn tinkering with the `chromedriver` approach, it appears we can do two things:\n\n- specify `image='type', image_filename='name'` to `plotly.offline.plot()`, which will auto-download the plot upon opening. When using the `selenium` webdriver, we don't have to do anything but load the file.\n- alternatively, the [original sketch](https://community.plot.ly/t/use-plotly-offline-to-save-chart-as-image-file/408/25) used the following (after loading the page):\n\n```\nexport_button = driver.find_element_by_xpath(\"//a[@data-title='Download plot as a png']\")\nexport_button.click()\n```\n\nI didn't use this for two reasons. The first is without needing it... why use it? The second is that I actually ended up getting *two* downloads for each iteration! I discovered that the two relevant options *might* not be mutually exclusive (see [this inquiry](https://community.plot.ly/t/is-image-foo-required-for-image-filename-bar-with-plotly-offline-plot/7476)). Basically, if you pass `image='png'`, the file auto-downloads upon opening... but running the `export_button.click()` *also* downloads it, so you get two files. In addition, removing `image='png'` but leaving `image_filename='name'` seems to ignore the filename so you get `newplot.png` upon the simulated click event.\n\nAt present, it seems best to rely on the auto-download feature as it's simpler and we can specify a filename.\n\nLastly, I had never used `pyvirtualdisplay` before. I didn't think it was necessary, as I was able to run the code just fine and get a file. Out of curiosity, I made a separate `.py` file and ran it *outside* of Jupyter lab and a `chromium` instance popped open and then closed. In addition, I accidentally discovered that you need the `Display()` instance created before the `webdriver`. Speaking from a total noob perspective, in my mind I imagine the browser redirecting to the fake display but only if it exists." | |
}, | |
{ | |
"metadata": { | |
"trusted": true | |
}, | |
"cell_type": "code", | |
"source": "import os\nimport pandas as pd\nimport plotly\nimport plotly.graph_objs as go\nimport time\n\nfrom selenium import webdriver\nfrom PIL import Image\nfrom pyvirtualdisplay import Display\n\n \n### from bokeh/io, slightly modified to avoid their import_required util\n### didn't ultimately use, but leaving in case I figure out how to stick wtih phentomjs\n### - https://github.com/bokeh/bokeh/blob/master/bokeh/io/export.py\ndef create_default_webdriver():\n '''Return phantomjs enabled webdriver'''\n phantomjs_path = detect_phantomjs()\n return webdriver.PhantomJS(executable_path=phantomjs_path, service_log_path=devnull)\n\n\n### based on last SO answer above\n### - https://stackoverflow.com/questions/38615811/how-to-download-a-file-with-python-selenium-and-phantomjs\ndef create_chromedriver_webdriver(dload_path):\n display = Display(visible=0)\n display.start()\n chrome_options = webdriver.ChromeOptions()\n prefs = {\"download.default_directory\": dload_path}\n chrome_options.add_experimental_option(\"prefs\", prefs)\n driver = webdriver.Chrome(chrome_options=chrome_options)\n return driver, display", | |
"execution_count": 1, | |
"outputs": [] | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "### create a plot\n\nHere we create some data and save the plot as an html file for reading in" | |
}, | |
{ | |
"metadata": { | |
"trusted": true | |
}, | |
"cell_type": "code", | |
"source": "df = pd.DataFrame(\n {'fruits': ['Apples', 'Pears', 'Nectarines', 'Plums', 'Grapes', 'Strawberries'],\n 'counts': [5, 3, 4, 2, 4, 6] })\n\ndata = [go.Bar(x=df['fruits'],\n y=df['counts'])]\n\ndload = os.path.expanduser('~/Downloads')\nhtml_file = 'plotly-fruit-plot.html'\nfname = 'plotly-fruit-plot'\n\n### original code contained height/width for the display and chromium webdriver\n### I found they didn't matter; specifying the image size to generate will \n### produce a plot of that size no matter the webdriver\nplotly.offline.plot(data, filename=html_file, auto_open=False,\n image_width=1280, image_height=800,\n image_filename=fname, image='png')", | |
"execution_count": null, | |
"outputs": [] | |
}, | |
{ | |
"metadata": {}, | |
"cell_type": "markdown", | |
"source": "### the magic\n\n" | |
}, | |
{ | |
"metadata": { | |
"trusted": true | |
}, | |
"cell_type": "code", | |
"source": "### create webdrive, open file, maximize, and sleep\ndriver, display = create_chromedriver_webdriver(dload)\n\ndriver.get('file:///{}'.format(os.path.abspath(html_file)))\n\n# make sure we give the file time to download\ntime.sleep(1)\n\n### was in the SO post and could be a more robust way to wait vs. just sleeping 1sec\n# while not(glob.glob(os.path.join(dl_location, filename))):\n# time.sleep(1)\n\ndriver.close()\ndisplay.stop()\n\nimage = Image.open('{}.png'.format(os.path.join(dload, fname)))\nimage", | |
"execution_count": 3, | |
"outputs": [ | |
{ | |
"data": { | |
"image/png": "iVBORw0KGgoAAAANSUhEUgAABQAAAAMgCAYAAAB8mM/7AAAzQklEQVR4nO3dd5iV5Zn48XsKHVSq\noNiNLiwKFgQMMWvUTZZogtHkwrYRNBJb4mVdxYIRsaGCJeouihE1Eo1lNVnFEkvEAusVg4KV4mJU\nVEQY6rTfH/w4C4s6Qzx4xpvP5y/mnJl37sHHwzPf877nlNXX19cHAAAAAJBSeakHAAAAAAA2HAEQ\nAAAAABITAAEAAAAgMQEQAAAAABITAAEAAAAgMQEQAAAAABITAAEAAAAgMQEQAAAAABITAAEAAAAg\nMQEQAAAAABITAAEAAAAgMQEQAAAAABITAAEAAAAgMQEQAAAAABITAAEAAAAgMQEQAAAAABITAAEA\nAAAgMQEQAAAAABITAAEAAAAgMQEQAAAAABITAAEAAAAgMQEQAAAAABITAAEAAAAgMQEQAAAAABIT\nAAEAAAAgMQEQAAAAABITAAEAAAAgMQEQAAAAABITAAEAAAAgMQEQAAAAABITAAEAAAAgMQEQAAAA\nABITAAEAAAAgMQEQAAAAABITAAEAAAAgMQEQAAAAABITAAEAAAAgMQEQAAAAABITAAEAAAAgMQEQ\nAAAAABITAAEAAAAgMQEQAAAAABITAAEAAAAgMQEQAAAAABITAAEAAAAgMQEQAAAAABITAAEAAAAg\nMQEQAAAAABITAAEAAAAgMQEQAAAAABITAAEAAAAgMQEQAAAAABITAAEAAAAgMQEQAAAAABITAAEA\nAAAgMQEQAAAAABITAAEAAAAgMQEQAAAAABITAAEAAAAgMQEQAAAAABITAAEAAAAgMQEQAAAAABIT\nAAEAAAAgMQEQAAAAABITAAEAAAAgMQEQAAAAABITAAEAAAAgMQEQAAAAABITAAEAAAAgMQEQAAAA\nABITAAEAAAAgMQEQAAAAABITAAEAAAAgMQEQAAAAABITAAEAAAAgMQEQAAAAABITAAEAAAAgMQEQ\nAAAAABITAAEAAAAgMQEQAAAAABITAAEAAAAgMQEQAAAAABITAAEAAAAgMQEQAAAAABITAAEAAAAg\nMQEQAAAAABITAAEAAAAgMQEQAAAAABITAAEAAAAgMQEQAAAAABITAAEAAAAgMQEQAAAAABITAAEA\nAAAgMQEQAAAAABITAAEAAAAgMQEQAAAAABITAAEAAAAgMQEQAAAAABITAAEAAAAgMQEQAAAAABIT\nAAEAAAAgMQEQAAAAABITAAEAAAAgMQEQAAAAABITAAEAAAAgMQEQAAAAABITAAEAAAAgMQEQAAAA\nABITAAEAAAAgMQEQAAAAABITAAEAAAAgMQEQAAAAABITAAEAAAAgMQEQAAAAABITAAEAAAAgMQEQ\nAAAAABITAAEAAAAgMQEQAAAAABITAAEAAAAgMQEQAAAAABITAAEAAAAgMQEQAAAAABITAAEAAAAg\nMQEQAAAAABITAAEAAAAgscpSD/B1UF1dHePGjYtnnnkmWrduHcOGDYsDDjig1GMBAAAAQIMEwEa4\n8847Y8mSJTFp0qSYO3duXHXVVbHPPvtEixYtSj0aAAAAAHyhsvr6+vpSD9HUHX744XH55ZdH9+7d\nSz0KAAAAAKwXrwHYgKqqqli4cGFMnTo1fvrTn8Zxxx0XU6ZMKfVYAAAAANAoLgFuQFVVVdTW1say\nZctiwoQJ8frrr8fZZ58dN998c3Ts2DGWL19e6hEBAAAAUmvZsmWpR/haEwAb0LZt26irq4sf/vCH\nUV5eHj169IiddtopXn311dhnn33CFdQAAABk0OPCJ0o9AsmM/XGv+G7PLqUegxAAG9S2bdto27Zt\nLF68ONq0aRMREfX19VFZueqvrlWrVqUcDwAAAKBJat68uW7SRHgNwEbYf//94/bbb4/a2tqYOXNm\nvPnmm9GjR49SjwUAAAAADRIAG+Hoo4+OxYsXxyGHHBKXXXZZnHnmmdG+fftSjwUAAAAADXIJcCO0\nadMmLrzwwlKPAQAAAADrzRmAAAAAAJCYAAgAAAAAiQmAAAAAAJCYAAgAAAAAiQmAAAAAAJCYAAgA\nAAAAiQmAAAAAAJCYAAgAAAAAiQmAAAAAAJCYAAgAAAAAiQmAAAAAAJCYAAgAAAAAiQmAAAAAAJCY\nAAgAAAAAiQmAAAAAAJCYAAgAAAAAiQmAAAAAAJCYAAgAAAAAiQmAAAAAAJCYAAgAAAAAiQmAAAAA\nAJCYAAgAAAAAiQmAAAAAAJCYAAgAAAAAiQmAAAAAAJCYAAgAAAAAiQmAAAAAAJCYAAgAAAAAiQmA\nAAAAAJCYAAgAAAAAiQmAAAAAAJCYAAgAAAAAiQmAAAAAAJCYAAgAAAAAiQmAAAAAAJCYAAgAAAAA\niQmAAAAAAJCYAAgAAAAAiQmAAAAAAJCYAAgAAAAAiQmAAAAAAJCYAAgAAAAAiQmAAAAAAJCYAAgA\nAAAAiQmAAAAAAJCYAAgAAAAAiQmAAAAAAJCYAAgAAAAAiQmAAAAAAJCYAAgAAAAAiQmAAAAAAJCY\nAAgAAAAAiQmAAAAAAJCYAAgAAAAAiQmAAAAAAJCYAAgAAAAAiQmAAAAAAJCYAAgAAAAAiQmAAAAA\nAJCYAAgAAAAAiQmAAAAAAJCYAAgAAAAAiQmAAAAAAJCYAAgAAAAAiQmAAAAAAJCYAAgAAAAAiQmA\nAAAAAJCYAAgAAAAAiQmAAAAAAJCYAAgAAAAAiQmAAAAAAJCYAAgAAAAAiQmAAAAAAJCYAAgAAAAA\niQmAAAAAAJCYAAgAAAAAiQmAAAAAAJCYAAgAAAAAiQmAAAAAAJCYAAgAAAAAiQmAAAAAAJCYAAgA\nAAAAiQmAAAAAAJCYAAgAAAAAiQmAAAAAAJCYAAgAAAAAiQmAAAAAAJCYAAgAAAAAiVWWeoCvg5NP\nPjneeuutKCsri4iINm3axN13313iqQAAAACgYQJgI1RVVcUNN9wQ2267balHAQAAAID14hLgRliy\nZEm0adOm1GMAAAAAwHpzBmAjVFVVxa9//et45ZVXYrPNNotjjz02+vXrV+qxAAAAAKBBAmAD6urq\nYt99942BAwfGiBEjYurUqTFq1Ki4+eabo0uXLrFy5cpSjwgAAADQ5NTU1BStmzRv3rwox9lYCYAN\nKC8vjzPOOKPw8YABA+If/uEfYvr06bHffvtFdXV1Ub7Pi3MXxq+fmlOUY8Fqt/5rn1KPAAAAwEaq\npqamaN1EAPxyBMAGLF++PGbNmhU9e/Ys3FZbWxvNmjWLiCjaawMurVkUU+cuLMqxYDWvXQkAAECp\ntGzZ0u+lTYQ3AWlATU1NnHXWWTFt2rSIiJg2bVrMnj07dt111xJPBgAAAAANcwZgA9q2bRvnn39+\n3HjjjfHRRx9F165d44ILLojNNtus1KMBAAAAQIMEwEbo27dv9O3bt9RjAAAAAMB6cwkwAAAAACQm\nAAIAAABAYgIgAAAAACQmAAIAAABAYgIgAAAAACQmAAIAAABAYgIgAAAAACQmAAIAAABAYgIgAAAA\nACQmAAIAAABAYgIgAAAAACQmAAIAAABAYgIgAAAAACQmAAIAAABAYgIgAAAAACQmAAIAAABAYgIg\nAAAAACQmAAIAAABAYgIgAAAAACQmAAIAAABAYgIgAAAAACQmAAIAAABAYgIgAAAAACQmAAIAAABA\nYgIgAAAAACQmAAIAAABAYgIgAAAAACQmAAIAAABAYgIgAAAAACQmAAIAAABAYgIgAAAAACQmAAIA\nAABAYgIgAAAAACQmAAIAAABAYgIgAAAAACQmAAIAAABAYgIgAAAAACQmAAIAAABAYgIgAAAAACQm\nAAIAAABAYgIgAAAAACQmAAIAAABAYgIgAAAAACQmAAIAAABAYgIgAAAAACQmAAIAAABAYgIgAAAA\nACQmAAIAAABAYgIgAAAAACQmAAIAAABAYgIgAAAAACQmAAIAAABAYgIgAAAAACQmAAIAAABAYgIg\nAAAAACQmAAIAAABAYgIgAAAAACQmAAIAAABAYgIgAAAAACQmAAIAAABAYgIgAAAAACQmAAIAAABA\nYgIgAAAAACQmAAIAAABAYgIgAAAAACQmAAIAAABAYgIgAAAAACQmAAIAAABAYgIgAAAAACQmAAIA\nAABAYgIgAAAAACQmAAIAAABAYgIgAAAAACQmAAIAAABAYgIgAAAAACQmAAIAAABAYgIgAAAAACQm\nAAIAAABAYgIgAAAAACQmAAIAAABAYgIgAAAAACQmAAIAAABAYgIgAAAAACQmAAIAAABAYgIgAAAA\nACQmAAIAAABAYgIgAAAAACQmAAIAAABAYgIgAAAAACQmAK6HRYsWxY9+9KN48MEHSz0KAAAAADSK\nALgebrzxxmjVqlWpxwAAAACARhMAG+nll1+O+fPnR//+/Us9CgAAAAA0WmWpB/g6qK6ujuuvvz7O\nPffceOCBB9a6r7a2tijfo66urijHgTWtuT7r6+tLOAl/j7KyslKPAACwXuw5gTXV1dUVrZtUVFQU\n5TgbKwGwEe66664YOHBgbL311uvcV1VVVZTvsXz58qIcB9a05voUk+CrNez2v5Z6BJI5YZ9tY8+t\nNyn1GLBR+vXTc2PaO5+WegwS2XPrTeOEfbYp9RiwwS1fvrxo3WTTTTctynE2VgJgA+bNmxfPPvts\nXHvttZ95f7EWYOvWS4tyHFiTB0goHb8oUmzL6ytik00EQCiFuQtXelynqLps2tpjOhuF1q1b+720\niRAAG/D888/HBx98EIcffnhERCxbtiwqKipi/vz5ccwxx5R4OgAAAAD4YgJgAw499NA49NBDCx9f\ne+21se2228ZBBx1UwqkAAAAAoHG8CzAAAAAAJOYMwPV08sknl3oEAAAAAGg0ZwACAAAAQGICIAAA\nAAAkJgACAAAAQGICIAAAAAAkJgACAAAAQGICIAAAAAAkJgACAAAAQGICIAAAAAAkJgACAAAAQGIC\nIAAAAAAkJgACAAAAQGICIAAAAAAkJgACAAAAQGICIAAAAAAkJgACAAAAQGICIAAAAAAkJgACAAAA\nQGICIAAAAAAkJgACAAAAQGICIAAAAAAkJgACAAAAQGICIAAAAAAkJgACAAAAQGICIAAAAAAkJgAC\nAAAAQGICIAAAAAAkJgACAAAAQGICIAAAAAAkJgACAAAAQGICIAAAAAAkJgACAAAAQGICIAAAAAAk\nJgACAAAAQGICIAAAAAAkJgACAAAAQGICIAAAAAAkJgACAAAAQGICIAAAAAAkJgACAAAAQGICIAAA\nAAAkJgACAAAAQGICIAAAAAAkJgACAAAAQGICIAAAAAAkJgACAAAAQGICIAAAAAAkJgACAAAAQGIC\nIAAAAAAkJgACAAAAQGICIAAAAAAkJgACAAAAQGICIAAAAAAkJgACAAAAQGICIAAAAAAkJgACAAAA\nQGICIAAAAAAkJgACAAAAQGICIAAAAAAkJgACAAAAQGICIAAAAAAkJgACAAAAQGICIAAAAAAkJgAC\nAAAAQGICIAAAAAAkJgACAAAAQGICIAAAAAAkJgACAAAAQGICIAAAAAAkJgACAAAAQGICIAAAAAAk\nJgACAAAAQGICIAAAAAAkJgACAAAAQGICIAAAAAAkJgACAAAAQGICIAAAAAAkJgACAAAAQGICIAAA\nAAAkJgACAAAAQGICIAAAAAAkJgACAAAAQGICIAAAAAAkJgACAAAAQGICIAAAAAAkJgACAAAAQGIC\nIAAAAAAkJgACAAAAQGICIAAAAAAkVlnqAb4OXn/99bjmmmvif/7nf6Jz587xs5/9LPr371/qsQAA\nAACgQc4AbEB9fX2MHDkyDj744HjggQfi2GOPjYsvvjhWrlxZ6tEAAAAAoEECYANWrlwZw4YNi/33\n3z/KyspiwIABUVdXFwsWLCj1aAAAAADQIJcAN6BFixZxwAEHREREdXV1PPzww7HllltGly5dSjwZ\nAAAAADRMAGyk5557Li644ILo1KlTXHDBBVFevurkyU8++aQox6+qqirKcWBNxVqfxTT5tY/j7Q+X\nlHoMEtmxc5s44B86lnoM2OCqqqqa5OM6bAyqq6tLPQLJrFy50mM6G4Vi7l/at29flONsrATARhow\nYED813/9V7z88stx7rnnxnXXXRebb7550RZg27bLi3IcWFNTfIB88u1Z8V+vvF/qMUhk0C7d4icD\ndiz1GLDBtW3btkk+rsPGoFmzZqUegWSaN2/uMZ2Ngv1L0+E1ABuwcOHCePzxxyMioqKiInbffffY\nZpttYsaMGSWeDAAAAAAaJgA2oKKiIsaOHRsvvvhiRETMmjUr3nrrrdh2221LOxgAAAAANIJLgBvQ\nrl27OO+882L8+PExevToaNeuXQwfPjy22267Uo8GAAAAAA0SABthr732ir322qvUYwAAAADAenMJ\nMAAAAAAkJgACAAAAQGICIAAAAAAkJgACAAAAQGICIAAAAAAkJgACAAAAQGICIAAAAAAkJgACAAAA\nQGICIAAAAAAkJgACAAAAQGICIAAAAAAkJgACAAAAQGICIAAAAAAkJgACAAAAQGICIAAAAAAkJgAC\nAAAAQGICIAAAAAAkJgACAAAAQGICIAAAAAAkJgACAAAAQGICIAAAAAAkJgACAAAAQGICIAAAAAAk\nJgACAAAAQGICIAAAAAAkJgACAAAAQGICIAAAAAAkJgACAAAAQGICIAAAAAAkJgACAAAAQGICIAAA\nAAAkJgACAAAAQGICIAAAAAAkJgACAAAAQGICIAAAAAAkJgACAAAAQGICIAAAAAAkJgACAAAAQGIC\nIAAAAAAkJgACAAAAQGICIAAAAAAkJgACAAAAQGICIAAAAAAkJgACAAAAQGICIAAAAAAkJgACAAAA\nQGICIAAAAAAkJgACAAAAQGICIAAAAAAkJgACAAAAQGICIAAAAAAkJgACAAAAQGICIAAAAAAkJgAC\nAAAAQGICIAAAAAAkJgACAAAAQGICIAAAAAAkJgACAAAAQGICIAAAAAAkJgACAAAAQGICIAAAAAAk\nJgACAAAAQGICIAAAAAAkJgACAAAAQGICIAAAAAAkJgACAAAAQGICIAAAAAAkJgACAAAAQGICIAAA\nAAAkJgACAAAAQGICIAAAAAAkJgACAAAAQGICIAAAAAAkJgACAAAAQGICIAAAAAAkJgACAAAAQGIC\nIAAAAAAkJgACAAAAQGICIAAAAAAkJgACAAAAQGICIAAAAAAkJgACAAAAQGICIAAAAAAkJgACAAAA\nQGICIAAAAAAkJgACAAAAQGICIAAAAAAkJgACAAAAQGKVpR7g62Du3LkxduzYmDVrVrRv3z6OO+64\n2HvvvUs9FgAAAAA0yBmAjTBq1Kj41re+Fffdd1+ceOKJcckll8Ty5ctLPRYAAAAANEgAbEBtbW0c\nfPDB8cMf/jDKy8ujb9++UVFREQsWLCj1aAAAAADQIJcAN6CioiIGDRpU+Pi1116LVq1aRdeuXUs4\nFQAAAAA0jgC4Ht5///249NJL44wzzojy8lUnT3766adFOfbSpUuLchxYU7HWZzFVV1eXegSSqa6u\nbpJrHYpt6dKlTW6tL1haHW9/ZA9DcfXdetNSj7AO+xeKzf6FjUUx9y+bbtr0/n34OhEAG2nWrFkx\ncuTIOOGEE2L33Xcv3N6uXbuiHL9ly6qiHAfWVKz1WUzNmjUr9QgkU1lZ2STXOhRby5Ytm9xaf3r2\ne3HyXdNLPQbJzBr9L6UeYR32LxSb/Qsbi6a4f9lYCYCN8N5778XIkSPjzDPPjF69eq113+ozAb+s\nYh0H1mRdsTEoKyuz1tkolJeXN7m13tTmIQfrio2B/Qsbi6a4f9lY+a/QCFdeeWUMGzZsnfgHAAAA\nAE2dMwAb8P7778fLL78cM2bMiMsvv7xw+znnnBMDBw4s4WQAAAAA0DABsAFdu3aNRx99tNRjAAAA\nAMDfxSXAAAAAAJCYAAgAAAAAiQmAAAAAAJCYAAgAAAAAiQmAAAAAAJCYAAgAAAAAiQmAAAAAAJCY\nAAgAAAAAiQmAAAAAAJCYAAgAAAAAiQmAAAAAAJCYAAgAAAAAiQmAAAAAAJCYAAgAAAAAiQmAAAAA\nAJCYAAgAAAAAiQmAAAAAAJCYAAgAAAAAiQmAAAAAAJCYAAgAAAAAiQmAAAAAAJCYAAgAAAAAiQmA\nAAAAAJCYAAgAAAAAiQmAAAAAAJCYAAgAAAAAiQmAAAAAAJCYAAgAAAAAiQmAAAAAAJCYAAgAAAAA\niQmAAAAAAJCYAAgAAAAAiQmAAAAAAJCYAAgAAAAAiQmAAAAAAJCYAAgAAAAAiQmAAAAAAJCYAAgA\nAAAAiQmAAAAAAJCYAAgAAAAAiQmAAAAAAJCYAAgAAAAAiQmAAAAAAJCYAAgAAAAAiQmAAAAAAJCY\nAAgAAAAAiQmAAAAAAJCYAAgAAAAAiQmAAAAAAJCYAAgAAAAAiQmAAAAAAJCYAAgAAAAAiQmAAAAA\nAJCYAAgAAAAAiQmAAAAAAJCYAAgAAAAAiQmAAAAAAJCYAAgAAAAAiQmAAAAAAJCYAAgAAAAAiQmA\nAAAAAJCYAAgAAAAAiQmAAAAAAJCYAAgAAAAAiQmAAAAAAJCYAAgAAAAAiQmAAAAAAJCYAAgAAAAA\niQmAAAAAAJCYAAgAAAAAiQmAAAAAAJCYAAgAAAAAiQmAAAAAAJCYAAgAAAAAiQmAAAAAAJCYAAgA\nAAAAiQmAAAAAAJCYAAgAAAAAiQmAAAAAAJCYAAgAAAAAiQmAAAAAAJCYAAgAAAAAiQmAAAAAAJCY\nAAgAAAAAiQmAAAAAAJCYAAgAAAAAiQmAAAAAAJCYAAgAAAAAiQmAjfTEE0/EQQcdFE8//XSpRwEA\nAACARqss9QBfB/fcc09Mnz49ttlmm1KPAgAAAADrxRmAjbDbbrvFyJEjo3Xr1qUeBQAAAADWizMA\nG2GHHXYo9QgAAAAA8HcRAL+kqqqqohxn+fLlRTkOrKlY67OYampqSj0CydTU1DTJtQ7Ftnz58ia3\n1u1f2BCa2jqPsH+h+Oxf2FgUc//Stm3bohxnYyUAfknNmzcvynEqK/2noPiKtT6LqbzcKw9QXOXl\n5dGiRYtSjwEbXGVlZZNb6/YvbAhNbZ1HRFRUVJR6BJIpLy9vknt1KLbKykprvYmwa/uSBECasqb4\nQCsAUmzl5eXRrFmzUo8BG1xlZWWTW+v2L2wITW2dR0SUlZWVegSSEQDZWAiATYffxAEAAAAgMQGw\nEY4//vgYNGhQ/OUvf4nRo0fHoEGD4qmnnir1WAAAAADQINdtNMINN9xQ6hEAAAAA4O/iDEAAAAAA\nSEwABAAAAIDEBEAAAAAASEwABAAAAIDEBEAAAAAASEwABAAAAIDEBEAAAAAASEwABAAAAIDEBEAA\nAAAASEwABAAAAIDEBEAAAAAASEwABAAAAIDEBEAAAAAASEwABAAAAIDEBEAAAAAASEwABAAAAIDE\nBEAAAAAASEwABAAAAIDEBEAAAAAASEwABAAAAIDEBEAAAAAASEwABAAAAIDEBEAAAAAASEwABAAA\nAIDEBEAAAAAASEwABAAAAIDEBEAAAAAASEwABAAAAIDEBEAAAAAASEwABAAAAIDEBEAAAAAASEwA\nBAAAAIDEBEAAAAAASEwABAAAAIDEBEAAAAAASEwABAAAAIDEBEAAAAAASEwABAAAAIDEBEAAAAAA\nSEwABAAAAIDEBEAAAAAASEwABAAAAIDEBEAAAAAASEwABAAAAIDEBEAAAAAASEwABAAAAIDEBEAA\nAAAASEwABAAAAIDEBEAAAAAASEwABAAAAIDEBEAAAAAASEwABAAAAIDEBEAAAAAASEwABAAAAIDE\nBEAAAAAASEwABAAAAIDEBEAAAAAASEwABAAAAIDEBEAAAAAASEwABAAAAIDEBEAAAAAASEwABAAA\nAIDEBEAAAAAASEwABAAAAIDEBEAAAAAASEwABAAAAIDEBEAAAAAASEwABAAAAIDEBEAAAAAASEwA\nBAAAAIDEBEAAAAAASEwABAAAAIDEBEAAAAAASEwABAAAAIDEBEAAAAAASEwABAAAAIDEBEAAAAAA\nSEwABAAAAIDEBEAAAAAASEwABAAAAIDEBEAAAAAASEwABAAAAIDEBEAAAAAASEwABAAAAIDEBEAA\nAAAASEwABAAAAIDEBEAAAAAASEwABAAAAIDEBMBG+Nvf/hannHJKHHjggXHMMcfEjBkzSj0SAAAA\nADSKANgIV1xxRfTt2zfuv//+GDp0aFx00UVRU1NT6rEAAAAAoEECYAMWLlwYb731VgwZMiQqKytj\n4MCBsdlmmzkLEAAAAICvBQGwAfPmzYtu3bpFRUVF4bbu3bvHvHnzSjgVAAAAADROZakHaOpWrFgR\nzZo1W+u25s2bx7JlyyIiYuLEiUX5Pq982jwi2hblWLBasdZnMc19p01EtCj1GCQyd+7cmDjx1VKP\n8Rk6lHoAknnqqafi479Ul3qMtdi/sCHYv7AxsH9hY1HM/ctRRx1VlONsrATABrRs2TKWLl261m1L\nliyJVq1aRUREfX19Ub7PP26yIn7Vc0VRjgWrFWl5FtWQraoioqrUY5BMU1zrv+r5calHIKGmttbt\nX9gQmto6j7B/YcNoimvd/oUNoSmu9Y2RANiA7t27x4cffhgrVqyIFi1WPes3d+7cOPTQQyMi4l//\n9V9LOR4AAAAAfCGvAdiATTfdNHr27Bm/+93voqamJp544omorq6OHj16lHo0AAAAAGhQWX2xrmFN\nbP78+XHppZfGG2+8EVtssUWcfvrpsdNOO5V6LAAAAABokABISfziF7+IFStWxE033fSljzV48OD4\nj//4j+jcuXMRJoPiOuWUU+K1116L8vJVJ1y3b98+Dj744MLLCEBTU1tbG9/73vdi8ODBceKJJxZu\nf/bZZ2Py5Mlx4YUX/l3Hfemll2KrrbZa78fqgw46KG655RaP8ZTcSy+9FGeddVbhzeGaNWsWPXr0\niJNPPjm23HLLuPfee2P27Nlx2mmnlXhS+PtMnTo1br/99njrrbeioqIitttuuzjyyCOjb9++pR4N\n1jF79uy46aab4u233466urro2rVrDBs2LPbYY4+IiHjkkUfiu9/9btG/74Z4rH/jjTdi1KhRcdtt\ntxXtmPBZXALMV27OnDnRunXr6Nq1a8yYMaPU48AGd/rpp8cf//jH+OMf/xi/+tWvYtKkSfHCCy8U\n5dh1dXVFOQ6sqXnz5vH000/H3Llzi3bM+++/Pz766KNGf/7qtT1x4sTo2LFj0eaAL6Nbt26Fx/NJ\nkybFNttsE6NHjy71WPClPffcc3HxxRfHoEGD4s4774zf/va38f3vfz8uuuiimDlz5jqfb/9BqV14\n4YUxcODAmDRpUtxzzz3xk5/8JEaOHBmLFy+O+vr6GD9+/Dpf0xTW7f+doa6uLnbYYYe49tprSzQR\nGxNvAsJXbvLkybHvvvtGs2bN4tFHH42ePXtGxKpnPi677LLYY4894o033oiqqqo4+eSTo3fv3nH3\n3XfHm2++GStXrowPP/wwmjdvHuecc846Z4S88MILcfPNN0d1dXV069YtTjvttOjYsWO8//77cdll\nl8WCBQuirq4uBg0aFIcddlgpfnw2cjvssEPsu+++8dJLL0W/fv1i3rx5cfXVV8eCBQuiVatWceKJ\nJ8Y//uM/RkTEo48+GnfccUfU1tZGly5d4qyzzoouXbrEPffcE7Nnz4633nor+vXrF4MGDbK+Kar6\n+vo46qij4oYbbohLL730Mz/njjvuiMceeyzKyspit912i5///OfRrFmzmDNnTlx11VUxf/786Nat\nW5x11lnx5JNPxrRp02L27NkxfPjw+OY3vxk33nhjTJkyJerq6qJ3795x2mmnRUVFRfzgBz+Iww47\nLCZNmhR33XVXHHXUUXHLLbfEokWL4vLLL4/+/fvH9OnTY8GCBXHSSSfFnnvu+YXz3H333fHQQw9F\nXV1ddOjQIf7t3/4tunXr9lX+dZJUy5Yt46ijjoqDDz44lixZstZ9gwYNittvvz06dOiw1seffPJJ\nXH755bHXXnvFX//616iqqopf/vKX8fvf/z7mzZsXvXv3jl/84hdRXV0dV155Zbz66qtRV1cXvXr1\nilNPPbXwhnRQbBMmTIijjz56rTOm/vmf/zn69OkTnTp1iohYZ/8xdOjQz30sP+igg+KII46IadOm\nxccffxwHHnhgHHLIIRFhv86XV1NTE3/7299iwIABhatsvv3tb8eOO+4Ybdq0iZEjR8bChQvj2GOP\njdGjR8exxx671t5i7ty5MW7cuFi8eHE0b948Tj755OjTp08cdthhMWbMmNhyyy3jySefjEsvvTTu\nv//+aNmyZdxzzz3xwQcfRLdu3WLZsmVx9tlnx6xZs6Jbt24xYsSI6Ny58+fu6998880YM2ZMbLvt\ntvHxxx/H8OHD1/r4uOOOW+sMQHsaNhRnAPKVqquri2eeeSYGDhwYe++9d7z44otRXV0dEREVFRXx\nzjvvRL9+/WLs2LExbNiwuPrqqwv3TZ06NU455ZS4/vrrY6eddoqJEyeudewFCxbEJZdcEmeffXZM\nmDAhdtttt8IzKXfffXfsueee8Zvf/CZuvPHGeOONN9bZrMNXpa6uLiorVz3/MmrUqNhvv/1iwoQJ\n8ctf/jIuuuiiqK6ujsWLF8fVV18dl1xySUycODG6d+8ed955Z0REVFZWxgsvvBAjR46MYcOGWd8U\nXW1tbQwaNCg++eSTmDJlyjr3T5kyJZ544om47rrrYvz48fHRRx/FQw89FPX19TF69Oj4yU9+Enfd\ndVf0798/xo0bF0OGDIktttgizjnnnBg4cGA8//zzMW3atLj55pvj1ltvjbfeeiuefvrpiFi1vhcv\nXhz33XdftGzZsvA9y8vLY86cOdGrV6+46qqr4qc//Wnh34HPm+fTTz+NO++8M2644YaYOHFiDBo0\nKKZOnfrV/CWyUVh9JkdFRUWjPn/1Ol79/8bqsz5GjBgRv/71r+PRRx+Njz/+OJ577rlYsGBB3Hbb\nbTFx4sTo0KFDvPnmmxvyR2EjtmTJkpg9e3YMHDhwnfu6dOlSCCz/d//xRY/lFRUVsXz58hgzZkyM\nGTMmbr311pg/f779OkVRWVkZAwYMiPPPPz8ef/zx+PjjjyMiYsstt4zy8vI49dRTo1mzZjF+/Pjo\n0qXLOnuLsWPHxsEHHxwTJ06MI444IsaNGxcREX369ClcoTZ9+vTYcccd4/XXX4+IiFdeeSV22223\niFgVsY8//viYNGlSbLnlloX9yOft6ysrKwvBcsyYMet8vCZ7GjYkAZCv1NSpU2PnnXeONm3aRMuW\nLaN3797x/PPPF+5v06ZN4XUb+vXrF++++258+umnERGxyy67xGabbRYREf3791/n8uH//u//jh49\nesR2220XEREHHnhgPPfcc4VnSF566aV47bXXolWrVnHBBRdEmzZtvoKfGP5XfX19vPnmm/Hkk09G\nv379Yv78+fHuu+/G9773vYiI2HnnnaNjx44xc+bMaNeuXdxzzz2FZ/V69+4d7733XuFY3/jGNwr3\nWd9sCOXl5XHCCSfETTfdVHiiZrUpU6bEAQccEG3atIny8vIYNGhQPPvsszF//vyYP39+4ZfIQw45\nJM4///x1jt2/f/+47rrronnz5oXXUVtzfX/rW9+KsrKydb6uZcuWhdei2mabbQqXFH/ePC1btoz6\n+vp4/PHHY9GiRfHd7343fvCDHxTt74iN27Jly2LixInRu3fvtWJ1Q9q2bVs403uLLbaIXXbZJZo3\nbx4tWrSIDh06xCeffBIdOnSId955J1588cWoqamJ4cOHR69evTbUj8JGrqqqKiJWvU7xameeeWb8\n+Mc/jh//+MdxxRVXFG5fc//R0GP56n8LOnbsGDvuuGO89tpr9usUzXnnnRcHHHBA/OEPf4ijjjoq\nhg8fXgjQn2XNvcXYsWNjv/32i4i199h9+vQpXPI+c+bM+P73vx+vvPJK4ePevXtHRESvXr1i6623\njoiIfffdN2bMmPGF+/qIVU8Y7bPPPoV5/u/Hq9nTsCG5BJiv1OTJk+PFF1+MwYMHR8Sqs0wWL14c\n3/rWtyIi1vpHvqKiIlq0aFHYlLRt27ZwX+vWrQu3r7Zw4cJ45ZVX4ogjjijc1qpVq1i4cGEMGTIk\nKioq4qqrropPPvkkhgwZUrgMATa0MWPGxFVXXRUREZ06dYqjjz46dt1113jjjTdi5cqVcdRRRxU+\nd/ny5bFw4cKoq6uL3/3udzFt2rSIWLU579KlS+HzNtlkk8KfrW82lN69e8f2228fv//972OrrbYq\n3L5w4cKYMmVKPPjggxGxahPbvn37WLhw4VqP45WVlYWzXdf06aefxr//+7/HO++8E2VlZfH+++8X\n/l2IWHt9r6lVq1aFP5eVlUVtbe0XztOiRYsYM2ZM/Pa3v42bb745dtpppzj11FOja9euf/9fChu1\n9957LwYNGhQRq14rs1evXnHmmWeu1zH+75mta17WW15eXrjk94QTToi77747Lrnkkvj2t78dxx9/\n/HqFRmisTTfdNCIiPvroo9h8880jIuLcc8+NmpqamDJlylpnGa35+NzQY/ln7d2XLFliv05RVFZW\nxuDBg2Pw4MGxYsWK+POf/xxXXHFFdO7cObbYYot1Pn/NtTtlypT4z//8z6ipqYna2tpY/b6ovXv3\njvvuuy+qqqqioqIidtttt7jmmmti3rx50blz58IeZ81Y3q5du6iqqoqFCxd+7r6+Xbt20a5du8LZ\ntKu/bs2PV7OnYUMSAPnKVFVVxcsvvxz33ntv4R30amtrY8iQIbFw4cKIiMKLtpaVlUV1dXWsWLGi\n8GC9aNGiwrEWL168zi+I7du3j9133/1z36FyyJAhMWTIkHj33XfjtNNOi1133TW+8Y1vbICfFNZ2\n+umnx/7777/O7e3bt49WrVrFHXfcsc59TzzxRDz77LNx9dVXR9u2beOxxx6LyZMnf+bxKyoqrG82\nmOHDh8dJJ50Uw4YNK9zWvn37OPLII+NHP/rRWp/7wQcfxKJFi6Kuri7Ky8ujuro63nvvvcKz5KtN\nmDAhIiKuueaaKC8vjyuvvPJLzfh580RE7LjjjnHeeedFTU1NTJw4Ma6//vq46KKLvtT3Y+PVrVu3\nRr1LY3l5eeEXyurq6nXOom2MffbZJ/bZZ5+oqqqKUaNGxUMPPeQd5NkgWrZsGT169IjHHnusEOZW\n77Nbt279uV/X0GP5okWLCnFi9d69efPm9ut8afPnz485c+bEXnvtFRERLVq0iP322y8ee+yxePvt\ntz8zAK720UcfxRVXXBE33nhjbL311vHhhx/GkUceGRERXbt2jaVLl8bUqVOjZ8+e0bVr1/jggw9i\n+vTphct/I1at59Wqqqpik002+cJ9/ezZsxv9s9nTsCG5BJivzJ/+9Kfo06dPIf5FrAoXffv2jT/9\n6U8REbFixYp45plnIiLiqaeeim222SbatWsXEated+H9998v3LfLLrusdfw99tgjXn311Xj33Xcj\nIuL111+P66+/PiIiLr744sKzl6ufvVm9MYdS6dy5c3Tr1i2eeOKJiFj1jN/o0aNj2bJlsWjRoth8\n882jbdu2sXjx4pg8eXIsW7bsM49jfbMhde3aNf7lX/4l7rrrrsJtAwYMiMceeyyWLl0aERF/+MMf\n4pFHHokuXbrE5ptvHo8++mhERDz44INxww03RMSqZ+pXn7m9aNGi2G677aK8vDzefvvt+Mtf/vK5\n67sxPm+emTNnxsiRI2PlypVRWVkZ3bt39/8GX4lOnToV3kX7qaee+sxL2r/IfffdF7fddlvU19dH\nmzZtolOnTtYuG9SwYcPirrvuigceeCA+/fTTWLFiRTz33HMxYcKE2H777T/zaxp6LH/sscciYtWZ\ns7NmzYoePXrYr1MUK1asiFGjRsWf//znqKuri/r6+vjrX/8ar7/+evTo0SMqKyujtrY2li9fvs7X\nLl68OFq1ahXdunWLurq6uO+++6Kuri5WrFgRERG77rpr3HfffYWXXdhqq63i4Ycfjt13371wjOnT\np8cHH3wQEf/7e+kX7evXhz0NG5IzAPnKTJ48+TOfyfjmN78Zd9xxR+y6667RrVu3mDFjRtxyyy1R\nVlYWp512WuHz9txzz7juuutizpw5sfnmm8eIESPWOk779u3jjDPOiAsvvDCWL18erVu3jpNOOiki\nIg499NAYN25cjBs3LsrKyuKAAw6InXbaacP+wNAII0aMiLFjx8att94a5eXlccghh0SrVq3in/7p\nn+Lxxx+Po48+Ojp16hTHHHNMXHDBBXHLLbcU3lVyNeubDe3www8vRL2IiL333jvmzJkTJ5xwQtTW\n1kb37t3j9NNPj7KyshgxYkRceeWVMX78+OjevXucddZZEbHqsX7UqFExdOjQOPTQQ+Oyyy6Lhx9+\nOHbeeef4+c9/HldccUXhddHW1+fN0759+9h8881j6NChUV5eHh06dIhTTz21KH8n8EWGDh0aY8eO\njS5dukS/fv2iffv2hTcMaYzvfOc7ccUVV8SRRx4ZZWVlsfPOO8eBBx64ASdmY9enT5+4+OKL47bb\nbotbbrkl6urqYvvtt4+hQ4fGd77znc/8moYeyzt16hQ/+9nPYsmSJXHcccdFx44dIyLs1/nSttpq\nq7jgggvitttuK7zMzup3lN5hhx0iYtWaPvzww2P06NFrfe12220X/fr1i6OPPjratWsXw4cPj5kz\nZ8aZZ54Z48aNi969e8cjjzwSPXv2jIiInj17xq233lpY17W1tdG/f/+49tpr1/m99PP29evDnoYN\nqaxeNqaJePvtt+NXv/pV/OY3v1nnvnvvvTfmzJnjQQ4AAJq4wYMHx/jx46NTp06lHgWA/88lwDQp\nX9SjtWoAAPh6sHcHaFoEQAAAAABIzCXAAAAAAJCYMwABAAAAIDEBEAAAAAASEwABAAAAIDEBEAAA\nAAASEwABAAAAIDEBEAAAAAASEwABAAAAIDEBEAAAAAASEwABAAAAIDEBEAAAAAASEwABAAAAIDEB\nEAAAAAASEwABAAAAIDEBEAAAAAASEwABAAAAIDEBEAAAAAASEwABAAAAIDEBEAAAAAASEwABAAAA\nIDEBEAAAAAASEwABAAAAIDEBEAAAAAASEwABAAAAIDEBEAAAAAASEwABAAAAIDEBEAAAAAASEwAB\nAAAAIDEBEAAAAAASEwABAAAAIDEBEAAAAAASEwABAAAAIDEBEAAAAAASEwABAAAAIDEBEAAAAAAS\nEwABAAAAIDEBEAAAAAASEwABAAAAIDEBEAAAAAASEwABAAAAIDEBEAAAAAASEwABAAAAIDEBEAAA\nAAASEwABAAAAIDEBEAAAAAASEwABAAAAIDEBEAAAAAASEwABAAAAIDEBEAAAAAASEwABAAAAIDEB\nEAAAAAASEwABAAAAIDEBEAAAAAASEwABAAAAIDEBEAAAAAASEwABAAAAIDEBEAAAAAASEwABAAAA\nIDEBEAAAAAASEwABAAAAIDEBEAAAAAASEwABAAAAIDEBEAAAAAASEwABAAAAILH/BwsnGpZjPbFq\nAAAAAElFTkSuQmCC\n", | |
"text/plain": "<PIL.PngImagePlugin.PngImageFile image mode=RGBA size=1280x800 at 0x7F97316DD550>" | |
}, | |
"execution_count": 3, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
] | |
} | |
], | |
"metadata": { | |
"kernelspec": { | |
"name": "python3", | |
"display_name": "Python 3", | |
"language": "python" | |
}, | |
"language_info": { | |
"name": "python", | |
"version": "3.6.3", | |
"mimetype": "text/x-python", | |
"codemirror_mode": { | |
"name": "ipython", | |
"version": 3 | |
}, | |
"pygments_lexer": "ipython3", | |
"nbconvert_exporter": "python", | |
"file_extension": ".py" | |
} | |
}, | |
"nbformat": 4, | |
"nbformat_minor": 2 | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment