{ "cells": [ { "cell_type": "markdown", "metadata": { "colab_type": "text", "id": "uhCvamGJSVUH" }, "source": [ "Modified a part of the code of https://github.com/rlvaugh/Impractical_Python_Projects to work with Colab\n", "\n", "Removed some docstrings and variables or changed them to Japanese" ] }, { "cell_type": "markdown", "metadata": { "colab_type": "text", "id": "Z0QteHMjwcm4" }, "source": [ "# Chap1" ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 51 }, "colab_type": "code", "collapsed": false, "id": "E2N22pkNwyZt", "outputId": "6553cafa-f70b-4969-9ba4-891c6bb9da8b" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "ixnay on the ottenrayway\n" ] } ], "source": [ "import sys\n", "VOWELS = 'aeiouy'\n", "# word = input('なんか単語入れる')\n", "word = 'ixnay on the ottenray'\n", "if word[0] in VOWELS:\n", " pig_Latin = word + 'way'\n", "else:\n", " pig_Latin = word[1:] + word[0] + 'ay'\n", "print()\n", "print(\"{}\".format(pig_Latin), file=sys.stderr)" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 374 }, "colab_type": "code", "collapsed": false, "id": "LwmM4EEgybPu", "outputId": "9943e180-ea37-45d1-f3e7-9910e970fe7a" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "defaultdict(,\n", " {'a': ['a', 'a', 'a', 'a', 'a', 'a'],\n", " 'c': ['c'],\n", " 'd': ['d'],\n", " 'e': ['e', 'e', 'e', 'e', 'e', 'e', 'e', 'e', 'e', 'e', 'e', 'e'],\n", " 'f': ['f', 'f', 'f'],\n", " 'h': ['h', 'h', 'h', 'h', 'h', 'h'],\n", " 'i': ['i', 'i', 'i', 'i', 'i'],\n", " 'j': ['j'],\n", " 'k': ['k', 'k'],\n", " 'l': ['l'],\n", " 'n': ['n', 'n'],\n", " 'o': ['o', 'o', 'o', 'o'],\n", " 'p': ['p', 'p'],\n", " 'r': ['r', 'r', 'r'],\n", " 's': ['s', 's', 's', 's', 's', 's', 's', 's'],\n", " 't': ['t', 't', 't', 't', 't', 't', 't', 't', 't', 't', 't', 't', 't', 't'],\n", " 'u': ['u', 'u'],\n", " 'v': ['v'],\n", " 'w': ['w'],\n", " 'y': ['y']})\n" ] } ], "source": [ "import sys\n", "import pprint\n", "from collections import defaultdict\n", "import string\n", "text = 'Take the first step in faith. You don’t have to see the whole staircase, just take the first step.'\n", "ALPHABET = string.ascii_lowercase\n", "\n", "d = defaultdict(list)\n", "for character in text:\n", " c = character.lower()\n", " if c in ALPHABET:\n", " d[c].append(c)\n", "pprint.pprint(d, width=110)" ] }, { "cell_type": "markdown", "metadata": { "colab_type": "text", "id": "OMXmuTqlwdid" }, "source": [ "# Chap2" ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 34 }, "colab_type": "code", "collapsed": false, "id": "j4CwH-xN0Ml1", "outputId": "a4e4cbc4-02f1-47cb-83b2-cc7d7f0fcd3a" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "['my', 'pet', 'is', 'a', 'dog']\n" ] } ], "source": [ "# aとi以外を取り除く\n", "word_list = ['my', 'pet', 'is', 'a', 'dog', 'b', 'c']\n", "clean = []\n", "ok = ('a', 'i')\n", "for w in word_list:\n", " if len(w) > 1:\n", " clean.append(w)\n", " elif len(w) == 1 and w in ok:\n", " clean.append(w)\n", " else:\n", " continue\n", "\n", "print(clean)" ] }, { "cell_type": "markdown", "metadata": { "colab_type": "text", "id": "zHxaZhBcwdlZ" }, "source": [ "# Chap3\n" ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 224 }, "colab_type": "code", "collapsed": false, "id": "ziLIsXig2bqJ", "outputId": "ce51131a-18a3-4595-b240-303a0f270cb2" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "--2020-08-20 10:44:17-- https://raw.githubusercontent.com/en-wl/wordlist/master/alt12dicts/2of4brif.txt\n", "Resolving raw.githubusercontent.com (raw.githubusercontent.com)... 151.101.0.133, 151.101.64.133, 151.101.128.133, ...\n", "Connecting to raw.githubusercontent.com (raw.githubusercontent.com)|151.101.0.133|:443... connected.\n", "HTTP request sent, awaiting response... 200 OK\n", "Length: 563527 (550K) [text/plain]\n", "Saving to: ‘2of4brif.txt’\n", "\n", "\r", "2of4brif.txt 0%[ ] 0 --.-KB/s \r", "2of4brif.txt 100%[===================>] 550.32K --.-KB/s in 0.07s \n", "\n", "2020-08-20 10:44:17 (7.86 MB/s) - ‘2of4brif.txt’ saved [563527/563527]\n", "\n" ] } ], "source": [ "# 必要なファイルをダウンロードする\n", "!wget https://raw.githubusercontent.com/en-wl/wordlist/master/alt12dicts/2of4brif.txt" ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 1000 }, "colab_type": "code", "collapsed": false, "id": "GI0JaL941kuW", "outputId": "4b529873-bfd7-46a6-919b-be09529f0314" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "rv\n", "em\n", "dv\n", "or\n", "mo\n", "ol\n", "lm\n", "oo\n", "dl\n", "ve\n", "mt\n", "to\n", "rd\n", "lt\n", "dr\n", "et\n", "tr\n", "dm\n", "md\n", "vr\n", "lr\n", "me\n", "ev\n", "te\n", "ed\n", "ld\n", "ov\n", "de\n", "rm\n", "ot\n", "vo\n", "vl\n", "el\n", "od\n", "dt\n", "ro\n", "vd\n", "mv\n", "er\n", "lv\n", "tl\n", "vm\n", "vt\n", "mr\n", "le\n", "eo\n", "tm\n", "rl\n", "om\n", "rt\n", "lo\n", "re\n", "do\n", "oe\n", "td\n", "tv\n", "ml\n", "\n", "Number of digrams = 57\n", "\n", "digram frequency count:\n", "dv 78\n", "rd 955\n", "lo 2312\n", "do 928\n", "ed 7287\n", "me 2460\n", "em 1571\n", "te 6856\n", "to 2022\n", "ot 1335\n", "ev 648\n", "re 6650\n", "om 1773\n", "or 3970\n", "er 9875\n", "et 2240\n", "de 3861\n", "tl 656\n", "le 4935\n", "rm 812\n", "od 809\n", "ol 2070\n", "rt 1474\n", "ve 2755\n", "ov 796\n", "el 2591\n", "ro 3168\n", "lv 138\n", "tr 2586\n", "dl 547\n", "mo 1531\n", "dr 601\n", "tm 136\n", "dm 100\n", "lt 500\n", "oo 1474\n", "eo 365\n", "vo 419\n", "rl 394\n", "lm 156\n", "ml 58\n", "ld 457\n", "oe 267\n", "lr 29\n", "mr 16\n", "dt 22\n", "rv 287\n", "vr 18\n", "mv 7\n", "td 27\n", "mt 4\n", "md 5\n", "tv 4\n", "vl 2\n" ] } ], "source": [ "import sys\n", "import re\n", "from collections import defaultdict\n", "from itertools import permutations\n", "\n", "def load(file):\n", " with open(file) as f:\n", " txt = f.read().strip().split('\\n')\n", " txt = [x.lower() for x in txt]\n", " return txt\n", "\n", "word_list = load('2of4brif.txt')\n", "\n", "name = 'Voldemort' #(tmvoordle)\n", "name = name.lower()\n", "\n", "# generate unique letter pairs from name\n", "digrams = set()\n", "perms = {''.join(i) for i in permutations(name)}\n", "for perm in perms:\n", " for i in range(0, len(perm) - 1):\n", " digrams.add(perm[i] + perm[i + 1])\n", "print(*digrams, sep='\\n')\n", "print(\"\\nNumber of digrams = {}\\n\".format(len(digrams)))\n", "\n", "# use regular expressions to find repeating digrams in a word\n", "mapped = defaultdict(int)\n", "for word in word_list:\n", " word = word.lower()\n", " for digram in digrams:\n", " for m in re.finditer(digram, word):\n", " mapped[digram] += 1\n", "\n", "print(\"digram frequency count:\")\n", "count = 0\n", "for k in mapped:\n", " print(\"{} {}\".format(k, mapped[k]))" ] }, { "cell_type": "markdown", "metadata": { "colab_type": "text", "id": "HAlXmGOuwdnm" }, "source": [ "# Chap4\n" ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 1000 }, "colab_type": "code", "collapsed": false, "id": "2Rz3VDc1273r", "outputId": "36daf0f4-db2b-42c3-b849-e9916b4cbc65" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "pattern = .\n", "pattern数 = 384\n", "[-1, -2, -3, -4]\n", "[-1, -2, -3, 4]\n", "[-1, -2, 3, -4]\n", "[-1, -2, 3, 4]\n", "[-1, 2, -3, -4]\n", "[-1, 2, -3, 4]\n", "[-1, 2, 3, -4]\n", "[-1, 2, 3, 4]\n", "[1, -2, -3, -4]\n", "[1, -2, -3, 4]\n", "[1, -2, 3, -4]\n", "[1, -2, 3, 4]\n", "[1, 2, -3, -4]\n", "[1, 2, -3, 4]\n", "[1, 2, 3, -4]\n", "[1, 2, 3, 4]\n", "[-1, -2, -4, -3]\n", "[-1, -2, -4, 3]\n", "[-1, -2, 4, -3]\n", "[-1, -2, 4, 3]\n", "[-1, 2, -4, -3]\n", "[-1, 2, -4, 3]\n", "[-1, 2, 4, -3]\n", "[-1, 2, 4, 3]\n", "[1, -2, -4, -3]\n", "[1, -2, -4, 3]\n", "[1, -2, 4, -3]\n", "[1, -2, 4, 3]\n", "[1, 2, -4, -3]\n", "[1, 2, -4, 3]\n", "[1, 2, 4, -3]\n", "[1, 2, 4, 3]\n", "[-1, -3, -2, -4]\n", "[-1, -3, -2, 4]\n", "[-1, -3, 2, -4]\n", "[-1, -3, 2, 4]\n", "[-1, 3, -2, -4]\n", "[-1, 3, -2, 4]\n", "[-1, 3, 2, -4]\n", "[-1, 3, 2, 4]\n", "[1, -3, -2, -4]\n", "[1, -3, -2, 4]\n", "[1, -3, 2, -4]\n", "[1, -3, 2, 4]\n", "[1, 3, -2, -4]\n", "[1, 3, -2, 4]\n", "[1, 3, 2, -4]\n", "[1, 3, 2, 4]\n", "[-1, -3, -4, -2]\n", "[-1, -3, -4, 2]\n", "[-1, -3, 4, -2]\n", "[-1, -3, 4, 2]\n", "[-1, 3, -4, -2]\n", "[-1, 3, -4, 2]\n", "[-1, 3, 4, -2]\n", "[-1, 3, 4, 2]\n", "[1, -3, -4, -2]\n", "[1, -3, -4, 2]\n", "[1, -3, 4, -2]\n", "[1, -3, 4, 2]\n", "[1, 3, -4, -2]\n", "[1, 3, -4, 2]\n", "[1, 3, 4, -2]\n", "[1, 3, 4, 2]\n", "[-1, -4, -2, -3]\n", "[-1, -4, -2, 3]\n", "[-1, -4, 2, -3]\n", "[-1, -4, 2, 3]\n", "[-1, 4, -2, -3]\n", "[-1, 4, -2, 3]\n", "[-1, 4, 2, -3]\n", "[-1, 4, 2, 3]\n", "[1, -4, -2, -3]\n", "[1, -4, -2, 3]\n", "[1, -4, 2, -3]\n", "[1, -4, 2, 3]\n", "[1, 4, -2, -3]\n", "[1, 4, -2, 3]\n", "[1, 4, 2, -3]\n", "[1, 4, 2, 3]\n", "[-1, -4, -3, -2]\n", "[-1, -4, -3, 2]\n", "[-1, -4, 3, -2]\n", "[-1, -4, 3, 2]\n", "[-1, 4, -3, -2]\n", "[-1, 4, -3, 2]\n", "[-1, 4, 3, -2]\n", "[-1, 4, 3, 2]\n", "[1, -4, -3, -2]\n", "[1, -4, -3, 2]\n", "[1, -4, 3, -2]\n", "[1, -4, 3, 2]\n", "[1, 4, -3, -2]\n", "[1, 4, -3, 2]\n", "[1, 4, 3, -2]\n", "[1, 4, 3, 2]\n", "[-2, -1, -3, -4]\n", "[-2, -1, -3, 4]\n", "[-2, -1, 3, -4]\n", "[-2, -1, 3, 4]\n", "[-2, 1, -3, -4]\n", "[-2, 1, -3, 4]\n", "[-2, 1, 3, -4]\n", "[-2, 1, 3, 4]\n", "[2, -1, -3, -4]\n", "[2, -1, -3, 4]\n", "[2, -1, 3, -4]\n", "[2, -1, 3, 4]\n", "[2, 1, -3, -4]\n", "[2, 1, -3, 4]\n", "[2, 1, 3, -4]\n", "[2, 1, 3, 4]\n", "[-2, -1, -4, -3]\n", "[-2, -1, -4, 3]\n", "[-2, -1, 4, -3]\n", "[-2, -1, 4, 3]\n", "[-2, 1, -4, -3]\n", "[-2, 1, -4, 3]\n", "[-2, 1, 4, -3]\n", "[-2, 1, 4, 3]\n", "[2, -1, -4, -3]\n", "[2, -1, -4, 3]\n", "[2, -1, 4, -3]\n", "[2, -1, 4, 3]\n", "[2, 1, -4, -3]\n", "[2, 1, -4, 3]\n", "[2, 1, 4, -3]\n", "[2, 1, 4, 3]\n", "[-2, -3, -1, -4]\n", "[-2, -3, -1, 4]\n", "[-2, -3, 1, -4]\n", "[-2, -3, 1, 4]\n", "[-2, 3, -1, -4]\n", "[-2, 3, -1, 4]\n", "[-2, 3, 1, -4]\n", "[-2, 3, 1, 4]\n", "[2, -3, -1, -4]\n", "[2, -3, -1, 4]\n", "[2, -3, 1, -4]\n", "[2, -3, 1, 4]\n", "[2, 3, -1, -4]\n", "[2, 3, -1, 4]\n", "[2, 3, 1, -4]\n", "[2, 3, 1, 4]\n", "[-2, -3, -4, -1]\n", "[-2, -3, -4, 1]\n", "[-2, -3, 4, -1]\n", "[-2, -3, 4, 1]\n", "[-2, 3, -4, -1]\n", "[-2, 3, -4, 1]\n", "[-2, 3, 4, -1]\n", "[-2, 3, 4, 1]\n", "[2, -3, -4, -1]\n", "[2, -3, -4, 1]\n", "[2, -3, 4, -1]\n", "[2, -3, 4, 1]\n", "[2, 3, -4, -1]\n", "[2, 3, -4, 1]\n", "[2, 3, 4, -1]\n", "[2, 3, 4, 1]\n", "[-2, -4, -1, -3]\n", "[-2, -4, -1, 3]\n", "[-2, -4, 1, -3]\n", "[-2, -4, 1, 3]\n", "[-2, 4, -1, -3]\n", "[-2, 4, -1, 3]\n", "[-2, 4, 1, -3]\n", "[-2, 4, 1, 3]\n", "[2, -4, -1, -3]\n", "[2, -4, -1, 3]\n", "[2, -4, 1, -3]\n", "[2, -4, 1, 3]\n", "[2, 4, -1, -3]\n", "[2, 4, -1, 3]\n", "[2, 4, 1, -3]\n", "[2, 4, 1, 3]\n", "[-2, -4, -3, -1]\n", "[-2, -4, -3, 1]\n", "[-2, -4, 3, -1]\n", "[-2, -4, 3, 1]\n", "[-2, 4, -3, -1]\n", "[-2, 4, -3, 1]\n", "[-2, 4, 3, -1]\n", "[-2, 4, 3, 1]\n", "[2, -4, -3, -1]\n", "[2, -4, -3, 1]\n", "[2, -4, 3, -1]\n", "[2, -4, 3, 1]\n", "[2, 4, -3, -1]\n", "[2, 4, -3, 1]\n", "[2, 4, 3, -1]\n", "[2, 4, 3, 1]\n", "[-3, -1, -2, -4]\n", "[-3, -1, -2, 4]\n", "[-3, -1, 2, -4]\n", "[-3, -1, 2, 4]\n", "[-3, 1, -2, -4]\n", "[-3, 1, -2, 4]\n", "[-3, 1, 2, -4]\n", "[-3, 1, 2, 4]\n", "[3, -1, -2, -4]\n", "[3, -1, -2, 4]\n", "[3, -1, 2, -4]\n", "[3, -1, 2, 4]\n", "[3, 1, -2, -4]\n", "[3, 1, -2, 4]\n", "[3, 1, 2, -4]\n", "[3, 1, 2, 4]\n", "[-3, -1, -4, -2]\n", "[-3, -1, -4, 2]\n", "[-3, -1, 4, -2]\n", "[-3, -1, 4, 2]\n", "[-3, 1, -4, -2]\n", "[-3, 1, -4, 2]\n", "[-3, 1, 4, -2]\n", "[-3, 1, 4, 2]\n", "[3, -1, -4, -2]\n", "[3, -1, -4, 2]\n", "[3, -1, 4, -2]\n", "[3, -1, 4, 2]\n", "[3, 1, -4, -2]\n", "[3, 1, -4, 2]\n", "[3, 1, 4, -2]\n", "[3, 1, 4, 2]\n", "[-3, -2, -1, -4]\n", "[-3, -2, -1, 4]\n", "[-3, -2, 1, -4]\n", "[-3, -2, 1, 4]\n", "[-3, 2, -1, -4]\n", "[-3, 2, -1, 4]\n", "[-3, 2, 1, -4]\n", "[-3, 2, 1, 4]\n", "[3, -2, -1, -4]\n", "[3, -2, -1, 4]\n", "[3, -2, 1, -4]\n", "[3, -2, 1, 4]\n", "[3, 2, -1, -4]\n", "[3, 2, -1, 4]\n", "[3, 2, 1, -4]\n", "[3, 2, 1, 4]\n", "[-3, -2, -4, -1]\n", "[-3, -2, -4, 1]\n", "[-3, -2, 4, -1]\n", "[-3, -2, 4, 1]\n", "[-3, 2, -4, -1]\n", "[-3, 2, -4, 1]\n", "[-3, 2, 4, -1]\n", "[-3, 2, 4, 1]\n", "[3, -2, -4, -1]\n", "[3, -2, -4, 1]\n", "[3, -2, 4, -1]\n", "[3, -2, 4, 1]\n", "[3, 2, -4, -1]\n", "[3, 2, -4, 1]\n", "[3, 2, 4, -1]\n", "[3, 2, 4, 1]\n", "[-3, -4, -1, -2]\n", "[-3, -4, -1, 2]\n", "[-3, -4, 1, -2]\n", "[-3, -4, 1, 2]\n", "[-3, 4, -1, -2]\n", "[-3, 4, -1, 2]\n", "[-3, 4, 1, -2]\n", "[-3, 4, 1, 2]\n", "[3, -4, -1, -2]\n", "[3, -4, -1, 2]\n", "[3, -4, 1, -2]\n", "[3, -4, 1, 2]\n", "[3, 4, -1, -2]\n", "[3, 4, -1, 2]\n", "[3, 4, 1, -2]\n", "[3, 4, 1, 2]\n", "[-3, -4, -2, -1]\n", "[-3, -4, -2, 1]\n", "[-3, -4, 2, -1]\n", "[-3, -4, 2, 1]\n", "[-3, 4, -2, -1]\n", "[-3, 4, -2, 1]\n", "[-3, 4, 2, -1]\n", "[-3, 4, 2, 1]\n", "[3, -4, -2, -1]\n", "[3, -4, -2, 1]\n", "[3, -4, 2, -1]\n", "[3, -4, 2, 1]\n", "[3, 4, -2, -1]\n", "[3, 4, -2, 1]\n", "[3, 4, 2, -1]\n", "[3, 4, 2, 1]\n", "[-4, -1, -2, -3]\n", "[-4, -1, -2, 3]\n", "[-4, -1, 2, -3]\n", "[-4, -1, 2, 3]\n", "[-4, 1, -2, -3]\n", "[-4, 1, -2, 3]\n", "[-4, 1, 2, -3]\n", "[-4, 1, 2, 3]\n", "[4, -1, -2, -3]\n", "[4, -1, -2, 3]\n", "[4, -1, 2, -3]\n", "[4, -1, 2, 3]\n", "[4, 1, -2, -3]\n", "[4, 1, -2, 3]\n", "[4, 1, 2, -3]\n", "[4, 1, 2, 3]\n", "[-4, -1, -3, -2]\n", "[-4, -1, -3, 2]\n", "[-4, -1, 3, -2]\n", "[-4, -1, 3, 2]\n", "[-4, 1, -3, -2]\n", "[-4, 1, -3, 2]\n", "[-4, 1, 3, -2]\n", "[-4, 1, 3, 2]\n", "[4, -1, -3, -2]\n", "[4, -1, -3, 2]\n", "[4, -1, 3, -2]\n", "[4, -1, 3, 2]\n", "[4, 1, -3, -2]\n", "[4, 1, -3, 2]\n", "[4, 1, 3, -2]\n", "[4, 1, 3, 2]\n", "[-4, -2, -1, -3]\n", "[-4, -2, -1, 3]\n", "[-4, -2, 1, -3]\n", "[-4, -2, 1, 3]\n", "[-4, 2, -1, -3]\n", "[-4, 2, -1, 3]\n", "[-4, 2, 1, -3]\n", "[-4, 2, 1, 3]\n", "[4, -2, -1, -3]\n", "[4, -2, -1, 3]\n", "[4, -2, 1, -3]\n", "[4, -2, 1, 3]\n", "[4, 2, -1, -3]\n", "[4, 2, -1, 3]\n", "[4, 2, 1, -3]\n", "[4, 2, 1, 3]\n", "[-4, -2, -3, -1]\n", "[-4, -2, -3, 1]\n", "[-4, -2, 3, -1]\n", "[-4, -2, 3, 1]\n", "[-4, 2, -3, -1]\n", "[-4, 2, -3, 1]\n", "[-4, 2, 3, -1]\n", "[-4, 2, 3, 1]\n", "[4, -2, -3, -1]\n", "[4, -2, -3, 1]\n", "[4, -2, 3, -1]\n", "[4, -2, 3, 1]\n", "[4, 2, -3, -1]\n", "[4, 2, -3, 1]\n", "[4, 2, 3, -1]\n", "[4, 2, 3, 1]\n", "[-4, -3, -1, -2]\n", "[-4, -3, -1, 2]\n", "[-4, -3, 1, -2]\n", "[-4, -3, 1, 2]\n", "[-4, 3, -1, -2]\n", "[-4, 3, -1, 2]\n", "[-4, 3, 1, -2]\n", "[-4, 3, 1, 2]\n", "[4, -3, -1, -2]\n", "[4, -3, -1, 2]\n", "[4, -3, 1, -2]\n", "[4, -3, 1, 2]\n", "[4, 3, -1, -2]\n", "[4, 3, -1, 2]\n", "[4, 3, 1, -2]\n", "[4, 3, 1, 2]\n", "[-4, -3, -2, -1]\n", "[-4, -3, -2, 1]\n", "[-4, -3, 2, -1]\n", "[-4, -3, 2, 1]\n", "[-4, 3, -2, -1]\n", "[-4, 3, -2, 1]\n", "[-4, 3, 2, -1]\n", "[-4, 3, 2, 1]\n", "[4, -3, -2, -1]\n", "[4, -3, -2, 1]\n", "[4, -3, 2, -1]\n", "[4, -3, 2, 1]\n", "[4, 3, -2, -1]\n", "[4, 3, -2, 1]\n", "[4, 3, 2, -1]\n", "[4, 3, 2, 1]\n" ] } ], "source": [ "from math import factorial\n", "from itertools import permutations, product\n", "# nの設定\n", "pattern_num= 4\n", "p = [x for x in range(1, pattern_num+1)]\n", "print(\"pattern = {}\".format(c))\n", "\n", "# 負の順列も考慮するため\n", "def perms(columns):\n", " results = []\n", " for perm in permutations(columns):\n", " for signs in product([-1, 1], repeat=len(columns)):\n", " results.append([i*sign for i, sign in zip(perm, signs)])\n", " return results\n", "\n", "comb = perms(p)\n", "print(\"pattern数 = {}\".format(len(comb)))\n", "print(*comb, sep=\"\\n\") # comment-out for num_cols > 4!" ] }, { "cell_type": "markdown", "metadata": { "colab_type": "text", "id": "Yp8RluDTwdqC" }, "source": [ "# Chap5\n" ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 224 }, "colab_type": "code", "collapsed": false, "id": "DdSkUzn73ddD", "outputId": "882a4e62-3103-4459-8d09-ef3310eec76b" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "--2020-08-20 10:44:29-- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/master/Chapter_5/colchester_message.txt\n", "Resolving raw.githubusercontent.com (raw.githubusercontent.com)... 151.101.0.133, 151.101.64.133, 151.101.128.133, ...\n", "Connecting to raw.githubusercontent.com (raw.githubusercontent.com)|151.101.0.133|:443... connected.\n", "HTTP request sent, awaiting response... 200 OK\n", "Length: 224 [text/plain]\n", "Saving to: ‘colchester_message.txt’\n", "\n", "\r", "colchester_message. 0%[ ] 0 --.-KB/s \r", "colchester_message. 100%[===================>] 224 --.-KB/s in 0s \n", "\n", "2020-08-20 10:44:29 (16.0 MB/s) - ‘colchester_message.txt’ saved [224/224]\n", "\n" ] } ], "source": [ "!wget https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/master/Chapter_5/colchester_message.txt" ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 204 }, "colab_type": "code", "collapsed": false, "id": "o8z7GKZu3xUJ", "outputId": "e2cc6ac0-7ec1-4739-d20d-ec889f10131e" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "ORIGINAL MESSAGE = Sir John: Odd and too hard, your lot. Still, we will band together and, like you, persevere. \n", "Who else could love their enemies, stand firm when all others fail, hate and despair? \n", "While we all can, let us feel hope. -R.T.\n", "\n", "1単語目の1文字目\n", "SJOathylSwwbtalypWecltesfwaofhadWwaclufh-\n", "2単語目の2文字目\n", "onaoeanohohthtaeeaso\n", "3単語目の3文字目\n", "drinkovaltine\n" ] } ], "source": [ "import sys\n", "\n", "def load(file):\n", " with open(file) as f:\n", " return f.read().strip()\n", "\n", "# filename = input()\n", "message = load('colchester_message.txt')\n", "\n", "# check loaded message & # of lines\n", "print(\"\\nORIGINAL MESSAGE = {}\\n\".format(message))\n", "\n", "# convert message to list and get length\n", "message = message.split()\n", "end = len(message)\n", "# increment = int(input())\n", "increment = 3 \n", "for i in range(1, increment + 1):\n", " msg = list() \n", " print('{}単語目の{}文字目'.format(i, i))\n", " count = i - 1\n", " location = i - 1\n", " for index, word in enumerate(message):\n", " if index == count:\n", " if location < len(word):\n", " msg.append(word[location])\n", " count += i\n", " else:\n", " print(\"Interval doesn't work\", file=sys.stderr)\n", " \n", " print(*msg, sep='')\n", " " ] }, { "cell_type": "markdown", "metadata": { "colab_type": "text", "id": "Y3VrepSkwdsb" }, "source": [ "# Chap6" ] }, { "cell_type": "code", "execution_count": 9, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 309 }, "colab_type": "code", "collapsed": false, "id": "4E8rdn3uCsSm", "outputId": "80ce154d-3485-48d6-ce9e-e20937102faf" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "--2020-08-20 10:44:37-- https://github.com/rlvaugh/Impractical_Python_Projects/raw/master/Chapter_6/realMessageChallenge.docx\n", "Resolving github.com (github.com)... 140.82.114.3\n", "Connecting to github.com (github.com)|140.82.114.3|:443... connected.\n", "HTTP request sent, awaiting response... 302 Found\n", "Location: https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/master/Chapter_6/realMessageChallenge.docx [following]\n", "--2020-08-20 10:44:37-- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/master/Chapter_6/realMessageChallenge.docx\n", "Resolving raw.githubusercontent.com (raw.githubusercontent.com)... 151.101.0.133, 151.101.64.133, 151.101.128.133, ...\n", "Connecting to raw.githubusercontent.com (raw.githubusercontent.com)|151.101.0.133|:443... connected.\n", "HTTP request sent, awaiting response... 200 OK\n", "Length: 13448 (13K) [application/octet-stream]\n", "Saving to: ‘realMessageChallenge.docx’\n", "\n", "\r", " realMessa 0%[ ] 0 --.-KB/s \r", "realMessageChalleng 100%[===================>] 13.13K --.-KB/s in 0.01s \n", "\n", "2020-08-20 10:44:37 (1.06 MB/s) - ‘realMessageChallenge.docx’ saved [13448/13448]\n", "\n" ] } ], "source": [ "!wget https://github.com/rlvaugh/Impractical_Python_Projects/raw/master/Chapter_6/realMessageChallenge.docx" ] }, { "cell_type": "code", "execution_count": 10, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 309 }, "colab_type": "code", "collapsed": false, "id": "J69xXgH1CvH6", "outputId": "30899ba4-1dc1-405d-a4b4-1b39aabcc1f6" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "--2020-08-20 10:44:40-- https://github.com/rlvaugh/Impractical_Python_Projects/raw/master/Chapter_6/fakeMessage.docx\n", "Resolving github.com (github.com)... 140.82.112.4\n", "Connecting to github.com (github.com)|140.82.112.4|:443... connected.\n", "HTTP request sent, awaiting response... 302 Found\n", "Location: https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/master/Chapter_6/fakeMessage.docx [following]\n", "--2020-08-20 10:44:40-- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/master/Chapter_6/fakeMessage.docx\n", "Resolving raw.githubusercontent.com (raw.githubusercontent.com)... 151.101.0.133, 151.101.64.133, 151.101.128.133, ...\n", "Connecting to raw.githubusercontent.com (raw.githubusercontent.com)|151.101.0.133|:443... connected.\n", "HTTP request sent, awaiting response... 200 OK\n", "Length: 12324 (12K) [application/octet-stream]\n", "Saving to: ‘fakeMessage.docx’\n", "\n", "\r", "fakeMessage.docx 0%[ ] 0 --.-KB/s \r", "fakeMessage.docx 100%[===================>] 12.04K --.-KB/s in 0s \n", "\n", "2020-08-20 10:44:40 (105 MB/s) - ‘fakeMessage.docx’ saved [12324/12324]\n", "\n" ] } ], "source": [ "!wget https://github.com/rlvaugh/Impractical_Python_Projects/raw/master/Chapter_6/fakeMessage.docx" ] }, { "cell_type": "code", "execution_count": 11, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 224 }, "colab_type": "code", "collapsed": false, "id": "HQyslYLwDP2A", "outputId": "965a14cd-20bf-48c0-de42-0e7d0491ed7e" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Collecting python-docx\n", "\u001b[?25l Downloading https://files.pythonhosted.org/packages/e4/83/c66a1934ed5ed8ab1dbb9931f1779079f8bca0f6bbc5793c06c4b5e7d671/python-docx-0.8.10.tar.gz (5.5MB)\n", "\u001b[K |████████████████████████████████| 5.5MB 2.8MB/s \n", "\u001b[?25hRequirement already satisfied: lxml>=2.3.2 in /usr/local/lib/python3.6/dist-packages (from python-docx) (4.2.6)\n", "Building wheels for collected packages: python-docx\n", " Building wheel for python-docx (setup.py) ... \u001b[?25l\u001b[?25hdone\n", " Created wheel for python-docx: filename=python_docx-0.8.10-cp36-none-any.whl size=184491 sha256=8f5cb487734e318edc8a42f9a1bdc9603bbae74335564c9515995440f839270b\n", " Stored in directory: /root/.cache/pip/wheels/18/0b/a0/1dd62ff812c857c9e487f27d80d53d2b40531bec1acecfa47b\n", "Successfully built python-docx\n", "Installing collected packages: python-docx\n", "Successfully installed python-docx-0.8.10\n" ] } ], "source": [ "!pip install python-docx" ] }, { "cell_type": "code", "execution_count": 12, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 256 }, "colab_type": "code", "collapsed": false, "id": "JtC2wmq38veT", "outputId": "4c05c250-4f30-4e6e-9101-e739d4032611" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "Number of blank lines in fake message = 8\n", "Number of lines in real message = 24\n", "\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "Fake message needs 16 more blank lines.\n" ] }, { "ename": "SystemExit", "evalue": "ignored", "output_type": "error", "traceback": [ "An exception has occurred, use %tb to see the full traceback.\n", "\u001b[0;31mSystemExit\u001b[0m\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "/usr/local/lib/python3.6/dist-packages/IPython/core/interactiveshell.py:2890: UserWarning: To exit: use 'exit', 'quit', or Ctrl-D.\n", " warn(\"To exit: use 'exit', 'quit', or Ctrl-D.\", stacklevel=1)\n" ] } ], "source": [ "import sys\n", "import docx\n", "from docx.shared import RGBColor, Pt\n", "\n", "fake_text = docx.Document('fakeMessage.docx')\n", "fake_list = []\n", "for paragraph in fake_text.paragraphs:\n", " fake_list.append(paragraph.text)\n", "\n", "real_text = docx.Document('realMessageChallenge.docx')\n", "real_list = []\n", "for paragraph in real_text.paragraphs:\n", " if len(paragraph.text) != 0:\n", " real_list.append(paragraph.text)\n", "\n", "def line_limit(fake, real):\n", " num_blanks = 0\n", " num_real = 0\n", " for line in fake:\n", " if line == '':\n", " num_blanks += 1\n", " num_real = len(real)\n", " diff = num_real - num_blanks\n", " print(\"\\nNumber of blank lines in fake message = {}\".format(num_blanks))\n", " print(\"Number of lines in real message = {}\\n\".format(num_real))\n", " if num_real > num_blanks:\n", " print(\"Fake message needs {} more blank lines.\"\n", " .format(diff), file=sys.stderr)\n", " sys.exit()\n", "\n", "line_limit(fake_list, real_list)\n", " \n", "# load template that sets style, font, margins, etc.\n", "doc = docx.Document('template.docx')\n", "\n", "# add letterhead\n", "doc.add_heading('Morland Holmes', 0)\n", "subtitle = doc.add_heading('Global Consultanting & Negotiations', 1)\n", "subtitle.alignment = 1 \n", "doc.add_heading('', 1)\n", "doc.add_paragraph('December 17, 2015')\n", "doc.add_paragraph('')\n", "\n", "def set_spacing(paragraph):\n", " \"\"\"Use docx to set line spacing between paragraphs.\"\"\"\n", " paragraph_format = paragraph.paragraph_format\n", " paragraph_format.space_before = Pt(0)\n", " paragraph_format.space_after = Pt(0)\n", "\n", "length_real = len(real_list)\n", "count_real = 0 # index of current line in real (hidden) message\n", "\n", "# interleave real and fake message lines\n", "for line in fake_list:\n", " if count_real < length_real and line == \"\":\n", " paragraph = doc.add_paragraph(real_list[count_real])\n", " paragraph_index = len(doc.paragraphs) - 1\n", " \n", " # set real message color to white\n", " run = doc.paragraphs[paragraph_index].runs[0] \n", " font = run.font\n", " font.color.rgb = RGBColor(255, 255, 255) # make it red to test\n", " count_real += 1\n", " \n", " else:\n", " paragraph = doc.add_paragraph(line)\n", " \n", " set_spacing(paragraph)\n", "\n", "doc.save('ciphertext_message_letterhead.docx')\n", "\n", "print(\"Done\")" ] }, { "cell_type": "markdown", "metadata": { "colab_type": "text", "id": "cxeupGCWwd1I" }, "source": [ "# Chap7" ] }, { "cell_type": "code", "execution_count": 13, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 1000 }, "colab_type": "code", "collapsed": false, "id": "t_JmfAhqFi-J", "outputId": "da63540f-d320-4256-b31c-cc31dabf6648" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "initial population weights = [372, 338, 393, 490, 446, 525, 237, 351, 322, 505, 448, 312, 379, 505, 234, 439, 405, 358, 325, 226]\n", "initial population fitness = 0.00761\n", "number to retain = 20\n", "Generation 0 fitness = 0.0076\n", "Generation 1 fitness = 0.0086\n", "Generation 2 fitness = 0.0093\n", "Generation 3 fitness = 0.0097\n", "Generation 4 fitness = 0.0100\n", "Generation 5 fitness = 0.0101\n", "Generation 6 fitness = 0.0103\n", "Generation 7 fitness = 0.0104\n", "Generation 8 fitness = 0.0105\n", "Generation 9 fitness = 0.0104\n", "Generation 10 fitness = 0.0105\n", "Generation 11 fitness = 0.0104\n", "Generation 12 fitness = 0.0105\n", "Generation 13 fitness = 0.0105\n", "Generation 14 fitness = 0.0106\n", "Generation 15 fitness = 0.0107\n", "Generation 16 fitness = 0.0110\n", "Generation 17 fitness = 0.0112\n", "Generation 18 fitness = 0.0114\n", "Generation 19 fitness = 0.0115\n", "Generation 20 fitness = 0.0116\n", "Generation 21 fitness = 0.0116\n", "Generation 22 fitness = 0.0117\n", "Generation 23 fitness = 0.0116\n", "Generation 24 fitness = 0.0117\n", "Generation 25 fitness = 0.0117\n", "Generation 26 fitness = 0.0118\n", "Generation 27 fitness = 0.0122\n", "Generation 28 fitness = 0.0126\n", "Generation 29 fitness = 0.0129\n", "Generation 30 fitness = 0.0131\n", "Generation 31 fitness = 0.0132\n", "Generation 32 fitness = 0.0134\n", "Generation 33 fitness = 0.0134\n", "Generation 34 fitness = 0.0135\n", "Generation 35 fitness = 0.0135\n", "Generation 36 fitness = 0.0135\n", "Generation 37 fitness = 0.0136\n", "Generation 38 fitness = 0.0136\n", "Generation 39 fitness = 0.0136\n", "Generation 40 fitness = 0.0137\n", "Generation 41 fitness = 0.0143\n", "Generation 42 fitness = 0.0148\n", "Generation 43 fitness = 0.0151\n", "Generation 44 fitness = 0.0154\n", "Generation 45 fitness = 0.0156\n", "Generation 46 fitness = 0.0158\n", "Generation 47 fitness = 0.0158\n", "Generation 48 fitness = 0.0159\n", "Generation 49 fitness = 0.0160\n", "Generation 50 fitness = 0.0160\n", "Generation 51 fitness = 0.0160\n", "Generation 52 fitness = 0.0160\n", "Generation 53 fitness = 0.0159\n", "Generation 54 fitness = 0.0160\n", "Generation 55 fitness = 0.0160\n", "Generation 56 fitness = 0.0160\n", "Generation 57 fitness = 0.0160\n", "Generation 58 fitness = 0.0160\n", "Generation 59 fitness = 0.0160\n", "Generation 60 fitness = 0.0160\n", "Generation 61 fitness = 0.0160\n", "Generation 62 fitness = 0.0160\n", "Generation 63 fitness = 0.0160\n", "Generation 64 fitness = 0.0160\n", "Generation 65 fitness = 0.0162\n", "Generation 66 fitness = 0.0168\n", "Generation 67 fitness = 0.0174\n", "Generation 68 fitness = 0.0180\n", "Generation 69 fitness = 0.0183\n", "Generation 70 fitness = 0.0186\n", "Generation 71 fitness = 0.0188\n", "Generation 72 fitness = 0.0191\n", "Generation 73 fitness = 0.0194\n", "Generation 74 fitness = 0.0201\n", "Generation 75 fitness = 0.0206\n", "Generation 76 fitness = 0.0211\n", "Generation 77 fitness = 0.0213\n", "Generation 78 fitness = 0.0215\n", "Generation 79 fitness = 0.0216\n", "Generation 80 fitness = 0.0216\n", "Generation 81 fitness = 0.0217\n", "Generation 82 fitness = 0.0217\n", "Generation 83 fitness = 0.0217\n", "Generation 84 fitness = 0.0216\n", "Generation 85 fitness = 0.0217\n", "Generation 86 fitness = 0.0217\n", "Generation 87 fitness = 0.0217\n", "Generation 88 fitness = 0.0217\n", "Generation 89 fitness = 0.0218\n", "Generation 90 fitness = 0.0223\n", "Generation 91 fitness = 0.0230\n", "Generation 92 fitness = 0.0235\n", "Generation 93 fitness = 0.0239\n", "Generation 94 fitness = 0.0244\n", "Generation 95 fitness = 0.0253\n", "Generation 96 fitness = 0.0260\n", "Generation 97 fitness = 0.0265\n", "Generation 98 fitness = 0.0272\n", "Generation 99 fitness = 0.0277\n", "Generation 100 fitness = 0.0286\n", "Generation 101 fitness = 0.0294\n", "Generation 102 fitness = 0.0301\n", "Generation 103 fitness = 0.0307\n", "Generation 104 fitness = 0.0320\n", "Generation 105 fitness = 0.0331\n", "Generation 106 fitness = 0.0340\n", "Generation 107 fitness = 0.0347\n", "Generation 108 fitness = 0.0350\n", "Generation 109 fitness = 0.0353\n", "Generation 110 fitness = 0.0356\n", "Generation 111 fitness = 0.0360\n", "Generation 112 fitness = 0.0362\n", "Generation 113 fitness = 0.0364\n", "Generation 114 fitness = 0.0367\n", "Generation 115 fitness = 0.0372\n", "Generation 116 fitness = 0.0378\n", "Generation 117 fitness = 0.0388\n", "Generation 118 fitness = 0.0395\n", "Generation 119 fitness = 0.0403\n", "Generation 120 fitness = 0.0410\n", "Generation 121 fitness = 0.0414\n", "Generation 122 fitness = 0.0415\n", "Generation 123 fitness = 0.0418\n", "Generation 124 fitness = 0.0421\n", "Generation 125 fitness = 0.0422\n", "Generation 126 fitness = 0.0424\n", "Generation 127 fitness = 0.0428\n", "Generation 128 fitness = 0.0445\n", "Generation 129 fitness = 0.0460\n", "Generation 130 fitness = 0.0470\n", "Generation 131 fitness = 0.0482\n", "Generation 132 fitness = 0.0499\n", "Generation 133 fitness = 0.0523\n", "Generation 134 fitness = 0.0535\n", "Generation 135 fitness = 0.0547\n", "Generation 136 fitness = 0.0552\n", "Generation 137 fitness = 0.0570\n", "Generation 138 fitness = 0.0583\n", "Generation 139 fitness = 0.0597\n", "Generation 140 fitness = 0.0602\n", "Generation 141 fitness = 0.0613\n", "Generation 142 fitness = 0.0616\n", "Generation 143 fitness = 0.0624\n", "Generation 144 fitness = 0.0641\n", "Generation 145 fitness = 0.0663\n", "Generation 146 fitness = 0.0683\n", "Generation 147 fitness = 0.0696\n", "Generation 148 fitness = 0.0701\n", "Generation 149 fitness = 0.0707\n", "Generation 150 fitness = 0.0707\n", "Generation 151 fitness = 0.0709\n", "Generation 152 fitness = 0.0712\n", "Generation 153 fitness = 0.0713\n", "Generation 154 fitness = 0.0711\n", "Generation 155 fitness = 0.0714\n", "Generation 156 fitness = 0.0716\n", "Generation 157 fitness = 0.0717\n", "Generation 158 fitness = 0.0732\n", "Generation 159 fitness = 0.0751\n", "Generation 160 fitness = 0.0768\n", "Generation 161 fitness = 0.0781\n", "Generation 162 fitness = 0.0789\n", "Generation 163 fitness = 0.0801\n", "Generation 164 fitness = 0.0806\n", "Generation 165 fitness = 0.0809\n", "Generation 166 fitness = 0.0811\n", "Generation 167 fitness = 0.0812\n", "Generation 168 fitness = 0.0814\n", "Generation 169 fitness = 0.0818\n", "Generation 170 fitness = 0.0826\n", "Generation 171 fitness = 0.0832\n", "Generation 172 fitness = 0.0855\n", "Generation 173 fitness = 0.0875\n", "Generation 174 fitness = 0.0895\n", "Generation 175 fitness = 0.0908\n", "Generation 176 fitness = 0.0919\n", "Generation 177 fitness = 0.0926\n", "Generation 178 fitness = 0.0936\n", "Generation 179 fitness = 0.0959\n", "Generation 180 fitness = 0.0992\n", "Generation 181 fitness = 0.1021\n", "Generation 182 fitness = 0.1040\n", "Generation 183 fitness = 0.1060\n", "Generation 184 fitness = 0.1071\n", "Generation 185 fitness = 0.1077\n", "Generation 186 fitness = 0.1081\n", "Generation 187 fitness = 0.1085\n", "Generation 188 fitness = 0.1092\n", "Generation 189 fitness = 0.1102\n", "Generation 190 fitness = 0.1109\n", "Generation 191 fitness = 0.1115\n", "Generation 192 fitness = 0.1114\n", "Generation 193 fitness = 0.1122\n", "Generation 194 fitness = 0.1117\n", "Generation 195 fitness = 0.1119\n", "Generation 196 fitness = 0.1124\n", "Generation 197 fitness = 0.1127\n", "Generation 198 fitness = 0.1133\n", "Generation 199 fitness = 0.1162\n", "Generation 200 fitness = 0.1199\n", "Generation 201 fitness = 0.1232\n", "Generation 202 fitness = 0.1249\n", "Generation 203 fitness = 0.1263\n", "Generation 204 fitness = 0.1283\n", "Generation 205 fitness = 0.1297\n", "Generation 206 fitness = 0.1346\n", "Generation 207 fitness = 0.1388\n", "Generation 208 fitness = 0.1439\n", "Generation 209 fitness = 0.1464\n", "Generation 210 fitness = 0.1484\n", "Generation 211 fitness = 0.1496\n", "Generation 212 fitness = 0.1503\n", "Generation 213 fitness = 0.1509\n", "Generation 214 fitness = 0.1512\n", "Generation 215 fitness = 0.1507\n", "Generation 216 fitness = 0.1515\n", "Generation 217 fitness = 0.1517\n", "Generation 218 fitness = 0.1522\n", "Generation 219 fitness = 0.1562\n", "Generation 220 fitness = 0.1608\n", "Generation 221 fitness = 0.1643\n", "Generation 222 fitness = 0.1681\n", "Generation 223 fitness = 0.1712\n", "Generation 224 fitness = 0.1723\n", "Generation 225 fitness = 0.1746\n", "Generation 226 fitness = 0.1749\n", "Generation 227 fitness = 0.1765\n", "Generation 228 fitness = 0.1774\n", "Generation 229 fitness = 0.1795\n", "Generation 230 fitness = 0.1862\n", "Generation 231 fitness = 0.1984\n", "Generation 232 fitness = 0.2150\n", "Generation 233 fitness = 0.2272\n", "Generation 234 fitness = 0.2324\n", "Generation 235 fitness = 0.2362\n", "Generation 236 fitness = 0.2387\n", "Generation 237 fitness = 0.2401\n", "Generation 238 fitness = 0.2403\n", "Generation 239 fitness = 0.2411\n", "Generation 240 fitness = 0.2427\n", "Generation 241 fitness = 0.2431\n", "Generation 242 fitness = 0.2424\n", "Generation 243 fitness = 0.2417\n", "Generation 244 fitness = 0.2437\n", "Generation 245 fitness = 0.2438\n", "Generation 246 fitness = 0.2420\n", "Generation 247 fitness = 0.2439\n", "Generation 248 fitness = 0.2424\n", "Generation 249 fitness = 0.2430\n", "Generation 250 fitness = 0.2441\n", "Generation 251 fitness = 0.2452\n", "Generation 252 fitness = 0.2456\n", "Generation 253 fitness = 0.2481\n", "Generation 254 fitness = 0.2572\n", "Generation 255 fitness = 0.2652\n", "Generation 256 fitness = 0.2752\n", "Generation 257 fitness = 0.2815\n", "Generation 258 fitness = 0.2853\n", "Generation 259 fitness = 0.2884\n", "Generation 260 fitness = 0.2916\n", "Generation 261 fitness = 0.2930\n", "Generation 262 fitness = 0.2931\n", "Generation 263 fitness = 0.2941\n", "Generation 264 fitness = 0.2946\n", "Generation 265 fitness = 0.2948\n", "Generation 266 fitness = 0.2949\n", "Generation 267 fitness = 0.2939\n", "Generation 268 fitness = 0.2951\n", "Generation 269 fitness = 0.2952\n", "Generation 270 fitness = 0.2954\n", "Generation 271 fitness = 0.2963\n", "Generation 272 fitness = 0.2979\n", "Generation 273 fitness = 0.2990\n", "Generation 274 fitness = 0.2984\n", "Generation 275 fitness = 0.2993\n", "Generation 276 fitness = 0.3002\n", "Generation 277 fitness = 0.2991\n", "Generation 278 fitness = 0.3005\n", "Generation 279 fitness = 0.3000\n", "Generation 280 fitness = 0.3007\n", "Generation 281 fitness = 0.3007\n", "Generation 282 fitness = 0.3007\n", "Generation 283 fitness = 0.3007\n", "Generation 284 fitness = 0.3007\n", "Generation 285 fitness = 0.3002\n", "Generation 286 fitness = 0.3008\n", "Generation 287 fitness = 0.3013\n", "Generation 288 fitness = 0.3034\n", "Generation 289 fitness = 0.3125\n", "Generation 290 fitness = 0.3241\n", "Generation 291 fitness = 0.3354\n", "Generation 292 fitness = 0.3436\n", "Generation 293 fitness = 0.3535\n", "Generation 294 fitness = 0.3621\n", "Generation 295 fitness = 0.3691\n", "Generation 296 fitness = 0.3736\n", "Generation 297 fitness = 0.3766\n", "Generation 298 fitness = 0.3791\n", "Generation 299 fitness = 0.3802\n", "Generation 300 fitness = 0.3828\n", "Generation 301 fitness = 0.3871\n", "Generation 302 fitness = 0.3992\n", "Generation 303 fitness = 0.4133\n", "Generation 304 fitness = 0.4202\n", "Generation 305 fitness = 0.4272\n", "Generation 306 fitness = 0.4303\n", "Generation 307 fitness = 0.4336\n", "Generation 308 fitness = 0.4363\n", "Generation 309 fitness = 0.4379\n", "Generation 310 fitness = 0.4373\n", "Generation 311 fitness = 0.4389\n", "Generation 312 fitness = 0.4394\n", "Generation 313 fitness = 0.4399\n", "Generation 314 fitness = 0.4402\n", "Generation 315 fitness = 0.4410\n", "Generation 316 fitness = 0.4407\n", "Generation 317 fitness = 0.4555\n", "Generation 318 fitness = 0.4700\n", "Generation 319 fitness = 0.4796\n", "Generation 320 fitness = 0.4862\n", "Generation 321 fitness = 0.4901\n", "Generation 322 fitness = 0.4925\n", "Generation 323 fitness = 0.4940\n", "Generation 324 fitness = 0.4950\n", "Generation 325 fitness = 0.4956\n", "Generation 326 fitness = 0.4943\n", "Generation 327 fitness = 0.4961\n", "Generation 328 fitness = 0.4963\n", "Generation 329 fitness = 0.4942\n", "Generation 330 fitness = 0.4955\n", "Generation 331 fitness = 0.4979\n", "Generation 332 fitness = 0.5034\n", "Generation 333 fitness = 0.5050\n", "Generation 334 fitness = 0.5135\n", "Generation 335 fitness = 0.5236\n", "Generation 336 fitness = 0.5382\n", "Generation 337 fitness = 0.5517\n", "Generation 338 fitness = 0.5637\n", "Generation 339 fitness = 0.5745\n", "Generation 340 fitness = 0.5852\n", "Generation 341 fitness = 0.5967\n", "Generation 342 fitness = 0.6078\n", "Generation 343 fitness = 0.6164\n", "Generation 344 fitness = 0.6202\n", "Generation 345 fitness = 0.6240\n", "Generation 346 fitness = 0.6236\n", "Generation 347 fitness = 0.6272\n", "Generation 348 fitness = 0.6287\n", "Generation 349 fitness = 0.6318\n", "Generation 350 fitness = 0.6426\n", "Generation 351 fitness = 0.6588\n", "Generation 352 fitness = 0.6658\n", "Generation 353 fitness = 0.6649\n", "Generation 354 fitness = 0.6751\n", "Generation 355 fitness = 0.6825\n", "Generation 356 fitness = 0.7073\n", "Generation 357 fitness = 0.7381\n", "Generation 358 fitness = 0.7609\n", "Generation 359 fitness = 0.7684\n", "Generation 360 fitness = 0.7825\n", "Generation 361 fitness = 0.7987\n", "Generation 362 fitness = 0.8150\n", "Generation 363 fitness = 0.8387\n", "Generation 364 fitness = 0.8509\n", "Generation 365 fitness = 0.8563\n", "Generation 366 fitness = 0.8701\n", "Generation 367 fitness = 0.8788\n", "Generation 368 fitness = 0.8842\n", "Generation 369 fitness = 0.8873\n", "Generation 370 fitness = 0.8855\n", "Generation 371 fitness = 0.8934\n", "Generation 372 fitness = 0.9052\n", "Generation 373 fitness = 0.9193\n", "Generation 374 fitness = 0.9299\n", "Generation 375 fitness = 0.9403\n", "Generation 376 fitness = 0.9450\n", "Generation 377 fitness = 0.9471\n", "Generation 378 fitness = 0.9505\n", "Generation 379 fitness = 0.9492\n", "Generation 380 fitness = 0.9529\n", "Generation 381 fitness = 0.9522\n", "Generation 382 fitness = 0.9539\n", "Generation 383 fitness = 0.9503\n", "Generation 384 fitness = 0.9543\n", "Generation 385 fitness = 0.9540\n", "Generation 386 fitness = 0.9563\n", "Generation 387 fitness = 0.9604\n", "Generation 388 fitness = 1.0058\n", "average weight per generation = [379, 431, 462, 486, 500, 507, 516, 518, 522, 521, 524, 522, 525, 525, 527, 535, 550, 561, 567, 573, 579, 582, 583, 582, 585, 586, 590, 610, 630, 644, 654, 662, 668, 671, 674, 674, 674, 678, 678, 679, 686, 712, 738, 756, 770, 781, 788, 792, 795, 797, 799, 799, 800, 797, 800, 800, 797, 801, 800, 797, 801, 801, 800, 799, 802, 808, 840, 868, 898, 913, 927, 941, 953, 970, 1004, 1031, 1054, 1063, 1074, 1079, 1082, 1083, 1084, 1085, 1081, 1086, 1084, 1083, 1087, 1089, 1113, 1147, 1175, 1194, 1222, 1266, 1301, 1326, 1359, 1386, 1431, 1472, 1505, 1535, 1600, 1656, 1698, 1733, 1751, 1765, 1780, 1798, 1811, 1821, 1833, 1862, 1888, 1937, 1975, 2015, 2048, 2071, 2075, 2091, 2105, 2108, 2118, 2138, 2224, 2299, 2349, 2412, 2496, 2613, 2675, 2735, 2762, 2852, 2917, 2985, 3012, 3063, 3077, 3120, 3206, 3316, 3415, 3478, 3505, 3537, 3536, 3545, 3561, 3566, 3555, 3571, 3577, 3584, 3658, 3754, 3838, 3906, 3944, 4002, 4028, 4045, 4054, 4061, 4070, 4090, 4129, 4162, 4273, 4377, 4477, 4541, 4595, 4631, 4682, 4792, 4958, 5104, 5202, 5300, 5354, 5384, 5405, 5422, 5462, 5511, 5545, 5574, 5567, 5607, 5583, 5592, 5622, 5632, 5663, 5811, 5993, 6159, 6246, 6316, 6415, 6484, 6728, 6942, 7195, 7318, 7417, 7477, 7516, 7543, 7557, 7532, 7573, 7585, 7610, 7811, 8040, 8212, 8407, 8561, 8614, 8730, 8747, 8827, 8870, 8976, 9311, 9918, 10749, 11359, 11619, 11809, 11935, 12004, 12013, 12052, 12133, 12156, 12118, 12084, 12185, 12190, 12102, 12193, 12119, 12150, 12203, 12258, 12278, 12403, 12860, 13260, 13759, 14076, 14266, 14421, 14581, 14651, 14653, 14707, 14727, 14738, 14746, 14697, 14754, 14758, 14768, 14817, 14895, 14948, 14917, 14967, 15008, 14952, 15026, 15000, 15032, 15034, 15035, 15036, 15037, 15007, 15037, 15066, 15168, 15626, 16205, 16768, 17179, 17673, 18106, 18454, 18680, 18829, 18957, 19010, 19138, 19352, 19957, 20666, 21012, 21358, 21514, 21680, 21815, 21893, 21863, 21944, 21972, 21995, 22010, 22049, 22033, 22774, 23502, 23979, 24311, 24504, 24624, 24698, 24748, 24781, 24714, 24806, 24813, 24710, 24776, 24894, 25168, 25250, 25674, 26179, 26911, 27584, 28186, 28725, 29260, 29834, 30390, 30819, 31010, 31199, 31181, 31362, 31435, 31589, 32129, 32938, 33291, 33243, 33753, 34123, 35364, 36903, 38044, 38419, 39126, 39936, 40748, 41934, 42545, 42813, 43507, 43941, 44209, 44363, 44277, 44672, 45262, 45963, 46493, 47013, 47250, 47355, 47526, 47460, 47644, 47611, 47693, 47514, 47715, 47697, 47812, 48019, 50288]\n", "\n", "number of generations = 389\n", "number of years = 38\n", "\n", "Runtime for this program was 0.13222432136535645 seconds.\n" ] } ], "source": [ "import time\n", "import random \n", "import statistics\n", "\n", "# 50000g\n", "GOAL = 50000\n", "NUM_RATS = 20\n", "INITIAL_MIN_WT = 200\n", "INITIAL_MAX_WT = 600\n", "INITIAL_MODE_WT = 300\n", "MUTATE_ODDS = 0.01\n", "MUTATE_MIN = 0.5\n", "MUTATE_MAX = 1.2\n", "LITTER_SIZE = 8\n", "LITTERS_PER_YEAR = 10\n", "GENERATION_LIMIT = 500\n", "\n", "# 前処理\n", "if NUM_RATS % 2 != 0:\n", " NUM_RATS += 1\n", "\n", "def populate(num_rats, min_wt, max_wt, mode_wt):\n", " # 母集団の体重の初期化(triangular)\n", " return [int(random.triangular(min_wt, max_wt, mode_wt))\\\n", " for i in range(num_rats)]\n", "\n", "def fitness(population, goal):\n", " ave = statistics.mean(population)\n", " return ave / goal\n", "\n", "def select(population, to_retain):\n", " # どれを残すか\n", " sorted_population = sorted(population)\n", " to_retain_by_sex = to_retain//2\n", " members_per_sex = len(sorted_population)//2\n", " females = sorted_population[:members_per_sex]\n", " males = sorted_population[members_per_sex:]\n", " selected_females = females[-to_retain_by_sex:]\n", " selected_males = males[-to_retain_by_sex:]\n", " return selected_males, selected_females\n", "\n", "def breed(males, females, litter_size):\n", " # 交配\n", " random.shuffle(males)\n", " random.shuffle(females)\n", " children = []\n", " for male, female in zip(males, females):\n", " for child in range(litter_size):\n", " child = random.randint(female, male)\n", " children.append(child)\n", " return children\n", "\n", "def mutate(children, mutate_odds, mutate_min, mutate_max):\n", " # 変異 \n", " for index, rat in enumerate(children):\n", " if mutate_odds >= random.random():\n", " children[index] = round(rat * random.uniform(mutate_min,\n", " mutate_max))\n", " return children\n", "\n", "ave_wt = []\n", "def main():\n", " \"\"\"Initialize population, select, breed, and mutate, display results.\"\"\"\n", " generations = 0\n", "\n", " parents = populate(NUM_RATS, INITIAL_MIN_WT, INITIAL_MAX_WT,\n", " INITIAL_MODE_WT)\n", " print(\"initial population weights = {}\".format(parents))\n", " popl_fitness = fitness(parents, GOAL)\n", " print(\"initial population fitness = {}\".format(popl_fitness))\n", " print(\"number to retain = {}\".format(NUM_RATS))\n", "\n", " while popl_fitness < 1 and generations < GENERATION_LIMIT:\n", " selected_males, selected_females = select(parents, NUM_RATS)\n", " children = breed(selected_males, selected_females, LITTER_SIZE)\n", " children = mutate(children, MUTATE_ODDS, MUTATE_MIN, MUTATE_MAX)\n", " parents = selected_males + selected_females + children\n", " popl_fitness = fitness(parents, GOAL)\n", " print(\"Generation {} fitness = {:.4f}\".format(generations,\n", " popl_fitness))\n", " ave_wt.append(int(statistics.mean(parents)))\n", " generations += 1\n", "\n", " print(\"average weight per generation = {}\".format(ave_wt))\n", " print(\"\\nnumber of generations = {}\".format(generations))\n", " print(\"number of years = {}\".format(int(generations / LITTERS_PER_YEAR)))\n", "\n", "\n", "start_time = time.time()\n", "main()\n", "end_time = time.time()\n", "duration = end_time - start_time\n", "print(\"\\nRuntime for this program was {} seconds.\".format(duration)) " ] }, { "cell_type": "code", "execution_count": 14, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 265 }, "colab_type": "code", "collapsed": false, "id": "w80zz2NJHCqZ", "outputId": "3f2b85c0-7441-416d-ce72-db0839a46ae1" }, "outputs": [ { "data": { "image/png": "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\n", "text/plain": [ "
" ] }, "metadata": { "needs_background": "light", "tags": [] }, "output_type": "display_data" } ], "source": [ "import matplotlib.pyplot as plt\n", "\n", "plt.plot(ave_wt)\n", "plt.show()" ] }, { "cell_type": "code", "execution_count": 15, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 1000 }, "colab_type": "code", "collapsed": false, "id": "j4A0_cVPIo3O", "outputId": "13567824-6cb8-4941-ac53-e238ea5daa28" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Combination = 6822858902\n", "[8, 0, 0, 0, 0, 0, 0, 0, 0, 0] [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]\n", "[0, 0, 0, 0, 0, 0, 0, 0, 0, 3] [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]\n", "[0, 0, 0, 0, 3, 0, 0, 0, 0, 0] [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]\n", "[0, 0, 0, 0, 0, 0, 0, 4, 0, 0] [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]\n", "[0, 0, 0, 0, 0, 5, 0, 0, 0, 0] [0, 0, 0, 0, 0, 5, 0, 0, 0, 0]\n", "[0, 0, 0, 0, 0, 5, 0, 0, 0, 0] [0, 0, 0, 0, 0, 5, 0, 0, 0, 0]\n", "[0, 0, 4, 0, 0, 5, 0, 0, 0, 0] [0, 0, 0, 0, 0, 5, 0, 0, 0, 0]\n", "[0, 0, 0, 0, 0, 5, 5, 0, 0, 0] [0, 0, 0, 0, 0, 5, 0, 0, 0, 0]\n", "[0, 0, 0, 0, 0, 5, 0, 0, 0, 6] [0, 0, 0, 0, 0, 5, 0, 0, 0, 0]\n", "[0, 0, 0, 0, 0, 1, 0, 0, 0, 0] [0, 0, 0, 0, 0, 5, 0, 0, 0, 0]\n", "[5, 0, 0, 0, 0, 5, 0, 0, 0, 0] [0, 0, 0, 0, 0, 5, 0, 0, 0, 0]\n", "[0, 0, 0, 0, 0, 5, 0, 0, 2, 0] [0, 0, 0, 0, 0, 5, 0, 0, 0, 0]\n", "[0, 0, 0, 0, 0, 5, 0, 0, 0, 1] [0, 0, 0, 0, 0, 5, 0, 0, 0, 0]\n", "[0, 0, 0, 8, 0, 5, 0, 0, 0, 0] [0, 0, 0, 0, 0, 5, 0, 0, 0, 0]\n", "[0, 0, 0, 0, 0, 5, 0, 0, 5, 0] [0, 0, 0, 0, 0, 5, 0, 0, 0, 0]\n", "[0, 0, 0, 0, 0, 5, 0, 0, 0, 1] [0, 0, 0, 0, 0, 5, 0, 0, 0, 0]\n", "[0, 0, 0, 0, 3, 5, 0, 0, 0, 0] [0, 0, 0, 0, 0, 5, 0, 0, 0, 0]\n", "[0, 0, 0, 0, 0, 5, 0, 0, 2, 0] [0, 0, 0, 0, 0, 5, 0, 0, 0, 0]\n", "[0, 0, 0, 0, 0, 5, 9, 0, 0, 0] [0, 0, 0, 0, 0, 5, 0, 0, 0, 0]\n", "[0, 0, 0, 0, 0, 5, 0, 7, 0, 0] [0, 0, 0, 0, 0, 5, 0, 0, 0, 0]\n", "[0, 0, 0, 0, 0, 5, 0, 0, 0, 0] [0, 0, 0, 0, 0, 5, 0, 0, 0, 0]\n", "[0, 0, 0, 0, 0, 4, 0, 0, 0, 0] [0, 0, 0, 0, 0, 5, 0, 0, 0, 0]\n", "[0, 7, 0, 0, 0, 5, 0, 0, 0, 0] [0, 0, 0, 0, 0, 5, 0, 0, 0, 0]\n", "[0, 0, 0, 0, 0, 5, 0, 0, 0, 4] [0, 0, 0, 0, 0, 5, 0, 0, 0, 0]\n", "[0, 0, 3, 0, 0, 5, 0, 0, 0, 0] [0, 0, 0, 0, 0, 5, 0, 0, 0, 0]\n", "[0, 0, 0, 0, 0, 5, 0, 0, 6, 0] [0, 0, 0, 0, 0, 5, 0, 0, 0, 0]\n", "[0, 0, 0, 0, 0, 5, 0, 7, 0, 0] [0, 0, 0, 0, 0, 5, 0, 0, 0, 0]\n", "[0, 0, 0, 0, 7, 5, 0, 0, 0, 0] [0, 0, 0, 0, 0, 5, 0, 0, 0, 0]\n", "[0, 0, 4, 0, 0, 5, 0, 0, 0, 0] [0, 0, 0, 0, 0, 5, 0, 0, 0, 0]\n", "[0, 6, 0, 0, 0, 5, 0, 0, 0, 0] [0, 0, 0, 0, 0, 5, 0, 0, 0, 0]\n", "[0, 0, 0, 0, 0, 5, 5, 0, 0, 0] [0, 0, 0, 0, 0, 5, 0, 0, 0, 0]\n", "[0, 0, 0, 0, 0, 5, 0, 0, 0, 0] [0, 0, 0, 0, 0, 5, 0, 0, 0, 0]\n", "[0, 0, 1, 0, 0, 5, 0, 0, 0, 0] [0, 0, 0, 0, 0, 5, 0, 0, 0, 0]\n", "[0, 0, 0, 8, 0, 5, 0, 0, 0, 0] [0, 0, 0, 0, 0, 5, 0, 0, 0, 0]\n", "[0, 0, 0, 0, 0, 5, 0, 0, 0, 0] [0, 0, 0, 0, 0, 5, 0, 0, 0, 0]\n", "[0, 0, 0, 0, 0, 5, 0, 0, 0, 1] [0, 0, 0, 0, 0, 5, 0, 0, 0, 0]\n", "[0, 0, 0, 8, 0, 5, 0, 0, 0, 0] [0, 0, 0, 0, 0, 5, 0, 0, 0, 0]\n", "[0, 6, 0, 0, 0, 5, 0, 0, 0, 0] [0, 0, 0, 0, 0, 5, 0, 0, 0, 0]\n", "[0, 0, 0, 0, 4, 5, 0, 0, 0, 0] [0, 0, 0, 0, 0, 5, 0, 0, 0, 0]\n", "[0, 0, 0, 3, 0, 5, 0, 0, 0, 0] [0, 0, 0, 0, 0, 5, 0, 0, 0, 0]\n", "[0, 0, 0, 0, 3, 5, 0, 0, 0, 0] [0, 0, 0, 0, 0, 5, 0, 0, 0, 0]\n", "[0, 0, 0, 0, 0, 6, 0, 0, 0, 0] [0, 0, 0, 0, 0, 5, 0, 0, 0, 0]\n", "[5, 0, 0, 0, 0, 5, 0, 0, 0, 0] [0, 0, 0, 0, 0, 5, 0, 0, 0, 0]\n", "[0, 0, 0, 0, 0, 5, 0, 0, 0, 0] [0, 0, 0, 0, 0, 5, 0, 0, 0, 0]\n", "[0, 0, 0, 0, 0, 5, 0, 0, 0, 0] [0, 0, 0, 0, 0, 5, 0, 0, 0, 0]\n", "[5, 0, 0, 0, 0, 5, 0, 0, 0, 0] [0, 0, 0, 0, 0, 5, 0, 0, 0, 0]\n", "[0, 0, 0, 0, 0, 3, 0, 0, 0, 0] [0, 0, 0, 0, 0, 5, 0, 0, 0, 0]\n", "[0, 0, 0, 0, 8, 5, 0, 0, 0, 0] [0, 0, 0, 0, 8, 5, 0, 0, 0, 0]\n", "[0, 0, 7, 0, 8, 5, 0, 0, 0, 0] [0, 0, 0, 0, 8, 5, 0, 0, 0, 0]\n", "[0, 0, 0, 0, 8, 5, 0, 0, 3, 0] [0, 0, 0, 0, 8, 5, 0, 0, 0, 0]\n", "[0, 0, 0, 0, 8, 5, 8, 0, 0, 0] [0, 0, 0, 0, 8, 5, 8, 0, 0, 0]\n", "[0, 0, 6, 0, 8, 5, 8, 0, 0, 0] [0, 0, 0, 0, 8, 5, 8, 0, 0, 0]\n", "[0, 0, 0, 0, 8, 5, 2, 0, 0, 0] [0, 0, 0, 0, 8, 5, 8, 0, 0, 0]\n", "[0, 0, 0, 0, 8, 3, 8, 0, 0, 0] [0, 0, 0, 0, 8, 5, 8, 0, 0, 0]\n", "[5, 0, 0, 0, 8, 5, 8, 0, 0, 0] [0, 0, 0, 0, 8, 5, 8, 0, 0, 0]\n", "[0, 0, 5, 0, 8, 5, 8, 0, 0, 0] [0, 0, 0, 0, 8, 5, 8, 0, 0, 0]\n", "[0, 0, 0, 6, 8, 5, 8, 0, 0, 0] [0, 0, 0, 0, 8, 5, 8, 0, 0, 0]\n", "[0, 0, 0, 0, 8, 1, 8, 0, 0, 0] [0, 0, 0, 0, 8, 5, 8, 0, 0, 0]\n", "[0, 0, 0, 0, 8, 3, 8, 0, 0, 0] [0, 0, 0, 0, 8, 5, 8, 0, 0, 0]\n", "[0, 0, 0, 0, 8, 5, 8, 9, 0, 0] [0, 0, 0, 0, 8, 5, 8, 9, 0, 0]\n", "[0, 0, 0, 0, 4, 5, 8, 9, 0, 0] [0, 0, 0, 0, 8, 5, 8, 9, 0, 0]\n", "[0, 0, 2, 0, 8, 5, 8, 9, 0, 0] [0, 0, 2, 0, 8, 5, 8, 9, 0, 0]\n", "[0, 7, 2, 0, 8, 5, 8, 9, 0, 0] [0, 0, 2, 0, 8, 5, 8, 9, 0, 0]\n", "[0, 0, 2, 0, 8, 5, 8, 9, 0, 0] [0, 0, 2, 0, 8, 5, 8, 9, 0, 0]\n", "[0, 0, 2, 7, 8, 5, 8, 9, 0, 0] [0, 0, 2, 0, 8, 5, 8, 9, 0, 0]\n", "[0, 0, 7, 0, 8, 5, 8, 9, 0, 0] [0, 0, 2, 0, 8, 5, 8, 9, 0, 0]\n", "[0, 0, 8, 0, 8, 5, 8, 9, 0, 0] [0, 0, 2, 0, 8, 5, 8, 9, 0, 0]\n", "[0, 0, 2, 0, 7, 5, 8, 9, 0, 0] [0, 0, 2, 0, 8, 5, 8, 9, 0, 0]\n", "[0, 0, 2, 0, 8, 5, 0, 9, 0, 0] [0, 0, 2, 0, 8, 5, 8, 9, 0, 0]\n", "[0, 0, 2, 6, 8, 5, 8, 9, 0, 0] [0, 0, 2, 0, 8, 5, 8, 9, 0, 0]\n", "[0, 1, 2, 0, 8, 5, 8, 9, 0, 0] [0, 0, 2, 0, 8, 5, 8, 9, 0, 0]\n", "[9, 0, 2, 0, 8, 5, 8, 9, 0, 0] [0, 0, 2, 0, 8, 5, 8, 9, 0, 0]\n", "[0, 0, 2, 0, 8, 5, 8, 9, 0, 0] [0, 0, 2, 0, 8, 5, 8, 9, 0, 0]\n", "[0, 0, 2, 0, 8, 5, 7, 9, 0, 0] [0, 0, 2, 0, 8, 5, 8, 9, 0, 0]\n", "[0, 0, 2, 0, 8, 5, 8, 5, 0, 0] [0, 0, 2, 0, 8, 5, 8, 9, 0, 0]\n", "[0, 0, 2, 0, 8, 5, 3, 9, 0, 0] [0, 0, 2, 0, 8, 5, 8, 9, 0, 0]\n", "[5, 0, 2, 0, 8, 5, 8, 9, 0, 0] [0, 0, 2, 0, 8, 5, 8, 9, 0, 0]\n", "[0, 0, 5, 0, 8, 5, 8, 9, 0, 0] [0, 0, 2, 0, 8, 5, 8, 9, 0, 0]\n", "[0, 0, 2, 0, 8, 5, 8, 0, 0, 0] [0, 0, 2, 0, 8, 5, 8, 9, 0, 0]\n", "[0, 0, 2, 0, 8, 5, 2, 9, 0, 0] [0, 0, 2, 0, 8, 5, 8, 9, 0, 0]\n", "[0, 0, 3, 0, 8, 5, 8, 9, 0, 0] [0, 0, 2, 0, 8, 5, 8, 9, 0, 0]\n", "[0, 0, 2, 0, 8, 5, 9, 9, 0, 0] [0, 0, 2, 0, 8, 5, 8, 9, 0, 0]\n", "[0, 0, 2, 0, 8, 5, 8, 9, 0, 0] [0, 0, 2, 0, 8, 5, 8, 9, 0, 0]\n", "[0, 0, 2, 0, 8, 5, 8, 9, 6, 0] [0, 0, 2, 0, 8, 5, 8, 9, 0, 0]\n", "[0, 0, 2, 0, 8, 5, 8, 9, 9, 0] [0, 0, 2, 0, 8, 5, 8, 9, 0, 0]\n", "[0, 0, 2, 0, 8, 1, 8, 9, 0, 0] [0, 0, 2, 0, 8, 5, 8, 9, 0, 0]\n", "[0, 0, 2, 0, 8, 5, 8, 9, 4, 0] [0, 0, 2, 0, 8, 5, 8, 9, 0, 0]\n", "[0, 0, 2, 8, 8, 5, 8, 9, 0, 0] [0, 0, 2, 0, 8, 5, 8, 9, 0, 0]\n", "[0, 2, 2, 0, 8, 5, 8, 9, 0, 0] [0, 0, 2, 0, 8, 5, 8, 9, 0, 0]\n", "[0, 0, 1, 0, 8, 5, 8, 9, 0, 0] [0, 0, 2, 0, 8, 5, 8, 9, 0, 0]\n", "[0, 0, 2, 0, 8, 5, 8, 4, 0, 0] [0, 0, 2, 0, 8, 5, 8, 9, 0, 0]\n", "[0, 0, 2, 0, 6, 5, 8, 9, 0, 0] [0, 0, 2, 0, 8, 5, 8, 9, 0, 0]\n", "[0, 0, 2, 0, 8, 5, 8, 9, 0, 0] [0, 0, 2, 0, 8, 5, 8, 9, 0, 0]\n", "[0, 1, 2, 0, 8, 5, 8, 9, 0, 0] [0, 0, 2, 0, 8, 5, 8, 9, 0, 0]\n", "[0, 0, 3, 0, 8, 5, 8, 9, 0, 0] [0, 0, 2, 0, 8, 5, 8, 9, 0, 0]\n", "[0, 0, 4, 0, 8, 5, 8, 9, 0, 0] [0, 0, 2, 0, 8, 5, 8, 9, 0, 0]\n", "[0, 0, 1, 0, 8, 5, 8, 9, 0, 0] [0, 0, 2, 0, 8, 5, 8, 9, 0, 0]\n", "[0, 0, 2, 1, 8, 5, 8, 9, 0, 0] [0, 0, 2, 0, 8, 5, 8, 9, 0, 0]\n", "[0, 0, 2, 0, 8, 5, 8, 9, 2, 0] [0, 0, 2, 0, 8, 5, 8, 9, 0, 0]\n", "[8, 0, 2, 0, 8, 5, 8, 9, 0, 0] [0, 0, 2, 0, 8, 5, 8, 9, 0, 0]\n", "[0, 0, 2, 0, 3, 5, 8, 9, 0, 0] [0, 0, 2, 0, 8, 5, 8, 9, 0, 0]\n", "[0, 0, 2, 0, 8, 5, 8, 9, 0, 0] [0, 0, 2, 0, 8, 5, 8, 9, 0, 0]\n", "[0, 0, 2, 0, 2, 5, 8, 9, 0, 0] [0, 0, 2, 0, 8, 5, 8, 9, 0, 0]\n", "[0, 0, 2, 0, 8, 5, 8, 9, 0, 7] [0, 0, 2, 0, 8, 5, 8, 9, 0, 0]\n", "[0, 0, 2, 6, 8, 5, 8, 9, 0, 0] [0, 0, 2, 0, 8, 5, 8, 9, 0, 0]\n", "[0, 2, 2, 0, 8, 5, 8, 9, 0, 0] [0, 0, 2, 0, 8, 5, 8, 9, 0, 0]\n", "[0, 0, 2, 1, 8, 5, 8, 9, 0, 0] [0, 0, 2, 0, 8, 5, 8, 9, 0, 0]\n", "[0, 0, 2, 0, 4, 5, 8, 9, 0, 0] [0, 0, 2, 0, 8, 5, 8, 9, 0, 0]\n", "[0, 0, 2, 0, 8, 5, 8, 9, 1, 0] [0, 0, 2, 0, 8, 5, 8, 9, 0, 0]\n", "[0, 0, 2, 0, 0, 5, 8, 9, 0, 0] [0, 0, 2, 0, 8, 5, 8, 9, 0, 0]\n", "[0, 0, 5, 0, 8, 5, 8, 9, 0, 0] [0, 0, 2, 0, 8, 5, 8, 9, 0, 0]\n", "[0, 0, 2, 5, 8, 5, 8, 9, 0, 0] [0, 0, 2, 0, 8, 5, 8, 9, 0, 0]\n", "[0, 0, 6, 0, 8, 5, 8, 9, 0, 0] [0, 0, 2, 0, 8, 5, 8, 9, 0, 0]\n", "[0, 0, 2, 8, 8, 5, 8, 9, 0, 0] [0, 0, 2, 0, 8, 5, 8, 9, 0, 0]\n", "[0, 0, 0, 0, 8, 5, 8, 9, 0, 0] [0, 0, 2, 0, 8, 5, 8, 9, 0, 0]\n", "[0, 0, 7, 0, 8, 5, 8, 9, 0, 0] [0, 0, 2, 0, 8, 5, 8, 9, 0, 0]\n", "[0, 8, 2, 0, 8, 5, 8, 9, 0, 0] [0, 8, 2, 0, 8, 5, 8, 9, 0, 0]\n", "[0, 8, 2, 0, 8, 5, 8, 9, 0, 2] [0, 8, 2, 0, 8, 5, 8, 9, 0, 2]\n", "[6, 8, 2, 0, 8, 5, 8, 9, 0, 2] [6, 8, 2, 0, 8, 5, 8, 9, 0, 2]\n", "[8, 8, 2, 0, 8, 5, 8, 9, 0, 2] [6, 8, 2, 0, 8, 5, 8, 9, 0, 2]\n", "[6, 8, 2, 0, 8, 5, 8, 9, 0, 0] [6, 8, 2, 0, 8, 5, 8, 9, 0, 2]\n", "[6, 8, 2, 0, 8, 5, 8, 9, 0, 2] [6, 8, 2, 0, 8, 5, 8, 9, 0, 2]\n", "[6, 8, 2, 7, 8, 5, 8, 9, 0, 2] [6, 8, 2, 0, 8, 5, 8, 9, 0, 2]\n", "[6, 8, 2, 0, 0, 5, 8, 9, 0, 2] [6, 8, 2, 0, 8, 5, 8, 9, 0, 2]\n", "[6, 8, 2, 0, 8, 5, 8, 6, 0, 2] [6, 8, 2, 0, 8, 5, 8, 9, 0, 2]\n", "[6, 8, 2, 0, 8, 5, 4, 9, 0, 2] [6, 8, 2, 0, 8, 5, 8, 9, 0, 2]\n", "[6, 8, 2, 0, 8, 5, 8, 9, 0, 9] [6, 8, 2, 0, 8, 5, 8, 9, 0, 2]\n", "[6, 8, 2, 0, 8, 5, 8, 9, 4, 2] [6, 8, 2, 0, 8, 5, 8, 9, 0, 2]\n", "[6, 0, 2, 0, 8, 5, 8, 9, 0, 2] [6, 8, 2, 0, 8, 5, 8, 9, 0, 2]\n", "[6, 8, 2, 0, 8, 0, 8, 9, 0, 2] [6, 8, 2, 0, 8, 5, 8, 9, 0, 2]\n", "[1, 8, 2, 0, 8, 5, 8, 9, 0, 2] [6, 8, 2, 0, 8, 5, 8, 9, 0, 2]\n", "[6, 8, 2, 0, 8, 5, 8, 9, 1, 2] [6, 8, 2, 0, 8, 5, 8, 9, 0, 2]\n", "[6, 8, 2, 0, 5, 5, 8, 9, 0, 2] [6, 8, 2, 0, 8, 5, 8, 9, 0, 2]\n", "[6, 8, 2, 0, 8, 9, 8, 9, 0, 2] [6, 8, 2, 0, 8, 5, 8, 9, 0, 2]\n", "[6, 8, 2, 0, 8, 5, 8, 9, 0, 9] [6, 8, 2, 0, 8, 5, 8, 9, 0, 2]\n", "[6, 8, 2, 0, 8, 6, 8, 9, 0, 2] [6, 8, 2, 0, 8, 5, 8, 9, 0, 2]\n", "[6, 1, 2, 0, 8, 5, 8, 9, 0, 2] [6, 8, 2, 0, 8, 5, 8, 9, 0, 2]\n", "[6, 8, 2, 0, 8, 6, 8, 9, 0, 2] [6, 8, 2, 0, 8, 5, 8, 9, 0, 2]\n", "[6, 8, 2, 0, 8, 3, 8, 9, 0, 2] [6, 8, 2, 0, 8, 5, 8, 9, 0, 2]\n", "[6, 8, 2, 0, 8, 5, 2, 9, 0, 2] [6, 8, 2, 0, 8, 5, 8, 9, 0, 2]\n", "[6, 8, 2, 0, 8, 5, 9, 9, 0, 2] [6, 8, 2, 0, 8, 5, 8, 9, 0, 2]\n", "[6, 8, 2, 0, 8, 5, 8, 9, 1, 2] [6, 8, 2, 0, 8, 5, 8, 9, 0, 2]\n", "[6, 1, 2, 0, 8, 5, 8, 9, 0, 2] [6, 8, 2, 0, 8, 5, 8, 9, 0, 2]\n", "[6, 8, 2, 0, 2, 5, 8, 9, 0, 2] [6, 8, 2, 0, 8, 5, 8, 9, 0, 2]\n", "[6, 8, 2, 0, 8, 5, 8, 9, 0, 6] [6, 8, 2, 0, 8, 5, 8, 9, 0, 2]\n", "[6, 8, 2, 3, 8, 5, 8, 9, 0, 2] [6, 8, 2, 0, 8, 5, 8, 9, 0, 2]\n", "[8, 8, 2, 0, 8, 5, 8, 9, 0, 2] [6, 8, 2, 0, 8, 5, 8, 9, 0, 2]\n", "[6, 8, 2, 0, 8, 5, 2, 9, 0, 2] [6, 8, 2, 0, 8, 5, 8, 9, 0, 2]\n", "[6, 8, 2, 0, 8, 5, 8, 9, 7, 2] [6, 8, 2, 0, 8, 5, 8, 9, 0, 2]\n", "[6, 8, 2, 0, 8, 5, 2, 9, 0, 2] [6, 8, 2, 0, 8, 5, 8, 9, 0, 2]\n", "[6, 8, 2, 4, 8, 5, 8, 9, 0, 2] [6, 8, 2, 0, 8, 5, 8, 9, 0, 2]\n", "[5, 8, 2, 0, 8, 5, 8, 9, 0, 2] [6, 8, 2, 0, 8, 5, 8, 9, 0, 2]\n", "[6, 8, 4, 0, 8, 5, 8, 9, 0, 2] [6, 8, 2, 0, 8, 5, 8, 9, 0, 2]\n", "[6, 8, 9, 0, 8, 5, 8, 9, 0, 2] [6, 8, 2, 0, 8, 5, 8, 9, 0, 2]\n", "[9, 8, 2, 0, 8, 5, 8, 9, 0, 2] [6, 8, 2, 0, 8, 5, 8, 9, 0, 2]\n", "[6, 9, 2, 0, 8, 5, 8, 9, 0, 2] [6, 8, 2, 0, 8, 5, 8, 9, 0, 2]\n", "[7, 8, 2, 0, 8, 5, 8, 9, 0, 2] [6, 8, 2, 0, 8, 5, 8, 9, 0, 2]\n", "[6, 8, 2, 2, 8, 5, 8, 9, 0, 2] [6, 8, 2, 2, 8, 5, 8, 9, 0, 2]\n", "Cracked! [6, 8, 2, 2, 8, 5, 8, 9, 0, 2] in 158 tries!\n", "\n", "0.01494 seconds.\n" ] } ], "source": [ "import time\n", "from random import randint, randrange\n", "\n", "def fitness(cmb, atm):\n", " g = 0\n", " for i, j in zip(cmb, atm):\n", " if i == j:\n", " g += 1\n", " return g\n", "\n", "def main():\n", " combination = '6822858902'\n", " print(\"Combination = {}\".format(combination))\n", " combo = [int(i) for i in combination]\n", " best_a = [0] * len(combo)\n", " best_a_g = fitness(combo, best_a)\n", " count = 0\n", " # evolve guess \n", " while best_a != combo:\n", " # crossover\n", " next_try = best_a[:]\n", " # mutate\n", " lock_wheel = randrange(0, len(combo))\n", " next_try[lock_wheel] = randint(0, 9)\n", " # grade & select\n", " next_try_g = fitness(combo, next_try)\n", " if next_try_g > best_a_g:\n", " best_a = next_try[:]\n", " best_a_g = next_try_g\n", " print(next_try, best_a)\n", " count += 1\n", "\n", " print(\"Cracked! {}\".format(best_a), end=' ')\n", " print(\"in {} tries!\".format(count))\n", "\n", " \n", "start_time = time.time()\n", "main()\n", "end_time = time.time()\n", "duration = end_time - start_time\n", "print(\"\\n{:.5f} seconds.\".format(duration)) " ] }, { "cell_type": "markdown", "metadata": { "colab_type": "text", "id": "tRVj0GNlwoyI" }, "source": [ "# Chap8" ] }, { "cell_type": "code", "execution_count": 16, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 224 }, "colab_type": "code", "collapsed": false, "id": "52P91pA4NUHR", "outputId": "c9adfa34-c69a-4c1f-e65a-9d4274a5d9fa" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "--2020-08-20 10:45:14-- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/master/Chapter_8/missing_words.json\n", "Resolving raw.githubusercontent.com (raw.githubusercontent.com)... 151.101.0.133, 151.101.64.133, 151.101.128.133, ...\n", "Connecting to raw.githubusercontent.com (raw.githubusercontent.com)|151.101.0.133|:443... connected.\n", "HTTP request sent, awaiting response... 200 OK\n", "Length: 894 [text/plain]\n", "Saving to: ‘missing_words.json’\n", "\n", "\r", "missing_words.json 0%[ ] 0 --.-KB/s \r", "missing_words.json 100%[===================>] 894 --.-KB/s in 0s \n", "\n", "2020-08-20 10:45:15 (52.4 MB/s) - ‘missing_words.json’ saved [894/894]\n", "\n" ] } ], "source": [ "!wget https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/master/Chapter_8/missing_words.json" ] }, { "cell_type": "code", "execution_count": 17, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 51 }, "colab_type": "code", "collapsed": false, "id": "rFvV_Y0ZNotp", "outputId": "33d94664-3721-448e-840a-d6a84da15c88" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Requirement already satisfied: nltk in /usr/local/lib/python3.6/dist-packages (3.2.5)\n", "Requirement already satisfied: six in /usr/local/lib/python3.6/dist-packages (from nltk) (1.15.0)\n" ] } ], "source": [ "!pip install nltk" ] }, { "cell_type": "code", "execution_count": 18, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 51 }, "colab_type": "code", "collapsed": false, "id": "KHRYawLJNCCU", "outputId": "8ffb40fa-88ae-45b4-bb33-5c06f83700d0" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[nltk_data] Downloading package cmudict to /root/nltk_data...\n", "[nltk_data] Unzipping corpora/cmudict.zip.\n" ] } ], "source": [ "import sys\n", "from string import punctuation\n", "import json\n", "from nltk.corpus import cmudict\n", "import nltk \n", "nltk.download('cmudict')\n", "\n", "with open('missing_words.json') as f:\n", " missing_words = json.load(f)\n", "\n", "cmudict = cmudict.dict()\n", "\n", "def count_syllables(words):\n", " words = words.replace('-', ' ')\n", " words = words.lower().split()\n", " num_sylls = 0\n", " for word in words:\n", " word = word.strip(punctuation)\n", " if word.endswith(\"'s\")or word.endswith(\"’s\"):\n", " word = word[:-2]\n", " if word in missing_words:\n", " num_sylls += missing_words[word]\n", " else:\n", " for phonemes in cmudict[word][0]:\n", " for phoneme in phonemes:\n", " if phoneme[-1].isdigit():\n", " num_sylls += 1\n", " return num_sylls" ] }, { "cell_type": "code", "execution_count": 19, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 1000 }, "colab_type": "code", "collapsed": false, "id": "0A8IyOAJIh8f", "outputId": "682e05a3-cb20-4a9c-df6c-03560357e30b" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "bolted 2\n", "classiestchew 1\n", "pondering 3\n", "supplements 3\n", "hellish 2\n", "monstrous 2\n", "assistant 3\n", "magnifies 3\n", "automate 3\n", "deliberative 5\n", "blindsided 3\n", "rites 1\n", "coverletsstockpiles 2\n", "immigrants 3\n", "insubordinate 5\n", "reaffirms 3\n", "initiate 4\n", "typographicsearchlight 2\n", "overboard 3\n", "presenting 3\n", "cannonadesperpetrate 3\n", "vetchcobbled 2\n", "retorted 3\n", "plagiaristsnewspaper 3\n", "accessorize 4\n", "misdeed 2\n", "scripts 1\n", "vigourovertaxed 3\n", "incident 3\n", "intimation 4\n", "marauder 3\n", "supernova 4\n", "sensitisesquadrant 2\n", "misjudged 2\n", "clinkspredecessors 4\n", "dungareesfavouredconsciousness 3\n", "response 2\n", "gallon 2\n", "configuration 5\n", "deemingcavity 3\n", "pulletsinterdicting 4\n", "places 2\n", "hoarieroddballs 2\n", "secure 2\n", "headstrong 2\n", "fireguardsrefutes 2\n", "faggeddistressingly 4\n", "offensivenesssnuff 1\n", "daunt 1\n", "vindicates 3\n", "friendship 2\n", "traveller 3\n", "unnoticed 3\n", "liveried 2\n", "snootinessprominent 3\n", "selfishness 3\n", "tuner 2\n", "vacationers 4\n", "demotion 3\n", "pancreas 3\n", "coltishpolluting 3\n", "crevice 2\n", "attractively 4\n", "attestationstopgap 2\n", "auctioneers 3\n", "sized 1\n", "weeks 1\n", "arightseamedflagonsidoliseyouthful 2\n", "portables 3\n", "awokenad 1\n", "upheavals 3\n", "supplies 2\n", "haystacks 2\n", "utilizablemalign 2\n" ] } ], "source": [ "import sys\n", "import random\n", "\n", "def load(file):\n", " with open(file) as in_file:\n", " txt = in_file.read().strip().split('\\n')\n", " txt = [x.lower() for x in txt]\n", " return txt\n", "\n", "word_list = load('2of4brif.txt')\n", "test_data = []\n", "num_words = 100\n", "test_data.extend(random.sample(word_list, num_words))\n", "\n", "for word in test_data:\n", " try:\n", " num_syllables = count_syllables(word)\n", " print(word, num_syllables, end='\\n')\n", " except KeyError:\n", " print(word, end='')\n", " # print(\" not found\", file=sys.stderr)" ] }, { "cell_type": "markdown", "metadata": { "colab_type": "text", "id": "Kly9UJAHwo08" }, "source": [ "# Chap9\n" ] }, { "cell_type": "code", "execution_count": 20, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 224 }, "colab_type": "code", "collapsed": false, "id": "lf7oBz9yQySW", "outputId": "733801b6-d10c-4434-f606-353d4d27d5d3" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "--2020-08-20 10:45:28-- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/master/Chapter_9/train.txt\n", "Resolving raw.githubusercontent.com (raw.githubusercontent.com)... 151.101.0.133, 151.101.64.133, 151.101.128.133, ...\n", "Connecting to raw.githubusercontent.com (raw.githubusercontent.com)|151.101.0.133|:443... connected.\n", "HTTP request sent, awaiting response... 200 OK\n", "Length: 368928 (360K) [text/plain]\n", "Saving to: ‘train.txt’\n", "\n", "\r", "train.txt 0%[ ] 0 --.-KB/s \r", "train.txt 100%[===================>] 360.28K --.-KB/s in 0.07s \n", "\n", "2020-08-20 10:45:28 (5.34 MB/s) - ‘train.txt’ saved [368928/368928]\n", "\n" ] } ], "source": [ "!wget https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/master/Chapter_9/train.txt" ] }, { "cell_type": "code", "execution_count": 21, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 1000 }, "colab_type": "code", "collapsed": false, "id": "pUqczSJSPq8X", "outputId": "964f11af-2ff5-4d7d-81d7-d3a0995e5c4f" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "\n", " A thousand monkeys at a thousand typewriters...\n", " or one computer...can sometimes produce a haiku.\n", "\n", "\n", " Japanese Haiku Generator\n", " 0 - Quit\n", " 1 - Generate a Haiku poem\n", " 2 - Regenerate Line 2\n", " 3 - Regenerate Line 3\n", " \n", "Choice: 1\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "dark hands of life the\n", "fragrance of plums: carrying\n", "me back to earth a\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "\n", "\n", "First line = Second line = Third line = \n", "\n", " Japanese Haiku Generator\n", " 0 - Quit\n", " 1 - Generate a Haiku poem\n", " 2 - Regenerate Line 2\n", " 3 - Regenerate Line 3\n", " \n", "Choice: 2\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "dark hands of life the\n", "fragrance of plums: carrying\n", "me back to earth a\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "\n", "\n", "First line = Second line = Third line = \n", "\n", " Japanese Haiku Generator\n", " 0 - Quit\n", " 1 - Generate a Haiku poem\n", " 2 - Regenerate Line 2\n", " 3 - Regenerate Line 3\n", " \n", "Choice: 1\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "a day in spring oh\n", "sorry tom cat bigger thrust\n", "the half-ends of worms\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "\n", "\n", "First line = Second line = Third line = \n", "\n", " Japanese Haiku Generator\n", " 0 - Quit\n", " 1 - Generate a Haiku poem\n", " 2 - Regenerate Line 2\n", " 3 - Regenerate Line 3\n", " \n", "Choice: 3\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "a day in spring oh\n", "sorry tom cat bigger thrust\n", "white before blue night\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "\n", "\n", "First line = Second line = Third line = \n", "\n", " Japanese Haiku Generator\n", " 0 - Quit\n", " 1 - Generate a Haiku poem\n", " 2 - Regenerate Line 2\n", " 3 - Regenerate Line 3\n", " \n", "Choice: 0\n", "\n", "Sayonara.\n" ] }, { "ename": "SystemExit", "evalue": "ignored", "output_type": "error", "traceback": [ "An exception has occurred, use %tb to see the full traceback.\n", "\u001b[0;31mSystemExit\u001b[0m\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "/usr/local/lib/python3.6/dist-packages/IPython/core/interactiveshell.py:2890: UserWarning: To exit: use 'exit', 'quit', or Ctrl-D.\n", " warn(\"To exit: use 'exit', 'quit', or Ctrl-D.\", stacklevel=1)\n" ] } ], "source": [ "\"\"\"Produce new haiku from training corpus of existing haiku.\"\"\"\n", "import sys\n", "import logging\n", "import random\n", "from collections import defaultdict\n", "\n", "logging.disable(logging.CRITICAL) # comment-out to enable debugging messages\n", "logging.basicConfig(level=logging.DEBUG, format='%(message)s')\n", "\n", "def load_training_file(file):\n", " \"\"\"Return a text file as a string.\"\"\"\n", " with open(file) as f:\n", " raw_haiku = f.read()\n", " return raw_haiku\n", "\n", "def prep_training(raw_haiku):\n", " \"\"\"Load string, remove newline, split words on spaces, and return list.\"\"\"\n", " corpus = raw_haiku.replace('\\n', ' ').split()\n", " return corpus\n", "\n", "def map_word_to_word(corpus):\n", " \"\"\"Load list & use dictionary to map word to word that follows.\"\"\"\n", " limit = len(corpus)-1\n", " dict1_to_1 = defaultdict(list)\n", " for index, word in enumerate(corpus):\n", " if index < limit:\n", " suffix = corpus[index + 1]\n", " dict1_to_1[word].append(suffix)\n", " logging.debug(\"map_word_to_word results for \\\"sake\\\" = %s\\n\", \n", " dict1_to_1['sake'])\n", " return dict1_to_1\n", "\n", "def map_2_words_to_word(corpus):\n", " \"\"\"Load list & use dictionary to map word-pair to trailing word.\"\"\"\n", " limit = len(corpus)-2\n", " dict2_to_1 = defaultdict(list)\n", " for index, word in enumerate(corpus):\n", " if index < limit:\n", " key = word + ' ' + corpus[index + 1]\n", " suffix = corpus[index + 2]\n", " dict2_to_1[key].append(suffix)\n", " logging.debug(\"map_2_words_to_word results for \\\"sake jug\\\" = %s\\n\",\n", " dict2_to_1['sake jug'])\n", " return dict2_to_1\n", "\n", "def random_word(corpus):\n", " \"\"\"Return random word and syllable count from training corpus.\"\"\"\n", " word = random.choice(corpus)\n", " num_syls = count_syllables(word)\n", " if num_syls > 4:\n", " random_word(corpus)\n", " else:\n", " logging.debug(\"random word & syllables = %s %s\\n\", word, num_syls)\n", " return (word, num_syls)\n", "\n", "def word_after_single(prefix, suffix_map_1, current_syls, target_syls):\n", " \"\"\"Return all acceptable words in a corpus that follow a single word.\"\"\"\n", " accepted_words = []\n", " suffixes = suffix_map_1.get(prefix)\n", " if suffixes != None:\n", " for candidate in suffixes:\n", " num_syls = count_syllables(candidate)\n", " if current_syls + num_syls <= target_syls:\n", " accepted_words.append(candidate)\n", " logging.debug(\"accepted words after \\\"%s\\\" = %s\\n\",\n", " prefix, set(accepted_words))\n", " return accepted_words\n", "\n", "\n", "def word_after_double(prefix, suffix_map_2, current_syls, target_syls):\n", " \"\"\"Return all acceptable words in a corpus that follow a word pair.\"\"\"\n", " accepted_words = []\n", " suffixes = suffix_map_2.get(prefix)\n", " if suffixes != None:\n", " for candidate in suffixes:\n", " num_syls = count_syllables(candidate)\n", " if current_syls + num_syls <= target_syls:\n", " accepted_words.append(candidate)\n", " logging.debug(\"accepted words after \\\"%s\\\" = %s\\n\",\n", " prefix, set(accepted_words))\n", " return accepted_words\n", "\n", "def haiku_line(suffix_map_1, suffix_map_2, corpus, end_prev_line, target_syls):\n", " \"\"\"Build a haiku line from a training corpus and return it.\"\"\"\n", " line = '2/3'\n", " line_syls = 0\n", " current_line = []\n", "\n", " if len(end_prev_line) == 0: # build first line\n", " line = '1'\n", " word, num_syls = random_word(corpus)\n", " current_line.append(word)\n", " line_syls += num_syls\n", " word_choices = word_after_single(word, suffix_map_1,\n", " line_syls, target_syls)\n", " while len(word_choices) == 0:\n", " prefix = random.choice(corpus)\n", " logging.debug(\"new random prefix = %s\", prefix)\n", " word_choices = word_after_single(prefix, suffix_map_1,\n", " line_syls, target_syls)\n", " word = random.choice(word_choices)\n", " num_syls = count_syllables(word)\n", " logging.debug(\"word & syllables = %s %s\", word, num_syls)\n", " line_syls += num_syls\n", " current_line.append(word)\n", " if line_syls == target_syls:\n", " end_prev_line.extend(current_line[-2:])\n", " return current_line, end_prev_line\n", "\n", " else: # build lines 2 & 3\n", " current_line.extend(end_prev_line)\n", "\n", " while True:\n", " logging.debug(\"line = %s\\n\", line)\n", " prefix = current_line[-2] + ' ' + current_line[-1]\n", " word_choices = word_after_double(prefix, suffix_map_2,\n", " line_syls, target_syls)\n", " while len(word_choices) == 0:\n", " index = random.randint(0, len(corpus) - 2)\n", " prefix = corpus[index] + ' ' + corpus[index + 1]\n", " logging.debug(\"new random prefix = %s\", prefix)\n", " word_choices = word_after_double(prefix, suffix_map_2,\n", " line_syls, target_syls)\n", " word = random.choice(word_choices)\n", " num_syls = count_syllables(word)\n", " logging.debug(\"word & syllables = %s %s\", word, num_syls)\n", " \n", " if line_syls + num_syls > target_syls:\n", " continue\n", " elif line_syls + num_syls < target_syls:\n", " current_line.append(word)\n", " line_syls += num_syls\n", " elif line_syls + num_syls == target_syls:\n", " current_line.append(word)\n", " break\n", "\n", " end_prev_line = []\n", " end_prev_line.extend(current_line[-2:])\n", "\n", " if line == '1':\n", " final_line = current_line[:]\n", " else:\n", " final_line = current_line[2:]\n", "\n", " return final_line, end_prev_line\n", "\n", "\n", "def main():\n", " \"\"\"Give user choice of building a haiku or modifying an existing haiku.\"\"\"\n", " intro = \"\"\"\\n\n", " A thousand monkeys at a thousand typewriters...\n", " or one computer...can sometimes produce a haiku.\\n\"\"\"\n", " print(\"{}\".format(intro))\n", "\n", " raw_haiku = load_training_file(\"train.txt\")\n", " corpus = prep_training(raw_haiku)\n", " suffix_map_1 = map_word_to_word(corpus)\n", " suffix_map_2 = map_2_words_to_word(corpus)\n", " final = []\n", "\n", " choice = None\n", " while choice != \"0\":\n", "\n", " print(\n", " \"\"\"\n", " Japanese Haiku Generator\n", " 0 - Quit\n", " 1 - Generate a Haiku poem\n", " 2 - Regenerate Line 2\n", " 3 - Regenerate Line 3\n", " \"\"\"\n", " )\n", "\n", " choice = input(\"Choice: \")\n", " print()\n", "\n", " # exit\n", " if choice == \"0\":\n", " print(\"Sayonara.\")\n", " sys.exit()\n", "\n", " # generate a full haiku\n", " elif choice == \"1\":\n", " final = []\n", " end_prev_line = []\n", " first_line, end_prev_line1 = haiku_line(suffix_map_1, suffix_map_2,\n", " corpus, end_prev_line, 5)\n", " final.append(first_line)\n", " line, end_prev_line2 = haiku_line(suffix_map_1, suffix_map_2,\n", " corpus, end_prev_line1, 7)\n", " final.append(line)\n", " line, end_prev_line3 = haiku_line(suffix_map_1, suffix_map_2,\n", " corpus, end_prev_line2, 5)\n", " final.append(line)\n", "\n", " # regenerate line 2\n", " elif choice == \"2\":\n", " if not final:\n", " print(\"Please generate a full haiku first (Option 1).\")\n", " continue\n", " else:\n", " line, end_prev_line2 = haiku_line(suffix_map_1, suffix_map_2,\n", " corpus, end_prev_line1, 7)\n", " final[1] = line\n", "\n", " # regenerate line 3\n", " elif choice == \"3\":\n", " if not final:\n", " print(\"Please generate a full haiku first (Option 1).\")\n", " continue\n", " else:\n", " line, end_prev_line3 = haiku_line(suffix_map_1, suffix_map_2,\n", " corpus, end_prev_line2, 5)\n", " final[2] = line\n", "\n", " # some unknown choice\n", " else:\n", " print(\"\\nSorry, but that isn't a valid choice.\", file=sys.stderr)\n", " continue\n", "\n", " # display results\n", " print()\n", " print(\"First line = \", end=\"\")\n", " print(' '.join(final[0]), file=sys.stderr)\n", " print(\"Second line = \", end=\"\")\n", " print(\" \".join(final[1]), file=sys.stderr)\n", " print(\"Third line = \", end=\"\")\n", " print(\" \".join(final[2]), file=sys.stderr)\n", " print()\n", "\n", " input(\"\\n\\nPress the Enter key to exit.\")\n", "\n", "\n", "main()" ] }, { "cell_type": "markdown", "metadata": { "colab_type": "text", "id": "sPh8OkCgwo4o" }, "source": [ "# Chap10" ] }, { "cell_type": "code", "execution_count": 22, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 842 }, "colab_type": "code", "collapsed": false, "id": "AZkSGSWPRLLO", "outputId": "ba31fa2a-05db-4659-cef3-7a1cc714c515" }, "outputs": [ { "data": { "image/png": "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\n", "text/plain": [ "" ] }, "execution_count": 22, "metadata": { "tags": [] }, "output_type": "execute_result" } ], "source": [ "import math\n", "from random import randint\n", "import tkinter\n", "from PIL import Image, ImageDraw\n", "\n", "width = 1000\n", "height = 1000\n", "bg='black'\n", "\n", "# root = tkinter.Tk()\n", "# root.title(\"Galaxy BR549\")\n", "# cv = tkinter.Canvas(root, width=width, height=height, bg=bg)\n", "image1 = Image.new(\"RGB\", (width, height), bg)\n", "draw = ImageDraw.Draw(image1)\n", "\n", "# cv.grid()\n", "# cv.configure(scrollregion=(-500, -400, 500, 400))\n", "x_add = 500\n", "y_add = 500\n", "oval_size = 0\n", "\n", "# build spiral arms\n", "num_spiral_stars = 500\n", "angle = 3.5\n", "core_diameter = 120\n", "spiral_stars = []\n", "for i in range(num_spiral_stars):\n", " theta = i * angle\n", " r = math.sqrt(i) / math.sqrt(num_spiral_stars)\n", " spiral_stars.append((r * math.cos(theta), r * math.sin(theta)))\n", "for x, y in spiral_stars:\n", " x = x * 350 + randint(-5, 3)\n", " y = y * 350 + randint(-5, 3)\n", " oval_size = randint(1, 3)\n", "# cv.create_oval(x-oval_size, y-oval_size, x+oval_size, y+oval_size,\n", "# fill='white', outline='')\n", " draw.ellipse([x-oval_size+x_add, y-oval_size+y_add, \n", " x+oval_size+x_add, y+oval_size+y_add], 'white')\n", " \n", "# build wisps\n", "wisps = []\n", "for i in range(2000):\n", " theta = i * angle\n", " # divide by num_spiral_stars for better dust lanes\n", " r = math.sqrt(i) / math.sqrt(num_spiral_stars)\n", " spiral_stars.append((r * math.cos(theta), r * math.sin(theta)))\n", "for x, y in spiral_stars:\n", " x = x * 330 + randint(-15, 10)\n", " y = y * 330 + randint(-15, 10)\n", " h = math.sqrt(x**2 + y**2)\n", " if h < 350:\n", " wisps.append((x, y))\n", "# cv.create_oval(x-1, y-1, x+1, y+1, fill='white', outline='') \n", " draw.ellipse([x-1+x_add, y-1+y_add, x+1+y_add, y+1+y_add], 'white') \n", " \n", "# build galactic core \n", "core = []\n", "for i in range(900):\n", " x = randint(-core_diameter, core_diameter)\n", " y = randint(-core_diameter, core_diameter)\n", " h = math.sqrt(x**2 + y**2)\n", " if h < core_diameter - 70:\n", " core.append((x, y))\n", " oval_size = randint(2, 4)\n", " # cv.create_oval(x-oval_size, y-oval_size, x+oval_size, y+oval_size,\n", " # fill='white', outline='')\n", " draw.ellipse([x-oval_size+x_add, y-oval_size+y_add, \n", " x+oval_size+x_add, y+oval_size+y_add], 'white') \n", " elif h < core_diameter:\n", " core.append((x, y))\n", " oval_size = randint(0, 2)\n", " # cv.create_oval(x-oval_size, y-oval_size, x+oval_size, y+oval_size,\n", " # fill='white', outline='')\n", " draw.ellipse([x-oval_size+x_add, y-oval_size+y_add, \n", " x+oval_size+x_add, y+oval_size+y_add], 'white')\n", "# cv.pack()\n", "# root.mainloop()\n", "\n", "# cv.update()\n", "# cv.postscript(file=\"file_name.ps\", colormode='color')\n", "\n", "# root.mainloop()\n", "image1.save('./galaxy_practice.jpg')\n", "image1" ] }, { "cell_type": "markdown", "metadata": { "colab_type": "text", "id": "t0WKS-mJwo71" }, "source": [ "# Chap11" ] }, { "cell_type": "markdown", "metadata": { "colab_type": "text", "id": "ZtYLsdAkiNd8" }, "source": [ "クラスに同じ誕生日の人がいる確率は70%くらいなんだよっていうものを実装" ] }, { "cell_type": "code", "execution_count": 23, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 867 }, "colab_type": "code", "collapsed": false, "id": "iwDVRObGgWgC", "outputId": "98ad55cc-c6a1-4fdf-da22-89f4a0b9688c" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "2人以上が同じ誕生日である確率\n", "部屋に2人しかいないときの確率は0.0025\n", "部屋に3人しかいないときの確率は0.0070\n", "部屋に4人しかいないときの確率は0.0180\n", "部屋に5人しかいないときの確率は0.0295\n", "部屋に6人しかいないときの確率は0.0415\n", "部屋に7人しかいないときの確率は0.0625\n", "部屋に8人しかいないときの確率は0.0750\n", "部屋に9人しかいないときの確率は0.1025\n", "部屋に10人しかいないときの確率は0.1285\n", "部屋に11人しかいないときの確率は0.1535\n", "部屋に12人しかいないときの確率は0.1785\n", "部屋に13人しかいないときの確率は0.1880\n", "部屋に14人しかいないときの確率は0.2530\n", "部屋に15人しかいないときの確率は0.2600\n", "部屋に16人しかいないときの確率は0.2990\n", "部屋に17人しかいないときの確率は0.3185\n", "部屋に18人しかいないときの確率は0.3545\n", "部屋に19人しかいないときの確率は0.3760\n", "部屋に20人しかいないときの確率は0.4250\n", "部屋に21人しかいないときの確率は0.4590\n", "部屋に22人しかいないときの確率は0.4675\n", "部屋に23人しかいないときの確率は0.5140\n", "部屋に24人しかいないときの確率は0.5370\n", "部屋に25人しかいないときの確率は0.5620\n", "部屋に26人しかいないときの確率は0.6120\n", "部屋に27人しかいないときの確率は0.6335\n", "部屋に28人しかいないときの確率は0.6650\n", "部屋に29人しかいないときの確率は0.6690\n", "部屋に30人しかいないときの確率は0.7110\n", "部屋に31人しかいないときの確率は0.7425\n", "部屋に32人しかいないときの確率は0.7545\n", "部屋に33人しかいないときの確率は0.7725\n", "部屋に34人しかいないときの確率は0.8005\n", "部屋に35人しかいないときの確率は0.8215\n", "部屋に36人しかいないときの確率は0.8320\n", "部屋に37人しかいないときの確率は0.8545\n", "部屋に38人しかいないときの確率は0.8555\n", "部屋に39人しかいないときの確率は0.8850\n", "部屋に40人しかいないときの確率は0.9005\n", "部屋に41人しかいないときの確率は0.9120\n", "部屋に42人しかいないときの確率は0.9175\n", "部屋に43人しかいないときの確率は0.9265\n", "部屋に44人しかいないときの確率は0.9225\n", "部屋に45人しかいないときの確率は0.9390\n", "部屋に46人しかいないときの確率は0.9520\n", "部屋に47人しかいないときの確率は0.9505\n", "部屋に48人しかいないときの確率は0.9610\n", "部屋に49人しかいないときの確率は0.9615\n", "部屋に50人しかいないときの確率は0.9705\n" ] } ], "source": [ "import random\n", "\n", "max_people = 50\n", "num_runs = 2000\n", "\n", "print('2人以上が同じ誕生日である確率')\n", "\n", "for people in range(2, max_people + 1):\n", " found_shared = 0\n", " for run in range(num_runs):\n", " bdays = []\n", " for i in range(0, people):\n", " bday = random.randrange(0, 364)\n", " bdays.append(bday) \n", " set_of_bdays = set(bdays)\n", " if len(set_of_bdays) < len(bdays):\n", " found_shared += 1 \n", " prob = found_shared/num_runs\n", " print('部屋に{}人しかいないときの確率は{:.4f}'.format(people, prob))" ] }, { "cell_type": "markdown", "metadata": { "colab_type": "text", "id": "kPRhZay-wwcH" }, "source": [ "# Chap12" ] }, { "cell_type": "code", "execution_count": 24, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 1000 }, "colab_type": "code", "collapsed": false, "id": "SCUgCxwwqeWU", "outputId": "b9f40ce1-b76e-4a9d-d24f-42c3856ad385" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "--2020-08-20 10:46:30-- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/master/Chapter_12/10-yr_TBond_returns_1926-2013_pct.txt\n", "Resolving raw.githubusercontent.com (raw.githubusercontent.com)... 151.101.0.133, 151.101.64.133, 151.101.128.133, ...\n", "Connecting to raw.githubusercontent.com (raw.githubusercontent.com)|151.101.0.133|:443... connected.\n", "HTTP request sent, awaiting response... 200 OK\n", "Length: 650 [text/plain]\n", "Saving to: ‘10-yr_TBond_returns_1926-2013_pct.txt’\n", "\n", "\r", " 10-yr_TBo 0%[ ] 0 --.-KB/s \r", "10-yr_TBond_returns 100%[===================>] 650 --.-KB/s in 0s \n", "\n", "2020-08-20 10:46:30 (34.4 MB/s) - ‘10-yr_TBond_returns_1926-2013_pct.txt’ saved [650/650]\n", "\n", "--2020-08-20 10:46:31-- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/master/Chapter_12/SP500_returns_1926-2013_pct.txt\n", "Resolving raw.githubusercontent.com (raw.githubusercontent.com)... 151.101.0.133, 151.101.64.133, 151.101.128.133, ...\n", "Connecting to raw.githubusercontent.com (raw.githubusercontent.com)|151.101.0.133|:443... connected.\n", "HTTP request sent, awaiting response... 200 OK\n", "Length: 438 [text/plain]\n", "Saving to: ‘SP500_returns_1926-2013_pct.txt’\n", "\n", "SP500_returns_1926- 100%[===================>] 438 --.-KB/s in 0s \n", "\n", "2020-08-20 10:46:32 (23.0 MB/s) - ‘SP500_returns_1926-2013_pct.txt’ saved [438/438]\n", "\n", "--2020-08-20 10:46:33-- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/master/Chapter_12/3_mo_TBill_rate_1926-2013_pct.txt\n", "Resolving raw.githubusercontent.com (raw.githubusercontent.com)... 151.101.0.133, 151.101.64.133, 151.101.128.133, ...\n", "Connecting to raw.githubusercontent.com (raw.githubusercontent.com)|151.101.0.133|:443... connected.\n", "HTTP request sent, awaiting response... 200 OK\n", "Length: 619 [text/plain]\n", "Saving to: ‘3_mo_TBill_rate_1926-2013_pct.txt’\n", "\n", "3_mo_TBill_rate_192 100%[===================>] 619 --.-KB/s in 0s \n", "\n", "2020-08-20 10:46:33 (35.3 MB/s) - ‘3_mo_TBill_rate_1926-2013_pct.txt’ saved [619/619]\n", "\n", "--2020-08-20 10:46:34-- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/master/Chapter_12/S-B-C_blend_1926-2013_pct.txt\n", "Resolving raw.githubusercontent.com (raw.githubusercontent.com)... 151.101.0.133, 151.101.64.133, 151.101.128.133, ...\n", "Connecting to raw.githubusercontent.com (raw.githubusercontent.com)|151.101.0.133|:443... connected.\n", "HTTP request sent, awaiting response... 200 OK\n", "Length: 670 [text/plain]\n", "Saving to: ‘S-B-C_blend_1926-2013_pct.txt’\n", "\n", "S-B-C_blend_1926-20 100%[===================>] 670 --.-KB/s in 0s \n", "\n", "2020-08-20 10:46:34 (39.7 MB/s) - ‘S-B-C_blend_1926-2013_pct.txt’ saved [670/670]\n", "\n", "--2020-08-20 10:46:35-- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/master/Chapter_12/S-B_blend_1926-2013_pct.txt\n", "Resolving raw.githubusercontent.com (raw.githubusercontent.com)... 151.101.0.133, 151.101.64.133, 151.101.128.133, ...\n", "Connecting to raw.githubusercontent.com (raw.githubusercontent.com)|151.101.0.133|:443... connected.\n", "HTTP request sent, awaiting response... 200 OK\n", "Length: 676 [text/plain]\n", "Saving to: ‘S-B_blend_1926-2013_pct.txt’\n", "\n", "S-B_blend_1926-2013 100%[===================>] 676 --.-KB/s in 0s \n", "\n", "2020-08-20 10:46:36 (37.2 MB/s) - ‘S-B_blend_1926-2013_pct.txt’ saved [676/676]\n", "\n", "--2020-08-20 10:46:40-- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/master/Chapter_12/annual_infl_rate_1926-2013_pct.txt\n", "Resolving raw.githubusercontent.com (raw.githubusercontent.com)... 151.101.0.133, 151.101.64.133, 151.101.128.133, ...\n", "Connecting to raw.githubusercontent.com (raw.githubusercontent.com)|151.101.0.133|:443... connected.\n", "HTTP request sent, awaiting response... 200 OK\n", "Length: 444 [text/plain]\n", "Saving to: ‘annual_infl_rate_1926-2013_pct.txt’\n", "\n", "annual_infl_rate_19 100%[===================>] 444 --.-KB/s in 0s \n", "\n", "2020-08-20 10:46:40 (22.4 MB/s) - ‘annual_infl_rate_1926-2013_pct.txt’ saved [444/444]\n", "\n" ] } ], "source": [ "!wget https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/master/Chapter_12/10-yr_TBond_returns_1926-2013_pct.txt\n", "!wget https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/master/Chapter_12/SP500_returns_1926-2013_pct.txt\n", "!wget https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/master/Chapter_12/3_mo_TBill_rate_1926-2013_pct.txt\n", "!wget https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/master/Chapter_12/S-B-C_blend_1926-2013_pct.txt\n", "!wget https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/master/Chapter_12/S-B_blend_1926-2013_pct.txt\n", "!wget https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/master/Chapter_12/annual_infl_rate_1926-2013_pct.txt" ] }, { "cell_type": "code", "execution_count": 25, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 459 }, "colab_type": "code", "collapsed": false, "id": "ds-7-S14wbSM", "outputId": "f766b224-71ba-4187-a3c1-c724bd951bc8" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "Note: Input data should be in percent, not decimal!\n", "\n", " stocks = SP500\n", " bonds = 10-yr Treasury Bond\n", " sb_blend = 50% SP500/50% TBond\n", "sbc_blend = 40% SP500/50% TBond/10% Cash\n", "\n", "Press ENTER to take default value shown in [brackets]. \n", "\n", "Enter investment type: (stocks, bonds, sb_blend, sbc_blend): \n", " [bonds]: bonds\n", "Input starting value of investments: \n", " [2000000]: 2000000\n", "Input annual pre-tax withdrawal for first 5 yrs(today's $): \n", " [100000]: 100000\n", "Input annual pre-tax withdrawal for remainder (today's $): \n", " [80000]: 80000\n", "Input minimum years in retirement: \n", " [18]: 18\n", "Input most-likely years in retirement: \n", " [25]: 25\n", "Input maximum years in retirement: \n", " [40]: 40\n", "Input number of cases to run: \n", " [50000]: 50000\n" ] } ], "source": [ "import sys\n", "import random\n", "import matplotlib.pyplot as plt\n", "\n", "def read_to_list(file_name):\n", " with open(file_name) as in_file:\n", " lines = [float(line.strip()) for line in in_file]\n", " decimal = [round(line / 100, 5) for line in lines]\n", " return decimal\n", "\n", "def default_input(prompt, default=None):\n", " prompt = '{} [{}]: '.format(prompt, default)\n", " response = input(prompt)\n", " if not response and default:\n", " return default\n", " else:\n", " return response\n", "\n", "# load data files with original data in percent form\n", "print(\"\\nNote: Input data should be in percent, not decimal!\\n\")\n", "try:\n", " bonds = read_to_list('10-yr_TBond_returns_1926-2013_pct.txt')\n", " stocks = read_to_list('SP500_returns_1926-2013_pct.txt')\n", " blend_40_50_10 = read_to_list('S-B-C_blend_1926-2013_pct.txt')\n", " blend_50_50 = read_to_list('S-B_blend_1926-2013_pct.txt')\n", " infl_rate = read_to_list('annual_infl_rate_1926-2013_pct.txt')\n", "except IOError as e:\n", " print(\"{}. \\nTerminating program.\".format(e), file=sys.stderr)\n", " sys.exit(1)\n", "\n", "# get user input; use dictionary for investment-type arguments \n", "investment_type_args = {'bonds': bonds, 'stocks': stocks,\n", " 'sb_blend': blend_50_50, 'sbc_blend': blend_40_50_10}\n", "\n", "# print input legend for user\n", "print(\" stocks = SP500\")\n", "print(\" bonds = 10-yr Treasury Bond\")\n", "print(\" sb_blend = 50% SP500/50% TBond\")\n", "print(\"sbc_blend = 40% SP500/50% TBond/10% Cash\\n\")\n", "\n", "print(\"Press ENTER to take default value shown in [brackets]. \\n\")\n", "\n", "# get user input\n", "invest_type = default_input(\"Enter investment type: (stocks, bonds, sb_blend,\"\\\n", " \" sbc_blend): \\n\", 'bonds').lower()\n", "while invest_type not in investment_type_args:\n", " invest_type = input(\"Invalid investment. Enter investment type \" \\\n", " \"as listed in prompt: \")\n", "\n", "start_value = default_input(\"Input starting value of investments: \\n\", \\\n", " '2000000')\n", "while not start_value.isdigit():\n", " start_value = input(\"Invalid input! Input integer only: \")\n", "\n", "withdrawal_1 = default_input(\"Input annual pre-tax withdrawal for \" \\\n", " \"first 5 yrs(today's $): \\n\", '100000')\n", "while not withdrawal_1.isdigit():\n", " withdrawal_1 = input(\"Invalid input! Input integer only: \")\n", "\n", "withdrawal_2 = default_input(\"Input annual pre-tax withdrawal for \" \\\n", " \"remainder (today's $): \\n\", '80000')\n", "while not withdrawal_2.isdigit():\n", " withdrawal_2 = input(\"Invalid input! Input integer only: \")\n", "\n", "min_years = default_input(\"Input minimum years in retirement: \\n\", '18')\n", "while not min_years.isdigit():\n", " min_years = input(\"Invalid input! Input integer only: \")\n", "\n", "most_likely_years = default_input(\"Input most-likely years in retirement: \\n\",\n", " '25')\n", "while not most_likely_years.isdigit():\n", " most_likely_years = input(\"Invalid input! Input integer only: \")\n", "\n", "max_years = default_input(\"Input maximum years in retirement: \\n\", '40')\n", "while not max_years.isdigit():\n", " max_years = input(\"Invalid input! Input integer only: \")\n", " \n", "num_cases = default_input(\"Input number of cases to run: \\n\", '50000')\n", "while not num_cases.isdigit():\n", " num_cases = input(\"Invalid input! Input integer only: \")\n", "\n", "# check for other erroneous input\n", "if not int(min_years) < int(most_likely_years) < int(max_years) \\\n", " or int(max_years) > 99:\n", " print(\"\\nProblem with input years.\", file=sys.stderr)\n", " print(\"Requires Min < ML < Max & Max <= 99.\", file=sys.stderr)\n", " sys.exit(1)\n", " \n", "def montecarlo(returns):\n", " \"\"\"Run MCS & return investment value at death & and # of times bankrupt.\"\"\"\n", " case_count = 0\n", " bankrupt_count = 0\n", " outcome = []\n", "\n", " while case_count < int(num_cases):\n", " investments = int(start_value)\n", " start_year = random.randrange(0, len(returns)) \n", " duration = int(random.triangular(int(min_years), int(max_years),\n", " int(most_likely_years))) \n", " end_year = start_year + duration \n", " lifespan = [i for i in range(start_year, end_year)]\n", " bankrupt = 'no'\n", "\n", " # build temporary lists for each case\n", " lifespan_returns = []\n", " lifespan_infl = []\n", " for i in lifespan:\n", " lifespan_returns.append(returns[i % len(returns)])\n", " lifespan_infl.append(infl_rate[i % len(infl_rate)])\n", " \n", " # loop through each year of retirement for each case run\n", " for index, i in enumerate(lifespan_returns):\n", " infl = lifespan_infl[index]\n", "\n", " # don't adjust for inflation the first year\n", " if index == 0:\n", " withdraw_infl_adj_1 = int(withdrawal_1)\n", " withdraw_infl_adj_2 = int(withdrawal_2)\n", " else:\n", " withdraw_infl_adj_1 = int(withdraw_infl_adj_1 * (1 + infl))\n", " withdraw_infl_adj_2 = int(withdraw_infl_adj_2 * (1 + infl))\n", "\n", " if index < 5:\n", " withdraw_infl_adj = withdraw_infl_adj_1\n", " else:\n", " withdraw_infl_adj = withdraw_infl_adj_2\n", "\n", " investments -= withdraw_infl_adj\n", " investments = int(investments * (1 + i))\n", "\n", " if investments <= 0:\n", " bankrupt = 'yes'\n", " break\n", "\n", " if bankrupt == 'yes':\n", " outcome.append(0)\n", " bankrupt_count += 1\n", " else:\n", " outcome.append(investments)\n", " \n", " case_count += 1\n", "\n", " return outcome, bankrupt_count\n", "\n", "def bankrupt_prob(outcome, bankrupt_count):\n", " \"\"\"Calculate & return chance of running out of money & print statistics.\"\"\"\n", " total = len(outcome)\n", " odds = round(100 * bankrupt_count / total, 1)\n", "\n", " print(\"\\nInvestment type: {}\".format(invest_type))\n", " print(\"Starting value: ${:,}\".format(int(start_value)))\n", " print(\"Annual withdrawal first 5 yrs: ${:,}\".format(int(withdrawal_1)))\n", " print(\"Annual withdrawal after 5 yrs: ${:,}\".format(int(withdrawal_2))) \n", " print(\"Years in retirement (min-ml-max): {}-{}-{}\"\n", " .format(min_years, most_likely_years, max_years))\n", " print(\"Number of runs: {:,}\\n\".format(len(outcome)))\n", " print(\"Odds of running out of money: {}%\\n\".format(odds))\n", " print(\"Average outcome: ${:,}\".format(int(sum(outcome) / total)))\n", " print(\"Minimum outcome: ${:,}\".format(min(i for i in outcome)))\n", " print(\"Maximum outcome: ${:,}\".format(max(i for i in outcome)))\n", "\n", " return odds" ] }, { "cell_type": "code", "execution_count": 26, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 526 }, "colab_type": "code", "collapsed": false, "id": "nhLeoGuHqWv3", "outputId": "551c7601-221c-4e6a-b1a0-8e70b859d978" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "Investment type: bonds\n", "Starting value: $2,000,000\n", "Annual withdrawal first 5 yrs: $100,000\n", "Annual withdrawal after 5 yrs: $80,000\n", "Years in retirement (min-ml-max): 18-25-40\n", "Number of runs: 50,000\n", "\n", "Odds of running out of money: 42.4%\n", "\n", "Average outcome: $1,455,728\n", "Minimum outcome: $0\n", "Maximum outcome: $14,033,611\n" ] }, { "data": { "image/png": "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\n", "text/plain": [ "
" ] }, "metadata": { "needs_background": "light", "tags": [] }, "output_type": "display_data" } ], "source": [ "outcome, bankrupt_count = montecarlo(investment_type_args[invest_type])\n", "odds = bankrupt_prob(outcome, bankrupt_count)\n", "\n", "# generate matplotlib bar chart \n", "plotdata = outcome[:3000] # only plot first 3000 runs\n", "plt.figure('Outcome by Case (showing first {} runs)'.format(len(plotdata)),\n", " figsize=(16, 5)) # size is width, height in inches\n", "index = [i + 1 for i in range(len(plotdata))]\n", "plt.bar(index, plotdata, color='black')\n", "plt.xlabel('Simulated Lives', fontsize=18)\n", "plt.ylabel('$ Remaining', fontsize=18)\n", "plt.ticklabel_format(style='plain', axis='y')\n", "ax = plt.gca()\n", "ax.get_yaxis().set_major_formatter(plt.FuncFormatter(lambda x, loc: \"{:,}\"\n", " .format(int(x))))\n", "plt.title('Probability of running out of money = {}%'.format(odds),\n", " fontsize=20, color='red')\n", "plt.show()" ] }, { "cell_type": "markdown", "metadata": { "colab_type": "text", "id": "t7PNsP0zv05G" }, "source": [ "# Chap13" ] }, { "cell_type": "code", "execution_count": 27, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 224 }, "colab_type": "code", "collapsed": false, "id": "nawcQk21y9mL", "outputId": "4dc1099b-874b-4477-b44d-2f97677a5cb4" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "--2020-08-20 10:47:28-- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/master/Chapter_13/tvashtar_plume.gif\n", "Resolving raw.githubusercontent.com (raw.githubusercontent.com)... 151.101.0.133, 151.101.64.133, 151.101.128.133, ...\n", "Connecting to raw.githubusercontent.com (raw.githubusercontent.com)|151.101.0.133|:443... connected.\n", "HTTP request sent, awaiting response... 200 OK\n", "Length: 14789 (14K) [image/gif]\n", "Saving to: ‘tvashtar_plume.gif’\n", "\n", "\r", "tvashtar_plume.gif 0%[ ] 0 --.-KB/s \r", "tvashtar_plume.gif 100%[===================>] 14.44K --.-KB/s in 0.01s \n", "\n", "2020-08-20 10:47:28 (1.14 MB/s) - ‘tvashtar_plume.gif’ saved [14789/14789]\n", "\n" ] } ], "source": [ "!wget https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/master/Chapter_13/tvashtar_plume.gif" ] }, { "cell_type": "code", "execution_count": 28, "metadata": { "colab": {}, "colab_type": "code", "collapsed": true, "id": "nyK14U_vUNWD" }, "outputs": [], "source": [ "!mkdir particles" ] }, { "cell_type": "code", "execution_count": 29, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 547 }, "colab_type": "code", "collapsed": false, "id": "2Kvguu_Yzgat", "outputId": "99330f89-cd63-47ec-e081-305f7fb0bc5f" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Reading package lists... Done\n", "Building dependency tree \n", "Reading state information... Done\n", "The following package was automatically installed and is no longer required:\n", " libnvidia-common-440\n", "Use 'apt autoremove' to remove it.\n", "The following NEW packages will be installed:\n", " xvfb\n", "0 upgraded, 1 newly installed, 0 to remove and 35 not upgraded.\n", "Need to get 784 kB of archives.\n", "After this operation, 2,266 kB of additional disk space will be used.\n", "Get:1 http://archive.ubuntu.com/ubuntu bionic-updates/universe amd64 xvfb amd64 2:1.19.6-1ubuntu4.4 [784 kB]\n", "Fetched 784 kB in 1s (1,125 kB/s)\n", "Selecting previously unselected package xvfb.\n", "(Reading database ... 144487 files and directories currently installed.)\n", "Preparing to unpack .../xvfb_2%3a1.19.6-1ubuntu4.4_amd64.deb ...\n", "Unpacking xvfb (2:1.19.6-1ubuntu4.4) ...\n", "Setting up xvfb (2:1.19.6-1ubuntu4.4) ...\n", "Processing triggers for man-db (2.8.3-2ubuntu0.1) ...\n", "Collecting pyvirtualdisplay\n", " Downloading https://files.pythonhosted.org/packages/d0/8a/643043cc70791367bee2d19eb20e00ed1a246ac48e5dbe57bbbcc8be40a9/PyVirtualDisplay-1.3.2-py2.py3-none-any.whl\n", "Collecting EasyProcess\n", " Downloading https://files.pythonhosted.org/packages/48/3c/75573613641c90c6d094059ac28adb748560d99bd27ee6f80cce398f404e/EasyProcess-0.3-py2.py3-none-any.whl\n", "Installing collected packages: EasyProcess, pyvirtualdisplay\n", "Successfully installed EasyProcess-0.3 pyvirtualdisplay-1.3.2\n", "Collecting pygame\n", "\u001b[?25l Downloading https://files.pythonhosted.org/packages/8e/24/ede6428359f913ed9cd1643dd5533aefeb5a2699cc95bea089de50ead586/pygame-1.9.6-cp36-cp36m-manylinux1_x86_64.whl (11.4MB)\n", "\u001b[K |████████████████████████████████| 11.4MB 2.9MB/s \n", "\u001b[?25hInstalling collected packages: pygame\n", "Successfully installed pygame-1.9.6\n" ] } ], "source": [ "!apt install xvfb\n", "!pip install pyvirtualdisplay\n", "!pip install pygame" ] }, { "cell_type": "code", "execution_count": 30, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 34 }, "colab_type": "code", "collapsed": false, "id": "8hTllC8Czmlr", "outputId": "f4ba2def-2567-4b65-e15a-1fef66a9f2f4" }, "outputs": [ { "data": { "text/plain": [ "" ] }, "execution_count": 30, "metadata": { "tags": [] }, "output_type": "execute_result" } ], "source": [ "from pyvirtualdisplay import Display\n", "d = Display()\n", "d.start()" ] }, { "cell_type": "code", "execution_count": 31, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 51 }, "colab_type": "code", "collapsed": false, "id": "hjDhF9spwiz0", "outputId": "7de0c4d6-24a4-4470-af37-fc6dc79fd6e0" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "pygame 1.9.6\n", "Hello from the pygame community. https://www.pygame.org/contribute.html\n" ] } ], "source": [ "import sys\n", "import math\n", "import random\n", "import pygame as pg\n", "\n", "pg.init()\n", " \n", "BLACK = (0, 0, 0)\n", "WHITE = (255, 255, 255)\n", "LT_GRAY = (180, 180, 180)\n", "GRAY = (120, 120, 120)\n", "DK_GRAY = (80, 80, 80)\n", "\n", "class Particle(pg.sprite.Sprite):\n", "\n", " gases_colors = {'SO2': LT_GRAY, 'CO2': GRAY, 'H2S': DK_GRAY, 'H2O': WHITE}\n", " \n", " VENT_LOCATION_XY = (320, 300) # mouth of volcano\n", " IO_SURFACE_Y = 308 # y location of Io surface\n", " GRAVITY = 0.5 # pixels-per-frame \n", " VELOCITY_SO2 = 8 # pixels-per-frame\n", " \n", " # scalars (SO2 atomic weight/particle atomic weight) used for velocity\n", " vel_scalar = {'SO2': 1, 'CO2': 1.45, 'H2S': 1.9, 'H2O': 3.6} \n", " \n", " def __init__(self, screen, background):\n", " super().__init__()\n", " self.screen = screen\n", " self.background = background\n", " self.image = pg.Surface((4, 4))\n", " self.rect = self.image.get_rect()\n", " self.gas = random.choice(list(Particle.gases_colors.keys()))\n", " self.color = Particle.gases_colors[self.gas]\n", " self.vel = Particle.VELOCITY_SO2 * Particle.vel_scalar[self.gas] \n", " self.x, self.y = Particle.VENT_LOCATION_XY\n", " self.vector()\n", "\n", " def vector(self):\n", " \"\"\"Calculate particle vector at launch.\"\"\"\n", " orient = random.uniform(60, 120) # 90 is vertical\n", " radians = math.radians(orient)\n", " self.dx = self.vel * math.cos(radians)\n", " self.dy = -self.vel * math.sin(radians) # negative as y increases down\n", " \n", " def update(self):\n", " \"\"\"Apply gravity, draw path, and handle boundary conditions.\"\"\"\n", " self.dy += Particle.GRAVITY\n", " pg.draw.line(self.background, self.color, (self.x, self.y),\n", " (self.x + self.dx, self.y + self.dy))\n", " self.x += self.dx\n", " self.y += self.dy\n", " if self.x < 0 or self.x > self.screen.get_width():\n", " self.kill()\n", " if self.y < 0 or self.y > Particle.IO_SURFACE_Y:\n", " self.kill()\n" ] }, { "cell_type": "code", "execution_count": 32, "metadata": { "colab": {}, "colab_type": "code", "collapsed": true, "id": "oVmnxgEXxa1D" }, "outputs": [], "source": [ "screen = pg.display.set_mode((639, 360))\n", "pg.display.set_caption('Io Volcano Simulator')\n", "background = pg.image.load('tvashtar_plume.gif')\n", "\n", "# Set-up color-coded legend\n", "legend_font = pg.font.SysFont('None', 24)\n", "water_label = legend_font.render('--- H2O', True, WHITE, BLACK)\n", "co2_label = legend_font.render('--- CO2', True, GRAY, BLACK)\n", "so2_label = legend_font.render('--- SO2/S2', True, LT_GRAY, BLACK)\n", "h2s_label = legend_font.render('--- H2S', True, DK_GRAY, BLACK)\n", "\n", "particles = pg.sprite.Group()\n", "\n", "clock = pg.time.Clock()\n", "\n", "img = []\n", "#while True:\n", "for i in range(100):\n", " clock.tick(25)\n", " particles.add(Particle(screen, background))\n", " for event in pg.event.get():\n", " if event.type == pg.QUIT:\n", " pg.quit()\n", " sys.exit()\n", "\n", " screen.blit(background, (0, 0))\n", " screen.blit(water_label, (40, 20))\n", " screen.blit(h2s_label, (40, 40))\n", " screen.blit(co2_label, (40, 60))\n", " screen.blit(so2_label, (40, 80))\n", " particles.update()\n", " particles.draw(screen)\n", " pg.image.save(screen, 'particles/particles' + str(i).zfill(4) + '.jpg')\n", " pg.display.flip()\n" ] }, { "cell_type": "code", "execution_count": 34, "metadata": { "colab": {}, "colab_type": "code", "collapsed": true, "id": "IXuPteUWEiv6" }, "outputs": [], "source": [ "from PIL import Image, ImageDraw\n", "\n", "images = list()\n", "for i in range(100):\n", " img = Image.open(\"particles/particles{0:04d}.jpg\".format(i))\n", " images.append(img)\n", "images[0].save('particles.gif',\n", " save_all=True, append_images=images[1:], optimize=False)" ] }, { "cell_type": "code", "execution_count": 35, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 743 }, "colab_type": "code", "collapsed": false, "id": "Rxm2-HYaIm8e", "outputId": "1a2bebbd-f765-4ee5-f4eb-22ff178c2f86" }, "outputs": [ { "data": { "text/html": [ "\n", "\n", "\n", "\n", "\n", "\n", "
\n", " \n", "
\n", " \n", "
\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
\n", "
\n", " \n", " \n", " \n", " \n", " \n", " \n", "
\n", "
\n", "
\n", "\n", "\n", "\n" ], "text/plain": [ "" ] }, "execution_count": 35, "metadata": { "tags": [] }, "output_type": "execute_result" }, { "data": { "image/png": "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\n", "text/plain": [ "
" ] }, "metadata": { "needs_background": "light", "tags": [] }, "output_type": "display_data" } ], "source": [ "# from JSAnimation.IPython_display import display_animation\n", "import matplotlib.pyplot as plt\n", "from matplotlib import animation\n", "from IPython.display import HTML\n", "\n", "plt.figure(figsize=(639/72, 360/72), dpi=72)\n", "patch = plt.imshow(images[0])\n", "plt.axis('off')\n", " \n", "def animate(i):\n", " patch.set_data(images[i])\n", " \n", "anim = animation.FuncAnimation(plt.gcf(), animate, frames=len(images), interval=50)\n", "HTML(anim.to_jshtml())" ] }, { "cell_type": "markdown", "metadata": { "colab_type": "text", "id": "dLd4zJb_v02C" }, "source": [ "# Chap14" ] }, { "cell_type": "code", "execution_count": 36, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 785 }, "colab_type": "code", "collapsed": false, "id": "WlQn3VBVQ16A", "outputId": "2382021d-4489-4487-cc5f-06b15e605747" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "--2020-08-20 10:49:36-- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/master/Chapter_14/mars.png\n", "Resolving raw.githubusercontent.com (raw.githubusercontent.com)... 151.101.0.133, 151.101.64.133, 151.101.128.133, ...\n", "Connecting to raw.githubusercontent.com (raw.githubusercontent.com)|151.101.0.133|:443... connected.\n", "HTTP request sent, awaiting response... 200 OK\n", "Length: 410710 (401K) [image/png]\n", "Saving to: ‘mars.png’\n", "\n", "mars.png 100%[===================>] 401.08K --.-KB/s in 0.07s \n", "\n", "2020-08-20 10:49:37 (5.74 MB/s) - ‘mars.png’ saved [410710/410710]\n", "\n", "--2020-08-20 10:49:39-- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/master/Chapter_14/mars_water.png\n", "Resolving raw.githubusercontent.com (raw.githubusercontent.com)... 151.101.0.133, 151.101.64.133, 151.101.128.133, ...\n", "Connecting to raw.githubusercontent.com (raw.githubusercontent.com)|151.101.0.133|:443... connected.\n", "HTTP request sent, awaiting response... 200 OK\n", "Length: 676787 (661K) [image/png]\n", "Saving to: ‘mars_water.png’\n", "\n", "mars_water.png 100%[===================>] 660.92K --.-KB/s in 0.07s \n", "\n", "2020-08-20 10:49:39 (8.74 MB/s) - ‘mars_water.png’ saved [676787/676787]\n", "\n", "--2020-08-20 10:49:40-- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/master/Chapter_14/satellite.png\n", "Resolving raw.githubusercontent.com (raw.githubusercontent.com)... 151.101.0.133, 151.101.64.133, 151.101.128.133, ...\n", "Connecting to raw.githubusercontent.com (raw.githubusercontent.com)|151.101.0.133|:443... connected.\n", "HTTP request sent, awaiting response... 200 OK\n", "Length: 1299 (1.3K) [image/png]\n", "Saving to: ‘satellite.png’\n", "\n", "satellite.png 100%[===================>] 1.27K --.-KB/s in 0s \n", "\n", "2020-08-20 10:49:41 (59.8 MB/s) - ‘satellite.png’ saved [1299/1299]\n", "\n", "--2020-08-20 10:49:42-- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/master/Chapter_14/satellite_crash_40x33.png\n", "Resolving raw.githubusercontent.com (raw.githubusercontent.com)... 151.101.0.133, 151.101.64.133, 151.101.128.133, ...\n", "Connecting to raw.githubusercontent.com (raw.githubusercontent.com)|151.101.0.133|:443... connected.\n", "HTTP request sent, awaiting response... 200 OK\n", "Length: 4763 (4.7K) [image/png]\n", "Saving to: ‘satellite_crash_40x33.png’\n", "\n", "satellite_crash_40x 100%[===================>] 4.65K --.-KB/s in 0s \n", "\n", "2020-08-20 10:49:42 (59.8 MB/s) - ‘satellite_crash_40x33.png’ saved [4763/4763]\n", "\n" ] } ], "source": [ "!wget https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/master/Chapter_14/mars.png\n", "!wget https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/master/Chapter_14/mars_water.png\n", "!wget https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/master/Chapter_14/satellite.png\n", "!wget https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/master/Chapter_14/satellite_crash_40x33.png" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "colab": {}, "colab_type": "code", "collapsed": true, "id": "LGrs4LASQj8m" }, "outputs": [], "source": [ "# !apt install xvfb\n", "# !pip install pyvirtualdisplay\n", "# !pip install pygame" ] }, { "cell_type": "code", "execution_count": 37, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 34 }, "colab_type": "code", "collapsed": false, "id": "7JkL51GUQj8r", "outputId": "48bc3149-d095-4974-f6b2-3917b9425bf2" }, "outputs": [ { "data": { "text/plain": [ "" ] }, "execution_count": 37, "metadata": { "tags": [] }, "output_type": "execute_result" } ], "source": [ "from pyvirtualdisplay import Display\n", "d = Display()\n", "d.start()" ] }, { "cell_type": "code", "execution_count": 38, "metadata": { "colab": {}, "colab_type": "code", "collapsed": true, "id": "V1aYpV5OS_N_" }, "outputs": [], "source": [ "import os\n", "import math\n", "import random\n", "import pygame as pg" ] }, { "cell_type": "code", "execution_count": 39, "metadata": { "colab": {}, "colab_type": "code", "collapsed": true, "id": "y045G9WyS_95" }, "outputs": [], "source": [ "!mkdir mars_orbiter" ] }, { "cell_type": "code", "execution_count": 40, "metadata": { "colab": {}, "colab_type": "code", "collapsed": true, "id": "QFaWyZBdN2HD" }, "outputs": [], "source": [ "WHITE = (255, 255, 255)\n", "BLACK = (0, 0, 0)\n", "RED = (255, 0, 0)\n", "GREEN = (0, 255, 0)\n", "LT_BLUE = (173, 216, 230)\n", "\n", "class Satellite(pg.sprite.Sprite):\n", " \"\"\"Satellite object that rotates to face planet & crashes & burns.\"\"\"\n", " \n", " def __init__(self, background):\n", " super().__init__()\n", " self.background = background\n", " self.image_sat = pg.image.load(\"satellite.png\").convert()\n", " self.image_crash = pg.image.load(\"satellite_crash_40x33.png\").convert()\n", " self.image = self.image_sat\n", " self.rect = self.image.get_rect()\n", " self.image.set_colorkey(BLACK) # sets transparent color\n", " self.x = random.randrange(315, 425)\n", " self.y = random.randrange(70, 180) \n", " self.dx = random.choice([-3, 3])\n", " self.dy = 0\n", " self.heading = 0 # initializes dish orientation\n", " self.fuel = 100\n", " self.mass = 1\n", " self.distance = 0 # initializes distance between satellite & planet\n", " # self.thrust = pg.mixer.Sound('thrust_audio.ogg')\n", " # self.thrust.set_volume(0.07) # valid values are 0-1\n", "\n", " def thruster(self, dx, dy):\n", " \"\"\"Execute actions associated with firing thrusters.\"\"\"\n", " self.dx += dx\n", " self.dy += dy\n", " self.fuel -= 2\n", " # self.thrust.play() \n", "\n", " def check_keys(self):\n", " \"\"\"Check if user presses arrow keys & call thruster() method.\"\"\"\n", " keys = pg.key.get_pressed() \n", " # fire thrusters\n", " if keys[pg.K_RIGHT]:\n", " self.thruster(dx=0.05, dy=0)\n", " elif keys[pg.K_LEFT]:\n", " self.thruster(dx=-0.05, dy=0)\n", " elif keys[pg.K_UP]:\n", " self.thruster(dx=0, dy=-0.05) \n", " elif keys[pg.K_DOWN]:\n", " self.thruster(dx=0, dy=0.05)\n", " \n", " def locate(self, planet):\n", " \"\"\"Calculate distance & heading to planet.\"\"\"\n", " px, py = planet.x, planet.y\n", " dist_x = self.x - px\n", " dist_y = self.y - py\n", " # get direction to planet to point dish\n", " planet_dir_radians = math.atan2(dist_x, dist_y)\n", " self.heading = planet_dir_radians * 180 / math.pi\n", " self.heading -= 90 # sprite is traveling tail-first \n", " self.distance = math.hypot(dist_x, dist_y)\n", "\n", " def rotate(self):\n", " \"\"\"Rotate satellite using degrees so dish faces planet.\"\"\"\n", " self.image = pg.transform.rotate(self.image_sat, self.heading)\n", " self.rect = self.image.get_rect()\n", "\n", " def path(self):\n", " \"\"\"Update satellite's position & draw line to trace orbital path.\"\"\"\n", " last_center = (self.x, self.y)\n", " self.x += self.dx\n", " self.y += self.dy\n", " pg.draw.line(self.background, WHITE, last_center, (self.x, self.y))\n", "\n", " def update(self):\n", " \"\"\"Update satellite object during game.\"\"\"\n", " self.check_keys()\n", " self.rotate()\n", " self.path()\n", " self.rect.center = (self.x, self.y) \n", " # change image to fiery red if in atmosphere\n", " if self.dx == 0 and self.dy == 0:\n", " self.image = self.image_crash\n", " self.image.set_colorkey(BLACK)\n", " \n", "class Planet(pg.sprite.Sprite):\n", " \"\"\"Planet object that rotates & projects gravity field.\"\"\"\n", " \n", " def __init__(self):\n", " super().__init__()\n", " self.image_mars = pg.image.load(\"mars.png\").convert()\n", " self.image_water = pg.image.load(\"mars_water.png\").convert() \n", " self.image_copy = pg.transform.scale(self.image_mars, (100, 100)) \n", " self.image_copy.set_colorkey(BLACK) \n", " self.rect = self.image_copy.get_rect()\n", " self.image = self.image_copy\n", " self.mass = 2000 \n", " self.x = 400 \n", " self.y = 320\n", " self.rect.center = (self.x, self.y)\n", " self.angle = math.degrees(0)\n", " self.rotate_by = math.degrees(0.01)\n", "\n", " def rotate(self):\n", " \"\"\"Rotate the planet image with each game loop.\"\"\"\n", " last_center = self.rect.center\n", " self.image = pg.transform.rotate(self.image_copy, self.angle)\n", " self.rect = self.image.get_rect()\n", " self.rect.center = last_center\n", " self.angle += self.rotate_by\n", "\n", " def gravity(self, satellite):\n", " \"\"\"Calculate impact of gravity on the satellite.\"\"\"\n", " G = 1.0 # gravitational constant for game\n", " dist_x = self.x - satellite.x\n", " dist_y = self.y - satellite.y\n", " distance = math.hypot(dist_x, dist_y) \n", " # normalize to a unit vector\n", " dist_x /= distance\n", " dist_y /= distance\n", " # apply gravity\n", " force = G * (satellite.mass * self.mass) / (math.pow(distance, 2))\n", " satellite.dx += (dist_x * force)\n", " satellite.dy += (dist_y * force)\n", " \n", " def update(self):\n", " \"\"\"Call the rotate method.\"\"\"\n", " self.rotate()\n", "\n", "def calc_eccentricity(dist_list):\n", " \"\"\"Calculate & return eccentricity from list of radii.\"\"\"\n", " apoapsis = max(dist_list)\n", " periapsis = min(dist_list)\n", " eccentricity = (apoapsis - periapsis) / (apoapsis + periapsis)\n", " return eccentricity\n", "\n", "def instruct_label(screen, text, color, x, y):\n", " \"\"\"Take screen, list of strings, color, & origin & render text to screen.\"\"\"\n", " instruct_font = pg.font.SysFont(None, 25)\n", " line_spacing = 22\n", " for index, line in enumerate(text):\n", " label = instruct_font.render(line, True, color, BLACK)\n", " screen.blit(label, (x, y + index * line_spacing))\n", "\n", "def box_label(screen, text, dimensions):\n", " \"\"\"Make fixed-size label from screen, text & left, top, width, height.\"\"\"\n", " readout_font = pg.font.SysFont(None, 27)\n", " base = pg.Rect(dimensions)\n", " pg.draw.rect(screen, WHITE, base, 0)\n", " label = readout_font.render(text, True, BLACK)\n", " label_rect = label.get_rect(center=base.center)\n", " screen.blit(label, label_rect)\n", "\n", "def mapping_on(planet):\n", " \"\"\"Show soil moisture image on Mars.\"\"\"\n", " last_center = planet.rect.center\n", " planet.image_copy = pg.transform.scale(planet.image_water, (100, 100))\n", " planet.image_copy.set_colorkey(BLACK)\n", " planet.rect = planet.image_copy.get_rect()\n", " planet.rect.center = last_center\n", "\n", "def mapping_off(planet):\n", " \"\"\"Restore normal planet image.\"\"\"\n", " planet.image_copy = pg.transform.scale(planet.image_mars, (100, 100))\n", " planet.image_copy.set_colorkey(BLACK)\n", "\n", "def cast_shadow(screen):\n", " \"\"\"Add optional terminator & shadow behind planet to screen.\"\"\"\n", " shadow = pg.Surface((400, 100), flags=pg.SRCALPHA) # tuple is w,h\n", " shadow.fill((0, 0, 0, 210)) # last number sets transparency\n", " screen.blit(shadow, (0, 270)) # tuple is top left coordinates\n", "\n" ] }, { "cell_type": "code", "execution_count": 41, "metadata": { "colab": {}, "colab_type": "code", "collapsed": true, "id": "6nJNxpOrP9rq" }, "outputs": [], "source": [ "pg.init() # initialize pygame\n", "\n", "# set-up display\n", "os.environ['SDL_VIDEO_WINDOW_POS'] = '700, 100' # set game window origin\n", "screen = pg.display.set_mode((800, 645), pg.FULLSCREEN) \n", "pg.display.set_caption(\"Mars Orbiter\")\n", "background = pg.Surface(screen.get_size())\n", "\n", "# enable sound mixer\n", "# pg.mixer.init()\n", "\n", "intro_text = [\n", " ' The Mars Orbiter experienced an error during Orbit insertion.',\n", " ' Use thrusters to correct to a circular mapping orbit without',\n", " ' running out of propellant or burning up in the atmosphere.'\n", " ]\n", "\n", "instruct_text1 = [\n", " 'Orbital altitude must be within 69-120 miles',\n", " 'Orbital Eccentricity must be < 0.05',\n", " 'Avoid top of atmosphere at 68 miles' \n", " ]\n", "\n", "instruct_text2 = [\n", " 'Left Arrow = Decrease Dx', \n", " 'Right Arrow = Increase Dx', \n", " 'Up Arrow = Decrease Dy', \n", " 'Down Arrow = Increase Dy', \n", " 'Space Bar = Clear Path',\n", " 'Escape = Exit Full Screen' \n", " ] \n", "\n", "# instantiate planet and satellite objects\n", "planet = Planet()\n", "planet_sprite = pg.sprite.Group(planet)\n", "sat = Satellite(background) \n", "sat_sprite = pg.sprite.Group(sat)\n", "\n", "# for circular orbit verification\n", "dist_list = [] \n", "eccentricity = 1\n", "eccentricity_calc_interval = 5 # optimized for 120 mile altitude\n", "\n", "# time-keeping\n", "clock = pg.time.Clock()\n", "fps = 30\n", "tick_count = 0\n", "\n", "# for soil moisture mapping functionality\n", "mapping_enabled = False\n", "\n", "running = True\n", "# while running:\n", "for i in range(1000):\n", " clock.tick(fps)\n", " tick_count += 1\n", " dist_list.append(sat.distance)\n", " \n", " # get keyboard input\n", " for event in pg.event.get():\n", " if event.type == pg.QUIT: # close window\n", " running = False\n", " elif event.type == pg.KEYDOWN and event.key == pg.K_ESCAPE:\n", " screen = pg.display.set_mode((800, 645)) # exit full screen\n", " elif event.type == pg.KEYDOWN and event.key == pg.K_SPACE:\n", " background.fill(BLACK) # clear path\n", " elif event.type == pg.KEYUP:\n", " sat.thrust.stop() # stop sound\n", " mapping_off(planet) # turn-off moisture map view\n", " elif mapping_enabled:\n", " if event.type == pg.KEYDOWN and event.key == pg.K_m:\n", " mapping_on(planet)\n", "\n", " # get heading & distance to planet & apply gravity \n", " sat.locate(planet) \n", " planet.gravity(sat)\n", "\n", " # calculate orbital eccentricity\n", " if tick_count % (eccentricity_calc_interval * fps) == 0:\n", " eccentricity = calc_eccentricity(dist_list)\n", " dist_list = [] \n", "\n", " # re-blit background for drawing command - prevents clearing path\n", " screen.blit(background, (0, 0))\n", " \n", " # Fuel/Altitude fail conditions\n", " if sat.fuel <= 0:\n", " instruct_label(screen, ['Fuel Depleted!'], RED, 340, 195)\n", " sat.fuel = 0\n", " sat.dx = 2\n", " elif sat.distance <= 68:\n", " instruct_label(screen, ['Atmospheric Entry!'], RED, 320, 195)\n", " sat.dx = 0\n", " sat.dy = 0\n", "\n", " # enable mapping functionality\n", " if eccentricity < 0.05 and sat.distance >= 69 and sat.distance <= 120:\n", " map_instruct = ['Press & hold M to map soil moisture']\n", " instruct_label(screen, map_instruct, LT_BLUE, 250, 175)\n", " mapping_enabled = True\n", " else:\n", " mapping_enabled = False\n", "\n", " planet_sprite.update()\n", " planet_sprite.draw(screen)\n", " sat_sprite.update()\n", " sat_sprite.draw(screen)\n", " pg.image.save(screen, 'mars_orbiter/mars_orbiter' + str(i).zfill(4) + '.jpg')\n", "\n", " # display intro text for 15 seconds \n", " if pg.time.get_ticks() <= 15000: # time in milliseconds\n", " instruct_label(screen, intro_text, GREEN, 145, 100)\n", "\n", " # display telemetry and instructions\n", " box_label(screen, 'Dx', (70, 20, 75, 20))\n", " box_label(screen, 'Dy', (150, 20, 80, 20))\n", " box_label(screen, 'Altitude', (240, 20, 160, 20))\n", " box_label(screen, 'Fuel', (410, 20, 160, 20))\n", " box_label(screen, 'Eccentricity', (580, 20, 150, 20))\n", " \n", " box_label(screen, '{:.1f}'.format(sat.dx), (70, 50, 75, 20)) \n", " box_label(screen, '{:.1f}'.format(sat.dy), (150, 50, 80, 20))\n", " box_label(screen, '{:.1f}'.format(sat.distance), (240, 50, 160, 20))\n", " box_label(screen, '{}'.format(sat.fuel), (410, 50, 160, 20))\n", " box_label(screen, '{:.8f}'.format(eccentricity), (580, 50, 150, 20))\n", " \n", " instruct_label(screen, instruct_text1, WHITE, 10, 575)\n", " instruct_label(screen, instruct_text2, WHITE, 570, 510)\n", " \n", " # add terminator & border\n", " cast_shadow(screen)\n", " pg.draw.rect(screen, WHITE, (1, 1, 798, 643), 1)\n", " pg.display.flip()" ] }, { "cell_type": "code", "execution_count": 42, "metadata": { "colab": {}, "colab_type": "code", "collapsed": true, "id": "4rBpNaCXTZSe" }, "outputs": [], "source": [ "from PIL import Image, ImageDraw\n", "\n", "images = list()\n", "for i in range(1000):\n", " img = Image.open(\"mars_orbiter/mars_orbiter{0:04d}.jpg\".format(i))\n", " images.append(img)\n", "images[0].save('mars_orbiter.gif',\n", " save_all=True, append_images=images[1:500], optimize=False)" ] }, { "cell_type": "code", "execution_count": 43, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 1000 }, "colab_type": "code", "collapsed": false, "id": "hh9sXRF-TZSj", "outputId": "c0ac8608-7504-4226-ba9b-bd3d502f8bb6" }, "outputs": [ { "data": { "text/html": [ "\n", "\n", "\n", "\n", "\n", "\n", "
\n", " \n", "
\n", " \n", "
\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
\n", "
\n", " \n", " \n", " \n", " \n", " \n", " \n", "
\n", "
\n", "
\n", "\n", "\n", "\n" ], "text/plain": [ "" ] }, "execution_count": 43, "metadata": { "tags": [] }, "output_type": "execute_result" }, { "data": { "image/png": "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3/8iIvJoUbgTkdn4jz2bOa3TFQOvjrthFogx8fjjj7O/v89LL/014Ji1414v8eyz/ey1wp2IPLoU7kRkNuxn/Ok8n94XQmj38WrBzNg+pdm5T9s+z5n7e271vf/1Bx+BnuZE5OFSQ4WIzEbrd3j/aJL7DMNp28MM53xss2ncSYyRnAshRGqt02fcj20fjG/nv5ehblkR+ShSuBORR5b/nbU1mwLe9qPajlh3xyzgXsm5DSd2r5y/ZXf/63/wO/5d7xUR+SjQsayIiIjII+rnOZYNF/FAREREROThULgTERERmRGFOxEREZEZUbgTERERmRGFOxEREZEZUbgTERERmRGFOxEREZEZUbgTERERmRGFOxEREZEZUbgTERERmRGFOxEREZEZUbgTERERmRGFOxEREZEZUbgTERERmRGFOxEREZEZUbgTERERmRGFOxEREZEZUbgTERERmRGFOxEREZEZUbgTERERmRGFOxEREZEZUbgTERERmRGFOxEREZEZUbgTERERmRGFOxEREZEZUbgTERERmRGFOxEREZEZUbgTERERmRGFOxEREZEZUbgTERERmRGFOxEREZEZUbgTERERmRGFOxEREZEZUbgTERERmRGFOxEREZEZUbgTERERmRGFOxEREZEZUbgTERERmRGFOxEREZEZUbgTERERmRGFOxEREZEZUbgTERERmRGFOxEREZEZUbgTERERmRGFOxEREZEZUbgTERERmRGFOxEREZEZUbgTERERmRGFOxEREZEZUbgTERERmRGFOxEREZEZUbgTERERmRGFOxEREZEZUbgTERERmRGFOxEREZEZUbgTERERmRGFOxEREZEZUbgTERERmRGFOxEREZEZUbgTERERmRGFOxEREZEZUbgTERERmRGFOxEREZEZUbgTERERmRGFOxEREZEZUbgTERERmRGFOxEREZEZUbgTERERmRGFOxEREZEZUbgTERERmRGFOxEREZEZUbgTERERmRGFOxEREZEZUbgTERERmRGFOxEREZEZUbgTERERmRGFOxEREZEZUbgTERERmRGFOxEREZEZUbgTERERmRGFOxEREZEZUbgTERERmRGFOxEREZEZUbgTERERmRGFOxEREZEZUbgTERERmRGFOxEREZEZUbgTERERmRGFOxEREZEZUbgTERERmRGFOxEREZEZUbgTERER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\n", "text/plain": [ "
" ] }, "metadata": { "needs_background": "light", "tags": [] }, "output_type": "display_data" } ], "source": [ "# from JSAnimation.IPython_display import display_animation\n", "import matplotlib.pyplot as plt\n", "from matplotlib import animation\n", "from IPython.display import HTML\n", "\n", "plt.figure(figsize=(800/144, 645/144), dpi=144)\n", "patch = plt.imshow(images[0])\n", "plt.axis('off')\n", " \n", "def animate(i):\n", " patch.set_data(images[i])\n", " \n", "anim = animation.FuncAnimation(plt.gcf(), animate, frames=len(images)-500, interval=50)\n", "HTML(anim.to_jshtml())" ] }, { "cell_type": "markdown", "metadata": { "colab_type": "text", "id": "F498oL_Av0z0" }, "source": [ "# Chap15" ] }, { "cell_type": "markdown", "metadata": { "colab_type": "text", "id": "36Qtq1poWjCR" }, "source": [ "soon..." ] }, { "cell_type": "markdown", "metadata": { "colab_type": "text", "id": "NMSu4glcv0xY" }, "source": [ "# Chap16\n" ] }, { "cell_type": "code", "execution_count": 44, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 972 }, "colab_type": "code", "collapsed": false, "id": "POteDEgXYcBs", "outputId": "0c3c5fd2-a488-45ab-ef30-11c6c1178896" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "--2020-08-20 10:52:31-- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/master/Chapter_16/Illinois_votes.txt\n", "Resolving raw.githubusercontent.com (raw.githubusercontent.com)... 151.101.0.133, 151.101.64.133, 151.101.128.133, ...\n", "Connecting to raw.githubusercontent.com (raw.githubusercontent.com)|151.101.0.133|:443... connected.\n", "HTTP request sent, awaiting response... 200 OK\n", "Length: 1893 (1.8K) [text/plain]\n", "Saving to: ‘Illinois_votes.txt’\n", "\n", "\r", "Illinois_votes.txt 0%[ ] 0 --.-KB/s \r", "Illinois_votes.txt 100%[===================>] 1.85K --.-KB/s in 0s \n", "\n", "2020-08-20 10:52:31 (33.8 MB/s) - ‘Illinois_votes.txt’ saved [1893/1893]\n", "\n", "--2020-08-20 10:52:33-- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/master/Chapter_16/Clinton_votes_Illinois.txt\n", "Resolving raw.githubusercontent.com (raw.githubusercontent.com)... 151.101.0.133, 151.101.64.133, 151.101.128.133, ...\n", "Connecting to raw.githubusercontent.com (raw.githubusercontent.com)|151.101.0.133|:443... connected.\n", "HTTP request sent, awaiting response... 200 OK\n", "Length: 529 [text/plain]\n", "Saving to: ‘Clinton_votes_Illinois.txt’\n", "\n", "Clinton_votes_Illin 100%[===================>] 529 --.-KB/s in 0s \n", "\n", "2020-08-20 10:52:33 (31.6 MB/s) - ‘Clinton_votes_Illinois.txt’ saved [529/529]\n", "\n", "--2020-08-20 10:52:35-- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/master/Chapter_16/Johnson_votes_Illinois.txt\n", "Resolving raw.githubusercontent.com (raw.githubusercontent.com)... 151.101.0.133, 151.101.64.133, 151.101.128.133, ...\n", "Connecting to raw.githubusercontent.com (raw.githubusercontent.com)|151.101.0.133|:443... connected.\n", "HTTP request sent, awaiting response... 200 OK\n", "Length: 433 [text/plain]\n", "Saving to: ‘Johnson_votes_Illinois.txt’\n", "\n", "Johnson_votes_Illin 100%[===================>] 433 --.-KB/s in 0s \n", "\n", "2020-08-20 10:52:35 (24.5 MB/s) - ‘Johnson_votes_Illinois.txt’ saved [433/433]\n", "\n", "--2020-08-20 10:52:38-- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/master/Chapter_16/Trump_votes_Illinois.txt\n", "Resolving raw.githubusercontent.com (raw.githubusercontent.com)... 151.101.0.133, 151.101.64.133, 151.101.128.133, ...\n", "Connecting to raw.githubusercontent.com (raw.githubusercontent.com)|151.101.0.133|:443... connected.\n", "HTTP request sent, awaiting response... 200 OK\n", "Length: 557 [text/plain]\n", "Saving to: ‘Trump_votes_Illinois.txt’\n", "\n", "Trump_votes_Illinoi 100%[===================>] 557 --.-KB/s in 0s \n", "\n", "2020-08-20 10:52:38 (29.4 MB/s) - ‘Trump_votes_Illinois.txt’ saved [557/557]\n", "\n", "--2020-08-20 10:52:39-- https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/master/Chapter_16/Stein_votes_Illinois.txt\n", "Resolving raw.githubusercontent.com (raw.githubusercontent.com)... 151.101.0.133, 151.101.64.133, 151.101.128.133, ...\n", "Connecting to raw.githubusercontent.com (raw.githubusercontent.com)|151.101.0.133|:443... connected.\n", "HTTP request sent, awaiting response... 200 OK\n", "Length: 374 [text/plain]\n", "Saving to: ‘Stein_votes_Illinois.txt’\n", "\n", "Stein_votes_Illinoi 100%[===================>] 374 --.-KB/s in 0s \n", "\n", "2020-08-20 10:52:40 (19.3 MB/s) - ‘Stein_votes_Illinois.txt’ saved [374/374]\n", "\n" ] } ], "source": [ "!wget https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/master/Chapter_16/Illinois_votes.txt\n", "!wget https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/master/Chapter_16/Clinton_votes_Illinois.txt\n", "!wget https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/master/Chapter_16/Johnson_votes_Illinois.txt\n", "!wget https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/master/Chapter_16/Trump_votes_Illinois.txt\n", "!wget https://raw.githubusercontent.com/rlvaugh/Impractical_Python_Projects/master/Chapter_16/Stein_votes_Illinois.txt" ] }, { "cell_type": "code", "execution_count": 45, "metadata": { "colab": {}, "colab_type": "code", "collapsed": true, "id": "zetIl5SfWmMa" }, "outputs": [], "source": [ "def load_data(filename):\n", " with open(filename) as f:\n", " lines = f.read().strip().split('\\n')\n", " return [int(i) for i in lines]\n", "\n", "def steal_votes(opponent_votes, candidate_votes, scalar):\n", " new_opponent_votes = []\n", " new_candidate_votes = []\n", " for opp_vote, can_vote in zip(opponent_votes, candidate_votes):\n", " new_opp_vote = round(opp_vote * scalar)\n", " new_opponent_votes.append(new_opp_vote)\n", " stolen_votes = opp_vote - new_opp_vote\n", " new_can_vote = can_vote + stolen_votes\n", " new_candidate_votes.append(new_can_vote)\n", " return new_opponent_votes, new_candidate_votes" ] }, { "cell_type": "code", "execution_count": 46, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 1000 }, "colab_type": "code", "collapsed": false, "id": "8kJGSh0nYTsH", "outputId": "99736082-454f-479b-9d8a-5fc73003342c" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "Trump winning target = 2,706,340 votes\n", "extra votes needed = 560,325\n", "scalar = 0.834\n", "\n", "old Trump: 22790 \t new Trump: 24299 \t old Clinton: 7676 \t new Clinton: 6402\n", "-----------------------------------------------------------------------------------------------\n", "old Trump: 1496 \t new Trump: 1715 \t old Clinton: 1262 \t new Clinton: 1053\n", "-----------------------------------------------------------------------------------------------\n", "old Trump: 4888 \t new Trump: 5316 \t old Clinton: 2068 \t new Clinton: 1725\n", "-----------------------------------------------------------------------------------------------\n", "old Trump: 12282 \t new Trump: 14005 \t old Clinton: 8986 \t new Clinton: 7495\n", "-----------------------------------------------------------------------------------------------\n", "old Trump: 1796 \t new Trump: 1890 \t old Clinton: 476 \t new Clinton: 397\n", "-----------------------------------------------------------------------------------------------\n", "old Trump: 9281 \t new Trump: 10454 \t old Clinton: 6029 \t new Clinton: 5029\n", "-----------------------------------------------------------------------------------------------\n", "old Trump: 1721 \t new Trump: 1860 \t old Clinton: 739 \t new Clinton: 616\n", "-----------------------------------------------------------------------------------------------\n", "old Trump: 4434 \t new Trump: 4922 \t old Clinton: 2447 \t new Clinton: 2041\n", "-----------------------------------------------------------------------------------------------\n", "old Trump: 3216 \t new Trump: 3522 \t old Clinton: 1621 \t new Clinton: 1352\n", "-----------------------------------------------------------------------------------------------\n", "old Trump: 33368 \t new Trump: 42739 \t old Clinton: 50137 \t new Clinton: 41818\n", "-----------------------------------------------------------------------------------------------\n", "old Trump: 10543 \t new Trump: 11340 \t old Clinton: 3992 \t new Clinton: 3330\n", "-----------------------------------------------------------------------------------------------\n", "old Trump: 5622 \t new Trump: 5987 \t old Clinton: 1877 \t new Clinton: 1566\n", "-----------------------------------------------------------------------------------------------\n", "old Trump: 5021 \t new Trump: 5233 \t old Clinton: 1020 \t new Clinton: 851\n", "-----------------------------------------------------------------------------------------------\n", "old Trump: 12412 \t new Trump: 13223 \t old Clinton: 3945 \t new Clinton: 3290\n", "-----------------------------------------------------------------------------------------------\n", "old Trump: 13003 \t new Trump: 14434 \t old Clinton: 7309 \t new Clinton: 6096\n", "-----------------------------------------------------------------------------------------------\n", "old Trump: 453287 \t new Trump: 735863 \t old Clinton: 1611946 \t new Clinton: 1344496\n", "-----------------------------------------------------------------------------------------------\n", "old Trump: 6277 \t new Trump: 6673 \t old Clinton: 1992 \t new Clinton: 1661\n", "-----------------------------------------------------------------------------------------------\n", "old Trump: 4206 \t new Trump: 4424 \t old Clinton: 1031 \t new Clinton: 860\n", "-----------------------------------------------------------------------------------------------\n", "old Trump: 19091 \t new Trump: 23024 \t old Clinton: 20466 \t new Clinton: 17070\n", "-----------------------------------------------------------------------------------------------\n", "old Trump: 5077 \t new Trump: 5476 \t old Clinton: 1910 \t new Clinton: 1593\n", "-----------------------------------------------------------------------------------------------\n", "old Trump: 5698 \t new Trump: 6096 \t old Clinton: 1949 \t new Clinton: 1626\n", "-----------------------------------------------------------------------------------------------\n", "old Trump: 166415 \t new Trump: 208781 \t old Clinton: 228622 \t new Clinton: 190690\n", "-----------------------------------------------------------------------------------------------\n", "old Trump: 5645 \t new Trump: 6004 \t old Clinton: 1793 \t new Clinton: 1496\n", "-----------------------------------------------------------------------------------------------\n", "old Trump: 2778 \t new Trump: 2867 \t old Clinton: 434 \t new Clinton: 362\n", "-----------------------------------------------------------------------------------------------\n", "old Trump: 13635 \t new Trump: 14271 \t old Clinton: 3083 \t new Clinton: 2571\n", "-----------------------------------------------------------------------------------------------\n", "old Trump: 7372 \t new Trump: 7730 \t old Clinton: 1819 \t new Clinton: 1517\n", "-----------------------------------------------------------------------------------------------\n", "old Trump: 4480 \t new Trump: 4783 \t old Clinton: 1414 \t new Clinton: 1179\n", "-----------------------------------------------------------------------------------------------\n", "old Trump: 13116 \t new Trump: 14020 \t old Clinton: 4727 \t new Clinton: 3943\n", "-----------------------------------------------------------------------------------------------\n", "old Trump: 8492 \t new Trump: 9679 \t old Clinton: 6133 \t new Clinton: 5115\n", "-----------------------------------------------------------------------------------------------\n", "old Trump: 1942 \t new Trump: 2066 \t old Clinton: 657 \t new Clinton: 548\n", "-----------------------------------------------------------------------------------------------\n", "old Trump: 4145 \t new Trump: 4378 \t old Clinton: 1205 \t new Clinton: 1005\n", "-----------------------------------------------------------------------------------------------\n", "old Trump: 13454 \t new Trump: 15035 \t old Clinton: 8065 \t new Clinton: 6727\n", "-----------------------------------------------------------------------------------------------\n", "old Trump: 3206 \t new Trump: 3362 \t old Clinton: 802 \t new Clinton: 669\n", "-----------------------------------------------------------------------------------------------\n", "old Trump: 6430 \t new Trump: 6854 \t old Clinton: 2139 \t new Clinton: 1784\n", "-----------------------------------------------------------------------------------------------\n", "old Trump: 1653 \t new Trump: 1734 \t old Clinton: 420 \t new Clinton: 350\n", "-----------------------------------------------------------------------------------------------\n", "old Trump: 2155 \t new Trump: 2376 \t old Clinton: 1155 \t new Clinton: 963\n", "-----------------------------------------------------------------------------------------------\n", "old Trump: 13985 \t new Trump: 15696 \t old Clinton: 8871 \t new Clinton: 7399\n", "-----------------------------------------------------------------------------------------------\n", "old Trump: 9750 \t new Trump: 10282 \t old Clinton: 2504 \t new Clinton: 2089\n", "-----------------------------------------------------------------------------------------------\n", "old Trump: 10843 \t new Trump: 13060 \t old Clinton: 11634 \t new Clinton: 9704\n", "-----------------------------------------------------------------------------------------------\n", "old Trump: 3975 \t new Trump: 4162 \t old Clinton: 924 \t new Clinton: 771\n", "-----------------------------------------------------------------------------------------------\n", "old Trump: 11695 \t new Trump: 12550 \t old Clinton: 4425 \t new Clinton: 3691\n", "-----------------------------------------------------------------------------------------------\n", "old Trump: 7748 \t new Trump: 8276 \t old Clinton: 2679 \t new Clinton: 2235\n", "-----------------------------------------------------------------------------------------------\n", "old Trump: 6121 \t new Trump: 6963 \t old Clinton: 4462 \t new Clinton: 3722\n", "-----------------------------------------------------------------------------------------------\n", "old Trump: 4649 \t new Trump: 4877 \t old Clinton: 1142 \t new Clinton: 953\n", "-----------------------------------------------------------------------------------------------\n", "old Trump: 82734 \t new Trump: 101956 \t old Clinton: 103665 \t new Clinton: 86465\n", "-----------------------------------------------------------------------------------------------\n", "old Trump: 25129 \t new Trump: 28705 \t old Clinton: 18971 \t new Clinton: 15823\n", "-----------------------------------------------------------------------------------------------\n", "old Trump: 24961 \t new Trump: 29657 \t old Clinton: 24884 \t new Clinton: 20755\n", "-----------------------------------------------------------------------------------------------\n", "old Trump: 10737 \t new Trump: 12640 \t old Clinton: 10083 \t new Clinton: 8410\n", "-----------------------------------------------------------------------------------------------\n", "old Trump: 109767 \t new Trump: 140998 \t old Clinton: 171095 \t new Clinton: 142707\n", "-----------------------------------------------------------------------------------------------\n", "old Trump: 26689 \t new Trump: 30421 \t old Clinton: 19543 \t new Clinton: 16300\n", "-----------------------------------------------------------------------------------------------\n", "old Trump: 4521 \t new Trump: 4774 \t old Clinton: 1290 \t new Clinton: 1076\n", "-----------------------------------------------------------------------------------------------\n", "old Trump: 8612 \t new Trump: 9715 \t old Clinton: 5528 \t new Clinton: 4611\n", "-----------------------------------------------------------------------------------------------\n", "old Trump: 10208 \t new Trump: 11025 \t old Clinton: 4023 \t new Clinton: 3356\n", "-----------------------------------------------------------------------------------------------\n", "old Trump: 8181 \t new Trump: 8842 \t old Clinton: 3313 \t new Clinton: 2763\n", "-----------------------------------------------------------------------------------------------\n", "old Trump: 26866 \t new Trump: 30292 \t old Clinton: 18343 \t new Clinton: 15300\n", "-----------------------------------------------------------------------------------------------\n", "old Trump: 14322 \t new Trump: 15628 \t old Clinton: 6689 \t new Clinton: 5579\n", "-----------------------------------------------------------------------------------------------\n", "old Trump: 70490 \t new Trump: 80096 \t old Clinton: 50587 \t new Clinton: 42194\n", "-----------------------------------------------------------------------------------------------\n", "old Trump: 11859 \t new Trump: 12702 \t old Clinton: 4369 \t new Clinton: 3644\n", "-----------------------------------------------------------------------------------------------\n", "old Trump: 3785 \t new Trump: 4139 \t old Clinton: 1789 \t new Clinton: 1492\n", "-----------------------------------------------------------------------------------------------\n", "old Trump: 4058 \t new Trump: 4454 \t old Clinton: 2014 \t new Clinton: 1680\n", "-----------------------------------------------------------------------------------------------\n", "old Trump: 4846 \t new Trump: 5143 \t old Clinton: 1558 \t new Clinton: 1300\n", "-----------------------------------------------------------------------------------------------\n", "old Trump: 6795 \t new Trump: 7812 \t old Clinton: 5288 \t new Clinton: 4411\n", "-----------------------------------------------------------------------------------------------\n", "old Trump: 71612 \t new Trump: 83249 \t old Clinton: 60803 \t new Clinton: 50715\n", "-----------------------------------------------------------------------------------------------\n", "old Trump: 37237 \t new Trump: 44249 \t old Clinton: 36196 \t new Clinton: 30190\n", "-----------------------------------------------------------------------------------------------\n", "old Trump: 4231 \t new Trump: 4596 \t old Clinton: 1817 \t new Clinton: 1516\n", "-----------------------------------------------------------------------------------------------\n", "old Trump: 4807 \t new Trump: 5410 \t old Clinton: 3071 \t new Clinton: 2561\n", "-----------------------------------------------------------------------------------------------\n", "old Trump: 12629 \t new Trump: 13703 \t old Clinton: 5535 \t new Clinton: 4617\n", "-----------------------------------------------------------------------------------------------\n", "old Trump: 8630 \t new Trump: 9331 \t old Clinton: 3504 \t new Clinton: 2923\n", "-----------------------------------------------------------------------------------------------\n", "old Trump: 9076 \t new Trump: 9997 \t old Clinton: 4696 \t new Clinton: 3917\n", "-----------------------------------------------------------------------------------------------\n", "old Trump: 4455 \t new Trump: 4747 \t old Clinton: 1481 \t new Clinton: 1235\n", "-----------------------------------------------------------------------------------------------\n", "old Trump: 14352 \t new Trump: 15926 \t old Clinton: 8050 \t new Clinton: 6714\n", "-----------------------------------------------------------------------------------------------\n", "old Trump: 35633 \t new Trump: 42746 \t old Clinton: 38060 \t new Clinton: 31745\n", "-----------------------------------------------------------------------------------------------\n", "old Trump: 6855 \t new Trump: 7340 \t old Clinton: 2462 \t new Clinton: 2054\n", "-----------------------------------------------------------------------------------------------\n", "old Trump: 5634 \t new Trump: 6180 \t old Clinton: 2645 \t new Clinton: 2206\n", "-----------------------------------------------------------------------------------------------\n", "old Trump: 5754 \t new Trump: 6038 \t old Clinton: 1413 \t new Clinton: 1179\n", "-----------------------------------------------------------------------------------------------\n", "old Trump: 1678 \t new Trump: 1753 \t old Clinton: 375 \t new Clinton: 313\n", "-----------------------------------------------------------------------------------------------\n", "old Trump: 1675 \t new Trump: 1847 \t old Clinton: 962 \t new Clinton: 802\n", "-----------------------------------------------------------------------------------------------\n", "old Trump: 1767 \t new Trump: 1986 \t old Clinton: 1147 \t new Clinton: 957\n", "-----------------------------------------------------------------------------------------------\n", "old Trump: 10023 \t new Trump: 10697 \t old Clinton: 3439 \t new Clinton: 2868\n", "-----------------------------------------------------------------------------------------------\n", "old Trump: 5739 \t new Trump: 6054 \t old Clinton: 1584 \t new Clinton: 1321\n", "-----------------------------------------------------------------------------------------------\n", "old Trump: 26998 \t new Trump: 32979 \t old Clinton: 32298 \t new Clinton: 26939\n", "-----------------------------------------------------------------------------------------------\n", "old Trump: 8276 \t new Trump: 8775 \t old Clinton: 2572 \t new Clinton: 2145\n", "-----------------------------------------------------------------------------------------------\n", "old Trump: 49944 \t new Trump: 57719 \t old Clinton: 40907 \t new Clinton: 34120\n", "-----------------------------------------------------------------------------------------------\n", "old Trump: 2524 \t new Trump: 2735 \t old Clinton: 1075 \t new Clinton: 897\n", "-----------------------------------------------------------------------------------------------\n", "old Trump: 1966 \t new Trump: 2070 \t old Clinton: 535 \t new Clinton: 446\n", "-----------------------------------------------------------------------------------------------\n", "old Trump: 8229 \t new Trump: 8680 \t old Clinton: 2288 \t new Clinton: 1908\n", "-----------------------------------------------------------------------------------------------\n", "old Trump: 53857 \t new Trump: 64875 \t old Clinton: 60756 \t new Clinton: 50676\n", "-----------------------------------------------------------------------------------------------\n", "old Trump: 1778 \t new Trump: 1933 \t old Clinton: 751 \t new Clinton: 626\n", "-----------------------------------------------------------------------------------------------\n", "old Trump: 11083 \t new Trump: 12568 \t old Clinton: 7768 \t new Clinton: 6479\n", "-----------------------------------------------------------------------------------------------\n", "old Trump: 38707 \t new Trump: 42834 \t old Clinton: 20685 \t new Clinton: 17253\n", "-----------------------------------------------------------------------------------------------\n", "old Trump: 5790 \t new Trump: 6246 \t old Clinton: 2402 \t new Clinton: 2003\n", "-----------------------------------------------------------------------------------------------\n", "old Trump: 19087 \t new Trump: 20984 \t old Clinton: 10039 \t new Clinton: 8373\n", "-----------------------------------------------------------------------------------------------\n", "old Trump: 4047 \t new Trump: 4274 \t old Clinton: 1151 \t new Clinton: 960\n", "-----------------------------------------------------------------------------------------------\n", "old Trump: 4275 \t new Trump: 4846 \t old Clinton: 2987 \t new Clinton: 2491\n", "-----------------------------------------------------------------------------------------------\n", "old Trump: 5571 \t new Trump: 5871 \t old Clinton: 1448 \t new Clinton: 1208\n", "-----------------------------------------------------------------------------------------------\n", "old Trump: 6967 \t new Trump: 7182 \t old Clinton: 1048 \t new Clinton: 874\n", "-----------------------------------------------------------------------------------------------\n", "old Trump: 5640 \t new Trump: 5914 \t old Clinton: 1412 \t new Clinton: 1178\n", "-----------------------------------------------------------------------------------------------\n", "old Trump: 12615 \t new Trump: 14713 \t old Clinton: 11035 \t new Clinton: 9204\n", "-----------------------------------------------------------------------------------------------\n", "old Trump: 132720 \t new Trump: 160533 \t old Clinton: 151927 \t new Clinton: 126720\n", "-----------------------------------------------------------------------------------------------\n", "old Trump: 21570 \t new Trump: 23225 \t old Clinton: 8581 \t new Clinton: 7157\n", "-----------------------------------------------------------------------------------------------\n", "old Trump: 55624 \t new Trump: 65991 \t old Clinton: 55713 \t new Clinton: 46469\n", "-----------------------------------------------------------------------------------------------\n", "old Trump: 13207 \t new Trump: 14244 \t old Clinton: 5092 \t new Clinton: 4247\n", "-----------------------------------------------------------------------------------------------\n", "TOTALS:\n", "old Trump: 2,146,015 \t new Trump: 2,706,340 \t old Clinton: 3,090,729 new Clinton: 2,577,922\n" ] } ], "source": [ "# load vote data\n", "c_votes = load_data('Clinton_votes_Illinois.txt')\n", "j_votes = load_data('Johnson_votes_Illinois.txt')\n", "s_votes = load_data('Stein_votes_Illinois.txt')\n", "t_votes = load_data('Trump_votes_Illinois.txt')\n", "\n", "total_votes = sum(c_votes + j_votes + s_votes + t_votes)\n", "\n", "\n", "# assume Trump amasses a plurality of the vote with 49%\n", "t_target = round(total_votes * 0.49)\n", "print(\"\\nTrump winning target = {:,} votes\".format(t_target))\n", "\n", "# calculate extra votes needed for Trump victory\n", "extra_votes_needed = abs(t_target - sum(t_votes))\n", "print(\"extra votes needed = {:,}\".format(extra_votes_needed))\n", "\n", "# calculate scalar needed to generate extra votes\n", "scalar = 1 - (extra_votes_needed / sum(c_votes + j_votes + s_votes))\n", "print(\"scalar = {:.3}\".format(scalar))\n", "print()\n", "\n", "# flip vote counts based on scalar & build new combined list of votes\n", "fake_counts = []\n", "new_c_votes, new_t_votes = steal_votes(c_votes, t_votes, scalar)\n", "fake_counts.extend(new_c_votes)\n", "new_j_votes, new_t_votes = steal_votes(j_votes, new_t_votes, scalar)\n", "fake_counts.extend(new_j_votes)\n", "new_s_votes, new_t_votes = steal_votes(s_votes, new_t_votes, scalar)\n", "fake_counts.extend(new_s_votes)\n", "fake_counts.extend(new_t_votes) # add last as has been changing up til now \n", "\n", "# compare old and new vote counts & totals in tabular form\n", "# switch-out \"Trump\" and \"Clinton\" as necessary\n", "for i in range(0, len(t_votes)):\n", " print(\"old Trump: {} \\t new Trump: {} \\t old Clinton: {} \\t \" \\\n", " \"new Clinton: {}\".\n", " format(t_votes[i], new_t_votes[i], c_votes[i], new_c_votes[i])) \n", " print(\"-\" * 95)\n", "print(\"TOTALS:\")\n", "print(\"old Trump: {:,} \\t new Trump: {:,} \\t old Clinton: {:,} \" \\\n", " \"new Clinton: {:,}\".format(sum(t_votes), sum(new_t_votes),\n", " sum(c_votes), sum(new_c_votes)))\n", " \n", "# write-out a text file to use as input to benford.py program\n", "# this program will check conformance of faked votes to Benford's Law\n", "with open('fake_Illinois_counts.txt', 'w') as f:\n", " for count in fake_counts:\n", " f.write(\"{}\\n\".format(count))" ] }, { "cell_type": "code", "execution_count": 47, "metadata": { "colab": {}, "colab_type": "code", "collapsed": true, "id": "4EUXUENjaGEI" }, "outputs": [], "source": [ "import sys\n", "import math\n", "from collections import defaultdict\n", "import matplotlib.pyplot as plt\n", "\n", "# Benford's Law percentages for leading digits 1-9\n", "BENFORD = [30.1, 17.6, 12.5, 9.7, 7.9, 6.7, 5.8, 5.1, 4.6]\n", "\n", "def load_data(filename):\n", " with open(filename) as f:\n", " return f.read().strip().split('\\n')\n", " \n", "def count_first_digits(data_list):\n", " first_digits = defaultdict(int) # default value of int is 0\n", " for sample in data_list:\n", " if sample == '':\n", " continue\n", " try:\n", " int(sample)\n", " except ValueError as e:\n", " print(e, file=sys.stderr)\n", " print(\"Samples must be integers. Exiting.\", file=sys.stderr)\n", " sys.exit(1)\n", " first_digits[sample[0]] += 1 \n", " \n", " # check for missing digits\n", " keys = [str(digit) for digit in range(1, 10)]\n", " for key in keys:\n", " if key not in first_digits:\n", " first_digits[key] = 0\n", " \n", " data_count = [v for (k, v) in sorted(first_digits.items())]\n", " total_count = sum(data_count)\n", " data_pct = [(i / total_count) * 100 for i in data_count]\n", " return data_count, data_pct, total_count\n", "\n", "def get_expected_counts(total_count):\n", " return [round(p * total_count / 100) for p in BENFORD]\n", "\n", "def chi_square_test(data_count, expected_counts):\n", " chi_square_stat = 0 # chi square test statistic\n", " for data, expected in zip(data_count, expected_counts):\n", " chi_square = math.pow(data - expected, 2)\n", " chi_square_stat += chi_square / expected\n", " print(\"\\nChi-squared Test Statistic = {:.3f}\".format(chi_square_stat))\n", " print(\"Critical value at a P-value of 0.05 is 15.51.\") \n", " return chi_square_stat < 15.51\n", "\n", "def bar_chart(data_pct):\n", " fig, ax = plt.subplots()\n", "\n", " index = [i + 1 for i in range(len(data_pct))] # 1st digits for x-axis\n", "\n", " # text for labels, title and ticks\n", " fig.canvas.set_window_title('Percentage First Digits')\n", " ax.set_title('Data vs. Benford Values', fontsize=15)\n", " ax.set_ylabel('Frequency (%)', fontsize=16)\n", " ax.set_xticks(index)\n", " ax.set_xticklabels(index, fontsize=14)\n", "\n", " # build bars \n", " rects = ax.bar(index, data_pct, width=0.95, color='black', label='Data')\n", "\n", " # attach a text label above each bar displaying its height\n", " for rect in rects:\n", " height = rect.get_height()\n", " ax.text(rect.get_x() + rect.get_width()/2, height,\n", " '{:0.1f}'.format(height), ha='center', va='bottom', \n", " fontsize=13)\n", "\n", " # plot Benford values as red dots\n", " ax.scatter(index, BENFORD, s=150, c='red', zorder=2, label='Benford')\n", "\n", " # Hide the right and top spines & add legend\n", " ax.spines['right'].set_visible(False)\n", " ax.spines['top'].set_visible(False)\n", " ax.legend(prop={'size':15}, frameon=False)\n", " \n", " plt.show()\n" ] }, { "cell_type": "code", "execution_count": 48, "metadata": { "colab": {}, "colab_type": "code", "collapsed": true, "id": "5svexOduY6lP" }, "outputs": [], "source": [ "def bar_benford(file):\n", " while True:\n", " filename = file\n", " try:\n", " data_list = load_data(file)\n", " except IOError as e:\n", " print(\"{}. Try again.\".format(e), file=sys.stderr)\n", " else:\n", " break\n", " data_count, data_pct, total_count = count_first_digits(data_list)\n", " expected_counts = get_expected_counts(total_count)\n", " print(\"\\nobserved counts = {}\".format(data_count))\n", " print(\"expected counts = {}\".format(expected_counts), \"\\n\")\n", "\n", " print(\"First Digit Probabilities:\")\n", " for i in range(1, 10):\n", " print(\"{}: observed: {:.3f} expected: {:.3f}\".\n", " format(i, data_pct[i - 1] / 100, BENFORD[i - 1] / 100))\n", "\n", " if chi_square_test(data_count, expected_counts):\n", " print(\"Observed distribution matches expected distribution.\")\n", " else:\n", " print(\"Observed distribution does not match expected.\", file=sys.stderr) \n", "\n", " bar_chart(data_pct)" ] }, { "cell_type": "code", "execution_count": 49, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 593 }, "colab_type": "code", "collapsed": false, "id": "fyjrqpm7amhG", "outputId": "de357917-28cd-41e8-f8a7-09fe326011c2" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "observed counts = [129, 62, 45, 48, 40, 25, 23, 21, 15]\n", "expected counts = [123, 72, 51, 40, 32, 27, 24, 21, 19] \n", "\n", "First Digit Probabilities:\n", "1: observed: 0.316 expected: 0.301\n", "2: observed: 0.152 expected: 0.176\n", "3: observed: 0.110 expected: 0.125\n", "4: observed: 0.118 expected: 0.097\n", "5: observed: 0.098 expected: 0.079\n", "6: observed: 0.061 expected: 0.067\n", "7: observed: 0.056 expected: 0.058\n", "8: observed: 0.051 expected: 0.051\n", "9: observed: 0.037 expected: 0.046\n", "\n", "Chi-squared Test Statistic = 7.019\n", "Critical value at a P-value of 0.05 is 15.51.\n", "Observed distribution matches expected distribution.\n" ] }, { "data": { "image/png": "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\n", "text/plain": [ "
" ] }, "metadata": { "needs_background": "light", "tags": [] }, "output_type": "display_data" } ], "source": [ "bar_benford('Illinois_votes.txt')" ] }, { "cell_type": "code", "execution_count": 50, "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 593 }, "colab_type": "code", "collapsed": false, "id": "NfmbCkm_cXur", "outputId": "52f19068-5a4f-4cdd-d008-1c5aaa15217f" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "observed counts = [124, 63, 44, 49, 30, 37, 20, 20, 21]\n", "expected counts = [123, 72, 51, 40, 32, 27, 24, 21, 19] \n", "\n", "First Digit Probabilities:\n", "1: observed: 0.304 expected: 0.301\n", "2: observed: 0.154 expected: 0.176\n", "3: observed: 0.108 expected: 0.125\n", "4: observed: 0.120 expected: 0.097\n", "5: observed: 0.074 expected: 0.079\n", "6: observed: 0.091 expected: 0.067\n", "7: observed: 0.049 expected: 0.058\n", "8: observed: 0.049 expected: 0.051\n", "9: observed: 0.051 expected: 0.046\n", "\n", "Chi-squared Test Statistic = 8.872\n", "Critical value at a P-value of 0.05 is 15.51.\n", "Observed distribution matches expected distribution.\n" ] }, { "data": { "image/png": "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\n", "text/plain": [ "
" ] }, "metadata": { "needs_background": "light", "tags": [] }, "output_type": "display_data" } ], "source": [ "bar_benford('fake_Illinois_counts.txt')" ] } ], "metadata": { "colab": { "collapsed_sections": [], "name": "impractical_python.ipynb", "provenance": [], "toc_visible": true }, "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.5.2" } }, "nbformat": 4, "nbformat_minor": 0 }