Skip to content

Instantly share code, notes, and snippets.

@jwhendy
Created December 27, 2017 01:16
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jwhendy/8542f9f330796814d98cf94a3c0caf6d to your computer and use it in GitHub Desktop.
Save jwhendy/8542f9f330796814d98cf94a3c0caf6d to your computer and use it in GitHub Desktop.
Display the source blob
Display the rendered blob
Raw
{
"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