Zombie outbreak in Chicago

The Walking Hipsters

Posted by Matt on December 14, 2014

Zombies

Inspired by this awesome blog post: Model of a zombie outbreak in Sweden, Norway and Finland (Denmark is fine). We decided it would be cool to use the same methodology and apply it to Chicago.

Let's be more realistic. It will probably start in Naperville.

Either that or a rabid tourist.

Max Berggren's Python Code

Try this badboy yourself

import numpy as np
import math
import matplotlib.pyplot as plt
from matplotlib import rcParams
import matplotlib.image as mpimg
rcParams['font.family'] = 'serif'
rcParams['font.size'] = 16
rcParams['figure.figsize'] = 12, 8
from PIL import Image
import os

beta = 0.010
gamma = 1

def euler_step(u, f, dt):
    return u + dt * f(u)

def original_f(u):
    S = u[0]
    I = u[1]
    R = u[2]
    
    new = np.array([-beta*(S[1:-1, 1:-1]*I[1:-1, 1:-1] + \
                            S[0:-2, 1:-1]*I[0:-2, 1:-1] + \
                            S[2:, 1:-1]*I[2:, 1:-1] + \
                            S[1:-1, 0:-2]*I[1:-1, 0:-2] + \
                            S[1:-1, 2:]*I[1:-1, 2:]),
                     beta*(S[1:-1, 1:-1]*I[1:-1, 1:-1] + \
                            S[0:-2, 1:-1]*I[0:-2, 1:-1] + \
                            S[2:, 1:-1]*I[2:, 1:-1] + \
                            S[1:-1, 0:-2]*I[1:-1, 0:-2] + \
                            S[1:-1, 2:]*I[1:-1, 2:]) - gamma*I[1:-1, 1:-1],
                     gamma*I[1:-1, 1:-1]
                    ])
    
    padding = np.zeros_like(u)
    padding[:,1:-1,1:-1] = new
    padding[0][padding[0] < 0] = 0
    padding[0][padding[0] > 255] = 255
    padding[1][padding[1] < 0] = 0
    padding[1][padding[1] > 255] = 255
    padding[2][padding[2] < 0] = 0
    padding[2][padding[2] > 255] = 255
    
    return padding


def new_f(u):
    S = u[0]
    I = u[1]
    R = u[2]
    
    new = np.array([-beta*(S[1:-1, 1:-1]*I[1:-1, 1:-1] + I[0:-2, 1:-1] + I[2:, 1:-1] + I[1:-1, 0:-2] + I[1:-1, 2:]),
                     beta*(S[1:-1, 1:-1]*I[1:-1, 1:-1] + I[0:-2, 1:-1] + I[2:, 1:-1] + I[1:-1, 0:-2] + I[1:-1, 2:]) - gamma*I[1:-1, 1:-1],
                     gamma*I[1:-1, 1:-1]
                    ])
    
    padding = np.zeros_like(u)
    padding[:,1:-1,1:-1] = new
    padding[0][padding[0] < 0] = 0
    padding[0][padding[0] > 255] = 255
    padding[1][padding[1] < 0] = 0
    padding[1][padding[1] > 255] = 255
    padding[2][padding[2] < 0] = 0
    padding[2][padding[2] > 255] = 255
    
    return padding

from PIL import Image,ImageOps
import urllib
link="https://people.hofstra.edu/geotrans/eng/ch6en/conc6en/img/popchange0010_chicago.jpg"
urllib.urlretrieve(link, "Chicago.png")
img = Image.open('Chicago.png')
img = ImageOps.invert(img)
img = img.resize((img.size[0]/2,img.size[1]/2)) 
img = 255 - np.asarray(img)
imgplot = plt.imshow(img)
imgplot.set_interpolation('nearest')


S_0 = img[:,:,1]
I_0 = np.zeros_like(S_0)
I_0[310,175] = 1 # patient zero

R_0 = np.zeros_like(S_0)

T = 900                        # final time
dt = 1                          # time increment
N = int(T/dt) + 1               # number of time-steps
t = np.linspace(0.0, T, N)      # time discretization

# initialize the array containing the solution for each time-step
u = np.empty((N, 3, S_0.shape[0], S_0.shape[1]))

u[0][0] = S_0
u[0][1] = I_0
u[0][2] = R_0

import matplotlib.cm as cm
theCM = cm.get_cmap("Reds")
theCM._init()
alphas = np.abs(np.linspace(0, 1, theCM.N))
theCM._lut[:-3,-1] = alphas

for n in range(N-1):
    u[n+1] = euler_step(u[n], original_f, dt)

from images2gif import writeGif

keyFrames = []
frames = 60.0

for i in range(0, N-1, int(N/frames)):
    imgplot = plt.imshow(img)#, vmin=0, vmax=255)
    imgplot.set_interpolation("nearest")
    imgplot = plt.imshow(u[i][1], vmin=0, cmap=theCM)
    imgplot.set_interpolation("nearest")
    filename = "outbreak" + str(i) + ".png"
    plt.savefig(filename)
    keyFrames.append(filename)

images = [Image.open(fn) for fn in keyFrames]
gifFilename = "outbreak11.gif"
writeGif(gifFilename, images, duration=0.3)
plt.clf()

I included the function 'new_f' because while 'original_f' makes for a really cool gif, it's not actually representitive of the SIR model. The infection terms in one specific cell (i,j) should depend only upon the susceptible population in that cell, and new_f represents that. Here's what that same Napervillian infection would look like, a little less swarming, but still pretty cool:

Check out some of the stuff we do