{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Pynamical: cobweb plots of the logistic map\n", "\n", "**Citation info**: Boeing, G. 2016. \"[Visual Analysis of Nonlinear Dynamical Systems: Chaos, Fractals, Self-Similarity and the Limits of Prediction](http://geoffboeing.com/publications/nonlinear-chaos-fractals-prediction/).\" *Systems*, 4 (4), 37. doi:10.3390/systems4040037.\n", "\n", "Pynamical documentation: http://pynamical.readthedocs.org" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### To draw these cobweb plots, pynamical does the following:\n", "\n", "1. Let x = 0.5\n", "1. Start on the x-axis at the point (x, 0)\n", "1. Draw a vertical line to the red function curve: this point has the coordinates (x, f(x))\n", "1. Draw a horizontal line from this point to the gray diagonal line: this point has the coordinates (f(x), f(x))\n", "1. Draw a vertical line from this point to the red function curve: this point has the coordinates (f(x), f(f(x)))\n", "1. Repeat steps 4 and 5 recursively one hundred times\n", "\n", "### To draw the red function curve and gray diagonal line, pynamical does the following:\n", "\n", "Run the logistic map once each for 1000 population values evenly spaced between 0 and 1. This gives us the results of the logistic equation (y values) across the entire range of possible population values (x values). The gray diagonal line is just a plot of y=x." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import glob\n", "import IPython.display as IPdisplay\n", "import matplotlib.pyplot as plt\n", "import numpy as np\n", "import pandas as pd\n", "from pynamical import cobweb_plot\n", "from PIL import Image\n", "%matplotlib inline" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Draw cobweb plots for several values of r" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "cobweb_plot(r=1.0)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "cobweb_plot(r=2.7)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "cobweb_plot(r=3.5)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "cobweb_plot(r=3.9)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Demonstrate a basin of attraction with three different initial values and same r" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "cobweb_plot(r=2.7, cobweb_x=0.1)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "cobweb_plot(r=2.7, cobweb_x=0.5)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "cobweb_plot(r=2.7, cobweb_x=0.9)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Now animate it" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# configure the animation: 70 rates between 0 and 3.999 plus 15 paused frames each at start and end\n", "save_folder = 'images/cobweb-animate'\n", "gif_filepath = 'images/animated-logistic-cobweb.gif'\n", "start_r = 0\n", "end_r = 3.999\n", "steps = 70\n", "rates = np.linspace(start_r, end_r, steps)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# create a plot for each growth rate\n", "for n, r in enumerate(rates):\n", " filename = '{:03d}'.format(n)\n", " title = 'Logistic Map Cobweb Plot, r={:.2f}'.format(r)\n", " cobweb_plot(r=r, filename=filename, folder=save_folder, dpi=90, title=title, show=False)\n", " plt.close()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# create a tuple of display durations, one for each frame\n", "first_last = 100 #show the first and last frames for 100 ms\n", "standard_duration = 10 #show all other frames for 10 ms\n", "durations = tuple([first_last] + [standard_duration] * (len(rates) - 2) + [first_last])" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# load all the static images into a list then save as an animated gif\n", "images = [Image.open(image) for image in sorted(glob.glob('{}/*.png'.format(save_folder)))]\n", "gif = images[0]\n", "gif.info['duration'] = durations #ms per frame\n", "gif.info['loop'] = 0 #how many times to loop (0=infinite)\n", "gif.save(fp=gif_filepath, format='gif', save_all=True, append_images=images[1:])" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# verify that the number of frames in the gif equals the number of image files and durations\n", "Image.open(gif_filepath).n_frames == len(images) == len(durations)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "IPdisplay.Image(url=gif_filepath)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## For more info:\n", " - [Read the journal article](http://geoffboeing.com/publications/nonlinear-chaos-fractals-prediction/)\n", " - [Pynamical documentation](http://pynamical.readthedocs.org)\n", " - [Chaos Theory and the Logistic Map](http://geoffboeing.com/2015/03/chaos-theory-logistic-map/)\n", " - [Visualizing Chaos and Randomness with Phase Diagrams](http://geoffboeing.com/2015/04/visualizing-chaos-and-randomness/)\n", " - [Animated 3D Plots in Python](http://geoffboeing.com/2015/04/animated-3d-plots-python/)" ] } ], "metadata": { "anaconda-cloud": {}, "kernelspec": { "display_name": "Python (pynamical)", "language": "python", "name": "pynamical" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.10.4" } }, "nbformat": 4, "nbformat_minor": 4 }