├── .gitignore ├── .gitmodules ├── Makefile ├── README.md ├── TODO.txt ├── content ├── 2012-08-08-memoryview-benchmarks.md ├── 2012-08-16-memoryview-benchmarks-2.md ├── 2012-08-18-matplotlib-animation-tutorial.md ├── 2012-08-24-numba-vs-cython.md ├── 2012-09-05-quantum-python.md ├── 2012-09-12-dynamic-programming-in-python.md ├── 2012-09-20-why-python-is-the-last.md ├── 2012-09-26-optical-illusions-in-matplotlib.md ├── 2012-10-04-blogging-with-ipython.md ├── 2012-10-07-xkcd-style-plots-in-matplotlib.md ├── 2012-10-14-scipy-sparse-graph-module-word-ladders.md ├── 2012-11-24-simple-3d-visualization-in-matplotlib.md ├── 2012-11-26-3d-interactive-rubiks-cube-in-python.md ├── 2012-12-01-a-primer-on-python-metaclasses.md ├── 2012-12-06-minesweeper-in-matplotlib.md ├── 2012-12-19-sparse-svds-in-python.md ├── 2013-01-03-will-scientists-ever-move-to-python-3.md ├── 2013-01-13-hacking-super-mario-bros-with-python.md ├── 2013-02-02-setting-up-a-mac-for-python-development.md ├── 2013-02-16-animating-the-lorentz-system-in-3d.md ├── 2013-03-23-matplotlib-and-the-future-of-visualization-in-python.md ├── 2013-04-15-code-golf-in-python-sudoku.md ├── 2013-04-29-benchmarking-nearest-neighbor-searches-in-python.md ├── 2013-05-07-migrating-from-octopress-to-pelican.md ├── 2013-05-12-embedding-matplotlib-animations.md ├── 2013-05-19-a-javascript-viewer-for-matplotlib-animations.md ├── 2013-05-28-a-simple-animation-the-magic-triangle.md ├── 2013-06-01-ipython-notebook-javascript-python-communication.md ├── 2013-06-15-numba-vs-cython-take-2.md ├── 2013-07-10-XKCD-plots-in-matplotlib.md ├── 2013-08-07-conways-game-of-life.md ├── 2013-08-28-understanding-the-fft.md ├── 2013-10-26-big-data-brain-drain.md ├── 2013-12-01-kernel-density-estimation.md ├── 2013-12-05-static-interactive-widgets.md ├── 2013-12-19-a-d3-viewer-for-matplotlib.md ├── 2014-01-10-d3-plugins-truly-interactive.md ├── 2014-03-11-frequentism-and-bayesianism-a-practical-intro.md ├── 2014-05-05-introduction-to-the-python-buffer-protocol.md ├── 2014-05-09-why-python-is-slow.md ├── 2014-06-06-frequentism-and-bayesianism-2-when-results-differ.md ├── 2014-06-10-is-seattle-really-seeing-an-uptick-in-cycling.md ├── 2014-06-12-frequentism-and-bayesianism-3-confidence-credibility.md ├── 2014-06-14-frequentism-and-bayesianism-4-bayesian-in-python.md ├── 2014-08-22-hacking-academia.md ├── 2014-09-02-on-frequentism-and-fried-chicken.md ├── 2014-10-16-how-bad-is-your-colormap.md ├── 2014-11-11-the-hipster-effect-interactive.md ├── 2015-02-24-optimizing-python-with-numpy-and-numba.md ├── 2015-06-13-lomb-scargle-in-python.md ├── 2015-07-06-model-complexity-myth.md ├── 2015-07-23-learning-seattles-work-habits-from-bicycle-counts.md ├── 2015-08-07-frequentism-and-bayesianism-5-model-selection.md ├── 2015-08-14-out-of-core-dataframes-in-python.md ├── 2015-10-17-analyzing-pronto-cycleshare-data-with-python-and-pandas.md ├── 2016-08-25-conda-myths-and-misconceptions.md ├── downloads │ ├── code │ │ ├── animate_square.py │ │ ├── basic_animation.py │ │ ├── convert_notebook.py │ │ ├── double_pendulum.py │ │ ├── double_pendulum_xkcd.py │ │ ├── example.py │ │ ├── hello_world.py │ │ ├── lorentz_animation.py │ │ ├── mario │ │ │ ├── animate_mario.py │ │ │ ├── draw_mario.py │ │ │ ├── mario_ROM.zip │ │ │ ├── test_mario.py │ │ │ └── view_pattern_table.py │ │ ├── minesweeper.py │ │ ├── particle_box.py │ │ ├── plot_svd_benchmarks.py │ │ └── schrodinger.py │ ├── notebooks │ │ ├── .gitignore │ │ ├── 3DCube.ipynb │ │ ├── AnimationEmbedding.ipynb │ │ ├── BufferProtocol.ipynb │ │ ├── CondaMyths.ipynb │ │ ├── FreqBayes.ipynb │ │ ├── FreqBayes2.ipynb │ │ ├── FreqBayes3.ipynb │ │ ├── FreqBayes4.ipynb │ │ ├── FreqBayes5.ipynb │ │ ├── GameOfLife.ipynb │ │ ├── HipsterEffect.ipynb │ │ ├── HowBadIsYourColormap.ipynb │ │ ├── IPythonWidgets.ipynb │ │ ├── JSAnimation.ipynb │ │ ├── JSInteraction.ipynb │ │ ├── KDEBench.ipynb │ │ ├── LombScarglePython.ipynb │ │ ├── MagicTriangle.ipynb │ │ ├── MetaClasses.ipynb │ │ ├── ModelComplexityMyth.ipynb │ │ ├── NUFFT.ipynb │ │ ├── NUFFT_lpr.txt │ │ ├── NumbaCython.ipynb │ │ ├── OutOfCoreMapping.ipynb │ │ ├── ProntoData.ipynb │ │ ├── SeattleCycling.ipynb │ │ ├── SeattleCycling2.ipynb │ │ ├── SudokuCodeGolf.ipynb │ │ ├── TestNotebook.ipynb │ │ ├── TreeBench.ipynb │ │ ├── UnderstandingTheFFT.ipynb │ │ ├── WhyPythonIsSlow.ipynb │ │ ├── XKCD_plots.ipynb │ │ ├── XKCD_sketch_path.ipynb │ │ ├── bayesian_blocks.ipynb │ │ ├── memview_bench.ipynb │ │ ├── memview_bench_2.ipynb │ │ ├── mpld3Demo.ipynb │ │ ├── mpld3_plugins.ipynb │ │ ├── nb_in_octopress.ipynb │ │ ├── numba_vs_cython.ipynb │ │ └── sparse-graph.ipynb │ └── videos │ │ ├── MagicCube.mp4 │ │ ├── MagicCube_frame.jpg │ │ ├── animate_square.mp4 │ │ ├── animate_square.png │ │ ├── basic_animation.mp4 │ │ ├── basic_animation_frame.png │ │ ├── double_pendulum.mp4 │ │ ├── double_pendulum_frame.png │ │ ├── double_pendulum_xkcd.mp4 │ │ ├── lorentz_attractor.mp4 │ │ ├── lorentz_attractor_frame.png │ │ ├── particle_box.mp4 │ │ ├── particle_box_frame.png │ │ ├── schrodinger_barrier.mp4 │ │ └── schrodinger_barrier_frame.png ├── favicon.png ├── figures │ ├── author_count.png │ ├── bayesblocks1.png │ ├── bayesblocks2.png │ ├── bayesblocks3.png │ ├── jet.png │ ├── mpl_version.png │ ├── svd_benchmarks.png │ └── xkcd_version.png └── images │ ├── Data_Science_VD.png │ ├── MagicTriangle.gif │ ├── OSX10.8.png │ ├── OSX_terminal.png │ ├── array_vs_list.png │ ├── bird_32_gray.png │ ├── bird_32_gray_fail.png │ ├── cint_vs_pyint.png │ ├── code_bg.png │ ├── dotted-border.png │ ├── dwf_tweet.png │ ├── emacs_icon.jpeg │ ├── email.png │ ├── galaxy.jpg │ ├── line-tile.png │ ├── mario.gif │ ├── mario_graphics1.png │ ├── mario_graphics2.png │ ├── mario_graphics3.png │ ├── mario_pattern_background.png │ ├── mario_pattern_foreground.png │ ├── mario_pattern_sourcecode.png │ ├── minesweeper.gif │ ├── minesweeper_2.gif │ ├── nodebox-physics-flock.jpg │ ├── noise.png │ ├── original_illusion.gif │ ├── pi_shaped.png │ ├── poly_color.gif │ ├── rss.png │ ├── search.png │ ├── sudoku.png │ ├── textmate_icon.jpg │ └── vim_icon.png ├── develop_server.sh ├── environment.yml ├── pelicanconf.py └── publishconf.py /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | *.pyc 3 | output 4 | _build 5 | _nb_header.html -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "pelican-plugins"] 2 | path = pelican-plugins 3 | url = git@github.com:getpelican/pelican-plugins.git 4 | [submodule "pelican-octopress-theme"] 5 | path = pelican-octopress-theme 6 | url = git@github.com:jakevdp/pelican-octopress-theme.git 7 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | PELICAN=pelican 2 | PELICANOPTS= 3 | 4 | BASEDIR=$(CURDIR) 5 | INPUTDIR=$(BASEDIR)/content 6 | OUTPUTDIR=$(BASEDIR)/output 7 | CONFFILE=$(BASEDIR)/pelicanconf.py 8 | PUBLISHCONF=$(BASEDIR)/publishconf.py 9 | DEPLOYREPOSITORY=jakevdp.github.io 10 | 11 | FTP_HOST=localhost 12 | FTP_USER=anonymous 13 | FTP_TARGET_DIR=/ 14 | 15 | SSH_HOST=localhost 16 | SSH_PORT=22 17 | SSH_USER=root 18 | SSH_TARGET_DIR=/var/www 19 | 20 | DROPBOX_DIR=~/Dropbox/Public/ 21 | 22 | help: 23 | @echo 'Makefile for a pelican Web site ' 24 | @echo ' ' 25 | @echo 'Usage: ' 26 | @echo ' make html (re)generate the web site ' 27 | @echo ' make clean remove the generated files ' 28 | @echo ' make regenerate regenerate files upon modification ' 29 | @echo ' make publish generate using production settings ' 30 | @echo ' make serve serve site at http://localhost:8000' 31 | @echo ' make devserver start/restart develop_server.sh ' 32 | @echo ' ssh_upload upload the web site via SSH ' 33 | @echo ' rsync_upload upload the web site via rsync+ssh ' 34 | @echo ' dropbox_upload upload the web site via Dropbox ' 35 | @echo ' ftp_upload upload the web site via FTP ' 36 | @echo ' github upload the web site via gh-pages ' 37 | @echo ' ' 38 | 39 | 40 | html: clean $(OUTPUTDIR)/index.html 41 | @echo 'Done' 42 | 43 | $(OUTPUTDIR)/%.html: 44 | $(PELICAN) $(INPUTDIR) -o $(OUTPUTDIR) -s $(CONFFILE) $(PELICANOPTS) 45 | 46 | clean: 47 | find $(OUTPUTDIR) -mindepth 1 -delete 48 | 49 | regenerate: clean 50 | $(PELICAN) -r $(INPUTDIR) -o $(OUTPUTDIR) -s $(CONFFILE) $(PELICANOPTS) 51 | 52 | serve: 53 | cd $(OUTPUTDIR) && python -m SimpleHTTPServer 54 | 55 | devserver: 56 | $(BASEDIR)/develop_server.sh restart 57 | 58 | publish: 59 | $(PELICAN) $(INPUTDIR) -o $(OUTPUTDIR) -s $(PUBLISHCONF) $(PELICANOPTS) 60 | 61 | ssh_upload: publish 62 | scp -P $(SSH_PORT) -r $(OUTPUTDIR)/* $(SSH_USER)@$(SSH_HOST):$(SSH_TARGET_DIR) 63 | 64 | rsync_upload: publish 65 | rsync -e "ssh -p $(SSH_PORT)" -P -rvz --delete $(OUTPUTDIR) $(SSH_USER)@$(SSH_HOST):$(SSH_TARGET_DIR) 66 | 67 | dropbox_upload: publish 68 | cp -r $(OUTPUTDIR)/* $(DROPBOX_DIR) 69 | 70 | ftp_upload: publish 71 | lftp ftp://$(FTP_USER)@$(FTP_HOST) -e "mirror -R $(OUTPUTDIR) $(FTP_TARGET_DIR) ; quit" 72 | 73 | deploy: publish 74 | if test -d _build; \ 75 | then echo " (_build directory exists)"; \ 76 | else mkdir _build; \ 77 | fi 78 | if test -d _build/$(DEPLOYREPOSITORY); \ 79 | then echo " (repository directory exists)"; \ 80 | else cd _build && git clone git@github.com:jakevdp/$(DEPLOYREPOSITORY).git; \ 81 | fi 82 | cd _build/$(DEPLOYREPOSITORY) && git pull 83 | rsync -r $(OUTPUTDIR)/* _build/$(DEPLOYREPOSITORY)/ 84 | cd _build/$(DEPLOYREPOSITORY) && git add . && git commit -m "make deploy" 85 | cd _build/$(DEPLOYREPOSITORY) && git push origin master 86 | 87 | .PHONY: html help clean regenerate serve devserver publish ssh_upload rsync_upload dropbox_upload ftp_upload github 88 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | OBSOLETE source for Pythonic Perambulations 2 | ------------------------------------------- 3 | 4 | This was the original source of my [Pythonic Perambulations](http://jakevdp.github.io) 5 | blog,built using the [Pelican](http://blog.getpelican.com/) blogging platform. 6 | See http://github.com/jakevdp/jakevdp.github.io-source/ for the current version 7 | 8 | Requirements 9 | ------------ 10 | 11 | - Recent version of [IPython](http://github.com/ipython/ipython). The 12 | liquid_tags plugin above requires IPython 1.0. Note that previously 13 | this could be built with the stand-alone nbconvert package. That 14 | no longer works with the recent liquid_tags plugin. 15 | 16 | - Recent version of [Pelican](http://github.com/getpelican/pelican). For 17 | the static paths (downloads, images, figures, etc.) to appear in the right 18 | place, Pelican 3.3+ must be used. 19 | -------------------------------------------------------------------------------- /TODO.txt: -------------------------------------------------------------------------------- 1 | - tags & categories 2 | -------------------------------------------------------------------------------- /content/2012-08-16-memoryview-benchmarks-2.md: -------------------------------------------------------------------------------- 1 | Title: Memoryview Benchmarks 2 2 | date: 2012-08-16 14:19 3 | comments: true 4 | slug: memoryview-benchmarks-2 5 | 6 | 7 | In the [previous post](/blog/2012/08/08/memoryview-benchmarks/), I explored 8 | how cython typed memoryviews can be used to speed up repeated array 9 | operations. It became clear that typed memoryviews are superior to 10 | the ndarray syntax for slicing, and as fast as raw pointers for single 11 | element access. In the comments, Mathieu brought up an interesting 12 | question: is the ndarray syntax as good as typed memoryviews if you're 13 | not doing slicing? 14 | 15 | The answer turns out to be yes, *unless* the compiler tries to inline your 16 | function. 17 | 18 | 19 | 20 | ### Inlined Memoryview ### 21 | 22 | We'll use a slightly simpler benchmark script here for simplicity. We'll 23 | use inlined typed memoryviews for the inner function, and call this function 24 | within an outer loop: 25 | 26 | ``` cython 27 | import numpy as np 28 | cimport numpy as np 29 | cimport cython 30 | 31 | @cython.boundscheck(False) 32 | @cython.wraparound(False) 33 | cdef inline double inner_func(double[:, ::1] X): 34 | return X[0, 0] 35 | 36 | def loop_1(int N, switch=True): 37 | cdef double[:, ::1] X = np.zeros((100, 100)) 38 | cdef int i 39 | for i in range(N): 40 | # this should be inlined by the compiler 41 | inner_func(X) 42 | ``` 43 | 44 | The inner function is called `N` times. We've used the "inline" keyword here: 45 | it turns out this is optional with common compiler optimizations turned on. 46 | `gcc` and other compilers are smart enough to figure out that this function 47 | should be inlined, even if the cython code doesn't mark it as such. Timing 48 | the function on one million loops gives us our comparison benchmark: 49 | 50 | %timeit loop_1(1E6) 51 | 100000 loops, best of 3: 10.1 us per loop 52 | 53 | Just over a millisecond to perform this loop one million times. 54 | 55 | ### Non-inlined Memoryview ### 56 | Because the compilers are generally so smart, we actually need to be a bit 57 | clever to make sure our function is *not* inlined. We'll do that through 58 | a switch statement in the loop call, which selects between two possible 59 | inner functions. This may seem a bit contrived, but it could come up 60 | in practice: for example, if we wanted to create a KDTree for nearest neighbor 61 | searches which can use one of several distance metrics within a single tree 62 | framework, we might be tempted to try a solution like this: 63 | 64 | ``` cython 65 | import numpy as np 66 | cimport numpy as np 67 | cimport cython 68 | 69 | ctypedef double (*inner_func_ptr)(double[:, ::1]) 70 | 71 | @cython.boundscheck(False) 72 | @cython.wraparound(False) 73 | cdef double inner_func_1(double[:, ::1] X): 74 | return X[0, 0] 75 | 76 | @cython.boundscheck(False) 77 | @cython.wraparound(False) 78 | cdef double inner_func_2(double[:, ::1] X): 79 | return X[0, 0] 80 | 81 | def loop_2(int N, switch=True): 82 | # use a switch to ensure that inlining can't happen: compilers 83 | # are usually smart enough to figure it out otherwise. 84 | cdef inner_func_ptr func 85 | if switch: 86 | func = inner_func_1 87 | else: 88 | func = inner_func_2 89 | 90 | cdef double[:, ::1] X = np.zeros((100, 100)) 91 | cdef int i 92 | for i in range(N): 93 | func(X) 94 | ``` 95 | By adding the switch function, it means the compiler cannot know at compile 96 | time which of the inner functions will be used in the loop, and they 97 | cannot be inlined. The timing results are as follows: 98 | 99 | %timeit loop_2(1E6) 100 | 10 loops, best of 3: 22.9 ms per loop 101 | 102 | Using a non-inlined function makes things significantly slower in this case! 103 | So, if you're repeatedly calling a small function, inlining can be *very* 104 | important for optimal execution times. 105 | 106 | ### The Problem with ndarray ### 107 | It turns out that beyond slicing, the problem with the ndarray type is that 108 | multi-dimensional arrays require relatively expensive buffer checks whenever 109 | a function is called. This causes similar code with ndarrays to be 110 | significantly slower: 111 | 112 | ``` cython 113 | import numpy as np 114 | cimport numpy as np 115 | cimport cython 116 | 117 | @cython.boundscheck(False) 118 | @cython.wraparound(False) 119 | cdef inline double inner_func(np.ndarray[double, ndim=2, mode='c'] X): 120 | return X[0, 0] 121 | 122 | def loop_3(int N, switch=True): 123 | cdef np.ndarray[double, ndim=2, mode='c'] X = np.zeros((100, 100)) 124 | cdef int i 125 | for i in range(N): 126 | inner_func(X) 127 | ``` 128 | Compiling this gives the following warning: 129 | 130 | warning: Buffer unpacking not optimized away. 131 | 132 | The result of this buffer unpacking in each loop is a much slower execution 133 | time: 134 | 135 | %timeit loop_3(1E6) 136 | 1 loops, best of 3: 617 ms per loop 137 | 138 | This is about 30 times slower than the non-inlined version of the memoryview, 139 | and 6000 times slower than the inlined memoryview above! 140 | Is there any way around this? 141 | Well, there are two options: raw pointers, or explicit inlining in the 142 | code (that is, copying and pasting your code). Both options will have 143 | speeds similar to that of the inlined memoryviews, but each solution 144 | is inconvenient in its own way. 145 | 146 | So why wouldn't you use memoryviews? Well, several projects strive to 147 | remain compatible with python 2.4 (one example is scipy) and python 2.4 is 148 | not compatible with cython's typed memoryviews. Other projects seek to 149 | remain compatible with earlier cython versions which don't support 150 | the relatively new memoryview syntax. 151 | In these situations, one of the two partial solutions above 152 | probably need to be used. In practice, I have usually resorted to passing 153 | around raw pointers. 154 | 155 | ### Summary ### 156 | Here are the timings we found above: 157 | 158 | - Inlined memoryviews: 0.1 ms 159 | - Non-inlined memoryviews: 22.9 ms 160 | - Inlined ndarray: 617 ms 161 | 162 | So we see yet another reason that typed memoryviews are superior to 163 | the ndarray syntax: they not only have very speedy slicing, but also 164 | play well with the compiler's inlining optimization. Granted, these 165 | time differences will be insignificant if your inlined function does 166 | some non-negligible amount of computation, but there may be situations 167 | where this affects things. 168 | 169 | ### Back to my problem ### 170 | If you recall, I started all of this because I wanted to create a binary tree 171 | that can compute pairwise distances with arbitrary distance metrics. Where 172 | do these results put me? Not in a great position, it turns out. Abstracting 173 | out the distance function so that the same machinery can be used with 174 | different functions will lead to speed penalties from the inability to inline. 175 | C++ libraries accomplish this through compile-time conditionals (i.e. templates) 176 | but cython doesn't have this capability. Duplicating the tree 177 | framework with a new hard-coded (and thus inlinable) distance metric may 178 | be the only option. That, or wrapping a templated C++ implementation. 179 | 180 | All of the above scripts are available as an ipython 181 | notebook: [memview_bench_2.ipynb](/downloads/notebooks/memview_bench_2.ipynb). 182 | For information on how to view this file, see the 183 | [IPython page](http://ipython.org/ipython-doc/dev/interactive/htmlnotebook.html) 184 | Alternatively, you can view this notebook (but not modify it) using the 185 | nbviewer [here](http://nbviewer.ipython.org/url/jakevdp.github.com/downloads/notebooks/memview_bench_2.ipynb). -------------------------------------------------------------------------------- /content/2012-08-18-matplotlib-animation-tutorial.md: -------------------------------------------------------------------------------- 1 | Title: Matplotlib Animation Tutorial 2 | date: 2012-08-18 08:01 3 | comments: true 4 | slug: matplotlib-animation-tutorial 5 | 6 | 7 | [Matplotlib](http://matplotlib.sourceforge.net) version 1.1 added some tools 8 | for creating 9 | [animations](http://matplotlib.sourceforge.net/api/animation_api.html) 10 | which are really slick. You can find some good example animations on 11 | the matplotlib 12 | [examples](http://matplotlib.sourceforge.net/examples/animation/index.html) 13 | page. I thought I'd share here some of the things I've learned when playing 14 | around with these tools. 15 | 16 | ### Basic Animation ### 17 | The animation tools center around the `matplotlib.animation.Animation` base 18 | class, which provides a framework around which the animation functionality 19 | is built. The main interfaces are `TimedAnimation` and `FuncAnimation`, 20 | which you can read more about in the 21 | [documentation](http://matplotlib.sourceforge.net/api/animation_api.html). 22 | Here I'll explore using the `FuncAnimation` tool, which I have found 23 | to be the most useful. 24 | 25 | 26 | 27 | First we'll use `FuncAnimation` to do a basic animation of a sine wave moving 28 | across the screen: 29 | 30 | {% include_code basic_animation.py lang:python Basic Animation %} 31 | 32 | Let's step through this and see what's going on. After importing required 33 | pieces of `numpy` and `matplotlib`, The script sets up the plot: 34 | ``` python 35 | fig = plt.figure() 36 | ax = plt.axes(xlim=(0, 2), ylim=(-2, 2)) 37 | line, = ax.plot([], [], lw=2) 38 | ``` 39 | Here we create a figure window, create a single axis in the figure, and then 40 | create our line object which will be modified in the animation. Note that 41 | here we simply plot an empty line: we'll add data to the line later. 42 | 43 | Next we'll create the functions which make the animation happen. `init()` 44 | is the function which will be called to create the base frame upon which 45 | the animation takes place. Here we use just a simple function which sets 46 | the line data to nothing. It is important that this function return the 47 | line object, because this tells the animator which objects on the plot to 48 | update after each frame: 49 | ``` python 50 | def init(): 51 | line.set_data([], []) 52 | return line, 53 | ``` 54 | The next piece is the animation function. It takes a single parameter, the 55 | frame number `i`, and draws a sine wave with a shift that depends on `i`: 56 | ``` 57 | # animation function. This is called sequentially 58 | def animate(i): 59 | x = np.linspace(0, 2, 1000) 60 | y = np.sin(2 * np.pi * (x - 0.01 * i)) 61 | line.set_data(x, y) 62 | return line, 63 | ``` 64 | Note that again here we return a tuple of the plot objects which have been 65 | modified. This tells the animation framework what parts of the plot should 66 | be animated. 67 | 68 | Finally, we create the animation object: 69 | ``` python 70 | anim = animation.FuncAnimation(fig, animate, init_func=init, 71 | frames=100, interval=20, blit=True) 72 | ``` 73 | This object needs to persist, so it must be assigned to a variable. We've 74 | chosen a 100 frame animation with a 20ms delay between frames. The 75 | `blit` keyword is an important one: this tells the animation to only re-draw 76 | the pieces of the plot which have changed. The time saved with `blit=True` 77 | means that the animations display much more quickly. 78 | 79 | We end with an optional save command, and then a show command to show the 80 | result. Here's what the script generates: 81 | {% video /downloads/videos/basic_animation.mp4 360 270 /downloads/videos/basic_animation_frame.png %} 82 | 83 | This framework for generating and saving animations is very powerful and 84 | flexible: if we put some physics into the `animate` function, the possibilities 85 | are endless. Below are a couple examples of some physics animations that 86 | I've been playing around with. 87 | 88 | ### Double Pendulum ### 89 | One of the examples provided on the matplotlib 90 | [example page](http://matplotlib.sourceforge.net/examples/animation/index.html) 91 | is an animation of a double pendulum. This example operates by precomputing 92 | the pendulum position over 10 seconds, and then animating the results. I 93 | saw this and wondered if python would be fast enough to compute the dynamics 94 | on the fly. It turns out it is: 95 | 96 | {% include_code double_pendulum.py lang:python Double Pendulum %} 97 | 98 | Here we've created a class which stores the state of the double pendulum 99 | (encoded in the angle of each arm plus the angular velocity of each arm) 100 | and also provides some functions for computing the dynamics. The animation 101 | functions are the same as above, but we just have a bit more complicated 102 | update function: it not only changes the position of the points, but also 103 | changes the text to keep track of time and energy (energy should be constant 104 | if our math is correct: it's comforting that it is). The video below 105 | lasts only ten seconds, but by running the script you can watch the 106 | pendulum chaotically oscillate until your laptop runs out of power: 107 | 108 | {% video /downloads/videos/double_pendulum.mp4 360 270 /downloads/videos/double_pendulum_frame.png %} 109 | 110 | ### Particles in a Box ### 111 | 112 | Another animation I created is the elastic collisions of a group of particles 113 | in a box under the force of gravity. The collisions are elastic: they conserve 114 | energy and 2D momentum, and the particles bounce realistically off the walls 115 | of the box and fall under the influence of a constant gravitational force: 116 | 117 | {% include_code particle_box.py lang:python Particles in a Box %} 118 | 119 | The math should be familiar to anyone with a physics background, and the 120 | result is pretty mesmerizing. I coded this up during a flight, and ended 121 | up just sitting and watching it for about ten minutes. 122 | 123 | {% video /downloads/videos/particle_box.mp4 360 270 /downloads/videos/particle_box_frame.png %} 124 | 125 | This is just the beginning: it might be an interesting exercise to add 126 | other elements, like computation of the temperature and pressure to demonstrate 127 | the ideal gas law, or real-time plotting of the velocity distribution to 128 | watch it approach the expected Maxwellian distribution. It opens up many 129 | possibilities for virtual physics demos... 130 | 131 | ### Summing it up ### 132 | The matplotlib animation module is an excellent addition to what was already 133 | an excellent package. I think I've just scratched the surface of what's 134 | possible with these tools... what cool animation ideas can you come up 135 | with? 136 | 137 | Edit: in a [followup post](/blog/2012/09/05/quantum-python), I show how 138 | these tools can be used to generate an animation of a simple Quantum 139 | Mechanical system. -------------------------------------------------------------------------------- /content/2012-08-24-numba-vs-cython.md: -------------------------------------------------------------------------------- 1 | Title: Numba vs Cython 2 | date: 2012-08-24 10:41 3 | comments: true 4 | slug: numba-vs-cython 5 | 6 | 7 | 8 | *For a more up-to-date comparison of Numba and Cython, see the* 9 | [*newer post*](http://jakevdp.github.io/blog/2013/06/15/numba-vs-cython-take-2/) 10 | *on this subject.* 11 | 12 | Often I'll tell people that I use python for computational analysis, and they 13 | look at me inquisitively. "Isn't python pretty slow?" They have a point. 14 | Python is an interpreted language, and as such cannot natively perform 15 | many operations as quickly as a compiled language such as C or Fortran. 16 | There is also the issue of the oft-misunderstood and much-maligned 17 | [GIL](http://wiki.python.org/moin/GlobalInterpreterLock), 18 | which calls into question python's ability to allow true parallel computing. 19 | 20 | Many solutions have been proposed: [PyPy](http://pypy.org/) is a much faster 21 | version of the core python language; 22 | [numexpr](http://code.google.com/p/numexpr/) provides optimized performance 23 | on certain classes of operations from within python; 24 | [weave](http://www.scipy.org/Weave/) allows inline inclusion of compiled 25 | C/C++ code; 26 | [cython](http://www.cython.org/) provides extra markup that allows python 27 | and/or python-like code to be compiled into C for fast operations. But 28 | a naysayer might point out: many of these "python" solutions in practice 29 | are not really python at all, but clever hacks into Fortran or C. 30 | 31 | 32 | 33 | I personally have no problem with this. I like python because it gives me a nice 34 | work-flow: it has a clean syntax, I don't need to spend my time hunting down 35 | memory errors, it's quick to try-out code snippets, it's easy to wrap legacy 36 | code written in C and Fortran, and I'm much more productive when writing 37 | python vs writing C or C++. [Numpy](http://numpy.scipy.org), 38 | [scipy](http://www.scipy.org), and [scikit-learn](http://www.scikit-learn.org) 39 | give me optimized routines for most of what I need to do on a daily basis, 40 | and if something more specialized comes up, cython has never failed me. 41 | Nevertheless, the whole setup is a bit clunky: 42 | why can't I have the best of both worlds: a beautiful, scripted, dynamically 43 | typed language like python, with the speed of C or Fortran? 44 | 45 | In recent years, new languages like [go](http://golang.org/) and 46 | [julia](http://julialang.org/) have popped up which try to address some of 47 | these issues. Julia in particular has a number of nice properties (see the 48 | [talk](http://www.youtube.com/watch?v=VCp1jUgVRgE) from Scipy 2012 for a 49 | good introduction) and uses [LLVM](http://llvm.org) to enable just-in-time 50 | (JIT) compilation and achieve some impressive benchmarks. Julia holds promise, 51 | but I'm not yet ready to abandon the incredible code-base and user-base 52 | of the python community. 53 | 54 | Enter [numba](http://numba.pydata.org/). This is an attempt to bring JIT 55 | compilation cleanly to python, using the LLVM framework. In a 56 | [recent post](/blog/2012/08/08/memoryview-benchmarks/), one commenter pointed 57 | out numba as an alternative to cython. I had heard about it before (See 58 | Travis Oliphant's scipy 2012 talk 59 | [here](http://www.youtube.com/watch?v=WYi1cymszqY)) but hadn't had the chance 60 | to try it out until now. Installation is a bit involved, but the directions 61 | on the [numba website](http://numba.pydata.org/) are pretty good. 62 | 63 | To test this out, I decided to run some benchmarks using the 64 | pairwise distance function I've explored before (see posts 65 | [here](/blog/2012/08/08/memoryview-benchmarks/) 66 | and [here](/blog/2012/08/16/memoryview-benchmarks-2/)). 67 | 68 | ### Pure Python Version ### 69 | The pure python version of the function looks like this: 70 | ``` python 71 | import numpy as np 72 | 73 | def pairwise_python(X, D): 74 | M = X.shape[0] 75 | N = X.shape[1] 76 | for i in range(M): 77 | for j in range(M): 78 | d = 0.0 79 | for k in range(N): 80 | tmp = X[i, k] - X[j, k] 81 | d += tmp * tmp 82 | D[i, j] = np.sqrt(d) 83 | ``` 84 | Not surprisingly, this is very slow. For an array consisting of 1000 points 85 | in three dimensions, execution takes over 12 seconds on my machine: 86 | ``` 87 | In [2]: import numpy as np 88 | In [3]: X = np.random.random((1000, 3)) 89 | In [4]: D = np.empty((1000, 1000)) 90 | In [5]: %timeit pairwise_python(X, D) 91 | 1 loops, best of 3: 12.1 s per loop 92 | ``` 93 | 94 | ### Numba Version ### 95 | Once numba is installed, we add only a single line to our above definition 96 | to allow numba to interface our code with LLVM: 97 | ``` 98 | import numpy as np 99 | from numba import double 100 | from numba.decorators import jit 101 | 102 | @jit(arg_types=[double[:,:], double[:,:]]) 103 | def pairwise_numba(X, D): 104 | M = X.shape[0] 105 | N = X.shape[1] 106 | for i in range(M): 107 | for j in range(M): 108 | d = 0.0 109 | for k in range(N): 110 | tmp = X[i, k] - X[j, k] 111 | d += tmp * tmp 112 | D[i, j] = np.sqrt(d) 113 | ``` 114 | I should emphasize that this is the *exact same* code, except for numba's 115 | `jit` decorator. The results are pretty astonishing: 116 | ``` 117 | In [2]: import numpy as np 118 | In [3]: X = np.random.random((1000, 3)) 119 | In [4]: D = np.empty((1000, 1000)) 120 | In [5]: %timeit pairwise_numba(X, D) 121 | 100 loops, best of 3: 15.5 ms per loop 122 | ``` 123 | This is a three order-of-magnitude speedup, simply by adding a numba 124 | decorator! 125 | 126 | ### Cython Version ### 127 | For completeness, let's do the same thing in cython. Cython 128 | takes a bit more than just some decorators: there are also type specifiers 129 | and other imports required. Additionally, we'll use the `sqrt` function 130 | from the C math library rather than from numpy. Here's the code: 131 | ``` cython 132 | cimport cython 133 | from libc.math cimport sqrt 134 | 135 | @cython.boundscheck(False) 136 | @cython.wraparound(False) 137 | def pairwise_cython(double[:, ::1] X, double[:, ::1] D): 138 | cdef int M = X.shape[0] 139 | cdef int N = X.shape[1] 140 | cdef double tmp, d 141 | for i in range(M): 142 | for j in range(M): 143 | d = 0.0 144 | for k in range(N): 145 | tmp = X[i, k] - X[j, k] 146 | d += tmp * tmp 147 | D[i, j] = sqrt(d) 148 | ``` 149 | Running this shows about a 30% speedup over numba: 150 | ``` 151 | In [2]: import numpy as np 152 | In [3]: X = np.random.random((1000, 3)) 153 | In [4]: D = np.empty((1000, 1000)) 154 | In [5]: %timeit pairwise_numba(X, D) 155 | 100 loops, best of 3: 9.86 ms per loop 156 | ``` 157 | 158 | ### The Takeaway ### 159 | So numba is 1000 times faster than a pure python implementation, and only 160 | marginally slower than nearly identical cython code. 161 | There are some caveats here: first of all, I have years of experience with 162 | cython, and only an hour's experience with numba. I've used every optimization 163 | I know for the cython version, and just the basic vanilla syntax for numba. 164 | There are likely ways to tweak the numba version to make it even faster, 165 | as indicated in the comments of 166 | [this post](/blog/2012/08/08/memoryview-benchmarks/). 167 | 168 | All in all, I should say I'm very impressed. Using numba, I added 169 | just a *single line* to the original python code, and 170 | was able to attain speeds competetive with a highly-optimized (and 171 | significantly less "pythonic") cython implementation. Based on this, 172 | I'm extremely excited to see what numba brings in the future. 173 | 174 | All the above code is available as an ipython notebook: 175 | [numba_vs_cython.ipynb](/downloads/notebooks/numba_vs_cython.ipynb). 176 | For information on how to view this file, see the 177 | [IPython page](http://ipython.org/ipython-doc/dev/interactive/htmlnotebook.html) 178 | Alternatively, you can view this notebook (but not modify it) using the 179 | nbviewer [here](http://nbviewer.ipython.org/url/jakevdp.github.com/downloads/notebooks/numba_vs_cython.ipynb). -------------------------------------------------------------------------------- /content/2012-09-20-why-python-is-the-last.md: -------------------------------------------------------------------------------- 1 | Title: Why Python is the Last Language You'll Have To Learn 2 | date: 2012-09-20 20:50 3 | comments: true 4 | slug: why-python-is-the-last 5 | 6 | 7 | This week, for part of a textbook I'm helping to write, 8 | I spent some time reading and researching the history of Python as 9 | a scientific computing tool. I had heard bits and pieces of this in the past, 10 | but it was fascinating to put it all together and learn about how all the 11 | individual contributions that have made Python what it is today. 12 | All of this got me thinking: for most of us, Python was a replacement for 13 | something: IDL, MatLab, Java, Mathematica, Perl... you name it. 14 | But what will replace Python? 15 | Ten years down the road, what language will people be espousing in 16 | blogs with awkwardly-alliterated titles? As I thought it through, I 17 | became more and more convinced that, at least in the scientific computing 18 | world, Python is here to stay. 19 | 20 | 21 | 22 | Now I’m not simply talking about inertia. Javascript has inertia, and that's 23 | a main reason that web admins still begrudgingly use it. But Python is 24 | different. Yes, it's everywhere, and yes, something so ubiquitous is 25 | hard to shake. But look at the 1970s: punch card programming was 26 | everywhere, and now it's nothing but a footnote. Inertia can be overcome 27 | with time. But I think Python’s hold is much deeper than that: I think it 28 | will remain relevant long into the future. Here’s why: 29 | 30 | 31 | 32 | ## GitHub ## 33 | The first reason Python will be around for a while is 34 | [GitHub](http://github.com). GitHub has done wonders both for Python 35 | and for the broader open source community. 36 | It has replaced the clunky Trac system of 37 | submitting static patches to projects, and removed the contribution barrier 38 | for the core scientific python projects. Numpy, Scipy, and Matplotlib were 39 | all moved to GitHub in late 2010, and the results have been impressive. 40 | I did some quick data mining of the commit logs on GitHub to learn 41 | about the rate of new author contributions to core python projects with 42 | time, and this is what I found: 43 | 44 | {% img /figures/author_count.png 600 [Cumulative number of contributors for python packages] %} 45 | 46 | See it? The late 2010 transition to GitHub is extremely apparent, 47 | and this reflects the first reason that NumPy, SciPy, Matplotlib, 48 | and Python will remain relevant far into the future. 49 | Python not only has an astoundingly large user-base; thanks to GitHub, 50 | it has an astoundingly large and ever-increasing *developer* base. 51 | And that means Python is well-poised to evolve as the needs of users change. 52 | 53 | 54 | 55 | ## Julia ## 56 | The second reason Python will be around for a while is 57 | [Julia](http://julialang.org/). This statement may strike some as strange: 58 | Julia is a 59 | language which aims to improve on many of Python’s weaknesses. 60 | It uses JIT compilation and efficient built-in array 61 | support to beat Python on nearly every benchmark. It seems a likely 62 | candidate to *replace* Python, not a support for my assertion that Python will 63 | remain relevant. But this is the thing: Python’s strength lies in community, 64 | and that community is incredibly difficult to replicate. 65 | 66 | A recent 67 | [thread](https://groups.google.com/forum/?fromgroups=#!topic/julia-dev/YftOOEfcwrk) 68 | on the julia-dev list highlights what I’m talking about: 69 | in it, Dag Seljebotn, a core developer of Cython, contrasts the 70 | strengths of Python (large user- and developer-base, large collection of 71 | libraries) with the strengths of Julia (state-of-the-art performance, 72 | JIT compilation). He 73 | proposes that the two languages should work together, each drawing from the 74 | strengths of the other. The response from the Julia community was incredibly 75 | positive. 76 | 77 | The Julia developers know that Julia can’t succeed as a scientific 78 | computing platform without building an active community, and currently the 79 | best way to do that is to work hand-in-hand with Python. After this thread 80 | on julia-dev, the Julia developers were invited to give 81 | [a talk](http://pyvideo.org/video/1204/julia-a-fast-dynamic-language-for-technical-comp) 82 | at the Scipy2012 conference, 83 | and I think many would say that some good bridges were built. 84 | I'm extremely excited about the prospects of Julia as a scientific computing 85 | platform, but it will only succeed if it can embrace Python, and if Python 86 | can embrace it. 87 | 88 | 89 | 90 | ## The next Travis Oliphant ## 91 | The third reason Python will be around for a while is this: *there will be 92 | another Travis Oliphant*. What do I mean by this? Well, if you go back 93 | ten years or so, the Python scientific community was looking a bit weak. 94 | Numeric was a 95 | well-established array interface, and the beginnings of SciPy were built 96 | upon it. But Numeric was clunky, so some folks got together and built 97 | Numarray. Numarray fixed some of the problems, but had its own weaknesses. 98 | The biggest problem, though, was that it split the community: 99 | when the core of your platform has divided allegiances, neither side wins. 100 | Travis realized this, and against the advice of many, audaciously set out 101 | on a quest to unify the two. The result was NumPy, which is now the 102 | unrivaled basis for nearly all scientific tools in Python. 103 | 104 | Python faces a similar crisis today: it is split in the area of 105 | High-performance and parallel computing. There is an alphabet soup of 106 | packages which aim to address this: 107 | Cython, PyPy, Theano, Numba, Numexpr, and more. But 108 | here’s the thing: someone *will* come along who has the audacity to strike 109 | out and unify them. I love this recent 110 | [tweet](http://twitter.com/dwf/status/246756226367643650) by Dave Warde-Farley: 111 | 112 | {% img /images/dwf_tweet.png 400 [Cumulative number of contributors for python packages] %} 113 | 114 | Somebody is going to do this: somebody will be the next Travis Oliphant 115 | and create NumTron to re-unite the community. 116 | Maybe Dave will be the next Travis Oliphant: he's done some great work on 117 | [Theano](http://deeplearning.net/software/theano/). 118 | But then again, the merging of Python and LLVM in Travis' 119 | [Numba](http://numba.pydata.org/) project is 120 | [pretty exciting](/blog/2012/08/24/numba-vs-cython/): 121 | maybe Travis Oliphant will be the next Travis Oliphant -- 122 | he’s done it before, after all. Or perhaps it will be someone 123 | we’ve never heard of, who as we speak is brewing a new idea in 124 | an unwatched GitHub repository. Time will tell, 125 | but I’m confident that it will happen. 126 | 127 | 128 | 129 | ## Conclusion ## 130 | Maybe I’ve convinced you, maybe I haven’t. But I’m going to continue using 131 | Python, and I predict you will too. We'll see what the future holds for 132 | scientific computing, but in my mind, Python remains a pretty solid bet. 133 | 134 | *Finally, a brief post-script on the history of Python: 135 | some of the most interesting sources I found were 136 | [this interview](http://www.artima.com/intv/pythonP.html) with Guido Van Rossum, 137 | the official [Scipy history page](http://www.scipy.org/History_of_SciPy), the 138 | [Scipy2012 talk](http://pyvideo.org/video/1192/matplotlib-lessons-from-middle-age-or-how-you) 139 | John Hunter gave this summer shortly before his sudden passing, 140 | and the [numpy-discussion post](http://mail.scipy.org/pipermail/numpy-discussion/2012-February/060640.html) 141 | John referenced in his talk. If you use Python regularly and have some 142 | time, I’d highly recommend browsing these: it’s incredible to see 143 | how the unwavering vision of folks throughout the years has led to what we 144 | have today: an unmatched open-source environment for scientific computing.* 145 | 146 | *Edit: also check out the 147 | [History of Python](http://python-history.blogspot.com/) blog. Thanks to 148 | Fernando for the tip.* -------------------------------------------------------------------------------- /content/2012-09-26-optical-illusions-in-matplotlib.md: -------------------------------------------------------------------------------- 1 | Title: Optical Illusions in Matplotlib 2 | date: 2012-09-26 07:27 3 | comments: true 4 | slug: optical-illusions-in-matplotlib 5 | 6 | 7 | A while ago I posted some information on the new matplotlib animation 8 | package (see my tutorial 9 | [here](/blog/2012/08/18/matplotlib-animation-tutorial) and 10 | a followup post [here](/blog/2012/09/05/quantum-python)). In them, I show 11 | how easy it is to use matplotlib to create simple animations. 12 | 13 | This morning I came across this cool optical illusion on 14 | [gizmodo](http://gizmodo.com/5945194/this-optical-trick-is-annoying-the-hell-out-of-me) 15 | 16 | {% img /images/original_illusion.gif %} 17 | 18 | 19 | 20 | It intrigued me, so I decided to see if I could create it using matplotlib. 21 | Using my previous template and a bit of geometry, I was able to finish it 22 | before breakfast! Here's the code: 23 | 24 | {% include_code animate_square.py lang:python Optical Illusion %} 25 | 26 | And here's the result: 27 | 28 | {% video /downloads/videos/animate_square.mp4 360 270 /downloads/videos/animate_square.png %} 29 | 30 | This just confirms my suspicion that a few lines of python really can do 31 | anything. -------------------------------------------------------------------------------- /content/2012-10-04-blogging-with-ipython.md: -------------------------------------------------------------------------------- 1 | Title: Blogging with IPython in Octopress 2 | date: 2012-10-04 18:40 3 | comments: true 4 | slug: blogging-with-ipython 5 | 6 | {% notebook nb_in_octopress.ipynb cells[1:] %} -------------------------------------------------------------------------------- /content/2012-10-07-xkcd-style-plots-in-matplotlib.md: -------------------------------------------------------------------------------- 1 | Title: XKCD-style plots in Matplotlib 2 | date: 2012-10-07 13:30 3 | comments: true 4 | slug: xkcd-style-plots-in-matplotlib 5 | 6 | {% notebook XKCD_plots.ipynb cells[2:] %} -------------------------------------------------------------------------------- /content/2012-10-14-scipy-sparse-graph-module-word-ladders.md: -------------------------------------------------------------------------------- 1 | Title: Sparse Graphs in Python: Playing with Word Ladders 2 | date: 2012-10-14 21:23 3 | comments: true 4 | slug: scipy-sparse-graph-module-word-ladders 5 | 6 | {% notebook sparse-graph.ipynb cells[2:] %} 7 | -------------------------------------------------------------------------------- /content/2012-11-24-simple-3d-visualization-in-matplotlib.md: -------------------------------------------------------------------------------- 1 | Title: Quaternions and Key Bindings: Simple 3D Visualization in Matplotlib 2 | date: 2012-11-24 11:04 3 | comments: true 4 | slug: simple-3d-visualization-in-matplotlib 5 | 6 | {% notebook 3DCube.ipynb cells[2:] %} 7 | -------------------------------------------------------------------------------- /content/2012-11-26-3d-interactive-rubiks-cube-in-python.md: -------------------------------------------------------------------------------- 1 | Title: 3D Interactive Rubik's Cube in Python 2 | date: 2012-11-26 22:00 3 | comments: true 4 | slug: 3d-interactive-rubiks-cube-in-python 5 | 6 | 7 | Over the weekend, I built a interactive 3D Rubik's cube simulator in python 8 | using only [matplotlib](http://matplotlib.org) for all the graphics and 9 | interaction. Check out the demonstration here: 10 | 11 | {% video /downloads/videos/MagicCube.mp4 680 400 /downloads/videos/MagicCube_frame.jpg %} 12 | 13 | You can browse the source code at the MagicCube github repository: 14 | [http://github.com/davidwhogg/MagicCube](http://github.com/davidwhogg/MagicCube). 15 | 16 | 17 | 18 | The 3D rendering is based on the quaternions and projections discussed in 19 | my [previous post](/blog/2012/11/24/simple-3d-visualization-in-matplotlib/), 20 | and many of the key bindings discussed there are used here. 21 | The additional component is the turning of each face. This is actually 22 | fairly simple to accomplish using the tools discussed above. 23 | 24 | For example, the right face is perpendicular to the x-axis in the cube-frame. 25 | Thus to turn it, you only need to manipulate polygons with x-coordinate 26 | between 1/3 and 1 inclusive (the cube has side-length 2 and is centered on 27 | the origin). The correct faces are found quickly using 28 | numpy's ``where`` statement, and a quaternion rotation is applied only to 29 | these faces. Easy! 30 | 31 | The code and interface still needs some work, and I'm hoping to add more 32 | features as I have time. After spending my long weekend working on this, 33 | I have some real work to catch-up on... 34 | 35 | *Update: I've heard from some folks that the key and mouse bindings don't 36 | work on some operating systems or backends. If you experience 37 | system-dependent bugs and have any insight into the problem, I'd appreciate 38 | help working out what's happening!* -------------------------------------------------------------------------------- /content/2012-12-01-a-primer-on-python-metaclasses.md: -------------------------------------------------------------------------------- 1 | Title: A Primer on Python Metaclasses 2 | date: 2012-12-01 07:25 3 | comments: true 4 | slug: a-primer-on-python-metaclasses 5 | 6 | {% notebook MetaClasses.ipynb cells[2:] %} 7 | -------------------------------------------------------------------------------- /content/2012-12-06-minesweeper-in-matplotlib.md: -------------------------------------------------------------------------------- 1 | Title: Minesweeper in Matplotlib 2 | date: 2012-12-06 18:23 3 | comments: true 4 | slug: minesweeper-in-matplotlib 5 | 6 | 7 | Lately I've been playing around with interactivity in matplotlib. A couple 8 | weeks ago, I discussed briefly how to use event callbacks to implement 9 | [simple 3D visualization](/blog/2012/11/24/simple-3d-visualization-in-matplotlib/) 10 | and later used this as a base for creating a 11 | [working 3D Rubik's cube](/blog/2012/11/26/3d-interactive-rubiks-cube-in-python) 12 | entirely in matplotlib. 13 | 14 | Today I have a different goal: re-create 15 | [minesweeper](http://en.wikipedia.org/wiki/Minesweeper_%28computer_game%29), 16 | that ubiquitous single-player puzzle game that most of us will admit to 17 | having binged on at least once or twice in their lives. In minesweeper, the 18 | goal is to discover and avoid hidden mines within a gridded minefield, and 19 | the process takes some logic and quick thinking. 20 | 21 | {% img /images/minesweeper_2.gif 800 %} 22 | 23 | 24 | 25 | To implement this in matplotlib, at its most stripped-down level, simply 26 | requires us to register mouse clicks on the plot window, and to have the 27 | window respond in the appropriate way. The rest is just the logic underneath. 28 | 29 | ## Event Callbacks ## 30 | Matplotlib contains several built-in event callbacks. You can register 31 | key presses (with ``'key_press_event'`` and ``'key_release_event'``), 32 | mouse clicks (with ``'button_press_event'`` and ``'button_release_event'``), 33 | mouse movement (with ``'motion_notify_event'``), and much more. For 34 | a full listing of the events that can be bound to functionality, see the 35 | documentation of the function ``'matplotlib.pyplot.connect'``. 36 | 37 | As a simple example, here we'll create a polygon and a function which is called 38 | each time the axis is clicked. The function ``on_click`` checks if the click 39 | occured within the polygon, and if so changes the polygon to a random 40 | color: 41 | 42 | ``` python 43 | import numpy as np 44 | import matplotlib.pyplot as plt 45 | 46 | fig = plt.figure() 47 | ax = fig.add_subplot(111, xlim=(-1, 2), ylim=(-1, 2)) 48 | polygon = plt.Polygon([[0, 0], [1, 0], [1, 1], [0, 1], [0, 0]]) 49 | ax.add_patch(polygon) 50 | 51 | # Function to be called when mouse is clicked 52 | def on_click(event): 53 | if polygon.contains_point((event.x, event.y)): 54 | polygon.set_facecolor(np.random.random(3)) 55 | fig.canvas.draw() 56 | 57 | # Connect the click function to the button press event 58 | fig.canvas.mpl_connect('button_press_event', on_click) 59 | plt.show() 60 | ``` 61 | The result will look something like this: 62 | 63 | {% img /images/poly_color.gif 400 %} 64 | 65 | Checking whether a click event is within a polygon or any other artist is 66 | a very common pattern. For this reason, matplotlib provides a built-in 67 | ``pick`` event. You can think of this as an event similar to a mouse click, 68 | but specifically generated by a plot artist when it is clicked. 69 | Furthermore, a ``pick`` event is associated back to that particular plot 70 | element, which can be easily referenced within the callback. 71 | Here is a code snippet which gives is equivalent to the code above, 72 | but uses pick events rather than button press events: 73 | 74 | ``` python 75 | import numpy as np 76 | import matplotlib.pyplot as plt 77 | 78 | fig = plt.figure() 79 | ax = fig.add_subplot(111, xlim=(-1, 2), ylim=(-1, 2)) 80 | polygon = plt.Polygon([[0, 0], [1, 0], [1, 1], [0, 1], [0, 0]]) 81 | ax.add_patch(polygon) 82 | 83 | # set the picker to True, so that pick events are registered 84 | polygon.set_picker(True) 85 | 86 | # create a function to be bound to pick events: here the event has an 87 | # attribute `artist` which points to the object which was clicked 88 | def on_pick(event): 89 | event.artist.set_facecolor(np.random.random(3)) 90 | fig.canvas.draw() 91 | 92 | # bind pick events to our on_pick function 93 | fig.canvas.mpl_connect('pick_event', on_pick) 94 | plt.show() 95 | ``` 96 | 97 | Here we have used just a single polygon, but there's nothing to stop us 98 | from using multiple interactive polygons in a single window. Add some 99 | logic beneath it all, and the results can be extremely flexible. We'll 100 | go through one in-depth example below. 101 | 102 | ## Minesweeper ## 103 | Using this simple machinery, let's create a basic implementation of the game 104 | Minesweeper. This involves creating a grid of polygons, with a certain number 105 | of them "containing" mines. Clicking the left mouse button will "uncover" 106 | the square, ending the game if a mine is underneath. If (as we'd hope) 107 | an uncovered square does not contain a mine, it will reveal a number 108 | reporting how many of the eight adjacent squares contain mines. 109 | The right mouse button is used to mark where we believe mines are. 110 | 111 | There are some other more sophisticated features in the below code -- 112 | for example, clicking 113 | an already uncovered square with the correct number of adjacent mines marked 114 | will automatically clear the surrounding squares -- but rather than enumerating 115 | every programming decision, I'll just show you the code. It's less than 116 | 200 lines, but the results are pretty nice: 117 | 118 | {% img /images/minesweeper.gif 440 440 %} 119 | 120 | {% include_code minesweeper.py lang:python Minesweeper %} 121 | 122 | There are still some things missing from this which are present in any good 123 | minesweeper implementation: a timer, the ability to reset the game without 124 | restarting the program, the ability to keep track of fastest times, and 125 | likely some more things I haven't thought of. 126 | 127 | Regardless, this little script shows how incredibly powerful a framework 128 | matplotlib is. 129 | It can create an interactive Rubik's cube one day, publication-quality plots 130 | the next, and round out the season with a blast back to a classic Windows 3.1 131 | time-sink. And for some reason, I find I have much more fun playing the 132 | minesweeper I built from scratch than the one that came with my system. 133 | 134 | Enjoy! -------------------------------------------------------------------------------- /content/2012-12-19-sparse-svds-in-python.md: -------------------------------------------------------------------------------- 1 | Title: Sparse SVDs in Python 2 | date: 2012-12-19 08:21 3 | comments: true 4 | slug: sparse-svds-in-python 5 | 6 | 7 | 8 | After [Fabian's post](http://fseoane.net/blog/2012/singular-value-decomposition-in-scipy/) on the topic, I have recently returned to thinking about the 9 | subject of sparse singular value decompositions (SVDs) in Python. 10 | 11 | For those who haven't used it, the SVD is an extremely powerful technique. 12 | It is the core routine of many applications, 13 | from filtering to dimensionality 14 | reduction to graph analysis to supervised classification and much, much more. 15 | 16 | I first came across the need for a fast sparse SVD when applying a technique 17 | called Locally Linear Embedding (LLE) to astronomy spectra: it was the first 18 | astronomy paper I published, and you can read it [here](http://adsabs.harvard.edu/abs/2009AJ....138.1365V). In LLE, one visualizes the nonlinear relationship 19 | between high-dimensional observations. The computational cost is extreme: for 20 | *N* objects, one must compute the null space (intimately related to the SVD) 21 | of a *N* by *N* matrix. Using direct methods (e.g. LAPACK), this can scale 22 | as bad as $\mathcal{O}[N^3]$ in both memory and speed! 23 | 24 | 25 | 26 | I needed a better option. I came across the package 27 | [ARPACK](http://www.caam.rice.edu/software/ARPACK/), a well-tested 28 | implementation of iterative Arnoldi Factorization written in Fortran. 29 | The shift-invert mode of ARPACK served my needs, so I spent some time 30 | extending the [scipy ARPACK wrapper](http://docs.scipy.org/doc/scipy/reference/tutorial/arpack.html) so I could address my problem. I also helped 31 | Fabian and others implement the beginnings of the [manifold learning](http://scikit-learn.org/dev/modules/manifold.html) module in scikit-learn. 32 | 33 | Even after moving on to other problems, I found that 34 | the SVD was at the core of nearly every component of my research 35 | while working toward my PhD. You can see this in 36 | [several](http://adsabs.harvard.edu/abs/2011AAS...21715304C) 37 | [other](http://adsabs.harvard.edu/abs/2011ApJ...727..118V) 38 | [projects](http://adsabs.harvard.edu/abs/2011AJ....142..203D) 39 | I was involved with over the years, including my 40 | [PhD Thesis](http://gradworks.umi.com/35/42/3542228.html), which centered 41 | on Astronomical applications of Karhunen-Loeve analysis -- a method, again, 42 | intimately linked with the SVD. 43 | 44 | Hopefully this brief tour has convinced you of the power of the SVD in 45 | addressing real research problems. Now to the code. 46 | 47 | # Sparse SVD Implementations # 48 | What I didn't know at the time I worked on the ARPACK wrapper is that there 49 | are several more good options available for computing SVDs - and most now have 50 | passable Python wrappers which integrate well with scipy's sparse matrices. 51 | I'll briefly describe them here. 52 | 53 | ## LAPACK ## 54 | [LAPACK](http://www.netlib.org/lapack/) 55 | is the standard specification of efficient linear algebra routines 56 | across computing systems, and contains routines to 57 | compute a direct (i.e. non-iterative) 58 | SVD of a dense matrix. The performance of LAPACK varies from system to 59 | system, and implementation to implementation. The algorithm is generally 60 | $\mathcal{O}[N^3]$, 61 | and partial decompositions are (in general) not available. Though 62 | not technically the same, I would group alternatives like 63 | [ATLAS](http://math-atlas.sourceforge.net/) (an optimized open-source 64 | matrix library) and [MKL](http://software.intel.com/en-us/intel-mkl) 65 | (Intel's proprietery library for fast numerics) in the same category. 66 | I don't have much personal experience with MKL, so if I'm not doing it justice, 67 | please feel free to admonish me in the comments! 68 | 69 | LAPACK is wrapped by Numpy and Scipy, and is 70 | at the core of many of the routines in ``numpy.linalg`` and 71 | ``scipy.linalg``, including the ``svd`` function in each. 72 | 73 | ## ARPACK ## 74 | As I mentioned above, [ARPACK](http://www.caam.rice.edu/software/ARPACK/) 75 | implements a fast iterative/partial eigenvalue decomposition on a general 76 | linear operator. One of its strengths is that unlike LAPACK, it does not 77 | depend on your matrix being stored in any standard layout: all that is required 78 | is to provide a routine which implements matrix-vector multiplication. This 79 | means that as well as dense matrices, ARPACK can be used on any sparse matrix 80 | or even a general linear operator which maps one vector space to another. 81 | 82 | ARPACK does not have a native SVD implementation, but it is possible to 83 | exploit the relationship between eigenvalue decompositions and singular 84 | value decompositions to compute an ARPACK svd: this is what the current 85 | ``svds`` routine in ``scipy.sparse.linalg`` does: see the documentation 86 | [here](http://docs.scipy.org/doc/scipy/reference/generated/scipy.sparse.linalg.svds.html). One issue with this implementation is that to compute the SVD of 87 | a matrix *M*, it must implicitly compute $M^T M$, 88 | and that may lead to issues of both 89 | numerical accuracy and computational efficiency. 90 | 91 | ## SVDLIBC ## 92 | Before Fabian's blog post, mentioned above, I had never heard of 93 | [SVDLIBC](http://tedlab.mit.edu/~dr/SVDLIBC/). It 94 | is also an Arnoldi-iteration based implementation, but SVDLIBC requires a 95 | specific sparse matrix format to operate. Fortunately for scipy users, this 96 | storage format maps directly to the CSC sparse matrix format, so the SVDLIBC 97 | svd can be computed without any memory copies of the scipy matrix (assuming, 98 | of course, your matrix is already stored as CSC or CSR!). A bare-bones python 99 | wrapper for the routine exists in the [sparsesvd](http://pypi.python.org/pypi/sparsesvd/) package. 100 | 101 | ## PROPACK ## 102 | [PROPACK](http://soi.stanford.edu/~rmunk/PROPACK/) is another well-tested 103 | Fortran package which computes the SVD directly using an Arnoldi Factorization 104 | scheme: like ARPACK, it only depends on a callback implementing left- and 105 | right-multiplication operations, rather than making use of any specific 106 | sparse storage format. Like SVDLIBC, it computes the svd directly, saving 107 | computation time and leading to greater numerical accuracy. From my brief 108 | search, it seems that 109 | no python wrapper is readily available (though I heard that David Cournapeau 110 | had worked on one). Until recently, the PROPACK license was unspecified, 111 | precluding its inclusion in Scipy or other BSD-licensed packages. It appears 112 | that just recently, PROPACK itself was moved to a BSD license, so there is 113 | now the possibility of including it in the Scipy universe. 114 | 115 | I have begun working on a full-featured PROPACK wrapper in the Scipy style, 116 | using the excellent F2Py Fortran interface generator. You can find the 117 | code in my [pypropack repository](https://github.com/jakevdp/pypropack) 118 | on Github. As of this writing, there is still a lot to do to make the 119 | code releasable, but there is enough there to enable some quick benchmarks. 120 | 121 | # Benchmark Comparisons # 122 | To benchmark these four SVD options, I used the following code: 123 | 124 | {% include_code plot_svd_benchmarks.py lang:python SVD Benchmarks %} 125 | 126 | This creates square sparse matrices, measures the computation time as a function 127 | of the matrix size, and plots the results. The results on my 3-year old 128 | linux box are below: 129 | 130 | {% img /figures/svd_benchmarks.png [SVD benchmarks] %} 131 | 132 | A few comments: First, as expected, LAPACK is much slower than the rest. This 133 | is due to two factors: first, LAPACK computes the full SVD, while the other 134 | methods compute only partial SVDs (the *k=5* largest singular values). 135 | Second, the LAPACK on my system is not 136 | well-optimized: I could probably reduce this by at least an order of magnitude 137 | if I were to use an ATLAS install optimized for my system. If you need a 138 | full SVD, it will be hard to beat LAPACK/ATLAS/MKL in terms of speed (but 139 | in terms of memory consumption, as 140 | [Fabian showed](http://fseoane.net/blog/2012/singular-value-decomposition-in-scipy/), 141 | LAPACK can be pretty bad). Because SVDLIBC, ARPACK, and PROPACK all use 142 | Lanczos/Arnoldi iteration, they should all similarly out-perform LAPACK on 143 | the memory question. 144 | 145 | Second, the good performance of SVDLIBC for small matrices is probably due to 146 | its direct use of the CSC memory within the Fortran code. For matrices this 147 | size, the Python overhead of invoking the callback in ARPACK and PROPACK kills 148 | any performance gains from the more sophisticated algorithms. As the matrices 149 | grow, we see that ARPACK and PROPACK begin to out-perform SVDLIBC. 150 | 151 | Finally, we see that for these test cases, PROPACK is consistently 152 | faster than ARPACK by a factor of 5 or so: nothing to scoff at! 153 | I haven't rigorously tested the claims of increased numerical stability 154 | in PROPACK, but those two pieces point to PROPACK as the 155 | preferred method by far. 156 | 157 | # Next Steps # 158 | I hope to continue developing the ``pypropack`` wrapper on github, and once 159 | I'm happy with it, incorporate it into Scipy's sparse linear algebra tools. 160 | I would love help with this: in particular, if there are any F2Py wizards out 161 | there, I'm currently having what I think is a 162 | [memory issue](https://github.com/jakevdp/pypropack/issues/1) 163 | with the callback function that I can't seem to track down. 164 | 165 | Hopefully this post has helped convince you of the importance of SVDs in 166 | scientific computing, and also of the benefits of working on PROPACK 167 | incorporation in the scientific Python universe. This sort of thing 168 | won't happen unless someone like **you** decides to work on it! That 169 | fact ends up being both a weakness and an incredible strength of 170 | open-source packages. 171 | 172 | Happy coding! -------------------------------------------------------------------------------- /content/2013-01-13-hacking-super-mario-bros-with-python.md: -------------------------------------------------------------------------------- 1 | Title: Hacking Super Mario Bros. with Python 2 | date: 2013-01-13 10:32 3 | comments: true 4 | slug: hacking-super-mario-bros-with-python 5 | 6 | 7 | 8 | This weekend I was coming home from the meeting of the 9 | [LSST](http://www.lsst.org) Dark Energy Science Collaboration, 10 | and found myself with a few extra hours in the airport. 11 | I started passing the time by poking around on the [imgur](http://imgur.com) 12 | gallery, and saw a couple animated gifs based on 13 | one of my all-time favorite games, Super Mario Bros. 14 | It got me wondering: could I use matplotlib's animation tools to create these 15 | sorts of gifs in Python? Over a few beers at an SFO bar, I started to try 16 | to figure it out. To spoil the punchline a bit, I managed to do it, and the 17 | result looks like this: 18 | 19 | {% img center /images/mario.gif %} 20 | 21 | This animation was created *entirely in Python and matplotlib*, by scraping the 22 | image data directly from the Super Mario Bros. ROM. Below I'll explain how 23 | I managed to do it. 24 | 25 | 26 | 27 | ## Scraping the Pixel Data ## 28 | 29 | Clearly, the first requirement for this pursuit 30 | is to get the pixel data used to construct the 31 | mario graphics. My first thought was to do something sophisticated like 32 | [dictionary learning](http://en.wikipedia.org/wiki/Machine_learning#Sparse_Dictionary_Learning) on a collection of screen-shots from the game 33 | to build up a library of thumbnails. That would be an interesting pursuit 34 | in itself, but it turns out it's much more straightforward to directly 35 | scrape the graphics from the source. 36 | 37 | It's possible to find digital copies of most 38 | Nintendo Entertainment System (NES) games online. 39 | These are known as ROMs, and can be played using one of 40 | several NES emulators available for various operating systems. 41 | I'm not sure about the legality of these 42 | digital game copies, so I won't provide a link to them here. But the internet 43 | being what it is, you can search Google for some variation of "Super Mario 44 | ROM" and pretty easily find a copy to download. 45 | 46 | One interesting aspect of ROMs for the original NES is that 47 | they use raw byte-strings to store 2-bit (i.e. 4-color), 8x8 thumbnails from 48 | which all of the game's graphics are built. 49 | The collection of these byte-strings 50 | are known as the "pattern table" for the game, and there is generally a 51 | separate pattern table for foreground and background images. 52 | In the case of NES games, there are 53 | 256 foreground and 256 background tiles, which can be extracted directly from 54 | the ROMs if you know where to look (incidentally, this is one of the things 55 | that made the NES an "8-bit" system. 2^8 = 256, so eight bits are required 56 | to specify any single tile from the table). 57 | 58 | ## Extracting Raw Bits from a File ## 59 | If you're able to obtain a copy of the ROM, the first step to getting at the 60 | graphics is to extract the raw bit information. 61 | This can be done easily in Python using ``numpy.unpackbits`` 62 | and ``numpy.frombuffer`` or ``numpy.fromfile``. 63 | Additionally, the ROMs are generally stored using 64 | zip compression. The uncompressed data can be extracted using Python's 65 | built-in ``zipfile`` module. Combining all of this, we extract the raw file 66 | bits using a function like the following: 67 | 68 | ``` python 69 | import zipfile 70 | import numpy as np 71 | 72 | def extract_bits(filename): 73 | if zipfile.is_zipfile(filename): 74 | zp = zipfile.ZipFile(filename) 75 | raw_buffer = zp.read(zp.filelist[0]) 76 | bytes = np.frombuffer(raw_buffer, dtype=np.uint8) 77 | else: 78 | bytes = np.fromfile(filename, dtype=np.uint8) 79 | return np.unpackbits(bytes) 80 | ``` 81 | 82 | This function checks whether the file is compressed using zip, and extracts 83 | the raw bit information in the appropriate way. 84 | 85 | ## Assembling the Pattern Tables ## 86 | The thumbnails which contain the game's graphics patterns are not at any set 87 | location within the file. The location is specified within the assembly 88 | code that comprises the program, but for our purposes 89 | it's much simpler to just visualize 90 | the data and find it by-eye. To accomplish this, 91 | I wrote a Python script 92 | (download it [here](/downloads/code/mario/view_pattern_table.py)) 93 | based on the above data extraction code 94 | which uses matplotlib to interactively display the contents of the file. 95 | Each thumbnail is composed from 128 bits: 96 | two 64-bit chunks each representing an 8x8 image with one bit per pixel. 97 | Stacking the two results in two bits per pixel, which are able to 98 | represent four colors within each thumbnail. 99 | The first few hundred chunks are difficult to interpret by-eye. They appear 100 | similar to a 2D bar code: in this case the "bar code" represents pieces of the 101 | assembly code which store the Super Mario Bros. program. 102 | 103 | {% img center /images/mario_pattern_sourcecode.png 400 %} 104 | 105 | Scrolling down toward the end of the file, however, we can quickly recognize 106 | the thumbnails which make up the game's graphics: 107 | 108 | {% img center /images/mario_pattern_foreground.png 400 %} 109 | 110 | This first pattern table contains all the foreground graphics for the game. 111 | Looking closely, the first few thumbnails 112 | are clearly recognizable as pieces of Mario's head and body. 113 | Going on we see pieces of various enemies in the game, as well as the iconic 114 | mushrooms and fire-flowers. 115 | 116 | {% img center /images/mario_pattern_background.png 400 %} 117 | 118 | The second pattern table contains all the background graphics for the game. 119 | Along with numbers and text, this contains the pieces which make up mario's 120 | world: bricks, blocks, clouds, bushes, and coins. 121 | Though all of the above tiles are shown in grayscale, we can add color by 122 | simply changing the matplotlib Colormap, as we'll see below. 123 | 124 | ## Combining Thumbnails and Adding Color ## 125 | Examining the pattern tables above, we can see that big Mario is made up of 126 | eight pattern tiles stitched together, while small Mario is made up of four. 127 | With a bit of trial and error, we can create each of the full frames and 128 | add color to make them look more authentic. Below are all of the frames used 129 | to animate Mario's motion throughout the game: 130 | 131 | {% img center /images/mario_graphics1.png 400 %} 132 | 133 | Similarly, we can use the thumbnails to construct some of the other 134 | familiar graphics from the game, including the goombas, koopa troopas, 135 | beetle baileys, mushrooms, fire flowers, and more. 136 | 137 | {% img center /images/mario_graphics2.png 350 %} 138 | 139 | The Python code to extract, assemble, and plot these images can be downloaded 140 | [here](/downloads/code/mario/draw_mario.py). 141 | 142 | ## Animating Mario ## 143 | With all of this in place, creating an animation of Mario is relatively easy. 144 | Using matplotlib's animation tools (described in a 145 | [previous post](/blog/2012/08/18/matplotlib-animation-tutorial/)), all it 146 | takes is to decide on the content of each frame, and stitch the frames together 147 | using matplotlib's animation toolkit. Putting together big Mario with some 148 | scenery and a few of his friends, we can create a cleanly looping animated gif. 149 | 150 | The code used to generate this animation is shown below. We use the same 151 | ``NESGraphics`` class used to draw the frames above, and stitch them together 152 | with a custom class that streamlines the building-up of the frames. 153 | By uncommenting the line near the bottom, the result will be saved as an 154 | animated GIF using the ImageMagick animation writer that I 155 | [recently contributed](https://github.com/matplotlib/matplotlib/pull/1337) 156 | to matplotlib. The ImageMatick plugin has not yet made it into a 157 | released matplotlib version, so using the save command below will 158 | require installing the development version of matplotlib, available for 159 | download on [github](http://github.com/matplotlib/matplotlib). 160 | 161 | {% include_code mario/animate_mario.py lang:python "Mario Animation" %} 162 | 163 | The result looks like this: 164 | 165 | {% img center /images/mario.gif %} 166 | 167 | Pretty good! With a bit more work, it would 168 | be relatively straightforward to use the above code to do some more 169 | sophisticated animations: perhaps recreate a full 170 | level from the original Super Mario Bros, or even design your own custom 171 | level. You might think about taking the extra step and trying to make Mario's 172 | movements interactive. This could be a lot of fun, but probably very difficult 173 | to do well within matplotlib. 174 | For tackling an interactive mario in Python, another framework such as 175 | [Tkinter](http://docs.python.org/2/library/tkinter.html) or 176 | [pygame](http://www.pygame.org/) might be a better choice. 177 | 178 | I hope you enjoyed this one as much as I did -- happy coding! 179 | -------------------------------------------------------------------------------- /content/2013-02-02-setting-up-a-mac-for-python-development.md: -------------------------------------------------------------------------------- 1 | Title: Setting Up a Mac for Python Development 2 | date: 2013-02-02 11:01 3 | comments: true 4 | slug: setting-up-a-mac-for-python-development 5 | 6 | 7 | 8 | *Edit, August 2013: my current favorite way to set up a python installation 9 | on mac (and any other system) is to use the 10 | [anaconda](https://store.continuum.io/) package offered by Continuum 11 | Analytics. It's free, full-featured, and extremely easy to use.* 12 | 13 | {% img left /images/OSX10.8.png 'OSX 10.8 Logo' %} A few weeks ago, 14 | after years of using Linux exclusively for all my computing, 15 | I started a research fellowship in a new department 16 | and found a brand new Macbook Pro on my 17 | desk. Naturally, my first instinct was to set up the system for efficient 18 | Python development. In order to help others who might find themself in a 19 | similar situation, I took some notes on the process, and I'll summarize 20 | what I learned below. 21 | 22 | 23 | 24 | First, a disclaimer: I can't promise that these suggestions are the best or most 25 | effective way to proceed. I'm by no stretch of the imagination 26 | a Mac expert: the last Apple product I used regularly was the trusty 27 | Macintosh Classic my parents bought when I was in middle school. I 28 | primarily used it for all-day marathons of 29 | [RoboSport](http://en.wikipedia.org/wiki/RoboSport) and 30 | [Civilization](http://en.wikipedia.org/wiki/Civilization_%28video_game%29), 31 | with occasional breaks to teach myself programming in Hypercard. But I digress. 32 | 33 | Before moving on to the summary of what I learned, 34 | I should note that all of the following was done on OSX 10.8: there 35 | will likely be differences between OSX versions. I've done my best to note 36 | all the relevant details, and I hope you will find this helpful! 37 | 38 | 39 | ## Accessing the Terminal ## 40 | {% img left /images/OSX_terminal.png 'OSX 10.8 Terminal Icon' %} 41 | Being from a Linux background, I was interested in setting up a 42 | Linux-like work environment, doing nearly everything from the terminal. 43 | Fortunately, OSX is built on unix, with a terminal integrated into the 44 | operating system. 45 | 46 | To open a terminal, open the finder, click "Applications" and search for 47 | "Terminal". To make it easier to access in the future, I dragged the icon down 48 | to the *dock*, the collection of icons usually found on the bottom or side 49 | of the screen. Clicking the icon will open a familiar bash terminal that 50 | can be used to explore your Mac in a more user-friendly way. 51 | 52 | 53 | 54 | ## Setting Up MacPorts ## 55 | To set up the rest of the system components, I opted to use MacPorts, which is 56 | a package management system similar to ``apt`` or ``yum`` on ubuntu and 57 | debian systems. There are probably alternatives to MacPorts, but I found 58 | it very intuitive, quick, and powerful. 59 | 60 | You can download MacPorts for free on the 61 | [MacPorts website](http://www.macports.org). You'll have to install it for 62 | the correct OSX version -- to check which OSX version you're running, 63 | click "about this computer" under the apple icon at the top-left of 64 | the desktop. Unfortunately, I was following the slightly outdated 65 | [MacPorts guide](http://guide.macports.org/) and got a few errors: 66 | 67 | ``` 68 | Error: No Xcode installation was found. 69 | Error: Please install Xcode and/or run xcode-select to specify its location. 70 | Error: 71 | Warning: xcodebuild exists but failed to execute 72 | Warning: Xcode does not appear to be installed; most ports will likely fail to build. 73 | ``` 74 | 75 | This indicates that [Xcode](https://developer.apple.com/xcode/) 76 | is not yet installed. Xcode is a a collection of developer tools for the 77 | Mac, and it can be freely downloaded at the Apple App store. You'll need 78 | to create an Apple account to access it, and then make sure you have a 79 | fast internet connection: the download is about 1.6GB. Once it's downloaded, 80 | find the XCode icon in the Applications menu and click to install. 81 | 82 | With this done, I tried installing MacPorts again, but still got an error: 83 | 84 | ``` 85 | Error: Unable to open port: can't read "build.cmd": Failed to locate 'make' in path: '/opt/local/bin:/opt/local/sbin:/bin:/sbin:/usr/bin:/usr/sbin' or at its MacPorts configuration time location, did you move it? 86 | ``` 87 | 88 | This indicates that the command-line tools are not installed by default. 89 | To fix this, run Xcode, select Xcode->preferences from the menu bar, click 90 | downloads, select "command-line tools", and click install. 91 | You'll also need Xorg tools, which I installed through 92 | [XQuartz](http://xquartz.macosforge.org/landing/). 93 | 94 | 95 | ## Installing Python ## 96 | Now that MacPorts is installed, it's very straightforward to install several 97 | versions of Python and other programs. MacPorts allows access to a standard 98 | repository of programs and packages, which can be explored, downloaded, and 99 | installed using the ``port`` command. 100 | 101 | First of all, run 102 | 103 | ``` 104 | sudo port selfupdate 105 | ``` 106 | 107 | which updates the MacPorts base to the latest release. Another useful 108 | thing to know about is the MacPorts ``search`` command. For example, to 109 | see all the available packages which mention "python", use 110 | 111 | ``` 112 | port search python 113 | ``` 114 | 115 | This will list all the python versions available. I installed both Python 116 | 2.7 and Python 3.3 117 | 118 | ``` 119 | sudo port install python27 120 | sudo port install python33 121 | ``` 122 | 123 | If you want ``python`` on the command-line to point to a particular version, 124 | this can be specified with the ``select`` command: 125 | 126 | ``` 127 | sudo port select --set python python27 128 | ``` 129 | 130 | You can now check that typing ``python --version`` in the terminal returns 131 | version 2.7. 132 | 133 | 134 | ## Installing Numpy, Scipy, etc. ## 135 | My scientific development in Python relies on several packages: 136 | particularly ``numpy``, ``scipy``, ``matplotlib``, ``ipython``, ``cython``, 137 | ``scikits-learn``, ``virtualenv``, ``nose``, ``pep8``, and ``pip``. 138 | Here is the series of commands to set these up for Python 2.7: 139 | 140 | ``` 141 | sudo port install py27-numpy 142 | sudo port install py27-scipy 143 | sudo port install py27-matplotlib 144 | 145 | sudo port install py27-tornado 146 | sudo port install py27-zmq # zmq & tornado needed for notebook/parallel 147 | sudo port install py27-ipython 148 | sudo port select --set ipython ipython27 149 | 150 | sudo port install py27-cython 151 | sudo port select --set python cython27 152 | 153 | sudo port install py27-scikits-learn 154 | 155 | sudo port install py27-virtualenv 156 | sudo port install virtualenv_select 157 | sudo port select --set virtualenv virtualenv27 158 | 159 | sudo port install py27-nose-testconfig 160 | sudo port select --set nosetests nosetests27 161 | 162 | sudo port install py27-pep8 163 | sudo port install pep8_select 164 | sudo port select --set pep8 pep827 165 | 166 | sudo port install py27-pip 167 | ``` 168 | Unfortunately, there seems to not yet be a ``port select`` command 169 | for pip. This bug has been reported and is noted 170 | [here](http://trac.macports.org/ticket/36178). 171 | 172 | 173 | ### Setting up a Virtual Environment ### 174 | For Python development, I find it vital to make a good use of virtual 175 | environments. Virtual environments, enabled by the 176 | [virtualenv](http://pypi.python.org/pypi/virtualenv) package, 177 | allow you to install several different versions of various python packages, 178 | such that the installations are mostly independent. I generally keep 179 | stable released versions of packages in the system-wide python install, 180 | and use these environments to develop the packages. That way, I can 181 | test the compilation/installation of a new feature in scipy or scikit-learn 182 | without breaking my tried-and-true system installation. 183 | 184 | Here we'll set up a virtual environment called ``default`` 185 | in a ``PyEnv`` subdirectory, and 186 | then install ``numpy`` in that environment using ``pip``. 187 | ``` 188 | mkdir ~/PyEnv 189 | cd ~/PyEnv 190 | virtualenv default 191 | source default/bin/activate 192 | pip install numpy 193 | ``` 194 | 195 | 196 | ## Other Programs to Install ## 197 | There are several other things I found helpful to install. First, the ``g95`` 198 | Fortran compiler for building scipy and other packages which require fortran: 199 | ``` 200 | sudo port install g95 201 | ``` 202 | Also, we'll install and configure the tool every open source developer needs: 203 | ``` 204 | sudo port install git 205 | git config --global user.name "John Doe" 206 | git config --global user.email john@doe.com 207 | ``` 208 | 209 | Another essential is a good text editor. There are several good open source 210 | options for this. 211 | 212 | {% img left /images/textmate_icon.jpg 80 80 'Textmate icon' %} 213 | **Textmate** is a Mac native text editor which has many nice features, works 214 | nicely on mac, and is fairly clean and nice to use: 215 | ``` 216 | sudo port install textmate2 217 | mate tmp.txt 218 | ``` 219 | 220 | {% img left /images/vim_icon.png 80 80 'Vim icon' %} 221 | **Vim** is another popular text editor: there is both a command-line version 222 | and a GUI version available: 223 | ``` 224 | sudo port install vim 225 | sudo port install MacVim 226 | vim tmp.txt 227 | ``` 228 | 229 | {% img left /images/emacs_icon.jpeg 80 80 'Emacs icon' %} 230 | **Emacs** is my text editor of choice, and like Vim there is both a 231 | command-line version and a GUI version: 232 | ``` 233 | sudo port install emacs 234 | sudo port install emacs-app 235 | emacs tmp.txt 236 | ``` 237 | There are several other GUI emacs versions available as well (e.g. 238 | ``xemacs`` and ``emacs-mac-app``): I found that I liked ``emacs-app`` the 239 | best. Unfortunately, it lives in the "Applications" folder, and there 240 | doesn't seem to be a way to configure the emacs GUI to work the same way 241 | as the default emacs behavior on linux (see the related discussion thread 242 | [here](http://stackoverflow.com/questions/10171280/how-to-launch-gui-emacs-from-command-line-in-osx)). 243 | I ended up putting the emacs GUI in the dock next to the terminal, and I 244 | access it from there. 245 | 246 | 247 | ## Final Thoughts ## 248 | I've had this setup on my new Macbook for about two weeks now, 249 | and it seems to be working well for my daily python programming tasks. 250 | I hope that this post will be useful to someone out there. I'm still 251 | learning as well -- if you have any pro tips for me or for other readers, 252 | feel free to leave them in the comments below! 253 | 254 | Happy hacking. 255 | -------------------------------------------------------------------------------- /content/2013-02-16-animating-the-lorentz-system-in-3d.md: -------------------------------------------------------------------------------- 1 | Title: Animating the Lorenz System in 3D 2 | date: 2013-02-16 08:05 3 | comments: true 4 | slug: animating-the-lorentz-system-in-3d 5 | 6 | 7 | One of the things I really enjoy about Python is how easy it makes it to solve 8 | interesting problems and visualize those solutions in a compelling way. I've 9 | done several posts on creating animations using matplotlib's relatively new 10 | [animation toolkit](http://matplotlib.sourceforge.net/api/animation_api.html): 11 | (some examples are a chaotic 12 | [double pendulum](/blog/2012/08/18/matplotlib-animation-tutorial/), 13 | the collisions of 14 | [particles in a box](/blog/2012/08/18/matplotlib-animation-tutorial/), 15 | the time-evolution of a 16 | [quantum-mechanical wavefunction](/blog/2012/09/05/quantum-python/), 17 | and even a scene from the classic video game, 18 | [Super Mario Bros.](/blog/2013/01/13/hacking-super-mario-bros-with-python/)). 19 | 20 | Recently, a reader [commented](/blog/2012/08/18/matplotlib-animation-tutorial/#comment-799781196) asking whether I might do a 3D animation example. Matplotlib 21 | has a decent 3D toolkit called 22 | [mplot3D](http://matplotlib.org/mpl_toolkits/mplot3d/index.html), 23 | and though I haven't previously seen it used in conjunction with the 24 | animation tools, there's nothing fundamental that prevents it. 25 | 26 | At the commenter's suggestion, I decided to try this out with a simple 27 | example of a chaotic system: the Lorenz equations. 28 | 29 | 30 | 31 | ## Solving the Lorenz System ## 32 | The [Lorenz Equations](http://en.wikipedia.org/wiki/Lorenz_system) are a 33 | system of three coupled, first-order, nonlinear differential equations 34 | which describe the trajectory of a particle through time. 35 | The system was originally derived by Lorenz as a model 36 | of atmospheric convection, but the deceptive simplicity 37 | of the equations have made them an often-used example in fields beyond 38 | atmospheric physics. 39 | 40 | The equations describe the evolution of the spatial variables $x$, $y$, 41 | and $z$, given the governing parameters $\sigma$, $\beta$, and $\rho$, 42 | through the specification of the time-derivatives of the spatial variables: 43 | 44 | ${\rm d}x/{\rm d}t = \sigma(y - x)$ 45 | 46 | ${\rm d}y/{\rm d}t = x(\rho - z) - y$ 47 | 48 | ${\rm d}z/{\rm d}t = xy - \beta z$ 49 | 50 | The resulting dynamics are entirely deterministic giving a starting point 51 | $(x_0, y_0, z_0)$ and a time interval $t$. Though it looks straightforward, 52 | for certain choices of the parameters $(\sigma, \rho, \beta)$, the 53 | trajectories become chaotic, and the resulting trajectories display some 54 | surprising properties. 55 | 56 | Though no general analytic solution exists for this system, the solutions 57 | can be computed numerically. 58 | Python makes this sort of problem very easy to solve: one can 59 | simply use Scipy's interface to 60 | [ODEPACK](https://computation.llnl.gov/casc/odepack/odepack_home.html), 61 | an optimized Fortran package for solving ordinary differential equations. 62 | Here's how the problem can be set up: 63 | 64 | ``` python 65 | import numpy as np 66 | from scipy import integrate 67 | 68 | # Note: t0 is required for the odeint function, though it's not used here. 69 | def lorentz_deriv((x, y, z), t0, sigma=10., beta=8./3, rho=28.0): 70 | """Compute the time-derivative of a Lorenz system.""" 71 | return [sigma * (y - x), x * (rho - z) - y, x * y - beta * z] 72 | 73 | x0 = [1, 1, 1] # starting vector 74 | t = np.linspace(0, 3, 1000) # one thousand time steps 75 | x_t = integrate.odeint(lorentz_deriv, x0, t) 76 | ``` 77 | 78 | That's all there is to it! 79 | 80 | ## Visualizing the results ## 81 | Now that we've computed these results, we can use matplotlib's 82 | animation and 3D plotting toolkits 83 | to visualize the trajectories of several particles. Because 84 | I've described the animation tools in-depth in a 85 | [previous post](/blog/2012/08/18/matplotlib-animation-tutorial/), 86 | I will skip that discussion here and jump straight into the code: 87 | 88 | {% include_code lorentz_animation.py lang:python Lorenz System %} 89 | 90 | The resulting animation looks something like this: 91 | 92 | {% video /downloads/videos/lorentz_attractor.mp4 360 270 /downloads/videos/lorentz_attractor_frame.png %} 93 | 94 | Notice that there are two locations in the space that seem to draw-in all 95 | paths: these are the so-called "Lorenz attractors", and have some interesting 96 | properties which you can read about elsewhere. The qualitative 97 | characteristics of these Lorenz attractors 98 | vary in somewhat surprising ways as the parameters 99 | $(\sigma, \rho, \beta)$ are changed. If you are so inclined, you may 100 | wish to download the above code and play with these values to see what 101 | the results look like. 102 | 103 | I hope that this brief exercise has shown you the power and flexibility of 104 | Python for understanding and visualizing a large array of problems, and 105 | perhaps given you the inspiration to explore similar problems. 106 | 107 | Happy coding! -------------------------------------------------------------------------------- /content/2013-04-15-code-golf-in-python-sudoku.md: -------------------------------------------------------------------------------- 1 | Title: Code Golf in Python: Sudoku 2 | date: 2013-04-15 16:00 3 | comments: true 4 | slug: code-golf-in-python-sudoku 5 | 6 | {% notebook SudokuCodeGolf.ipynb cells[2:] %} 7 | -------------------------------------------------------------------------------- /content/2013-04-29-benchmarking-nearest-neighbor-searches-in-python.md: -------------------------------------------------------------------------------- 1 | Title: Benchmarking Nearest Neighbor Searches in Python 2 | date: 2013-04-29 08:56 3 | comments: true 4 | slug: benchmarking-nearest-neighbor-searches-in-python 5 | 6 | {% notebook TreeBench.ipynb cells[2:] %} 7 | -------------------------------------------------------------------------------- /content/2013-05-12-embedding-matplotlib-animations.md: -------------------------------------------------------------------------------- 1 | Title: Embedding Matplotlib Animations in IPython Notebooks 2 | date: 2013-05-12 19:00 3 | comments: true 4 | slug: embedding-matplotlib-animations 5 | 6 | {% notebook AnimationEmbedding.ipynb cells[2:] %} -------------------------------------------------------------------------------- /content/2013-05-19-a-javascript-viewer-for-matplotlib-animations.md: -------------------------------------------------------------------------------- 1 | Title: A Javascript Viewer for Matplotlib Animations 2 | date: 2013-05-19 07:00 3 | comments: true 4 | slug: a-javascript-viewer-for-matplotlib-animations 5 | 6 | {% notebook JSAnimation.ipynb cells[2:] %} -------------------------------------------------------------------------------- /content/2013-05-28-a-simple-animation-the-magic-triangle.md: -------------------------------------------------------------------------------- 1 | Title: A Simple Animation: The Magic Triangle 2 | date: 2013-05-28 21:00 3 | comments: true 4 | slug: a-simple-animation-the-magic-triangle 5 | 6 | {% notebook MagicTriangle.ipynb cells[2:] %} -------------------------------------------------------------------------------- /content/2013-06-01-ipython-notebook-javascript-python-communication.md: -------------------------------------------------------------------------------- 1 | Title: IPython Notebook: Javascript/Python Bi-directional Communication 2 | date: 2013-06-01 10:00 3 | comments: true 4 | slug: ipython-notebook-javascript-python-communication 5 | 6 | {% notebook JSInteraction.ipynb cells[2:] %} -------------------------------------------------------------------------------- /content/2013-06-15-numba-vs-cython-take-2.md: -------------------------------------------------------------------------------- 1 | Title: Numba vs. Cython: Take 2 2 | date: 2013-06-15 08:00 3 | comments: true 4 | slug: numba-vs-cython-take-2 5 | 6 | {% notebook NumbaCython.ipynb cells[2:] %} -------------------------------------------------------------------------------- /content/2013-07-10-XKCD-plots-in-matplotlib.md: -------------------------------------------------------------------------------- 1 | Title: XKCD Plots in Matplotlib: Going the Whole Way 2 | date: 2013-07-10 16:00 3 | comments: true 4 | slug: XKCD-plots-in-matplotlib 5 | 6 | {% notebook XKCD_sketch_path.ipynb cells[2:] %} -------------------------------------------------------------------------------- /content/2013-08-07-conways-game-of-life.md: -------------------------------------------------------------------------------- 1 | Title: The Game of Life in Python 2 | date: 2013-08-07 16:00 3 | comments: true 4 | slug: conways-game-of-life 5 | 6 | {% notebook GameOfLife.ipynb cells[2:] %} -------------------------------------------------------------------------------- /content/2013-08-28-understanding-the-fft.md: -------------------------------------------------------------------------------- 1 | Title: Understanding the FFT Algorithm 2 | date: 2013-08-28 13:00 3 | comments: true 4 | slug: understanding-the-fft 5 | 6 | {% notebook UnderstandingTheFFT.ipynb cells[2:] %} -------------------------------------------------------------------------------- /content/2013-12-01-kernel-density-estimation.md: -------------------------------------------------------------------------------- 1 | Title: Kernel Density Estimation in Python 2 | date: 2013-12-01 08:00 3 | comments: true 4 | slug: kernel-density-estimation 5 | 6 | {% notebook KDEBench.ipynb cells[2:] %} 7 | -------------------------------------------------------------------------------- /content/2013-12-05-static-interactive-widgets.md: -------------------------------------------------------------------------------- 1 | Title: Static Interactive Widgets for IPython Notebooks 2 | date: 2013-12-05 19:00 3 | comments: true 4 | slug: static-interactive-widgets 5 | 6 | {% notebook IPythonWidgets.ipynb cells[2:] %} 7 | -------------------------------------------------------------------------------- /content/2013-12-19-a-d3-viewer-for-matplotlib.md: -------------------------------------------------------------------------------- 1 | Title: A D3 Viewer for Matplotlib Visualizations 2 | date: 2013-12-19 13:00 3 | comments: true 4 | slug: a-d3-viewer-for-matplotlib 5 | 6 | {% notebook mpld3Demo.ipynb cells[2:] %} 7 | -------------------------------------------------------------------------------- /content/2014-01-10-d3-plugins-truly-interactive.md: -------------------------------------------------------------------------------- 1 | Title: D3 Plugins: Truly Interactive Matplotlib In Your Browser 2 | date: 2014-01-10 16:00 3 | comments: true 4 | slug: d3-plugins-truly-interactive 5 | 6 | {% notebook mpld3_plugins.ipynb cells[2:] %} 7 | -------------------------------------------------------------------------------- /content/2014-03-11-frequentism-and-bayesianism-a-practical-intro.md: -------------------------------------------------------------------------------- 1 | Title: Frequentism and Bayesianism: A Practical Introduction 2 | date: 2014-03-11 11:00 3 | comments: true 4 | slug: frequentism-and-bayesianism-a-practical-intro 5 | 6 | {% notebook FreqBayes.ipynb cells[2:] %} -------------------------------------------------------------------------------- /content/2014-05-05-introduction-to-the-python-buffer-protocol.md: -------------------------------------------------------------------------------- 1 | Title: An Introduction to the Python Buffer Protocol 2 | date: 2014-05-05 16:00 3 | comments: true 4 | slug: introduction-to-the-python-buffer-protocol 5 | 6 | {% notebook BufferProtocol.ipynb cells[2:] %} -------------------------------------------------------------------------------- /content/2014-05-09-why-python-is-slow.md: -------------------------------------------------------------------------------- 1 | Title: Why Python is Slow: Looking Under the Hood 2 | date: 2014-05-09 07:00 3 | comments: true 4 | slug: why-python-is-slow 5 | 6 | {% notebook WhyPythonIsSlow.ipynb cells[2:] %} -------------------------------------------------------------------------------- /content/2014-06-06-frequentism-and-bayesianism-2-when-results-differ.md: -------------------------------------------------------------------------------- 1 | Title: Frequentism and Bayesianism II: When Results Differ 2 | date: 2014-06-06 2:00 3 | comments: true 4 | slug: frequentism-and-bayesianism-2-when-results-differ 5 | 6 | {% notebook FreqBayes2.ipynb cells[2:] %} -------------------------------------------------------------------------------- /content/2014-06-10-is-seattle-really-seeing-an-uptick-in-cycling.md: -------------------------------------------------------------------------------- 1 | Title: Is Seattle Really Seeing an Uptick In Cycling? 2 | date: 2014-06-10 8:30 3 | comments: true 4 | slug: is-seattle-really-seeing-an-uptick-in-cycling 5 | 6 | {% notebook SeattleCycling.ipynb cells[2:] %} -------------------------------------------------------------------------------- /content/2014-06-12-frequentism-and-bayesianism-3-confidence-credibility.md: -------------------------------------------------------------------------------- 1 | Title: Frequentism and Bayesianism III: Confidence, Credibility, and why Frequentism and Science do not Mix 2 | date: 2014-06-12 12:00 3 | comments: true 4 | slug: frequentism-and-bayesianism-3-confidence-credibility 5 | 6 | {% notebook FreqBayes3.ipynb cells[2:] %} -------------------------------------------------------------------------------- /content/2014-06-14-frequentism-and-bayesianism-4-bayesian-in-python.md: -------------------------------------------------------------------------------- 1 | Title: Frequentism and Bayesianism IV: How to be a Bayesian in Python 2 | date: 2014-06-14 09:30 3 | comments: true 4 | slug: frequentism-and-bayesianism-4-bayesian-in-python 5 | 6 | {% notebook FreqBayes4.ipynb cells[2:] %} -------------------------------------------------------------------------------- /content/2014-09-02-on-frequentism-and-fried-chicken.md: -------------------------------------------------------------------------------- 1 | Title: On Frequentism and Fried Chicken 2 | date: 2014-09-02 16:00 3 | comments: true 4 | slug: on-frequentism-and-fried-chicken 5 | 6 | 7 | My recent series of posts on [Frequentism and Bayesianism](http://jakevdp.github.io/blog/2014/03/11/frequentism-and-bayesianism-a-practical-intro/) have drawn a lot of comments, but recently Frederick J. Ross, a UW colleague whom I have not yet had the pleasure of meeting, penned a particularly strong-worded critique: [Bayesian vs frequentist: squabbling among the ignorant](http://madhadron.com/posts/2014-08-30-frequentist_and_bayesian_statistics.html). Here I want to briefly explore and respond to the points he makes in the post. 8 | 9 | 10 | Mr. Ross doesn't mince words. He starts as follows: 11 | 12 | > Every so often some comparison of Bayesian and frequentist statistics comes to my attention. Today it was on a blog called [Pythonic Perambulations](http://jakevdp.github.io/blog/2014/03/11/frequentism-and-bayesianism-a-practical-intro/). It's the work of amateurs. 13 | 14 | He goes on to lodge specific complaints about subtleties I glossed-over in the four posts, all of which seem to miss one salient detail: the posts were an explicit response to my observation that "many scientific researchers never have opportunity to learn the distinctions between Frequentist and Bayesian methods and the different practical approaches that result..." That is, I aimed the discussion not toward someone with a deep background in statistics, but at someone who *can't even name the fundamental differences between frequentism and Bayesianism.* 15 | 16 | Did I gloss over advanced subtleties in this introductory primer? Certainly. As interesting as it may have been for Mr. Ross and other well-read folks had I delved into, say, the deeper questions of assumptions implicit in [frequentist constraints vs. Bayesian priors](http://www.stat.berkeley.edu/~stark/Preprints/constraintsPriors12.pdf), it would have distracted from the purpose of the posts, and would have lost the very readers for whom the posts were written. 17 | 18 | Rethinking the Debate 19 | --------------------- 20 | Thus, we see that his first set of complaints can be chalked-up to a simple misunderstanding of the intended audience: that's an honest mistake, and I won't make more of it. But he goes beyond this, and proposes his own final answer to the centuries-old debate between frequentists and Bayesians. As he writes: "Which one is right? The answer, as usual when faced with a dichotomy, is neither." 21 | 22 | This should pique your interest: he's claiming that not only am I, a humble blogger, an ignorant amateur (which may be true), but that luminaries of the science and statistics world — people like Neyman, Pearson, Fisher, Jaynes, Jeffreys, Savage, and many others who sometimes ardently addressed this question — are simply ignorant squabblers within the field which they all but created. I doubt I'm alone in finding this sweeping indictment a bit suspect. 23 | 24 | But let's skip these charges and dig further: what third route does Mr. Ross propose to trample all this precedent? The answer is decision theory: 25 | 26 | > Probability, as a mathematical theory, has no need of an interpretation... the real battleground is statistics, and the real purpose is to choose an action based on data. The formulation that everyone uses for this, from machine learning to the foundations of Bayesian statistics, is decision theory. 27 | 28 | His argument is that frequentist and Bayesian methods, in a reductionist sense, are both simply means of reaching a decision based on data, and can therefore be viewed as related branches of decision theory. He goes on to define some notation which explains how any statistical procedure can be formulated as a question of progressing from data, via some loss function, to a particular decision. Frequentist and Bayesian approaches are simply manifestations of this unified theory which use particular loss functions, and thus squabbling about them is the pastime of the ignorant. 29 | 30 | I'd like to offer an analogy in response to this idea. 31 | 32 | Baked or Fried? 33 | --------------- 34 | One day in the kitchen, two chefs begin arguing about who makes the best chicken. Chef Hugh prefers his chicken fried: the quick action of the hot oil results light, crispy spiced outer breading complementing the tender meat it encloses. Chef Wolfgang, on the other hand, swears by baked chicken, asserting that its gentler process leaves more moisture, and allows more time for complex flavors to seep into the meat. They decide to have a cook-off: Fried vs. Baked, to decide once and for all which method is the best. 35 | 36 | They're just beginning their preparations in the test kitchen when Rick, the local Food Theorist, storms through the door. He follows these chefs on Twitter, and has heard about this great Fried vs. Baked debate. Given his clear expertise on the matter, he wants to offer his final say on the question. As Food Theorists are wont to do, he starts lecturing them: 37 | 38 | "Truly, I'm not really sure what this whole contest is about. Don't you know that baking and frying are both doing essentially the same thing? Proteins denature as they heat. Water evaporates, sugar caramelizes, and the Maillard Reaction turns carbohydrates and amino acids into a crispy crust. If you could just get into your ignorant heads that any cooking method is simply a manifestation of these simple principles, you'd realize that neither method is better, and we wouldn't need to waste our time on this silly competition." 39 | 40 | At this point, Chef Hugh and Chef Wolfgang pause, catch each other's gaze for a moment, and burst into a hearty laughter. They turn around continue the task of actually turning the raw chicken meat into an edible meal, enjoying the craft and camaraderie of cooking even in the midst of their friendly disagreement. Rick slips out the door and heads home to gaze at his navel while eating a dinner of microwaved pizza bagels. 41 | 42 | 43 | Baked or Fried? Bayesian or Frequentist? 44 | ---------------------------------------- 45 | So what's my point here? The fact is that everything our Food Theorist has said is technically correct: from a completely reductionist perspective, cooking meat is nothing more than controlled denaturing of proteins, evaporation of water, and other well-understood chemical processes. But to *actually prepare a meal*, you can't stop with the theory. You have to figure out how to apply that knowledge in practice, and that requires decisions about whether to use an oven, a deep fryer, or a charcoal grill. 46 | 47 | Similarly, everything Mr. Ross said in his blog post is more or less true, but you can't stop there. Applying his decision theory in practice requires making some choices: despite his protests, you actually *do* have to decide how to map your theory of probability onto reality reflected in data, and that requires some actual philosophical choices about how you treat probability, which lead to fundamentally different questions being answered. 48 | 49 | 50 | Frequentism vs. Bayesianism, Again 51 | ---------------------------------- 52 | This brings us back to the original question Mr. Ross (not I) posed: Frequentism vs. Bayesianism: which is correct? As I've maintained throughout my posts (and as Mr. Ross seems to have overlooked when reading them): neither is correct. Or both. It really depends on the situation. As I have attempted to make clear, if you're asking questions about long-term limiting frequencies of repeated processes, classical frequentist approaches are probably your best bet. If you're hoping to update your knowledge about the world based on a finite set of data, Bayesian approaches are more appropriate. 53 | 54 | While I have argued that Frequentist approaches [answer the wrong question](https://jakevdp.github.io/blog/2014/06/12/frequentism-and-bayesianism-3-confidence-credibility/) in most scientific settings, I have never claimed that frequentism is fundamentally flawed, or that it is "wrong": on the contrary, in that particular post I went to great length to use Monte Carlo simulations to show that the frequentist approach *does* in fact give the correct answer to the question it asks. Frequentist and Bayesian approaches answer different statistical questions, and that is a fact you must realize in order to use them. 55 | 56 | So where does this leave us? Well, Mr. Ross seems to have based his critique largely on misunderstandings: my intended audience was novices rather than experts, and despite his claims otherwise I have never held either the frequentist or Bayesian approach as globally correct at the expense of the other. His protests notwithstanding, I maintain that in practice, frequentism and Bayesianism remain as different as fried and baked chicken: you can huff and puff about unified theoretical frameworks until your face is blue, but at the end of the day you need to choose between the oven and the fryer. -------------------------------------------------------------------------------- /content/2014-10-16-how-bad-is-your-colormap.md: -------------------------------------------------------------------------------- 1 | Title: How Bad Is Your Colormap? 2 | date: 2014-10-16 09:30 3 | comments: true 4 | slug: how-bad-is-your-colormap 5 | 6 | {% notebook HowBadIsYourColormap.ipynb cells[2:] %} -------------------------------------------------------------------------------- /content/2014-11-11-the-hipster-effect-interactive.md: -------------------------------------------------------------------------------- 1 | Title: The Hipster Effect: An IPython Interactive Exploration 2 | date: 2014-11-11 21:00 3 | comments: true 4 | slug: the-hipster-effect-interactive 5 | 6 | {% notebook HipsterEffect.ipynb cells[2:] %} -------------------------------------------------------------------------------- /content/2015-02-24-optimizing-python-with-numpy-and-numba.md: -------------------------------------------------------------------------------- 1 | Title: Optimizing Python in the Real World: NumPy, Numba, and the NUFFT 2 | date: 2015-02-24 12:00 3 | comments: true 4 | slug: optimizing-python-with-numpy-and-numba 5 | 6 | {% notebook NUFFT.ipynb cells[2:] %} -------------------------------------------------------------------------------- /content/2015-06-13-lomb-scargle-in-python.md: -------------------------------------------------------------------------------- 1 | Title: Fast Lomb-Scargle Periodograms in Python 2 | date: 2015-06-13 14:00 3 | comments: true 4 | slug: lomb-scargle-in-python 5 | 6 | {% notebook LombScarglePython.ipynb cells[2:] %} -------------------------------------------------------------------------------- /content/2015-07-06-model-complexity-myth.md: -------------------------------------------------------------------------------- 1 | Title: The Model Complexity Myth 2 | date: 2015-07-06 08:00 3 | comments: true 4 | slug: model-complexity-myth 5 | 6 | {% notebook ModelComplexityMyth.ipynb cells[2:] %} -------------------------------------------------------------------------------- /content/2015-07-23-learning-seattles-work-habits-from-bicycle-counts.md: -------------------------------------------------------------------------------- 1 | Title: Learning Seattle's Work Habits from Bicycle Counts (Updated!) 2 | date: 2015-07-23 08:00 3 | comments: true 4 | slug: learning-seattles-work-habits-from-bicycle-counts 5 | 6 | {% notebook SeattleCycling2.ipynb cells[2:] %} -------------------------------------------------------------------------------- /content/2015-08-07-frequentism-and-bayesianism-5-model-selection.md: -------------------------------------------------------------------------------- 1 | Title: Frequentism and Bayesianism V: Model Selection 2 | date: 2015-08-07 11:00 3 | comments: true 4 | slug: frequentism-and-bayesianism-5-model-selection 5 | 6 | {% notebook FreqBayes5.ipynb cells[2:] %} -------------------------------------------------------------------------------- /content/2015-08-14-out-of-core-dataframes-in-python.md: -------------------------------------------------------------------------------- 1 | Title: Out-of-Core Dataframes in Python: Dask and OpenStreetMap 2 | date: 2015-08-14 11:00 3 | comments: true 4 | slug: out-of-core-dataframes-in-python 5 | 6 | {% notebook OutOfCoreMapping.ipynb cells[2:] %} -------------------------------------------------------------------------------- /content/2015-10-17-analyzing-pronto-cycleshare-data-with-python-and-pandas.md: -------------------------------------------------------------------------------- 1 | Title: Analyzing Pronto CycleShare Data with Python and Pandas 2 | date: 2015-10-17 21:00 3 | comments: true 4 | slug: analyzing-pronto-cycleshare-data-with-python-and-pandas 5 | 6 | {% notebook ProntoData.ipynb cells[2:] %} -------------------------------------------------------------------------------- /content/2016-08-25-conda-myths-and-misconceptions.md: -------------------------------------------------------------------------------- 1 | Title: Conda: Myths and Misconceptions 2 | date: 2016-08-25 09:00 3 | comments: true 4 | slug: conda-myths-and-misconceptions 5 | 6 | {% notebook CondaMyths.ipynb cells[2:] %} -------------------------------------------------------------------------------- /content/downloads/code/animate_square.py: -------------------------------------------------------------------------------- 1 | """ 2 | Optical Illusion in Matplotlib 3 | 4 | author: Jake Vanderplas 5 | email: vanderplas@astro.washington.edu 6 | website: http://jakevdp.github.com 7 | license: BSD 8 | Please feel free to use and modify this, but keep the above information. 9 | Thanks! 10 | """ 11 | 12 | import numpy as np 13 | from matplotlib import pyplot as plt 14 | from matplotlib import animation 15 | from matplotlib.patches import RegularPolygon 16 | 17 | # First set up the figure, the axis, and the plot element we want to animate 18 | fig = plt.figure() 19 | ax = plt.axes(xlim=(-2.5, 2.5), ylim=(-2.5, 2.5), aspect='equal') 20 | 21 | # Add lines and patches 22 | lines = ax.plot(np.zeros((2, 4)), np.zeros((2, 2)), lw=2, color='black') 23 | squares = [RegularPolygon((np.sqrt(2) * x, np.sqrt(2) * y), 24 | 4, radius=0.6, orientation=0, color='black') 25 | for (x, y) in [(1, 0), (-1, 0), (0, 1), (0, -1)]] 26 | for sq in squares: 27 | ax.add_patch(sq) 28 | 29 | 30 | # initialization function: plot the background of each frame 31 | def init(): 32 | for line in lines: 33 | line.set_data([], []) 34 | for sq in squares: 35 | sq.set_alpha(0) 36 | return lines + squares 37 | 38 | 39 | # animation function. This is called sequentially 40 | def animate(i): 41 | # Set transparency level for squares 42 | level = 0.5 - 0.8 * np.cos(0.005 * i * np.pi) 43 | level = min(level, 1) 44 | level = max(level, 0) 45 | 46 | for sq in squares: 47 | sq.set_alpha(level) 48 | 49 | # Set location for lines 50 | s = 0.6 51 | d1 = 0.2 * np.sin(0.05 * i * np.pi) 52 | d2 = 0.2 * np.cos(0.05 * i * np.pi) 53 | 54 | lines[0].set_data([(1 + d1 - s) / np.sqrt(2), 55 | (1 + d1 + s) / np.sqrt(2)], 56 | [(1 + d1 + s) / np.sqrt(2), 57 | (1 + d1 - s) / np.sqrt(2)]) 58 | 59 | lines[1].set_data([(-1 + d1 - s) / np.sqrt(2), 60 | (-1 + d1 + s) / np.sqrt(2)], 61 | [(-1 + d1 + s) / np.sqrt(2), 62 | (-1 + d1 - s) / np.sqrt(2)]) 63 | 64 | lines[2].set_data([(-1 - d2 - s) / np.sqrt(2), 65 | (-1 - d2 + s) / np.sqrt(2)], 66 | [(1 + d2 - s) / np.sqrt(2), 67 | (1 + d2 + s) / np.sqrt(2)]) 68 | 69 | lines[3].set_data([(1 - d2 - s) / np.sqrt(2), 70 | (1 - d2 + s) / np.sqrt(2)], 71 | [(-1 + d2 - s) / np.sqrt(2), 72 | (-1 + d2 + s) / np.sqrt(2)]) 73 | 74 | return lines + squares 75 | 76 | # call the animator. blit=True means only re-draw parts that have changed. 77 | anim = animation.FuncAnimation(fig, animate, init_func=init, 78 | frames=400, interval=28, blit=True) 79 | 80 | 81 | # save the animation as an mp4. This requires ffmpeg or mencoder to be 82 | # installed. The extra_args ensure that the x264 codec is used, so that 83 | # the video can be embedded in html5. You may need to adjust this for 84 | # your system: for more information, see 85 | # http://matplotlib.sourceforge.net/api/animation_api.html 86 | #anim.save('basic_animation.mp4', fps=30, extra_args=['-vcodec', 'libx264']) 87 | 88 | plt.show() 89 | -------------------------------------------------------------------------------- /content/downloads/code/basic_animation.py: -------------------------------------------------------------------------------- 1 | """ 2 | Matplotlib Animation Example 3 | 4 | author: Jake Vanderplas 5 | email: vanderplas@astro.washington.edu 6 | website: http://jakevdp.github.com 7 | license: BSD 8 | Please feel free to use and modify this, but keep the above information. Thanks! 9 | """ 10 | 11 | import numpy as np 12 | from matplotlib import pyplot as plt 13 | from matplotlib import animation 14 | 15 | # First set up the figure, the axis, and the plot element we want to animate 16 | fig = plt.figure() 17 | ax = plt.axes(xlim=(0, 2), ylim=(-2, 2)) 18 | line, = ax.plot([], [], lw=2) 19 | 20 | # initialization function: plot the background of each frame 21 | def init(): 22 | line.set_data([], []) 23 | return line, 24 | 25 | # animation function. This is called sequentially 26 | def animate(i): 27 | x = np.linspace(0, 2, 1000) 28 | y = np.sin(2 * np.pi * (x - 0.01 * i)) 29 | line.set_data(x, y) 30 | return line, 31 | 32 | # call the animator. blit=True means only re-draw the parts that have changed. 33 | anim = animation.FuncAnimation(fig, animate, init_func=init, 34 | frames=200, interval=20, blit=True) 35 | 36 | # save the animation as an mp4. This requires ffmpeg or mencoder to be 37 | # installed. The extra_args ensure that the x264 codec is used, so that 38 | # the video can be embedded in html5. You may need to adjust this for 39 | # your system: for more information, see 40 | # http://matplotlib.sourceforge.net/api/animation_api.html 41 | anim.save('basic_animation.mp4', fps=30, extra_args=['-vcodec', 'libx264']) 42 | 43 | plt.show() 44 | -------------------------------------------------------------------------------- /content/downloads/code/convert_notebook.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | 4 | try: 5 | nbconvert = sys.argv[1] 6 | notebook = sys.argv[2] 7 | except: 8 | print "usage: python convert_notebook.py /path/to/nbconvert.py /path/to/notebook_file.ipynb" 9 | sys.exit(-1) 10 | 11 | # convert notebook 12 | os.system('%s -f blogger-html %s' % (nbconvert, notebook)) 13 | 14 | # get out filenames 15 | outfile_root = os.path.splitext(notebook)[0] 16 | body_file = outfile_root + '.html' 17 | header_file = outfile_root + '_header.html' 18 | 19 | 20 | # read the files 21 | body = open(body_file).read() 22 | header = open(header_file).read() 23 | 24 | 25 | # replace the highlight tags 26 | body = body.replace('class="highlight"', 'class="highlight-ipynb"') 27 | header = header.replace('highlight', 'highlight-ipynb') 28 | 29 | 30 | # specify
 tags
31 | body = body.replace('"
45 | header = header.replace('body {', 'div.ipynb {')
46 | 
47 | 
48 | # specialize headers
49 | header = header.replace('html, body,',
50 |                         '\n'.join((('h1.ipynb h2.ipynb h3.ipynb '
51 |                                     'h4.ipynb h5.ipynb h6.ipynb {'),
52 |                                    'h1.ipynb h2.ipynb ... {',
53 |                                    '  margin: 0;',
54 |                                    '  padding: 0;',
55 |                                    '  border: 0;',
56 |                                    '  font-size: 100%;',
57 |                                    '  font: inherit;',
58 |                                    '  vertical-align: baseline;',
59 |                                    '}\n',
60 |                                    'html, body,')))
61 | for h in '123456':
62 |     body = body.replace('
  4 | License: GPL.
  5 | Feel free to use and distribute, but keep this attribution intact.
  6 | """
  7 | from collections import defaultdict
  8 | import zipfile
  9 | import numpy as np
 10 | from matplotlib import pyplot as plt
 11 | from matplotlib.colors import ListedColormap
 12 | from matplotlib import animation
 13 | 
 14 | 
 15 | class NESGraphics(object):
 16 |     """Class interface for stripping graphics from an NES ROM"""
 17 |     def __init__(self, filename='mario_ROM.zip', offset=2049):
 18 |         self.offset = offset
 19 |         if zipfile.is_zipfile(filename):
 20 |             zp = zipfile.ZipFile(filename)
 21 |             data = np.unpackbits(np.frombuffer(zp.read(zp.filelist[0]),
 22 |                                                dtype=np.uint8))
 23 |         else:
 24 |             data = np.unpackbits(np.fromfile(filename, dtype=np.uint8))
 25 |         self.data = data.reshape((-1, 8, 8))
 26 | 
 27 |     def generate_image(self, A, C=None, transparent=False):
 28 |         """Generate an image from the pattern table.
 29 | 
 30 |         Parameters
 31 |         ----------
 32 |         A : array_like
 33 |             an array of integers indexing the thumbnails to use.
 34 |             The upper-left corner of the image is A[0, 0], and the
 35 |             bottom-right corner is A[-1, -1].  A negative index indicates
 36 |             that the thumbnail should be flipped horizontally.
 37 |         C : array-like
 38 |             The color table for A.  C should have shape A.shape + (4,).
 39 |             C[i, j] gives the values associated with the four bits of A
 40 |             for the output image.
 41 |         transparent : array_like
 42 |             if true, then zero-values in A will be masked for transparency
 43 | 
 44 |         Returns
 45 |         -------
 46 |         im : ndarray or masked array
 47 |              the image encoded by A and C
 48 |         """
 49 |         A = np.asarray(A)
 50 |         if C is None:
 51 |             C = range(4)
 52 | 
 53 |         # broadcast C to the shape of A
 54 |         C = np.asarray(C) + np.zeros(A.shape + (1,))
 55 | 
 56 |         im = np.zeros((8 * A.shape[0], 8 * A.shape[1]))
 57 |         for i in range(A.shape[0]):
 58 |             for j in range(A.shape[1]):
 59 |                 # extract bits
 60 |                 ind = 2 * (abs(A[i, j]) + self.offset)
 61 |                 thumb = self.data[ind] + 2 * self.data[ind + 1]
 62 | 
 63 |                 # set bit colors
 64 |                 thumb = C[i, j, thumb]
 65 | 
 66 |                 # flip image if negative
 67 |                 if A[i, j] < 0:
 68 |                     thumb = thumb[:, ::-1]
 69 |                 im[8 * i:8 * (i + 1), 8 * j:8 * (j + 1)] = thumb
 70 | 
 71 |         if transparent:
 72 |             im = np.ma.masked_equal(im, 0)
 73 | 
 74 |         return im
 75 | 
 76 | 
 77 | class NESAnimator():
 78 |     """Class for animating NES graphics"""
 79 |     def __init__(self, framesize, figsize=(8, 6),
 80 |                  filename='mario_ROM.zip', offset=2049):
 81 |         self.NG = NESGraphics()
 82 |         self.figsize = figsize
 83 |         self.framesize = framesize
 84 |         self.frames = defaultdict(lambda: [])
 85 |         self.ims = {}
 86 | 
 87 |     def add_frame(self, key, A, C=None, ctable=None,
 88 |                   offset=(0, 0), transparent=True):
 89 |         """add a frame to the animation.
 90 |         A & C are passed to NESGraphics.generate_image"""
 91 |         cmap = ListedColormap(ctable)
 92 |         im = self.NG.generate_image(A, C, transparent=transparent)
 93 |         self.frames[key].append((im, cmap, offset))
 94 | 
 95 |     def _initialize(self):
 96 |         """initialize animation"""
 97 |         A = np.ma.masked_equal(np.zeros((2, 2)), 0)
 98 |         for i, key in enumerate(sorted(self.frames.keys())):
 99 |             self.ims[key] = self.ax.imshow(A, interpolation='nearest',
100 |                                            zorder=i + 1)
101 |         self.ax.set_xlim(0, self.framesize[1])
102 |         self.ax.set_ylim(0, self.framesize[0])
103 | 
104 |         return tuple(self.ims[key] for key in sorted(self.ims.keys()))
105 | 
106 |     def _animate(self, i):
107 |         """animation step"""
108 |         for key in sorted(self.frames.keys()):
109 |             im, cmap, offset = self.frames[key][i % len(self.frames[key])]
110 | 
111 |             self.ims[key].set_data(im)
112 |             self.ims[key].set_cmap(cmap)
113 |             self.ims[key].set_clim(0, len(cmap.colors) - 1)
114 |             self.ims[key].set_extent((offset[1],
115 |                                       im.shape[1] / 8 + offset[1],
116 |                                       offset[0],
117 |                                       im.shape[0] / 8 + offset[0]))
118 | 
119 |         return tuple(self.ims[key] for key in sorted(self.ims.keys()))
120 | 
121 |     def animate(self, interval, frames, blit=True):
122 |         """animate the frames"""
123 |         self.fig = plt.figure(figsize=self.figsize)
124 |         self.ax = self.fig.add_axes([0, 0, 1, 1], frameon=False,
125 |                                     xticks=[], yticks=[])
126 |         self.ax.xaxis.set_major_formatter(plt.NullFormatter())
127 |         self.ax.yaxis.set_major_formatter(plt.NullFormatter())
128 |         self.anim = animation.FuncAnimation(self.fig,
129 |                                             self._animate,
130 |                                             init_func=self._initialize,
131 |                                             frames=frames, interval=interval,
132 |                                             blit=blit)
133 |         self.fig.anim = self.anim
134 |         return self.anim
135 | 
136 | 
137 | def animate_mario():
138 |     NA = NESAnimator(framesize=(12, 16), figsize=(4, 3))
139 | 
140 |     # Set up the background frames
141 |     bg = np.zeros((12, 18), dtype=int)
142 |     bg_colors = np.arange(4) + np.zeros((12, 18, 4))
143 |     bg_ctable = ['#88AACC', 'tan', 'brown', 'black',
144 |                  'green', '#DDAA11', '#FFCC00']
145 | 
146 |     # blue sky
147 |     bg.fill(292)
148 | 
149 |     # brown bricks on the ground
150 |     bg[10] = 9 * [436, 437]
151 |     bg[11] = 9 * [438, 439]
152 | 
153 |     # little green hill 
154 |     bg[8, 3:5] = [305, 306]
155 |     bg[9, 2:6] = [304, 308, 294, 307]
156 |     bg_colors[8, 3:5] = [0, 1, 4, 3]
157 |     bg_colors[9, 2:6] = [0, 1, 4, 3]
158 | 
159 |     # brown bricks
160 |     bg[2, 10:18] = 325
161 |     bg[3, 10:18] = 327
162 | 
163 |     # gold question block
164 |     bg[2, 12:14] = [339, 340]
165 |     bg[3, 12:14] = [341, 342]
166 |     bg_colors[2:4, 12:14] = [0, 6, 2, 3]
167 |     
168 |     # duplicate background for clean wrapping
169 |     bg = np.hstack([bg, bg])
170 |     bg_colors = np.hstack([bg_colors, bg_colors])
171 | 
172 |     # get index of yellow pixels to make them flash
173 |     i_yellow = np.where(bg_colors == 6)
174 | 
175 |     # create background frames by offsetting the image
176 |     for offset in range(36):
177 |         bg_colors[i_yellow] = [6, 6, 6, 6, 5, 5, 2, 5, 5][offset % 9]
178 |         NA.add_frame('bg', bg, bg_colors, bg_ctable,
179 |                      offset=(0, -0.5 * offset),
180 |                      transparent=False)
181 | 
182 |     # Create mario frames
183 |     mario_colors = ['white', 'red', 'orange', 'brown']
184 |     NA.add_frame('mario', [[0, 1], [2, 3], [4, 5], [6, 7]],
185 |                  ctable=mario_colors, offset=(2, 10))
186 |     NA.add_frame('mario', [[8, 9], [10, 11], [12, 13], [14, 15]],
187 |                  ctable=mario_colors, offset=(2, 10))
188 |     NA.add_frame('mario', [[16, 17], [18, 19], [20, 21], [22, 23]],
189 |                  ctable=mario_colors, offset=(2, 10))
190 | 
191 |     # Create koopa-troopa frames
192 |     troopa_colors = ['white', 'green', 'white', 'orange']
193 |     NA.add_frame('troopa', [[252, 160], [161, 162], [163, 164]],
194 |                  ctable=troopa_colors, offset=(2, 7))
195 |     NA.add_frame('troopa', [[252, 165], [166, 167], [168, 169]],
196 |                  ctable=troopa_colors, offset=(2, 7))
197 | 
198 |     # Create goomba frames
199 |     goomba_colors = ['white', 'black', '#EECCCC', '#BB3333']
200 |     NA.add_frame('goomba', [[112, 113], [114, 115]],
201 |                  ctable=goomba_colors, offset=(2, 4))
202 |     NA.add_frame('goomba', [[112, 113], [-115, -114]],
203 |                  ctable=goomba_colors, offset=(2, 4))
204 | 
205 |     return NA.animate(interval=100, frames=36)
206 | 
207 | 
208 | if __name__ == '__main__':
209 |     anim = animate_mario()
210 | 
211 |     # saving as animated gif requires matplotlib 0.13+ and imagemagick
212 |     #anim.save('mario_animation.gif', writer='imagemagick', fps=10)
213 | 
214 |     plt.show()
215 | 


--------------------------------------------------------------------------------
/content/downloads/code/mario/draw_mario.py:
--------------------------------------------------------------------------------
  1 | """Extract and draw graphics from Mario
  2 | 
  3 | By Jake Vanderplas, 2013 
  4 | License: GPL.
  5 | Feel free to use and distribute, but keep this attribution intact.
  6 | """
  7 | import zipfile
  8 | import numpy as np
  9 | from matplotlib import pyplot as plt
 10 | from matplotlib.colors import ListedColormap
 11 | 
 12 | 
 13 | class NESGraphics(object):
 14 |     """Class interface for stripping graphics from an NES ROM"""
 15 |     def __init__(self, filename='mario_ROM.zip', offset=2049):
 16 |         self.offset = offset
 17 |         if zipfile.is_zipfile(filename):
 18 |             zp = zipfile.ZipFile(filename)
 19 |             data = np.unpackbits(np.frombuffer(zp.read(zp.filelist[0]),
 20 |                                                dtype=np.uint8))
 21 |         else:
 22 |             data = np.unpackbits(np.fromfile(filename, dtype=np.uint8))
 23 |         self.data = data.reshape((-1, 8, 8))
 24 | 
 25 |     def generate_image(self, A, C=None, transparent=False):
 26 |         """Generate an image from the pattern table.
 27 | 
 28 |         Parameters
 29 |         ----------
 30 |         A : array_like
 31 |             an array of integers indexing the thumbnails to use.
 32 |             The upper-left corner of the image is A[0, 0], and the
 33 |             bottom-right corner is A[-1, -1].  A negative index indicates
 34 |             that the thumbnail should be flipped horizontally.
 35 |         C : array-like
 36 |             The color table for A.  C should have shape A.shape + (4,).
 37 |             C[i, j] gives the values associated with the four bits of A
 38 |             for the output image.
 39 |         transparent : array_like
 40 |             if true, then zero-values in A will be masked for transparency
 41 | 
 42 |         Returns
 43 |         -------
 44 |         im : ndarray or masked array
 45 |              the image encoded by A and C
 46 |         """
 47 |         A = np.asarray(A)
 48 |         if C is None:
 49 |             C = range(4)
 50 | 
 51 |         # broadcast C to the shape of A
 52 |         C = np.asarray(C) + np.zeros(A.shape + (1,))
 53 | 
 54 |         im = np.zeros((8 * A.shape[0], 8 * A.shape[1]))
 55 |         for i in range(A.shape[0]):
 56 |             for j in range(A.shape[1]):
 57 |                 # extract bits
 58 |                 ind = 2 * (abs(A[i, j]) + self.offset)
 59 |                 thumb = self.data[ind] + 2 * self.data[ind + 1]
 60 | 
 61 |                 # set bit colors
 62 |                 thumb = C[i, j, thumb]
 63 | 
 64 |                 # flip image if negative
 65 |                 if A[i, j] < 0:
 66 |                     thumb = thumb[:, ::-1]
 67 |                 im[8 * i:8 * (i + 1), 8 * j:8 * (j + 1)] = thumb
 68 | 
 69 |         if transparent:
 70 |             im = np.ma.masked_equal(im, 0)
 71 | 
 72 |         return im
 73 | 
 74 | 
 75 | def draw_mario():
 76 |     """Draw a grid of mario graphics"""
 77 |     NG = NESGraphics()
 78 |     cmap = ListedColormap(['white', 'red', 'orange', 'brown'])
 79 |     im = 252 + np.zeros((17, 19))
 80 | 
 81 |     # Big Mario row 1
 82 |     im[1:5, 1:3] = [[0, 1], [2, 3], [4, 5], [6, 7]]
 83 |     im[1:5, 4:6] = [[8, 9], [10, 11], [12, 13], [14, 15]]
 84 |     im[1:5, 7:9] = [[16, 17], [18, 19], [20, 21], [22, 23]]
 85 |     im[1:5, 10:12] = [[24, 25], [26, 27], [28, 29], [30, 31]]
 86 |     im[1:5, 13:15] = [[32, 33], [34, 35], [36, 37], [38, 39]]
 87 |     im[1:5, 16:18] = [[0, 1], [76, 77], [74, -74], [75, -75]]
 88 | 
 89 |     # Small Mario row 1
 90 |     im[9:13, 1:3] = [[8, 9], [40, 41], [42, 43], [44, 45]]
 91 |     im[9:13, 4:6] = [[8, 9], [10, 11], [12, 48], [49, 45]]
 92 |     im[9:13, 7:9] = [[8, 9], [10, 11], [46, 47], [49, 45]]
 93 |     im[9:13, 10:12] = [[8, 9], [10, 11], [12, 48], [92, 93]]
 94 |     im[9:13, 13:15] = [[8, 9], [10, 11], [12, 48], [94, 95]]
 95 |     im[9:13, 16:18] = [[252, 252], [8, 9], [88, 89], [90, -90]]
 96 | 
 97 |     # Big Mario row 2
 98 |     im[6:8, 1:3] = [[54, 55], [56, 57]]
 99 |     im[6:8, 4:6] = [[50, 51], [52, 53]]
100 |     im[6:8, 7:9] = [[58, 55], [59, 60]]
101 |     im[6:8, 10:12] = [[61, 62], [63, 64]]
102 |     im[6:8, 13:15] = [[50, 65], [66, 67]]
103 |     im[6:8, 16:18] = [[58, 55], [79, -79]]
104 | 
105 |     # Small Mario row 2
106 |     im[14:16, 1:3] = [[50, 51], [68, 69]]
107 |     im[14:16, 4:6] = [[50, 51], [70, 71]]
108 |     im[14:16, 7:9] = [[50, 51], [72, 73]]
109 |     im[14:16, 10:12] = [[50, 51], [144, 145]]
110 |     im[14:16, 13:15] = [[58, 55], [146, 147]]
111 |     im[14:16, 16:18] = [[158, -158], [159, -159]]
112 | 
113 |     im = NG.generate_image(im)
114 |     
115 |     fig = plt.figure(figsize=(6, 6 * 17. / 19.))
116 |     ax = fig.add_axes((0, 0, 1, 1), xticks=[], yticks=[])
117 |     ax.imshow(im, cmap=cmap, interpolation='nearest', clim=(0, 3))
118 | 
119 | 
120 | def draw_graphics():
121 |     """Draw foreground and background mario graphics"""
122 |     NG = NESGraphics()
123 |     cmap = ListedColormap(['#88AACC', 'black', '#EECCCC', '#BB3333',
124 |                            'green', 'orange', 'red', 'gold',
125 |                            '#EEEEEE', 'gray'])
126 |     
127 |     im = 252 + np.zeros((17, 16))
128 |     colors = np.zeros((17, 16, 4))
129 | 
130 |     # question block
131 |     im[1:3, 1:3] = [[339, 340], [341, 342]]
132 |     colors[1:3, 1:3] = [0, 7, 3, 1]
133 | 
134 |     # coin
135 |     im[1:3, 4:6] = [[421, 422], [423, 424]]
136 |     colors[1:3, 4:6] = [0, 7, 8, 1]
137 | 
138 |     # mushroom
139 |     im[1:3, 7:9] = [[118, 119], [120, 121]]
140 |     colors[1:3, 7:9] = [0, 6, 8, 5]
141 | 
142 |     # 1-up mushroom
143 |     im[1:3, 10:12] = [[118, 119], [120, 121]]
144 |     colors[1:3, 10:12] = [0, 4, 8, 7]
145 | 
146 |     # fire flower
147 |     im[1:3, 13:15] = [[214, -214], [217, -217]]
148 |     colors[1, 13:15] = [0, 6, 8, 5]
149 |     colors[2, 13:15] = [0, 4, 4, 4]
150 | 
151 |     # green koopa-troopa
152 |     im[4:7, 1:3] = [[252, 160], [161, 162], [163, 164]]
153 |     im[4:7, 4:6] = [[252, 165], [166, 167], [168, 169]]
154 |     colors[4:7, 1:6] = [0, 4, 8, 5]
155 | 
156 |     # red koopa-troopa
157 |     im[4:7, 7:9] = [[252, 160], [161, 162], [163, 164]]
158 |     im[4:7, 10:12] = [[252, 165], [166, 167], [168, 169]]
159 |     colors[4:7, 7:12] = [0, 6, 8, 5]
160 | 
161 |     # cloud turtle
162 |     im[4:7, 13:15] = [[185, 184], [432, 434], [433, 435]]
163 |     colors[4, 13:15] = [0, 4, 8, 5]
164 |     colors[5:7, 13:15] = [0, 8, 8, 4]
165 | 
166 |     # spiny
167 |     im[8:10, 1:3] = [[148, -148], [149, -149]]
168 |     im[8:10, 4:6] = [[150, 151], [152, 153]]
169 |     im[8:10, 7:9] = [[154, 155], [156, 157]]
170 |     colors[8:10, 1:9] = [0, 6, 8, 5]
171 | 
172 |     # Goombas
173 |     im[8:10, 10:12] = [[112, 113], [114, 115]]
174 |     im[8:10, 13:15] = [[112, 113], [-115, -114]]
175 |     colors[8:10, 10:15] = [0, 1, 2, 3]
176 | 
177 |     # fish
178 |     im[11:13, 1:3] = [[178, 179], [180, 181]]
179 |     im[11:13, 4:6] = [[182, 179], [183, 181]]
180 |     colors[11:13, 1:6] = [0, 9, 8, 5]
181 | 
182 |     # beetle
183 |     im[11:13, 7:9] = [[170, 171], [172, 173]]
184 |     im[11:13, 10:12] = [[174, 175], [176, 177]]
185 |     colors[11:13, 7:12] = [0, 1, 2, 3]
186 | 
187 |     # flag
188 |     im[14:16, 1:3] = [[84, 85], [86, 87]]
189 |     colors[14:16, 1:3] = [0, 6, 8, 5]
190 | 
191 |     # brown block
192 |     im[14:16, 4:6] = [[343, 344], [345, 346]]
193 |     colors[14:16, 4:6] = [0, 7, 3, 1]
194 | 
195 |     # brown block 2
196 |     im[14:16, 7:9] = [[436, 437], [438,439]]
197 |     colors[14:16, 7:9] = [0, 2, 3, 1]
198 | 
199 |     # bricks
200 |     im[14:16, 10:12] = [[325, 325], [327, 327]]
201 |     colors[14:16, 10:12] = [0, 2, 3, 1]
202 | 
203 |     # cannon
204 |     im[12:16, 13:15] = [[454, 455], [456, 457], [458, 459], [460, 461]]
205 |     colors[12:16, 13:15] = [0, 2, 3, 1]
206 |     
207 |     im = NG.generate_image(im, colors)
208 |     
209 |     fig = plt.figure(figsize=(5, 5 * 17. / 16.))
210 |     ax = fig.add_axes((0, 0, 1, 1), xticks=[], yticks=[])
211 |     ax.imshow(im, cmap=cmap, interpolation='nearest', clim=(0, 9))
212 | 
213 | if __name__ == '__main__':
214 |     draw_mario()
215 |     draw_graphics()
216 |     plt.show()
217 | 


--------------------------------------------------------------------------------
/content/downloads/code/mario/mario_ROM.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakevdp/PythonicPerambulations/4bba4ebbaf661b0874567db63dfc140c172ecb68/content/downloads/code/mario/mario_ROM.zip


--------------------------------------------------------------------------------
/content/downloads/code/mario/test_mario.py:
--------------------------------------------------------------------------------
  1 | import zipfile
  2 | import numpy as np
  3 | from matplotlib import pyplot as plt
  4 | from matplotlib.colors import ListedColormap
  5 | 
  6 | BGCOLOR = '#AAAACC'
  7 | background = ListedColormap([BGCOLOR])
  8 | mario = ListedColormap([BGCOLOR, 'red', 'orange', 'brown'])
  9 | fire_mario = ListedColormap([BGCOLOR, 'white', 'orange', 'red'])
 10 | 
 11 | NUL = 252
 12 | 
 13 | 
 14 | class MarioDisplay(object):
 15 |     @classmethod
 16 |     def show_mario(cls):
 17 |         return cls(np.array(
 18 |                 [[[0, 1], [2, 3], [4, 5], [6, 7]],
 19 |                  [[8, 9], [10, 11], [12, 13], [14, 15]],
 20 |                  [[16, 17], [18, 19], [20, 21], [22, 23]],
 21 |                  [[24, 25], [26, 27], [28, 29], [30, 31]],
 22 |                  [[32, 33], [34, 35], [36, 37], [38, 39]],
 23 |                  [[8, 9], [40, 41], [42, 43], [44, 45]],
 24 |                  [[8, 9], [10, 11], [12, 48], [49, 45]],
 25 |                  [[8, 9], [10, 11], [46, 47], [49, 45]],
 26 |                  [[0, 1], [76, 77], [74, -74], [75, -75]],
 27 |                  [[NUL, NUL], [8, 9], [88, 89], [90, -90]],
 28 |                  [[8, 9], [10, 11], [12, 48], [92, 93]],
 29 |                  [[8, 9], [10, 11], [12, 48], [94, 95]],
 30 |                  [[NUL, NUL], [NUL, NUL], [50, 51], [52, 53]],
 31 |                  [[NUL, NUL], [NUL, NUL], [54, 55], [56, 57]],
 32 |                  [[NUL, NUL], [NUL, NUL], [58, 55], [59, 60]],
 33 |                  [[NUL, NUL], [NUL, NUL], [61, 62], [63, 64]],
 34 |                  [[NUL, NUL], [NUL, NUL], [50, 65], [66, 67]],
 35 |                  [[NUL, NUL], [NUL, NUL], [50, 51], [68, 69]],
 36 |                  [[NUL, NUL], [NUL, NUL], [50, 51], [70, 71]],
 37 |                  [[NUL, NUL], [NUL, NUL], [50, 51], [72, 73]],
 38 |                  [[NUL, NUL], [NUL, NUL], [58, 55], [79, -79]],
 39 |                  [[NUL, NUL], [NUL, NUL], [50, 51], [144, 145]],
 40 |                  [[NUL, NUL], [NUL, NUL], [58, 55], [146, 147]],
 41 |                  [[NUL, NUL], [NUL, NUL], [158, -158], [159, -159]]]),
 42 |                    cmap=mario)
 43 | 
 44 |     @classmethod
 45 |     def show_random(cls):
 46 |         return cls(np.array(
 47 |                 [[[NUL, NUL], [NUL, NUL], [112, 113], [114, 115]],
 48 |                  [[NUL, NUL], [NUL, NUL], [112, 113], [-115, -114]],
 49 |                  [[NUL, NUL], [NUL, NUL], [118, 119], [120, 121]],
 50 |                  [[NUL, NUL], [NUL, NUL], [150, 151], [152, 153]],
 51 |                  [[NUL, NUL], [NUL, NUL], [154, 155], [156, 157]],
 52 |                  [[NUL, NUL], [NUL, 160], [161, 162], [163, 164]],
 53 |                  [[NUL, NUL], [NUL, 165], [166, 167], [168, 169]],
 54 |                  [[NUL, NUL], [NUL, NUL], [170, 171], [172, 173]],
 55 |                  [[NUL, NUL], [NUL, NUL], [174, 175], [176, 177]],
 56 |                  [[NUL, NUL], [NUL, NUL], [339, 340], [341, 342]],
 57 |                  [[NUL, NUL], [NUL, NUL], [343, 344], [345, 346]],
 58 |                  [[NUL, NUL], [NUL, NUL], [432, 434], [433, 435]]]))
 59 | 
 60 |     def __init__(self, frames, filename='mario_ROM.zip',
 61 |                  pattern_offset=2049, cmap=plt.cm.binary):
 62 |         zp = zipfile.ZipFile(filename)
 63 |         self.data = np.unpackbits(np.frombuffer(zp.read(zp.filelist[0]),
 64 |                                                 dtype=np.uint8))
 65 | 
 66 |         self.frames = frames
 67 |         self.pattern_offset = pattern_offset
 68 |         self.cmap = cmap
 69 | 
 70 |         self.fig, self.ax = plt.subplots(subplot_kw=dict(xticks=[], yticks=[]))
 71 | 
 72 |         xlim = (-32.5, 47.5)
 73 |         ylim = (-16.5, 31.5)
 74 | 
 75 |         self.ax.set_xlim(xlim)
 76 |         self.ax.set_ylim(ylim[::-1])
 77 | 
 78 |         self.bg = plt.imshow(np.ones((2, 2)),
 79 |                              extent=xlim + ylim,
 80 |                              cmap=background)
 81 | 
 82 |         self.fig.canvas.mpl_connect('key_press_event', self.key_press)
 83 |         self.show_frame(0)
 84 |         self.im.set_cmap(cmap)
 85 |         self.im.set_interpolation('nearest')
 86 |         self.im.set_clim(0, 3)
 87 | 
 88 |     def get_tile(self, offset, flatten=True):
 89 |         bit_index = (offset + self.pattern_offset) * 128
 90 |         thumb = self.data[bit_index:bit_index + 128].reshape((2, 8, 8))
 91 | 
 92 |         if flatten:
 93 |             return thumb[0] + 2 * thumb[1]
 94 |         else:
 95 |             return thumb
 96 | 
 97 |     def _get_tiles(self, arr):
 98 |         arr = np.atleast_2d(arr)
 99 |         assert arr.ndim == 2
100 |         imarr = np.zeros((8 * arr.shape[0], 8 * arr.shape[1]),
101 |                          dtype=int)
102 | 
103 |         for i in range(arr.shape[0]):
104 |             for j in range(arr.shape[1]):
105 |                 tile = self.get_tile(abs(arr[i, j]))
106 |                 if arr[i, j] < 0:
107 |                     tile = tile[:, ::-1]
108 |                 imarr[8 * i:8 * (i + 1), 8 * j:8 * (j + 1)] = tile
109 |         return np.ma.masked_where(np.equal(imarr, 0), imarr)
110 | 
111 |     def show_tiles(self, arr):
112 |         imarr = self._get_tiles(arr)
113 |         if hasattr(self, 'im'):
114 |             self.im.set_data(imarr)
115 |         else:
116 |             self.im = self.ax.imshow(imarr)
117 | 
118 |     def show_frame(self, i):
119 |         self.curr_frame = i
120 |         self.show_tiles(self.frames[i])
121 | 
122 |     def key_press(self, event):
123 |         if not hasattr(self, 'curr_frame'):
124 |             self.curr_frame = -1
125 |         if event.key == 'right':
126 |             self.show_frame((self.curr_frame + 1) % len(self.frames))
127 |             self.fig.canvas.draw()
128 |         elif event.key == 'left':
129 |             self.show_frame((self.curr_frame - 1 + len(self.frames))
130 |                             % len(self.frames))
131 |             self.fig.canvas.draw()
132 | 
133 | #m = MarioDisplay.show_mario()
134 | m = MarioDisplay.show_random()
135 |         
136 | plt.show()
137 | 


--------------------------------------------------------------------------------
/content/downloads/code/mario/view_pattern_table.py:
--------------------------------------------------------------------------------
 1 | """Script to view the pattern table of an NES ROM
 2 | 
 3 |    usage: view_pattern_table.py 
 4 | 
 5 | By Jake Vanderplas, 2013 
 6 | License: GPL.
 7 | Feel free to use and distribute, but keep this attribution intact.
 8 | """
 9 | import zipfile
10 | import numpy as np
11 | from matplotlib import pyplot as plt
12 | 
13 | 
14 | class ROMViewer(object):
15 |     """Visually inspect an NES ROM"""
16 |     def __init__(self, filename, N1=16, N2=16, sep=1):
17 |         if zipfile.is_zipfile(filename):
18 |             zp = zipfile.ZipFile(filename)
19 |             data = np.unpackbits(np.frombuffer(zp.read(zp.filelist[0]),
20 |                                                dtype=np.uint8))
21 |         else:
22 |             data = np.unpackbits(np.fromfile(filename, dtype=np.uint8))
23 | 
24 |         self.data = data.reshape((-1, 8, 8))
25 |         self.N1 = N1
26 |         self.N2 = N2
27 |         self.sep = sep
28 | 
29 |         self.fig, self.ax = plt.subplots(figsize=(6, 6),
30 |                                          subplot_kw=dict(xticks=[], yticks=[]))
31 |         self.fig.subplots_adjust(bottom=0.04, top=0.94, left=0.05, right=0.95)
32 |         self.fig.rom_viewer = self  # needed for object persistence
33 |         self.update_offset(0)
34 |         self.fig.canvas.mpl_connect('key_press_event', self.key_press)
35 | 
36 |     def update_offset(self, offset):
37 |         """update offset and re-draw figure"""
38 |         offset = max(offset, 0)
39 |         offset = min(offset, self.data.shape[0] / 2 - self.N1 * self.N2)
40 |         self.current_offset = offset
41 |         self.ax.set_title('offset = %i' % offset)
42 | 
43 |         # starting at offset, take 128-bit chunks and view as
44 |         # 2-bit 8x8 thumbnails in an 8x8 grid
45 |         im_array = np.zeros((self.sep + self.N1 * (8 + self.sep),
46 |                              self.sep + self.N2 * (8 + self.sep)))
47 | 
48 |         for i in range(self.N1):
49 |             for j in range(self.N2):
50 |                 thumb = self.data[2 * offset] + 2 * self.data[2 * offset + 1]
51 |                 ind_i = self.sep + (8 + self.sep) * i
52 |                 ind_j = self.sep + (8 + self.sep) * j
53 |                 im_array[ind_i:ind_i + 8, ind_j:ind_j + 8] = thumb
54 |                 offset += 1
55 | 
56 |         if not hasattr(self, 'im'):
57 |             self.im = self.ax.imshow(im_array, cmap=plt.cm.binary,
58 |                                      interpolation='nearest')
59 |         else:
60 |             self.im.set_data(im_array)
61 |             plt.draw()
62 | 
63 |     def key_press(self, event):
64 |         """Use arrow keys to navigate"""
65 |         offset_dict = dict(right=1, left=-1,
66 |                            up=-self.N1 * self.N2,
67 |                            down=self.N1 * self.N2,
68 |                            home=-self.data.shape[0],
69 |                            end=self.data.shape[0])
70 | 
71 |         if event.key in offset_dict:
72 |             self.update_offset(self.current_offset + offset_dict[event.key])
73 | 
74 | 
75 | if __name__ == '__main__':
76 |     import sys
77 |     try:
78 |         filename = sys.argv[1]
79 |     except:
80 |         print __doc__
81 |         sys.exit(0)
82 | 
83 |     ROMViewer(filename)
84 |     plt.show()
85 | 


--------------------------------------------------------------------------------
/content/downloads/code/minesweeper.py:
--------------------------------------------------------------------------------
  1 | """
  2 | Matplotlib Minesweeper
  3 | ----------------------
  4 | A simple Minesweeper implementation in matplotlib.
  5 | 
  6 | Author: Jake Vanderplas , Dec. 2012
  7 | License: BSD
  8 | """
  9 | import numpy as np
 10 | from itertools import product
 11 | from scipy.signal import convolve2d
 12 | import matplotlib.pyplot as plt
 13 | from matplotlib.patches import RegularPolygon
 14 | 
 15 | 
 16 | class MineSweeper(object):
 17 |     covered_color = '#DDDDDD'
 18 |     uncovered_color = '#AAAAAA'
 19 |     edge_color = '#888888'
 20 |     count_colors = ['none', 'blue', 'green', 'red', 'darkblue',
 21 |                     'darkred', 'darkgreen', 'black', 'black']
 22 |     flag_vertices = np.array([[0.25, 0.2], [0.25, 0.8],
 23 |                               [0.75, 0.65], [0.25, 0.5]])
 24 | 
 25 |     @classmethod
 26 |     def beginner(cls):
 27 |         return cls(8, 8, 10)
 28 | 
 29 |     @classmethod
 30 |     def intermediate(cls):
 31 |         return cls(16, 16, 40)
 32 | 
 33 |     @classmethod
 34 |     def expert(cls):
 35 |         return cls(30, 16, 99)
 36 | 
 37 |     def __init__(self, width, height, nmines):
 38 |         self.width, self.height, self.nmines = width, height, nmines
 39 | 
 40 |         # Create the figure and axes
 41 |         self.fig = plt.figure(figsize=((width + 2) / 3., (height + 2) / 3.))
 42 |         self.ax = self.fig.add_axes((0.05, 0.05, 0.9, 0.9),
 43 |                                     aspect='equal', frameon=False,
 44 |                                     xlim=(-0.05, width + 0.05),
 45 |                                     ylim=(-0.05, height + 0.05))
 46 |         for axis in (self.ax.xaxis, self.ax.yaxis):
 47 |             axis.set_major_formatter(plt.NullFormatter())
 48 |             axis.set_major_locator(plt.NullLocator())
 49 | 
 50 |         # Create the grid of squares
 51 |         self.squares = np.array([[RegularPolygon((i + 0.5, j + 0.5),
 52 |                                                  numVertices=4,
 53 |                                                  radius=0.5 * np.sqrt(2),
 54 |                                                  orientation=np.pi / 4,
 55 |                                                  ec=self.edge_color,
 56 |                                                  fc=self.covered_color)
 57 |                                   for j in range(height)]
 58 |                                  for i in range(width)])
 59 |         [self.ax.add_patch(sq) for sq in self.squares.flat]
 60 | 
 61 |         # define internal state variables
 62 |         self.mines = None
 63 |         self.counts = None
 64 |         self.clicked = np.zeros((self.width, self.height), dtype=bool)
 65 |         self.flags = np.zeros((self.width, self.height), dtype=object)
 66 |         self.game_over = False
 67 | 
 68 |         # Create event hook for mouse clicks
 69 |         self.fig.canvas.mpl_connect('button_press_event', self._button_press)
 70 | 
 71 |     def _draw_mine(self, i, j):
 72 |         self.ax.add_patch(plt.Circle((i + 0.5, j + 0.5), radius=0.25,
 73 |                                      ec='black', fc='black'))
 74 | 
 75 |     def _draw_red_X(self, i, j):
 76 |         self.ax.text(i + 0.5, j + 0.5, 'X', color='r', fontsize=20,
 77 |                      ha='center', va='center')
 78 | 
 79 |     def _toggle_mine_flag(self, i, j):
 80 |         if self.clicked[i, j]:
 81 |             pass
 82 |         elif self.flags[i, j]:
 83 |             self.ax.patches.remove(self.flags[i, j])
 84 |             self.flags[i, j] = None
 85 |         else:
 86 |             self.flags[i, j] = plt.Polygon(self.flag_vertices + [i, j],
 87 |                                             fc='red', ec='black', lw=2)
 88 |             self.ax.add_patch(self.flags[i, j])
 89 | 
 90 |     def _reveal_unmarked_mines(self):
 91 |         for (i, j) in zip(*np.where(self.mines & ~self.flags.astype(bool))):
 92 |             self._draw_mine(i, j)
 93 | 
 94 |     def _cross_out_wrong_flags(self):
 95 |         for (i, j) in zip(*np.where(~self.mines & self.flags.astype(bool))):
 96 |             self._draw_red_X(i, j)
 97 | 
 98 |     def _mark_remaining_mines(self):
 99 |         for (i, j) in zip(*np.where(self.mines & ~self.flags.astype(bool))):
100 |             self._toggle_mine_flag(i, j)
101 | 
102 |     def _setup_mines(self, i, j):
103 |         # randomly place mines on a grid, but not on space (i, j)
104 |         idx = np.concatenate([np.arange(i * self.height + j),
105 |                               np.arange(i * self.height + j + 1,
106 |                                         self.width * self.height)])
107 |         np.random.shuffle(idx)
108 |         self.mines = np.zeros((self.width, self.height), dtype=bool)
109 |         self.mines.flat[idx[:self.nmines]] = 1
110 | 
111 |         # count the number of mines bordering each square
112 |         self.counts = convolve2d(self.mines.astype(complex), np.ones((3, 3)),
113 |                                  mode='same').real.astype(int)
114 | 
115 |     def _click_square(self, i, j):
116 |         # if this is the first click, then set up the mines
117 |         if self.mines is None:
118 |             self._setup_mines(i, j)
119 | 
120 |         # if there is a flag or square is already clicked, do nothing
121 |         if self.flags[i, j] or self.clicked[i, j]:
122 |             return
123 |         self.clicked[i, j] = True
124 | 
125 |         # hit a mine: game over
126 |         if self.mines[i, j]:
127 |             self.game_over = True
128 |             self._reveal_unmarked_mines()
129 |             self._draw_red_X(i, j)
130 |             self._cross_out_wrong_flags()
131 | 
132 |         # square with no surrounding mines: clear out all adjacent squares
133 |         elif self.counts[i, j] == 0:
134 |             self.squares[i, j].set_facecolor(self.uncovered_color)
135 |             for ii in range(max(0, i - 1), min(self.width, i + 2)):
136 |                 for jj in range(max(0, j - 1), min(self.height, j + 2)):
137 |                     self._click_square(ii, jj)
138 | 
139 |         # hit an empty square: reveal the number
140 |         else:
141 |             self.squares[i, j].set_facecolor(self.uncovered_color)
142 |             self.ax.text(i + 0.5, j + 0.5, str(self.counts[i, j]),
143 |                          color=self.count_colors[self.counts[i, j]],
144 |                          ha='center', va='center', fontsize=18,
145 |                          fontweight='bold')
146 | 
147 |         # if all remaining squares are mines, mark them and end game
148 |         if self.mines.sum() == (~self.clicked).sum():
149 |             self.game_over = True
150 |             self._mark_remaining_mines()
151 | 
152 |     def _button_press(self, event):
153 |         if self.game_over or (event.xdata is None) or (event.ydata is None):
154 |             return
155 |         i, j = map(int, (event.xdata, event.ydata))
156 |         if (i < 0 or j < 0 or i >= self.width or j >= self.height):
157 |             return
158 | 
159 |         # left mouse button: reveal square.  If the square is already clicked
160 |         # and the correct # of mines are marked, then clear surroundig squares
161 |         if event.button == 1:
162 |             if (self.clicked[i, j]):
163 |                 flag_count = self.flags[max(0, i - 1):i + 2,
164 |                                         max(0, j - 1):j + 2].astype(bool).sum()
165 |                 if self.counts[i, j] == flag_count:
166 |                     for ii, jj in product(range(max(0, i - 1),
167 |                                                 min(self.width, i + 2)),
168 |                                           range(max(0, j - 1),
169 |                                                 min(self.height, j + 2))):
170 |                         self._click_square(ii, jj)
171 |             else:
172 |                 self._click_square(i, j)
173 | 
174 |         # right mouse button: mark/unmark flag
175 |         elif (event.button == 3) and (not self.clicked[i, j]):
176 |             self._toggle_mine_flag(i, j)
177 | 
178 |         self.fig.canvas.draw()
179 | 
180 | 
181 | if __name__ == '__main__':
182 |     ms = MineSweeper.intermediate()
183 |     plt.show()
184 | 


--------------------------------------------------------------------------------
/content/downloads/code/particle_box.py:
--------------------------------------------------------------------------------
  1 | """
  2 | Animation of Elastic collisions with Gravity
  3 | 
  4 | author: Jake Vanderplas
  5 | email: vanderplas@astro.washington.edu
  6 | website: http://jakevdp.github.com
  7 | license: BSD
  8 | Please feel free to use and modify this, but keep the above information. Thanks!
  9 | """
 10 | import numpy as np
 11 | from scipy.spatial.distance import pdist, squareform
 12 | 
 13 | import matplotlib.pyplot as plt
 14 | import scipy.integrate as integrate
 15 | import matplotlib.animation as animation
 16 | 
 17 | class ParticleBox:
 18 |     """Orbits class
 19 |     
 20 |     init_state is an [N x 4] array, where N is the number of particles:
 21 |        [[x1, y1, vx1, vy1],
 22 |         [x2, y2, vx2, vy2],
 23 |         ...               ]
 24 | 
 25 |     bounds is the size of the box: [xmin, xmax, ymin, ymax]
 26 |     """
 27 |     def __init__(self,
 28 |                  init_state = [[1, 0, 0, -1],
 29 |                                [-0.5, 0.5, 0.5, 0.5],
 30 |                                [-0.5, -0.5, -0.5, 0.5]],
 31 |                  bounds = [-2, 2, -2, 2],
 32 |                  size = 0.04,
 33 |                  M = 0.05,
 34 |                  G = 9.8):
 35 |         self.init_state = np.asarray(init_state, dtype=float)
 36 |         self.M = M * np.ones(self.init_state.shape[0])
 37 |         self.size = size
 38 |         self.state = self.init_state.copy()
 39 |         self.time_elapsed = 0
 40 |         self.bounds = bounds
 41 |         self.G = G
 42 | 
 43 |     def step(self, dt):
 44 |         """step once by dt seconds"""
 45 |         self.time_elapsed += dt
 46 |         
 47 |         # update positions
 48 |         self.state[:, :2] += dt * self.state[:, 2:]
 49 | 
 50 |         # find pairs of particles undergoing a collision
 51 |         D = squareform(pdist(self.state[:, :2]))
 52 |         ind1, ind2 = np.where(D < 2 * self.size)
 53 |         unique = (ind1 < ind2)
 54 |         ind1 = ind1[unique]
 55 |         ind2 = ind2[unique]
 56 | 
 57 |         # update velocities of colliding pairs
 58 |         for i1, i2 in zip(ind1, ind2):
 59 |             # mass
 60 |             m1 = self.M[i1]
 61 |             m2 = self.M[i2]
 62 | 
 63 |             # location vector
 64 |             r1 = self.state[i1, :2]
 65 |             r2 = self.state[i2, :2]
 66 | 
 67 |             # velocity vector
 68 |             v1 = self.state[i1, 2:]
 69 |             v2 = self.state[i2, 2:]
 70 | 
 71 |             # relative location & velocity vectors
 72 |             r_rel = r1 - r2
 73 |             v_rel = v1 - v2
 74 | 
 75 |             # momentum vector of the center of mass
 76 |             v_cm = (m1 * v1 + m2 * v2) / (m1 + m2)
 77 | 
 78 |             # collisions of spheres reflect v_rel over r_rel
 79 |             rr_rel = np.dot(r_rel, r_rel)
 80 |             vr_rel = np.dot(v_rel, r_rel)
 81 |             v_rel = 2 * r_rel * vr_rel / rr_rel - v_rel
 82 | 
 83 |             # assign new velocities
 84 |             self.state[i1, 2:] = v_cm + v_rel * m2 / (m1 + m2)
 85 |             self.state[i2, 2:] = v_cm - v_rel * m1 / (m1 + m2) 
 86 | 
 87 |         # check for crossing boundary
 88 |         crossed_x1 = (self.state[:, 0] < self.bounds[0] + self.size)
 89 |         crossed_x2 = (self.state[:, 0] > self.bounds[1] - self.size)
 90 |         crossed_y1 = (self.state[:, 1] < self.bounds[2] + self.size)
 91 |         crossed_y2 = (self.state[:, 1] > self.bounds[3] - self.size)
 92 | 
 93 |         self.state[crossed_x1, 0] = self.bounds[0] + self.size
 94 |         self.state[crossed_x2, 0] = self.bounds[1] - self.size
 95 | 
 96 |         self.state[crossed_y1, 1] = self.bounds[2] + self.size
 97 |         self.state[crossed_y2, 1] = self.bounds[3] - self.size
 98 | 
 99 |         self.state[crossed_x1 | crossed_x2, 2] *= -1
100 |         self.state[crossed_y1 | crossed_y2, 3] *= -1
101 | 
102 |         # add gravity
103 |         self.state[:, 3] -= self.M * self.G * dt
104 | 
105 | 
106 | #------------------------------------------------------------
107 | # set up initial state
108 | np.random.seed(0)
109 | init_state = -0.5 + np.random.random((50, 4))
110 | init_state[:, :2] *= 3.9
111 | 
112 | box = ParticleBox(init_state, size=0.04)
113 | dt = 1. / 30 # 30fps
114 | 
115 | 
116 | #------------------------------------------------------------
117 | # set up figure and animation
118 | fig = plt.figure()
119 | fig.subplots_adjust(left=0, right=1, bottom=0, top=1)
120 | ax = fig.add_subplot(111, aspect='equal', autoscale_on=False,
121 |                      xlim=(-3.2, 3.2), ylim=(-2.4, 2.4))
122 | 
123 | # particles holds the locations of the particles
124 | particles, = ax.plot([], [], 'bo', ms=6)
125 | 
126 | # rect is the box edge
127 | rect = plt.Rectangle(box.bounds[::2],
128 |                      box.bounds[1] - box.bounds[0],
129 |                      box.bounds[3] - box.bounds[2],
130 |                      ec='none', lw=2, fc='none')
131 | ax.add_patch(rect)
132 | 
133 | def init():
134 |     """initialize animation"""
135 |     global box, rect
136 |     particles.set_data([], [])
137 |     rect.set_edgecolor('none')
138 |     return particles, rect
139 | 
140 | def animate(i):
141 |     """perform animation step"""
142 |     global box, rect, dt, ax, fig
143 |     box.step(dt)
144 | 
145 |     ms = int(fig.dpi * 2 * box.size * fig.get_figwidth()
146 |              / np.diff(ax.get_xbound())[0])
147 |     
148 |     # update pieces of the animation
149 |     rect.set_edgecolor('k')
150 |     particles.set_data(box.state[:, 0], box.state[:, 1])
151 |     particles.set_markersize(ms)
152 |     return particles, rect
153 | 
154 | ani = animation.FuncAnimation(fig, animate, frames=600,
155 |                               interval=10, blit=True, init_func=init)
156 | 
157 | 
158 | # save the animation as an mp4.  This requires ffmpeg or mencoder to be
159 | # installed.  The extra_args ensure that the x264 codec is used, so that
160 | # the video can be embedded in html5.  You may need to adjust this for
161 | # your system: for more information, see
162 | # http://matplotlib.sourceforge.net/api/animation_api.html
163 | #ani.save('particle_box.mp4', fps=30, extra_args=['-vcodec', 'libx264'])
164 | 
165 | plt.show()
166 | 


--------------------------------------------------------------------------------
/content/downloads/code/plot_svd_benchmarks.py:
--------------------------------------------------------------------------------
  1 | import numpy as np
  2 | import matplotlib.pyplot as plt
  3 | from time import time
  4 | 
  5 | from scipy.sparse import csc_matrix
  6 | 
  7 | 
  8 | def sparse_matrix(N1, N2, f, conversion=np.asarray, rseed=0):
  9 |     """create NxN matrix with an approximate fraction f of nonzero entries"""
 10 |     rng = np.random.RandomState(rseed)
 11 |     M = rng.rand(N1, N2)
 12 |     M[M > f] = 0
 13 |     return conversion(M)
 14 | 
 15 | 
 16 | def time_svd(svdfunc, N1, N2, f, rseed=0, bestof=3, args=None, matfunc=np.asarray, **kwargs):
 17 |     if args is None:
 18 |         args = ()
 19 |     
 20 |     N1_N2_f = np.broadcast(N1, N2, f)
 21 |     times = []
 22 |     for (N1, N2, f) in N1_N2_f:
 23 |         M = sparse_matrix(N1, N2, f, matfunc, rseed)
 24 |         t_best = np.inf
 25 |         
 26 |         for i in range(bestof):
 27 |             t0 = time()
 28 |             res = svdfunc(M, *args, **kwargs)
 29 |             t1 = time()
 30 |             t_best = min(t_best, t1 - t0)
 31 |             
 32 |         times.append(t_best)
 33 |     
 34 |     return np.array(times).reshape(N1_N2_f.shape)
 35 | 
 36 | 
 37 | def plot_propack(ax, N1, N2, f, k):
 38 |     from pypropack import svdp
 39 |     print "computing execution times for propack..."
 40 |     t = time_svd(svdp, N1, N2, f, k=k, kmax=100,
 41 |                  matfunc=csc_matrix)
 42 |     ax.plot(N1, t, label='propack (k=%i)' % k)
 43 | 
 44 | 
 45 | def plot_arpack(ax, N1, N2, f, k):
 46 |     from scipy.sparse.linalg import svds
 47 |     print "computing execution times for arpack..."
 48 |     t = time_svd(svds, N1, N2, f, k=k, matfunc=csc_matrix)
 49 |     ax.plot(N1, t, label='arpack (k=%i)' % k)
 50 | 
 51 | 
 52 | def plot_svdlibc(ax, N1, N2, f, k):
 53 |     from sparsesvd import sparsesvd
 54 |     print "computing execution times for svdlibc..."
 55 |     t = time_svd(sparsesvd, N1, N2, f, args=(5,), matfunc=csc_matrix)
 56 |     ax.plot(N1, t, label='svdlibc (k=%i)' % k)
 57 | 
 58 | 
 59 | def plot_lapack(ax, N1, N2, f, k):
 60 |     from scipy.linalg import svd
 61 |     print "computing execution times for lapack..."
 62 |     t = time_svd(svd, N1, N2, f, full_matrices=False)
 63 |     ax.plot(N1, t, label='lapack (full)')
 64 | 
 65 | 
 66 | if __name__ == '__main__':
 67 |     N = 2 ** np.arange(3, 12)
 68 |     f = 0.6
 69 |     k = 5
 70 | 
 71 |     fig, ax = plt.subplots(subplot_kw=dict(xscale='log', yscale='log'))
 72 | 
 73 |     try:
 74 |         plot_propack(ax, N, N, f, k)
 75 |     except ImportError:
 76 |         print "propack cannot be loaded"
 77 | 
 78 |     try:
 79 |         plot_arpack(ax, N, N, f, k)
 80 |     except ImportError:
 81 |         print "scipy arpack wrapper cannot be loaded"
 82 | 
 83 |     try:
 84 |         plot_svdlibc(ax, N, N, f, k)
 85 |     except ImportError:
 86 |         print "svdlibc cannot be loaded"
 87 | 
 88 |     try:
 89 |         plot_lapack(ax, N, N, f, k)
 90 |     except ImportError:
 91 |         print "scipy lapack wrapper cannot be loaded"
 92 | 
 93 |     ax.legend(loc=2)
 94 |     ax.set_xlabel('N')
 95 |     ax.set_ylabel('t (s)')
 96 |     ax.set_title('Execution Times for k=5')
 97 |     ax.grid(color='gray')
 98 | 
 99 |     plt.show()
100 | 


--------------------------------------------------------------------------------
/content/downloads/code/schrodinger.py:
--------------------------------------------------------------------------------
  1 | """
  2 | General Numerical Solver for the 1D Time-Dependent Schrodinger's equation.
  3 | 
  4 | author: Jake Vanderplas
  5 | email: vanderplas@astro.washington.edu
  6 | website: http://jakevdp.github.com
  7 | license: BSD
  8 | Please feel free to use and modify this, but keep the above information. Thanks!
  9 | """
 10 | 
 11 | import numpy as np
 12 | from matplotlib import pyplot as pl
 13 | from matplotlib import animation
 14 | from scipy.fftpack import fft,ifft
 15 | 
 16 | 
 17 | class Schrodinger(object):
 18 |     """
 19 |     Class which implements a numerical solution of the time-dependent
 20 |     Schrodinger equation for an arbitrary potential
 21 |     """
 22 |     def __init__(self, x, psi_x0, V_x,
 23 |                  k0 = None, hbar=1, m=1, t0=0.0):
 24 |         """
 25 |         Parameters
 26 |         ----------
 27 |         x : array_like, float
 28 |             length-N array of evenly spaced spatial coordinates
 29 |         psi_x0 : array_like, complex
 30 |             length-N array of the initial wave function at time t0
 31 |         V_x : array_like, float
 32 |              length-N array giving the potential at each x
 33 |         k0 : float
 34 |             the minimum value of k.  Note that, because of the workings of the
 35 |             fast fourier transform, the momentum wave-number will be defined
 36 |             in the range
 37 |               k0 < k < 2*pi / dx
 38 |             where dx = x[1]-x[0].  If you expect nonzero momentum outside this
 39 |             range, you must modify the inputs accordingly.  If not specified,
 40 |             k0 will be calculated such that the range is [-k0,k0]
 41 |         hbar : float
 42 |             value of planck's constant (default = 1)
 43 |         m : float
 44 |             particle mass (default = 1)
 45 |         t0 : float
 46 |             initial tile (default = 0)
 47 |         """
 48 |         # Validation of array inputs
 49 |         self.x, psi_x0, self.V_x = map(np.asarray, (x, psi_x0, V_x))
 50 |         N = self.x.size
 51 |         assert self.x.shape == (N,)
 52 |         assert psi_x0.shape == (N,)
 53 |         assert self.V_x.shape == (N,)
 54 | 
 55 |         # Set internal parameters
 56 |         self.hbar = hbar
 57 |         self.m = m
 58 |         self.t = t0
 59 |         self.dt_ = None
 60 |         self.N = len(x)
 61 |         self.dx = self.x[1] - self.x[0]
 62 |         self.dk = 2 * np.pi / (self.N * self.dx)
 63 | 
 64 |         # set momentum scale
 65 |         if k0 == None:
 66 |             self.k0 = -0.5 * self.N * self.dk
 67 |         else:
 68 |             self.k0 = k0
 69 |         self.k = self.k0 + self.dk * np.arange(self.N)
 70 | 
 71 |         self.psi_x = psi_x0
 72 |         self.compute_k_from_x()
 73 | 
 74 |         # variables which hold steps in evolution of the
 75 |         self.x_evolve_half = None
 76 |         self.x_evolve = None
 77 |         self.k_evolve = None
 78 | 
 79 |         # attributes used for dynamic plotting
 80 |         self.psi_x_line = None
 81 |         self.psi_k_line = None
 82 |         self.V_x_line = None
 83 | 
 84 |     def _set_psi_x(self, psi_x):
 85 |         self.psi_mod_x = (psi_x * np.exp(-1j * self.k[0] * self.x)
 86 |                           * self.dx / np.sqrt(2 * np.pi))
 87 | 
 88 |     def _get_psi_x(self):
 89 |         return (self.psi_mod_x * np.exp(1j * self.k[0] * self.x)
 90 |                 * np.sqrt(2 * np.pi) / self.dx)
 91 | 
 92 |     def _set_psi_k(self, psi_k):
 93 |         self.psi_mod_k = psi_k * np.exp(1j * self.x[0]
 94 |                                         * self.dk * np.arange(self.N))
 95 | 
 96 |     def _get_psi_k(self):
 97 |         return self.psi_mod_k * np.exp(-1j * self.x[0] * 
 98 |                                         self.dk * np.arange(self.N))
 99 |     
100 |     def _get_dt(self):
101 |         return self.dt_
102 | 
103 |     def _set_dt(self, dt):
104 |         if dt != self.dt_:
105 |             self.dt_ = dt
106 |             self.x_evolve_half = np.exp(-0.5 * 1j * self.V_x
107 |                                          / self.hbar * dt )
108 |             self.x_evolve = self.x_evolve_half * self.x_evolve_half
109 |             self.k_evolve = np.exp(-0.5 * 1j * self.hbar /
110 |                                     self.m * (self.k * self.k) * dt)
111 |     
112 |     psi_x = property(_get_psi_x, _set_psi_x)
113 |     psi_k = property(_get_psi_k, _set_psi_k)
114 |     dt = property(_get_dt, _set_dt)
115 | 
116 |     def compute_k_from_x(self):
117 |         self.psi_mod_k = fft(self.psi_mod_x)
118 | 
119 |     def compute_x_from_k(self):
120 |         self.psi_mod_x = ifft(self.psi_mod_k)
121 | 
122 |     def time_step(self, dt, Nsteps = 1):
123 |         """
124 |         Perform a series of time-steps via the time-dependent
125 |         Schrodinger Equation.
126 | 
127 |         Parameters
128 |         ----------
129 |         dt : float
130 |             the small time interval over which to integrate
131 |         Nsteps : float, optional
132 |             the number of intervals to compute.  The total change
133 |             in time at the end of this method will be dt * Nsteps.
134 |             default is N = 1
135 |         """
136 |         self.dt = dt
137 | 
138 |         if Nsteps > 0:
139 |             self.psi_mod_x *= self.x_evolve_half
140 | 
141 |         for i in xrange(Nsteps - 1):
142 |             self.compute_k_from_x()
143 |             self.psi_mod_k *= self.k_evolve
144 |             self.compute_x_from_k()
145 |             self.psi_mod_x *= self.x_evolve
146 | 
147 |         self.compute_k_from_x()
148 |         self.psi_mod_k *= self.k_evolve
149 | 
150 |         self.compute_x_from_k()
151 |         self.psi_mod_x *= self.x_evolve_half
152 | 
153 |         self.compute_k_from_x()
154 | 
155 |         self.t += dt * Nsteps
156 | 
157 | 
158 | ######################################################################
159 | # Helper functions for gaussian wave-packets
160 | 
161 | def gauss_x(x, a, x0, k0):
162 |     """
163 |     a gaussian wave packet of width a, centered at x0, with momentum k0
164 |     """ 
165 |     return ((a * np.sqrt(np.pi)) ** (-0.5)
166 |             * np.exp(-0.5 * ((x - x0) * 1. / a) ** 2 + 1j * x * k0))
167 | 
168 | def gauss_k(k,a,x0,k0):
169 |     """
170 |     analytical fourier transform of gauss_x(x), above
171 |     """
172 |     return ((a / np.sqrt(np.pi))**0.5
173 |             * np.exp(-0.5 * (a * (k - k0)) ** 2 - 1j * (k - k0) * x0))
174 | 
175 | 
176 | ######################################################################
177 | # Utility functions for running the animation
178 | 
179 | def theta(x):
180 |     """
181 |     theta function :
182 |       returns 0 if x<=0, and 1 if x>0
183 |     """
184 |     x = np.asarray(x)
185 |     y = np.zeros(x.shape)
186 |     y[x > 0] = 1.0
187 |     return y
188 | 
189 | def square_barrier(x, width, height):
190 |     return height * (theta(x) - theta(x - width))
191 | 
192 | ######################################################################
193 | # Create the animation
194 | 
195 | # specify time steps and duration
196 | dt = 0.01
197 | N_steps = 50
198 | t_max = 120
199 | frames = int(t_max / float(N_steps * dt))
200 | 
201 | # specify constants
202 | hbar = 1.0   # planck's constant
203 | m = 1.9      # particle mass
204 | 
205 | # specify range in x coordinate
206 | N = 2 ** 11
207 | dx = 0.1
208 | x = dx * (np.arange(N) - 0.5 * N)
209 | 
210 | # specify potential
211 | V0 = 1.5
212 | L = hbar / np.sqrt(2 * m * V0)
213 | a = 3 * L
214 | x0 = -60 * L
215 | V_x = square_barrier(x, a, V0)
216 | V_x[x < -98] = 1E6
217 | V_x[x > 98] = 1E6
218 | 
219 | # specify initial momentum and quantities derived from it
220 | p0 = np.sqrt(2 * m * 0.2 * V0)
221 | dp2 = p0 * p0 * 1./80
222 | d = hbar / np.sqrt(2 * dp2)
223 | 
224 | k0 = p0 / hbar
225 | v0 = p0 / m
226 | psi_x0 = gauss_x(x, d, x0, k0)
227 | 
228 | # define the Schrodinger object which performs the calculations
229 | S = Schrodinger(x=x,
230 |                 psi_x0=psi_x0,
231 |                 V_x=V_x,
232 |                 hbar=hbar,
233 |                 m=m,
234 |                 k0=-28)
235 | 
236 | ######################################################################
237 | # Set up plot
238 | fig = pl.figure()
239 | 
240 | # plotting limits
241 | xlim = (-100, 100)
242 | klim = (-5, 5)
243 | 
244 | # top axes show the x-space data
245 | ymin = 0
246 | ymax = V0
247 | ax1 = fig.add_subplot(211, xlim=xlim,
248 |                       ylim=(ymin - 0.2 * (ymax - ymin),
249 |                             ymax + 0.2 * (ymax - ymin)))
250 | psi_x_line, = ax1.plot([], [], c='r', label=r'$|\psi(x)|$')
251 | V_x_line, = ax1.plot([], [], c='k', label=r'$V(x)$')
252 | center_line = ax1.axvline(0, c='k', ls=':',
253 |                           label = r"$x_0 + v_0t$")
254 | 
255 | title = ax1.set_title("")
256 | ax1.legend(prop=dict(size=12))
257 | ax1.set_xlabel('$x$')
258 | ax1.set_ylabel(r'$|\psi(x)|$')
259 | 
260 | # bottom axes show the k-space data
261 | ymin = abs(S.psi_k).min()
262 | ymax = abs(S.psi_k).max()
263 | ax2 = fig.add_subplot(212, xlim=klim,
264 |                       ylim=(ymin - 0.2 * (ymax - ymin),
265 |                             ymax + 0.2 * (ymax - ymin)))
266 | psi_k_line, = ax2.plot([], [], c='r', label=r'$|\psi(k)|$')
267 | 
268 | p0_line1 = ax2.axvline(-p0 / hbar, c='k', ls=':', label=r'$\pm p_0$')
269 | p0_line2 = ax2.axvline(p0 / hbar, c='k', ls=':')
270 | mV_line = ax2.axvline(np.sqrt(2 * V0) / hbar, c='k', ls='--',
271 |                       label=r'$\sqrt{2mV_0}$')
272 | ax2.legend(prop=dict(size=12))
273 | ax2.set_xlabel('$k$')
274 | ax2.set_ylabel(r'$|\psi(k)|$')
275 | 
276 | V_x_line.set_data(S.x, S.V_x)
277 | 
278 | ######################################################################
279 | # Animate plot
280 | def init():
281 |     psi_x_line.set_data([], [])
282 |     V_x_line.set_data([], [])
283 |     center_line.set_data([], [])
284 | 
285 |     psi_k_line.set_data([], [])
286 |     title.set_text("")
287 |     return (psi_x_line, V_x_line, center_line, psi_k_line, title)
288 | 
289 | def animate(i):
290 |     S.time_step(dt, N_steps)
291 |     psi_x_line.set_data(S.x, 4 * abs(S.psi_x))
292 |     V_x_line.set_data(S.x, S.V_x)
293 |     center_line.set_data(2 * [x0 + S.t * p0 / m], [0, 1])
294 | 
295 |     psi_k_line.set_data(S.k, abs(S.psi_k))
296 |     title.set_text("t = %.2f" % S.t)
297 |     return (psi_x_line, V_x_line, center_line, psi_k_line, title)
298 | 
299 | # call the animator.  blit=True means only re-draw the parts that have changed.
300 | anim = animation.FuncAnimation(fig, animate, init_func=init,
301 |                                frames=frames, interval=30, blit=True)
302 | 
303 | 
304 | # uncomment the following line to save the video in mp4 format.  This
305 | # requires either mencoder or ffmpeg to be installed on your system
306 | 
307 | #anim.save('schrodinger_barrier.mp4', fps=15, extra_args=['-vcodec', 'libx264'])
308 | 
309 | pl.show()
310 | 


--------------------------------------------------------------------------------
/content/downloads/notebooks/.gitignore:
--------------------------------------------------------------------------------
1 | .ipynb_checkpoints


--------------------------------------------------------------------------------
/content/downloads/notebooks/NUFFT_lpr.txt:
--------------------------------------------------------------------------------
 1 | Timer unit: 1e-06 s
 2 | 
 3 | Total time: 0.040097 s
 4 | File: 
 5 | Function: nufft_python at line 14
 6 | 
 7 | Line #      Hits         Time  Per Hit   % Time  Line Contents
 8 | ==============================================================
 9 |     14                                           def nufft_python(x, c, M, df=1.0, eps=1E-15, iflag=1):
10 |     15                                               """Fast Non-Uniform Fourier Transform with Python"""
11 |     16         1           41     41.0      0.1      Msp, Mr, tau = _compute_grid_params(M, eps)
12 |     17         1            3      3.0      0.0      N = len(x)
13 |     18                                           
14 |     19                                               # Construct the convolved grid
15 |     20         1           19     19.0      0.0      ftau = np.zeros(Mr, dtype=c.dtype)
16 |     21         1            2      2.0      0.0      Mr = ftau.shape[0]
17 |     22         1            3      3.0      0.0      hx = 2 * np.pi / Mr
18 |     23         1           11     11.0      0.0      mm = np.arange(-Msp, Msp)
19 |     24      1001         1493      1.5      3.7      for i in range(N):
20 |     25      1000         2801      2.8      7.0          xi = (x[i] * df) % (2 * np.pi)
21 |     26      1000         3024      3.0      7.5          m = 1 + int(xi // hx)
22 |     27      1000        21048     21.0     52.5          spread = np.exp(-0.25 * (xi - hx * (m + mm)) ** 2 / tau)
23 |     28      1000        11406     11.4     28.4          ftau[(m + mm) % Mr] += c[i] * spread
24 |     29                                           
25 |     30                                               # Compute the FFT on the convolved grid
26 |     31         1            2      2.0      0.0      if iflag < 0:
27 |     32                                                   Ftau = (1 / Mr) * np.fft.fft(ftau)
28 |     33                                               else:
29 |     34         1          183    183.0      0.5          Ftau = np.fft.ifft(ftau)
30 |     35         1           15     15.0      0.0      Ftau = np.concatenate([Ftau[-(M//2):], Ftau[:M//2 + M % 2]])
31 |     36                                           
32 |     37                                               # Deconvolve the grid using convolution theorem
33 |     38         1           14     14.0      0.0      k = nufftfreqs(M)
34 |     39         1           32     32.0      0.1      return (1 / N) * np.sqrt(np.pi / tau) * np.exp(tau * k ** 2) * Ftau


--------------------------------------------------------------------------------
/content/downloads/notebooks/TestNotebook.ipynb:
--------------------------------------------------------------------------------
 1 | {
 2 |  "metadata": {
 3 |   "name": "TestNotebook"
 4 |  },
 5 |  "nbformat": 3,
 6 |  "nbformat_minor": 0,
 7 |  "worksheets": [
 8 |   {
 9 |    "cells": [
10 |     {
11 |      "cell_type": "heading",
12 |      "level": 2,
13 |      "metadata": {},
14 |      "source": [
15 |       "This Is An IPython Notebook"
16 |      ]
17 |     },
18 |     {
19 |      "cell_type": "markdown",
20 |      "metadata": {},
21 |      "source": [
22 |       "Here is some code:"
23 |      ]
24 |     },
25 |     {
26 |      "cell_type": "code",
27 |      "collapsed": false,
28 |      "input": [
29 |       "import numpy\n",
30 |       "print numpy.random.random(10)"
31 |      ],
32 |      "language": "python",
33 |      "metadata": {},
34 |      "outputs": [
35 |       {
36 |        "output_type": "stream",
37 |        "stream": "stdout",
38 |        "text": [
39 |         "[ 0.25463203  0.55637185  0.02743774  0.57380221  0.52378531  0.95099357\n",
40 |         "  0.70975568  0.19575853  0.589227    0.06959599]\n"
41 |        ]
42 |       }
43 |      ],
44 |      "prompt_number": 1
45 |     },
46 |     {
47 |      "cell_type": "markdown",
48 |      "metadata": {},
49 |      "source": [
50 |       "Here is some math:\n",
51 |       "\n",
52 |       "$$e^{i\\pi} + 1 = 0$$"
53 |      ]
54 |     }
55 |    ],
56 |    "metadata": {}
57 |   }
58 |  ]
59 | }


--------------------------------------------------------------------------------
/content/downloads/notebooks/memview_bench_2.ipynb:
--------------------------------------------------------------------------------
  1 | {
  2 |  "metadata": {
  3 |   "name": "memview_bench_2"
  4 |  },
  5 |  "nbformat": 3,
  6 |  "nbformat_minor": 0,
  7 |  "worksheets": [
  8 |   {
  9 |    "cells": [
 10 |     {
 11 |      "cell_type": "heading",
 12 |      "level": 1,
 13 |      "metadata": {},
 14 |      "source": [
 15 |       "Memoryview Benchmarks: Round 2"
 16 |      ]
 17 |     },
 18 |     {
 19 |      "cell_type": "code",
 20 |      "collapsed": false,
 21 |      "input": [
 22 |       "%load_ext cythonmagic"
 23 |      ],
 24 |      "language": "python",
 25 |      "metadata": {},
 26 |      "outputs": [],
 27 |      "prompt_number": 1
 28 |     },
 29 |     {
 30 |      "cell_type": "heading",
 31 |      "level": 2,
 32 |      "metadata": {},
 33 |      "source": [
 34 |       "Inlined Memoryview"
 35 |      ]
 36 |     },
 37 |     {
 38 |      "cell_type": "code",
 39 |      "collapsed": false,
 40 |      "input": [
 41 |       "%%cython\n",
 42 |       "\n",
 43 |       "import numpy as np\n",
 44 |       "cimport numpy as np\n",
 45 |       "cimport cython\n",
 46 |       "\n",
 47 |       "@cython.boundscheck(False)\n",
 48 |       "@cython.wraparound(False)\n",
 49 |       "cdef inline double inner_func(double[:, ::1] X):\n",
 50 |       "    return X[0, 0]\n",
 51 |       "\n",
 52 |       "def loop_1(int N, switch=True):\n",
 53 |       "    cdef double[:, ::1] X = np.zeros((100, 100))\n",
 54 |       "    cdef int i\n",
 55 |       "    for i in range(N):\n",
 56 |       "        # this should be inlined by the compiler\n",
 57 |       "        inner_func(X)"
 58 |      ],
 59 |      "language": "python",
 60 |      "metadata": {},
 61 |      "outputs": [
 62 |       {
 63 |        "output_type": "stream",
 64 |        "stream": "stdout",
 65 |        "text": [
 66 |         "missing cimport: /home/vanderplas/.config/ipython/cython/_cython_magic_aad7a6b46ed24a8baf2373c6b8784ac3.pyx\n",
 67 |         "cython\n"
 68 |        ]
 69 |       }
 70 |      ],
 71 |      "prompt_number": 2
 72 |     },
 73 |     {
 74 |      "cell_type": "code",
 75 |      "collapsed": false,
 76 |      "input": [
 77 |       "timeit loop_1(1E6)"
 78 |      ],
 79 |      "language": "python",
 80 |      "metadata": {},
 81 |      "outputs": [
 82 |       {
 83 |        "output_type": "stream",
 84 |        "stream": "stdout",
 85 |        "text": [
 86 |         "100000 loops, best of 3: 10.1 us per loop\n"
 87 |        ]
 88 |       }
 89 |      ],
 90 |      "prompt_number": 3
 91 |     },
 92 |     {
 93 |      "cell_type": "heading",
 94 |      "level": 2,
 95 |      "metadata": {},
 96 |      "source": [
 97 |       "Inlined ndarray"
 98 |      ]
 99 |     },
100 |     {
101 |      "cell_type": "markdown",
102 |      "metadata": {},
103 |      "source": [
104 |       "Now we'll repeat, but make a dummy function such that we\n",
105 |       "can guarantee that `inner_func` will not be inlined"
106 |      ]
107 |     },
108 |     {
109 |      "cell_type": "code",
110 |      "collapsed": false,
111 |      "input": [
112 |       "%%cython\n",
113 |       "\n",
114 |       "import numpy as np\n",
115 |       "cimport numpy as np\n",
116 |       "cimport cython\n",
117 |       "\n",
118 |       "ctypedef double (*inner_func_ptr)(double[:, ::1])\n",
119 |       "\n",
120 |       "@cython.boundscheck(False)\n",
121 |       "@cython.wraparound(False)\n",
122 |       "cdef double inner_func_1(double[:, ::1] X):\n",
123 |       "    return X[0, 0]\n",
124 |       "\n",
125 |       "@cython.boundscheck(False)\n",
126 |       "@cython.wraparound(False)\n",
127 |       "cdef double inner_func_2(double[:, ::1] X):\n",
128 |       "    return X[0, 0]\n",
129 |       "\n",
130 |       "def loop_2(int N, switch=True):\n",
131 |       "    # use a switch to ensure that inlining can't happen: compilers\n",
132 |       "    # are usually smart enough to figure it out otherwise.\n",
133 |       "    cdef inner_func_ptr func\n",
134 |       "    if switch:\n",
135 |       "        func = inner_func_1\n",
136 |       "    else:\n",
137 |       "        func = inner_func_2\n",
138 |       "        \n",
139 |       "    cdef double[:, ::1] X = np.zeros((100, 100))\n",
140 |       "    cdef int i\n",
141 |       "    for i in range(N):\n",
142 |       "        func(X)"
143 |      ],
144 |      "language": "python",
145 |      "metadata": {},
146 |      "outputs": [
147 |       {
148 |        "output_type": "stream",
149 |        "stream": "stdout",
150 |        "text": [
151 |         "missing cimport: /home/vanderplas/.config/ipython/cython/_cython_magic_ac4b8923a5786ceee8b7f92131a08998.pyx\n",
152 |         "cython\n"
153 |        ]
154 |       }
155 |      ],
156 |      "prompt_number": 4
157 |     },
158 |     {
159 |      "cell_type": "code",
160 |      "collapsed": false,
161 |      "input": [
162 |       "%timeit loop_2(1E6)"
163 |      ],
164 |      "language": "python",
165 |      "metadata": {},
166 |      "outputs": [
167 |       {
168 |        "output_type": "stream",
169 |        "stream": "stdout",
170 |        "text": [
171 |         "10 loops, best of 3: 22.9 ms per loop\n"
172 |        ]
173 |       }
174 |      ],
175 |      "prompt_number": 5
176 |     },
177 |     {
178 |      "cell_type": "markdown",
179 |      "metadata": {},
180 |      "source": [
181 |       "In this case, inlining improves the computation speed by a factor of 2000!"
182 |      ]
183 |     },
184 |     {
185 |      "cell_type": "heading",
186 |      "level": 2,
187 |      "metadata": {},
188 |      "source": [
189 |       "Attempting to inline ndarrays"
190 |      ]
191 |     },
192 |     {
193 |      "cell_type": "markdown",
194 |      "metadata": {},
195 |      "source": [
196 |       "Here we'll replicate the fast method above, but with a typed numpy array\n",
197 |       "rather than a typed memoryview:"
198 |      ]
199 |     },
200 |     {
201 |      "cell_type": "code",
202 |      "collapsed": false,
203 |      "input": [
204 |       "%%cython\n",
205 |       "\n",
206 |       "import numpy as np\n",
207 |       "cimport numpy as np\n",
208 |       "cimport cython\n",
209 |       "\n",
210 |       "@cython.boundscheck(False)\n",
211 |       "@cython.wraparound(False)\n",
212 |       "cdef inline double inner_func(np.ndarray[double, ndim=2, mode='c'] X):\n",
213 |       "    return X[0, 0]\n",
214 |       "\n",
215 |       "def loop_3(int N, switch=True):\n",
216 |       "    cdef np.ndarray[double, ndim=2, mode='c'] X = np.zeros((100, 100))\n",
217 |       "    cdef int i\n",
218 |       "    for i in range(N):\n",
219 |       "        inner_func(X)\n",
220 |       "        "
221 |      ],
222 |      "language": "python",
223 |      "metadata": {},
224 |      "outputs": [
225 |       {
226 |        "output_type": "stream",
227 |        "stream": "stdout",
228 |        "text": [
229 |         "missing cimport: /home/vanderplas/.config/ipython/cython/_cython_magic_0523a248c0a6af0427879e879eda0095.pyx\n",
230 |         "cython\n"
231 |        ]
232 |       },
233 |       {
234 |        "output_type": "stream",
235 |        "stream": "stderr",
236 |        "text": [
237 |         "warning: /home/vanderplas/.config/ipython/cython/_cython_magic_0523a248c0a6af0427879e879eda0095.pyx:8:30: Buffer unpacking not optimized away.\n",
238 |         "warning: /home/vanderplas/.config/ipython/cython/_cython_magic_0523a248c0a6af0427879e879eda0095.pyx:8:30: Buffer unpacking not optimized away.\n"
239 |        ]
240 |       }
241 |      ],
242 |      "prompt_number": 6
243 |     },
244 |     {
245 |      "cell_type": "markdown",
246 |      "metadata": {},
247 |      "source": [
248 |       "These warnings indicate a problem: buffer unpacking\n",
249 |       "cannot be optimized for the numpy array dtype.\n",
250 |       "Let's see how this compares to the other implementations:"
251 |      ]
252 |     },
253 |     {
254 |      "cell_type": "code",
255 |      "collapsed": false,
256 |      "input": [
257 |       "print \"inlined memview:\"\n",
258 |       "%timeit loop_1(1E6)\n",
259 |       "print \"non-inlined memview:\"\n",
260 |       "%timeit loop_2(1E6)\n",
261 |       "print \"inlined ndarray:\"\n",
262 |       "%timeit loop_3(1E6)"
263 |      ],
264 |      "language": "python",
265 |      "metadata": {},
266 |      "outputs": [
267 |       {
268 |        "output_type": "stream",
269 |        "stream": "stdout",
270 |        "text": [
271 |         "inlined memview:\n",
272 |         "100000 loops, best of 3: 10.1 us per loop"
273 |        ]
274 |       },
275 |       {
276 |        "output_type": "stream",
277 |        "stream": "stdout",
278 |        "text": [
279 |         "\n",
280 |         "non-inlined memview:\n",
281 |         "10 loops, best of 3: 22.9 ms per loop"
282 |        ]
283 |       },
284 |       {
285 |        "output_type": "stream",
286 |        "stream": "stdout",
287 |        "text": [
288 |         "\n",
289 |         "inlined ndarray:\n",
290 |         "1 loops, best of 3: 617 ms per loop"
291 |        ]
292 |       },
293 |       {
294 |        "output_type": "stream",
295 |        "stream": "stdout",
296 |        "text": [
297 |         "\n"
298 |        ]
299 |       }
300 |      ],
301 |      "prompt_number": 7
302 |     },
303 |     {
304 |      "cell_type": "markdown",
305 |      "metadata": {},
306 |      "source": [
307 |       "The result for the ndarray is many times slower than\n",
308 |       "either the inlined or the non-inlined example above!"
309 |      ]
310 |     }
311 |    ],
312 |    "metadata": {}
313 |   }
314 |  ]
315 | }


--------------------------------------------------------------------------------
/content/downloads/videos/MagicCube.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakevdp/PythonicPerambulations/4bba4ebbaf661b0874567db63dfc140c172ecb68/content/downloads/videos/MagicCube.mp4


--------------------------------------------------------------------------------
/content/downloads/videos/MagicCube_frame.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakevdp/PythonicPerambulations/4bba4ebbaf661b0874567db63dfc140c172ecb68/content/downloads/videos/MagicCube_frame.jpg


--------------------------------------------------------------------------------
/content/downloads/videos/animate_square.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakevdp/PythonicPerambulations/4bba4ebbaf661b0874567db63dfc140c172ecb68/content/downloads/videos/animate_square.mp4


--------------------------------------------------------------------------------
/content/downloads/videos/animate_square.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakevdp/PythonicPerambulations/4bba4ebbaf661b0874567db63dfc140c172ecb68/content/downloads/videos/animate_square.png


--------------------------------------------------------------------------------
/content/downloads/videos/basic_animation.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakevdp/PythonicPerambulations/4bba4ebbaf661b0874567db63dfc140c172ecb68/content/downloads/videos/basic_animation.mp4


--------------------------------------------------------------------------------
/content/downloads/videos/basic_animation_frame.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakevdp/PythonicPerambulations/4bba4ebbaf661b0874567db63dfc140c172ecb68/content/downloads/videos/basic_animation_frame.png


--------------------------------------------------------------------------------
/content/downloads/videos/double_pendulum.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakevdp/PythonicPerambulations/4bba4ebbaf661b0874567db63dfc140c172ecb68/content/downloads/videos/double_pendulum.mp4


--------------------------------------------------------------------------------
/content/downloads/videos/double_pendulum_frame.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakevdp/PythonicPerambulations/4bba4ebbaf661b0874567db63dfc140c172ecb68/content/downloads/videos/double_pendulum_frame.png


--------------------------------------------------------------------------------
/content/downloads/videos/double_pendulum_xkcd.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakevdp/PythonicPerambulations/4bba4ebbaf661b0874567db63dfc140c172ecb68/content/downloads/videos/double_pendulum_xkcd.mp4


--------------------------------------------------------------------------------
/content/downloads/videos/lorentz_attractor.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakevdp/PythonicPerambulations/4bba4ebbaf661b0874567db63dfc140c172ecb68/content/downloads/videos/lorentz_attractor.mp4


--------------------------------------------------------------------------------
/content/downloads/videos/lorentz_attractor_frame.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakevdp/PythonicPerambulations/4bba4ebbaf661b0874567db63dfc140c172ecb68/content/downloads/videos/lorentz_attractor_frame.png


--------------------------------------------------------------------------------
/content/downloads/videos/particle_box.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakevdp/PythonicPerambulations/4bba4ebbaf661b0874567db63dfc140c172ecb68/content/downloads/videos/particle_box.mp4


--------------------------------------------------------------------------------
/content/downloads/videos/particle_box_frame.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakevdp/PythonicPerambulations/4bba4ebbaf661b0874567db63dfc140c172ecb68/content/downloads/videos/particle_box_frame.png


--------------------------------------------------------------------------------
/content/downloads/videos/schrodinger_barrier.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakevdp/PythonicPerambulations/4bba4ebbaf661b0874567db63dfc140c172ecb68/content/downloads/videos/schrodinger_barrier.mp4


--------------------------------------------------------------------------------
/content/downloads/videos/schrodinger_barrier_frame.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakevdp/PythonicPerambulations/4bba4ebbaf661b0874567db63dfc140c172ecb68/content/downloads/videos/schrodinger_barrier_frame.png


--------------------------------------------------------------------------------
/content/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakevdp/PythonicPerambulations/4bba4ebbaf661b0874567db63dfc140c172ecb68/content/favicon.png


--------------------------------------------------------------------------------
/content/figures/author_count.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakevdp/PythonicPerambulations/4bba4ebbaf661b0874567db63dfc140c172ecb68/content/figures/author_count.png


--------------------------------------------------------------------------------
/content/figures/bayesblocks1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakevdp/PythonicPerambulations/4bba4ebbaf661b0874567db63dfc140c172ecb68/content/figures/bayesblocks1.png


--------------------------------------------------------------------------------
/content/figures/bayesblocks2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakevdp/PythonicPerambulations/4bba4ebbaf661b0874567db63dfc140c172ecb68/content/figures/bayesblocks2.png


--------------------------------------------------------------------------------
/content/figures/bayesblocks3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakevdp/PythonicPerambulations/4bba4ebbaf661b0874567db63dfc140c172ecb68/content/figures/bayesblocks3.png


--------------------------------------------------------------------------------
/content/figures/jet.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakevdp/PythonicPerambulations/4bba4ebbaf661b0874567db63dfc140c172ecb68/content/figures/jet.png


--------------------------------------------------------------------------------
/content/figures/mpl_version.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakevdp/PythonicPerambulations/4bba4ebbaf661b0874567db63dfc140c172ecb68/content/figures/mpl_version.png


--------------------------------------------------------------------------------
/content/figures/svd_benchmarks.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakevdp/PythonicPerambulations/4bba4ebbaf661b0874567db63dfc140c172ecb68/content/figures/svd_benchmarks.png


--------------------------------------------------------------------------------
/content/figures/xkcd_version.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakevdp/PythonicPerambulations/4bba4ebbaf661b0874567db63dfc140c172ecb68/content/figures/xkcd_version.png


--------------------------------------------------------------------------------
/content/images/Data_Science_VD.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakevdp/PythonicPerambulations/4bba4ebbaf661b0874567db63dfc140c172ecb68/content/images/Data_Science_VD.png


--------------------------------------------------------------------------------
/content/images/MagicTriangle.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakevdp/PythonicPerambulations/4bba4ebbaf661b0874567db63dfc140c172ecb68/content/images/MagicTriangle.gif


--------------------------------------------------------------------------------
/content/images/OSX10.8.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakevdp/PythonicPerambulations/4bba4ebbaf661b0874567db63dfc140c172ecb68/content/images/OSX10.8.png


--------------------------------------------------------------------------------
/content/images/OSX_terminal.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakevdp/PythonicPerambulations/4bba4ebbaf661b0874567db63dfc140c172ecb68/content/images/OSX_terminal.png


--------------------------------------------------------------------------------
/content/images/array_vs_list.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakevdp/PythonicPerambulations/4bba4ebbaf661b0874567db63dfc140c172ecb68/content/images/array_vs_list.png


--------------------------------------------------------------------------------
/content/images/bird_32_gray.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakevdp/PythonicPerambulations/4bba4ebbaf661b0874567db63dfc140c172ecb68/content/images/bird_32_gray.png


--------------------------------------------------------------------------------
/content/images/bird_32_gray_fail.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakevdp/PythonicPerambulations/4bba4ebbaf661b0874567db63dfc140c172ecb68/content/images/bird_32_gray_fail.png


--------------------------------------------------------------------------------
/content/images/cint_vs_pyint.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakevdp/PythonicPerambulations/4bba4ebbaf661b0874567db63dfc140c172ecb68/content/images/cint_vs_pyint.png


--------------------------------------------------------------------------------
/content/images/code_bg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakevdp/PythonicPerambulations/4bba4ebbaf661b0874567db63dfc140c172ecb68/content/images/code_bg.png


--------------------------------------------------------------------------------
/content/images/dotted-border.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakevdp/PythonicPerambulations/4bba4ebbaf661b0874567db63dfc140c172ecb68/content/images/dotted-border.png


--------------------------------------------------------------------------------
/content/images/dwf_tweet.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakevdp/PythonicPerambulations/4bba4ebbaf661b0874567db63dfc140c172ecb68/content/images/dwf_tweet.png


--------------------------------------------------------------------------------
/content/images/emacs_icon.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakevdp/PythonicPerambulations/4bba4ebbaf661b0874567db63dfc140c172ecb68/content/images/emacs_icon.jpeg


--------------------------------------------------------------------------------
/content/images/email.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakevdp/PythonicPerambulations/4bba4ebbaf661b0874567db63dfc140c172ecb68/content/images/email.png


--------------------------------------------------------------------------------
/content/images/galaxy.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakevdp/PythonicPerambulations/4bba4ebbaf661b0874567db63dfc140c172ecb68/content/images/galaxy.jpg


--------------------------------------------------------------------------------
/content/images/line-tile.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakevdp/PythonicPerambulations/4bba4ebbaf661b0874567db63dfc140c172ecb68/content/images/line-tile.png


--------------------------------------------------------------------------------
/content/images/mario.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakevdp/PythonicPerambulations/4bba4ebbaf661b0874567db63dfc140c172ecb68/content/images/mario.gif


--------------------------------------------------------------------------------
/content/images/mario_graphics1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakevdp/PythonicPerambulations/4bba4ebbaf661b0874567db63dfc140c172ecb68/content/images/mario_graphics1.png


--------------------------------------------------------------------------------
/content/images/mario_graphics2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakevdp/PythonicPerambulations/4bba4ebbaf661b0874567db63dfc140c172ecb68/content/images/mario_graphics2.png


--------------------------------------------------------------------------------
/content/images/mario_graphics3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakevdp/PythonicPerambulations/4bba4ebbaf661b0874567db63dfc140c172ecb68/content/images/mario_graphics3.png


--------------------------------------------------------------------------------
/content/images/mario_pattern_background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakevdp/PythonicPerambulations/4bba4ebbaf661b0874567db63dfc140c172ecb68/content/images/mario_pattern_background.png


--------------------------------------------------------------------------------
/content/images/mario_pattern_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakevdp/PythonicPerambulations/4bba4ebbaf661b0874567db63dfc140c172ecb68/content/images/mario_pattern_foreground.png


--------------------------------------------------------------------------------
/content/images/mario_pattern_sourcecode.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakevdp/PythonicPerambulations/4bba4ebbaf661b0874567db63dfc140c172ecb68/content/images/mario_pattern_sourcecode.png


--------------------------------------------------------------------------------
/content/images/minesweeper.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakevdp/PythonicPerambulations/4bba4ebbaf661b0874567db63dfc140c172ecb68/content/images/minesweeper.gif


--------------------------------------------------------------------------------
/content/images/minesweeper_2.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakevdp/PythonicPerambulations/4bba4ebbaf661b0874567db63dfc140c172ecb68/content/images/minesweeper_2.gif


--------------------------------------------------------------------------------
/content/images/nodebox-physics-flock.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakevdp/PythonicPerambulations/4bba4ebbaf661b0874567db63dfc140c172ecb68/content/images/nodebox-physics-flock.jpg


--------------------------------------------------------------------------------
/content/images/noise.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakevdp/PythonicPerambulations/4bba4ebbaf661b0874567db63dfc140c172ecb68/content/images/noise.png


--------------------------------------------------------------------------------
/content/images/original_illusion.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakevdp/PythonicPerambulations/4bba4ebbaf661b0874567db63dfc140c172ecb68/content/images/original_illusion.gif


--------------------------------------------------------------------------------
/content/images/pi_shaped.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakevdp/PythonicPerambulations/4bba4ebbaf661b0874567db63dfc140c172ecb68/content/images/pi_shaped.png


--------------------------------------------------------------------------------
/content/images/poly_color.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakevdp/PythonicPerambulations/4bba4ebbaf661b0874567db63dfc140c172ecb68/content/images/poly_color.gif


--------------------------------------------------------------------------------
/content/images/rss.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakevdp/PythonicPerambulations/4bba4ebbaf661b0874567db63dfc140c172ecb68/content/images/rss.png


--------------------------------------------------------------------------------
/content/images/search.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakevdp/PythonicPerambulations/4bba4ebbaf661b0874567db63dfc140c172ecb68/content/images/search.png


--------------------------------------------------------------------------------
/content/images/sudoku.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakevdp/PythonicPerambulations/4bba4ebbaf661b0874567db63dfc140c172ecb68/content/images/sudoku.png


--------------------------------------------------------------------------------
/content/images/textmate_icon.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakevdp/PythonicPerambulations/4bba4ebbaf661b0874567db63dfc140c172ecb68/content/images/textmate_icon.jpg


--------------------------------------------------------------------------------
/content/images/vim_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jakevdp/PythonicPerambulations/4bba4ebbaf661b0874567db63dfc140c172ecb68/content/images/vim_icon.png


--------------------------------------------------------------------------------
/develop_server.sh:
--------------------------------------------------------------------------------
 1 | #!/usr/bin/env bash
 2 | ##
 3 | # This section should match your Makefile
 4 | ##
 5 | PELICAN=pelican
 6 | PELICANOPTS=
 7 | 
 8 | BASEDIR=$(pwd)
 9 | INPUTDIR=$BASEDIR/content
10 | OUTPUTDIR=$BASEDIR/output
11 | CONFFILE=$BASEDIR/pelicanconf.py
12 | 
13 | ###
14 | # Don't change stuff below here unless you are sure
15 | ###
16 | 
17 | SRV_PID=$BASEDIR/srv.pid
18 | PELICAN_PID=$BASEDIR/pelican.pid
19 | 
20 | function usage(){
21 |   echo "usage: $0 (stop) (start) (restart)"
22 |   echo "This starts pelican in debug and reload mode and then launches"
23 |   echo "A SimpleHTTP server to help site development. It doesn't read"
24 |   echo "your pelican options so you edit any paths in your Makefile"
25 |   echo "you will need to edit it as well"
26 |   exit 3
27 | }
28 | 
29 | function shut_down(){
30 |   if [[ -f $SRV_PID ]]; then
31 |     PID=$(cat $SRV_PID)
32 |     PROCESS=$(ps -p $PID | tail -n 1 | awk '{print $4}')
33 |     if [[ $PROCESS != "" ]]; then
34 |       echo "Killing SimpleHTTPServer"
35 |       kill $PID
36 |     else
37 |       echo "Stale PID, deleting"
38 |     fi
39 |     rm $SRV_PID
40 |   else
41 |     echo "SimpleHTTPServer PIDFile not found"
42 |   fi
43 | 
44 |   if [[ -f $PELICAN_PID ]]; then
45 |     PID=$(cat $PELICAN_PID)
46 |     PROCESS=$(ps -p $PID | tail -n 1 | awk '{print $4}')
47 |     if [[ $PROCESS != "" ]]; then
48 |       echo "Killing Pelican"
49 |       kill $PID
50 |     else
51 |       echo "Stale PID, deleting"
52 |     fi
53 |     rm $PELICAN_PID
54 |   else
55 |     echo "Pelican PIDFile not found"
56 |   fi
57 | }
58 | 
59 | function start_up(){
60 |   echo "Starting up Pelican and SimpleHTTPServer"
61 |   shift
62 |   $PELICAN --debug --autoreload -r $INPUTDIR -o $OUTPUTDIR -s $CONFFILE $PELICANOPTS &
63 |   echo $! > $PELICAN_PID
64 |   cd $OUTPUTDIR
65 |   python -m SimpleHTTPServer &
66 |   echo $! > $SRV_PID
67 |   cd $BASEDIR
68 |   sleep 1 && echo 'Pelican and SimpleHTTPServer processes now running in background.'
69 | }
70 | 
71 | ###
72 | #  MAIN
73 | ###
74 | [[ $# -ne 1 ]] && usage
75 | if [[ $1 == "stop" ]]; then
76 |   shut_down
77 | elif [[ $1 == "restart" ]]; then
78 |   shut_down
79 |   start_up
80 | elif [[ $1 == "start" ]]; then
81 |   start_up
82 | else
83 |   usage
84 | fi
85 | 


--------------------------------------------------------------------------------
/environment.yml:
--------------------------------------------------------------------------------
 1 | name: blogbuild
 2 | dependencies:
 3 | - dateutil=2.1=py27_2
 4 | - distribute=0.6.45=py27_1
 5 | - freetype=2.4.10=1
 6 | - hyde=0.8.5=py27_0
 7 | - ipython=1.2.1=py27_0
 8 | - ipython-notebook=2.2.0=py27_0
 9 | - jinja2=2.7.3=py27_1
10 | - libpng=1.5.13=1
11 | - libsodium=0.4.5=0
12 | - markdown=2.3.1=py27_0
13 | - markupsafe=0.18=py27_0
14 | - matplotlib=1.3.1=np18py27_1
15 | - numpy=1.8.1=py27_0
16 | - openssl=1.0.1h=0
17 | - pip=1.5.6=py27_0
18 | - pygments=1.6=py27_0
19 | - pyparsing=2.0.1=py27_0
20 | - python=2.7.8=1
21 | - python.app=1.2=py27_3
22 | - pytz=2014.4=py27_0
23 | - pyyaml=3.11=py27_0
24 | - pyzmq=14.3.1=py27_0
25 | - readline=6.2=2
26 | - scikit-learn=0.15.0=np18py27_0
27 | - scipy=0.14.0=np18py27_0
28 | - setuptools=5.7=py27_0
29 | - six=1.7.3=py27_0
30 | - sqlite=3.8.4.1=0
31 | - ssl_match_hostname=3.4.0.2=py27_0
32 | - tk=8.5.15=0
33 | - tornado=4.0.1=py27_0
34 | - yaml=0.1.4=1
35 | - zeromq=4.0.4=0
36 | - zlib=1.2.7=1
37 | - pip:
38 |   - astroml==0.2
39 |   - astroml-addons==0.2.1
40 |   - backports.ssl-match-hostname==3.4.0.2
41 |   - beautifulsoup4==4.3.2
42 |   - blinker==1.3
43 |   - commando==0.3.4
44 |   - docutils==0.12
45 |   - doit==0.26.0
46 |   - feedgenerator==1.7
47 |   - fswrap==0.1.2
48 |   - logbook==0.7.0
49 |   - lxml==3.4.0
50 |   - macfsevents==0.3
51 |   - mako==1.0.0
52 |   - natsort==3.5.0
53 |   - nikola==7.1.0
54 |   - pelican==3.3
55 |   - pillow==2.5.3
56 |   - pyrss2gen==1.1
57 |   - python-dateutil==2.2
58 |   - requests==2.3.0
59 |   - smartypants==1.6.0.3
60 |   - typogrify==2.0.0
61 |   - unidecode==0.04.16
62 |   - wikipedia==1.3.1
63 |   - wsgiref==0.1.2
64 |   - yapsy==1.10.423
65 | 
66 | 


--------------------------------------------------------------------------------
/pelicanconf.py:
--------------------------------------------------------------------------------
 1 | #!/usr/bin/env python
 2 | # -*- coding: utf-8 -*- #
 3 | import os
 4 | 
 5 | AUTHOR = u'Jake Vanderplas'
 6 | 
 7 | SITENAME = u'Pythonic Perambulations'
 8 | SITESUBTITLE = u'Musings and ramblings through the world of Python and beyond'
 9 | SITEURL = '' # change in publishconf.py
10 | 
11 | # Times and dates
12 | DEFAULT_DATE_FORMAT = '%b %d, %Y'
13 | TIMEZONE = 'US/Pacific'
14 | DEFAULT_LANG = u'en'
15 | 
16 | # Set the article URL
17 | ARTICLE_URL = 'blog/{date:%Y}/{date:%m}/{date:%d}/{slug}/'
18 | ARTICLE_SAVE_AS = 'blog/{date:%Y}/{date:%m}/{date:%d}/{slug}/index.html'
19 | 
20 | # Title menu options
21 | MENUITEMS = [('Archives', '/archives.html'),
22 |              ('Home Page', 'http://www.astro.washington.edu/users/vanderplas')]
23 | NEWEST_FIRST_ARCHIVES = False
24 | 
25 | #Github include settings
26 | #GITHUB_USER = 'jakevdp'
27 | #GITHUB_REPO_COUNT = 0
28 | #GITHUB_SKIP_FORK = True
29 | #GITHUB_SHOW_USER_LINK = True
30 | 
31 | # Blogroll
32 | #LINKS =  (('Pelican', 'http://docs.notmyidea.org/alexis/pelican/'),
33 | #          ('Python.org', 'http://python.org'),
34 | #          ('Jinja2', 'http://jinja.pocoo.org'),
35 | #          ('You can modify those links in your config file', '#'),)
36 | 
37 | # Social widget
38 | #SOCIAL = (('You can add links in your config file', '#'),
39 | #          ('Another social link', '#'),)
40 | 
41 | DEFAULT_PAGINATION = 10
42 | 
43 | # STATIC_OUT_DIR requires https://github.com/jakevdp/pelican/tree/specify-static
44 | #STATIC_OUT_DIR = ''
45 | #STATIC_PATHS = ['images', 'figures', 'downloads']
46 | #FILES_TO_COPY = [('favicon.png', 'favicon.png')]
47 | 
48 | # This requires Pelican 3.3+
49 | STATIC_PATHS = ['images', 'figures', 'downloads', 'favicon.png']
50 | 
51 | CODE_DIR = 'downloads/code'
52 | NOTEBOOK_DIR = 'downloads/notebooks'
53 | 
54 | # Theme and plugins
55 | THEME = 'pelican-octopress-theme/'
56 | PLUGIN_PATH = 'pelican-plugins'
57 | PLUGINS = ['summary', 'liquid_tags.img', 'liquid_tags.video',
58 |            'liquid_tags.include_code', 'liquid_tags.notebook',
59 |            'liquid_tags.literal']
60 | 
61 | DISPLAY_PAGES_ON_MENU = False
62 | 
63 | 
64 | # The theme file should be updated so that the base header contains the line:
65 | #
66 | #  {% if EXTRA_HEADER %}
67 | #    {{ EXTRA_HEADER }}
68 | #  {% endif %}
69 | # 
70 | # This header file is automatically generated by the notebook plugin
71 | if not os.path.exists('_nb_header.html'):
72 |     import warnings
73 |     warnings.warn("_nb_header.html not found.  "
74 |                   "Rerun make html to finalize build.")
75 | else:
76 |     EXTRA_HEADER = open('_nb_header.html').read().decode('utf-8')
77 | 
78 | # Sharing
79 | TWITTER_USER = 'jakevdp'
80 | GOOGLE_PLUS_USER = 'jakevdp'
81 | GOOGLE_PLUS_ONE = True
82 | GOOGLE_PLUS_HIDDEN = False
83 | FACEBOOK_LIKE = False
84 | TWITTER_TWEET_BUTTON = True
85 | TWITTER_LATEST_TWEETS = True
86 | TWITTER_FOLLOW_BUTTON = True
87 | TWITTER_TWEET_COUNT = 3
88 | TWITTER_SHOW_REPLIES = 'false'
89 | TWITTER_SHOW_FOLLOWER_COUNT = 'true'
90 | 
91 | 
92 | # RSS/Atom feeds
93 | FEED_DOMAIN = SITEURL
94 | FEED_ATOM = 'atom.xml'
95 | 
96 | 
97 | # Search
98 | SEARCH_BOX = True
99 | 


--------------------------------------------------------------------------------
/publishconf.py:
--------------------------------------------------------------------------------
 1 | #!/usr/bin/env python
 2 | # -*- coding: utf-8 -*- #
 3 | 
 4 | import sys
 5 | sys.path.append('.')
 6 | from pelicanconf import *
 7 | 
 8 | SITEURL = 'https://jakevdp.github.io'
 9 | 
10 | DELETE_OUTPUT_DIRECTORY = True
11 | 
12 | # Following items are often useful when publishing
13 | 
14 | # Uncomment following line for absolute URLs in production:
15 | #RELATIVE_URLS = False
16 | 
17 | GOOGLE_ANALYTICS = 'UA-34061646-1'
18 | DISQUS_SITENAME = 'pythonicperambulations'
19 | 


--------------------------------------------------------------------------------