Building your own Covid-19 Epidemic Simple Model Using Python | by Sadegh Maghsoudi | Towards Data Science

If you live in this world, there is a very small chance you may have not yet heard of this novel Coronavirus Covid-19 and the pandemic it caused literally all over the world. Actually, it is a good chance that you are reading this while self quarantined at your home, social distancing from public and like many, have very much of extra time to deal with. So, if you are familiar with programming, why not try this: build your own epidemic model at home. Which helps you boost your programming abilities, and, helps you understand the concepts and basic dynamics of epidemics.
But first of all, please note that it is simply an article to show how to use Python to prepare a basic model for Covid-19 epidemic. Which on the other hand means: It Is Not a model you could use to predict what's to come. It is Not your DIY model which brings you the ability to simply ignore what official authorities recommend you to do. Neither it is a visualization tool to prove others on social media how doomed we are! If you’r OK with these disclaimers now let’s start our coding.
The main concepts we assume here, is to model a finite number of individuals of a sample society interacting with each other on a daily basis and the chance of contamination which each interaction brings. For the sake of simplicity, I generated the group randomly, with proximities represents the amount of interaction each person has with it’s nearest neighbors. Note that a person’s neighbor may be his/her family, colleagues, classmates or anybody else who he/she is in contact with every day. I assumed that this interaction remains the same for most persons in the sample, but to make things more interesting, let’s assume a fraction of group,I took 10%, which we call them Transporters, move randomly each day in the model; which means their neighbors change each day. I’ve added these group to represent those shopkeepers, taxi drivers, police officers, etc. who interact with new individuals each day. I’ve marked transporters with a circle and their number to keep track of each.And finally, to complete our Day Zero initialization, what we need more is a Patient Zero! which I colored red.
After initialization, the simulations goes like this on each day:
Transporters move randomly.
People interact with their neighbors.
Infection spreads randomly with a predefined chance of contamination from a patient to a healthy guy during interaction.
A certain fraction of patients, Covid-19 (+), randomly hospitalize, assuming their symptoms got worst.
A certain fraction of hospitalized patients, randomly selected, die.
Patients who has endured the period of disease, let’s say 14 days, with mild symptoms will be cured and immune against the disease.
And tomorrow . . . we repeat all above again. And that goes on until no new infection could be possible.
Coding the above mentioned algorithm would be an easy task for like all programming languages, but for visualization purposes, Python has a lot to offer using matplotlib.
Let’s see what happens if we run this simulation with a random dataset of 1000 individuals and infection chance of 25% in each interaction:
Let’s see what the statistics looked like during that simulation:
Since the simulation takes some time, I had python to report me what is exactly going on with my society during the simulation in a log:
-------------------------------Day: 0- - - - - - - - - - - - - - - - Healthy         999Covid-19(+)       1Hospitalized      0Cured             0Dead              0Name: 0, dtype: int64-------------------------------Day: 1- - - - - - - - - - - - - - - - Healthy         987.0Covid-19(+)      13.0Hospitalized      0.0Cured             0.0Dead              0.0Name: 1, dtype: float64-------------------------------Day: 2- - - - - - - - - - - - - - - - Healthy         977.0Covid-19(+)      23.0Hospitalized      0.0Cured             0.0Dead              0.0Name: 2, dtype: float64-------------------------------.. . . . . . . . . -------------------------------Day: 53- - - - - - - - - - - - - - - - Healthy           1.0Covid-19(+)       0.0Hospitalized      0.0Cured           951.0Dead             48.0Name: 53, dtype: float64
And I had python to automatically do all the job to make all those charts and list and video and save it on the format I chose. But note that it may take some simulation run time if you choose big numbers for quantity and change some of the assumptions in a wrong way.
For example, simulating 10,000 individuals took me around 24 hours until day 34 which you may see the simulation results below:
Now that you have developed your own sample model, you may find it interesting to search for more scientific models developed by professional epidemiologists or you may see one here from Washington Post.
Below, you may find my python code to above illustrated model in which you can change parameters easily and see what happens with different assumptions.
# -*- coding: utf-8 -*-"""Created on Sat Feb 29 21:57:53 2020@author: Sadegh Maghsoudi"""# Functions --------------------------------------------------def point(xlimit,ylimit):    import random    x = random.uniform(0,xlimit)    y = random.uniform(0,ylimit)    return x,ydef Generate(GrupSize,xlimit,ylimit):    import pandas as pd    import math    df = pd.DataFrame(columns='X,Y,Covid-19,Day'.split(','))        for i in range(GrupSize):        df.loc[i,'X'], df.loc[i,'Y'] = point(xlimit,ylimit)        df.loc[i,'Covid-19'] = False        samplesize = math.floor(GrupSize/100)    MoversList = df.sample(n = samplesize).index.values.tolist()        StatofDay = pd.DataFrame(columns='Healthy,Covid-19(+),Hospitalized,Cured,Dead'.split(','))    return df , StatofDay, MoversList    def plt1color(df):    cols=[]    for l in df.index:        if df.loc[l,'Covid-19']==True: #Infected            cols.append('red')        elif df.loc[l,'Covid-19']==666: #Dead            cols.append('black')        elif df.loc[l,'Covid-19']==115: #Hospitalized            cols.append('yellow')        elif df.loc[l,'Covid-19']==7: #Cured            cols.append('green')        else:            cols.append('blue') #Healthy    return colsdef plt2color(Stat):    cols=[]    for i in Stat.columns:        if i=='Covid-19(+)': #Infected            cols.append('red')        elif i=='Dead': #Dead            cols.append('black')        elif i=='Hospitalized': #Hospitalized            cols.append('yellow')        elif i=='Cured': #Cured            cols.append('green')        else:            cols.append('blue') #Healthy    return colsdef Plot():    import matplotlib.pyplot as plt    global df, fig, Stat, Day, Moverslist    cols=plt1color(df)    ld = ['Healthy','Covid-19(+)','Hospitalized','Cured','Death Toll']    axs[0].cla()    axs[0].scatter(df['X'],df['Y'],s=1,c=cols)    for i in MoversList:        axs[0].scatter(df.loc[i,'X'],df.loc[i,'Y'],s=6,facecolors='none', edgecolors='black')        axs[0].text(df.loc[i,'X']+0.02, df.loc[i,'Y']+0.02, str(i), fontsize=5)    cols=plt2color(Stat)    sDay = str(Day)    title = 'Day' + sDay    axs[0].set_title(title,loc='left')    axs[0].set_yticklabels([])    axs[0].set_xticklabels([])    axs[0].tick_params(#    axis='both',       # changes apply to the x-axis    which='both',      # both major and minor ticks are affected    bottom=False,      # ticks along the bottom edge are off    top=False,         # ticks along the top edge are off    right=False,      # ticks along the right edge are off    left=False,         # ticks along the left edge are off    labelbottom=False) # labels along the bottom edge are off    axs[1].cla()    axs[1].plot(Stat.Healthy,label=ld[0],color=cols[0])    axs[1].plot(Stat['Covid-19(+)'],label=ld[1],color=cols[1])    axs[1].plot(Stat.Hospitalized,label=ld[2],color=cols[2])    axs[1].plot(Stat.Cured,label=ld[3],color=cols[3])    axs[1].plot(Stat.Dead,label=ld[4],color=cols[4])#    axs[1].set_prop_cycle(color=cols)    axs[1].legend(bbox_to_anchor=(0, 1), loc='upper left', borderaxespad=0.)    plt.xlabel('Days')    plt.show()    if Day<10 : sDay = '0' + sDay    title = 'Day' + sDay + '.png'    plt.savefig(title)returndef Png_to_gif():    from PIL import Image    import glob        # Create frames    frames = []    imgs = glob.glob("*.png")    for i in imgs:        new_frame = Image.open(i)        frames.append(new_frame)            # Save into GIF    frames[0].save('png_to_gif.gif', format='GIF',          append_images=frames[1:],          save_all=True,          duration=500, loop=0)def infect(Person):    import random    global df,Day    if random.random()>0.25 and Day>3 : return    if df.loc[Person,'Covid-19']==False:        df.loc[Person,'Covid-19'], df.loc[Person,'Day'] = True, Day        def Move(xlimit,ylimit):    """     Move Movers Randomly    """    import random    global df, MoversList    for i in MoversList:        if (df.loc[i,'Covid-19']==115) or (df.loc[i,'Covid-19']==666) : MoversList.remove(i)        df.loc[i,'X'], df.loc[i,'Y'] = (df.loc[i,'X']+random.uniform(1,xlimit/3))%xlimit, (df.loc[i,'Y']+random.uniform(1,ylimit/3))%ylimitdef check(i,j):    import math    global df, YesterdayPatients, Distlimit    Dist = math.sqrt((df.loc[i,'X']-df.loc[j,'X'])**2+(df.loc[i,'Y']-df.loc[j,'Y'])**2)    flag = ((YesterdayPatients[i]==True) ^ (YesterdayPatients[j]==True)) and Dist<Distlimit    return flag    def interact():    global Day, df    for i in range(len(df)):        for j in range(i):            if check(i,j):                if (df.loc[i,'Covid-19']==False) :                    infect(i)                else:                    infect(j)def kill():    import math    global df        samplesize = math.floor(len(df[df['Covid-19']==True])*.005+len(df[df['Covid-19']==115])*.005)    if samplesize>len(df[df['Covid-19']==True]): return    df.loc[df[df['Covid-19']==True].sample(n = samplesize).index.values.tolist(),'Covid-19']=666    returndef hospitilize():    import math    global df    samplesize = math.floor(len(df[df['Covid-19']==True])*0.03)    if samplesize>len(df[df['Covid-19']==True]): return    df.loc[df[df['Covid-19']==True].sample(n = samplesize).index.values.tolist(),'Covid-19']=115    returndef cure():    global df, Day    df.loc[(df['Day']<Day-10) & (df['Covid-19']==True) ,'Covid-19'] = 7    df.loc[(df['Day']<Day-21) & (df['Covid-19']==115) ,'Covid-19'] = 7    return        def Tomorrow(): # To Be checked and Resolved!!!    global df, Day    Day +=1    kill()    hospitilize()    cure()    Move(xlimit,ylimit)    interact()def Count(Day):    global df, Stat        List = list(df['Covid-19'])        Stat.loc[Day,'Healthy'] = List.count(False)    Stat.loc[Day,'Covid-19(+)'] = List.count(True)        Stat.loc[Day,'Hospitalized'] = List.count(115)        Stat.loc[Day,'Cured'] = List.count(7)    Stat.loc[Day,'Dead'] = List.count(666)         return    def write_log(*args):    global log_file    line = ' '.join([str(a) for a in args])    log_file.write(line+'\n')    print(line)    # Main -------------------------------------------------------------------import matplotlib.pyplot as pltimport randomlog_file = open("Log.txt","w+")# -------------------------------------------#   I N P U T   V A R I A B L E S   H E R E  |# -------------------------------------------#                                            |n = 1000    #                                |xlimit,ylimit=30,30   #                      |Distlimit = 1.5 #                            |#                                            |# -------------------------------------------write_log(31*'-')write_log("Here's the Input Data:")write_log(8*'- - ') write_log('Numper of Sample:',n)write_log('X & Y limites: ',xlimit,', ',ylimit)write_log('Distance required for Contamination:', Distlimit)# Day = 0,  Generating Model...Day = 0df, Stat, MoversList = Generate(n,xlimit,ylimit)infect(random.randrange(n))fig, axs = plt.subplots(2)fig.suptitle('Covid-19 Epidemic Sample Model', fontsize=16)Plot()Count(Day)write_log(31*'-')write_log('Day:',Day)write_log(8*'- - ')    write_log(Stat.loc[Day])# Day=1YesterdayPatients = list(df['Covid-19'])Tomorrow()Plot()Count(Day)write_log(31*'-')write_log('Day:',Day)write_log(8*'- - ')    write_log(Stat.loc[Day])    #Main Loop  .  .  .countsames = 0while Stat.loc[Day, 'Healthy']>0 and Day<100:    log_file = open("Log.txt","a+")    if (list(Stat.loc[Day])==list(Stat.loc[Day-1])):         countsames +=1        if countsames>2 : break    else :        countsames = 0        YesterdayPatients = list(df['Covid-19'])    Tomorrow()    Plot()    Count(Day)    write_log(31*'-')    write_log('Day:',Day)    write_log(8*'- - ')        write_log(Stat.loc[Day])    log_file.close()Png_to_gif()Stat.to_excel('Stat.xlsx')Stat.plot(title='Statistical Data Vs. Days Passed')plt.savefig('Stat')
Note from the editors: Towards Data Science is a Medium publication primarily based on the study of data science and machine learning. We are not health professionals or epidemiologists, and the opinions of this article should not be interpreted as professional advice. To learn more about the coronavirus pandemic, you can click here.