In [1]:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib

from matplotlib import animation, rc
from IPython.display import HTML
import random
import pandas as pd

#Graph Modules
import plotly.io as pio
import plotly.express as px
import plotly
pio.renderers.default = 'iframe_connected'
import seaborn as sns
In [2]:
%matplotlib inline

Simulation Covid 19

In [3]:
defaultY = 200
defaultX = 400

class Person():
    def __init__(self, infected, xPlus, yPlus, boardX = defaultX, boardY = defaultY, borders = None):
        self.position = [int(random.uniform(1, boardX)), int(random.uniform(1, boardY))]
        
        self.rateX = xPlus
        self.rateY = yPlus
        
        self.infected = infected
        self.immune = False
        
        self.xPlus = random.choice([int(random.uniform(2, self.rateX)), int(random.uniform(-int(self.rateX), -2))])
        self.yPlus = random.choice([int(random.uniform(2, self.rateY)), int(random.uniform(-int(self.rateY), -2))])
        
        self.boardX = boardX
        self.boardY = boardY
        
        self.border = borders
        # [ [x, y, True], [x, y, False] ]
        # True -> Vertical False -> Horizontal
        
        
    def move(self):
        #print('before : ', self.position)
        self.position = [int(self.position[0] + self.xPlus), int(self.position[1] + self.yPlus)]
        #print('after : ', self.position)
        
        #Hit the Corner
        if (self.position[0] + self.xPlus) < 0:
            self.xPlus = int(random.uniform(2, self.rateX))
        elif (self.position[0] + self.xPlus) >= self.boardX:
            self.xPlus = int(random.uniform(-self.rateX, -2))
            
        if (self.position[1] + self.yPlus) < 0:
            self.yPlus = int(random.uniform(2, self.rateY))
        elif (self.position[1] + self.yPlus) > self.boardY:
            self.yPlus = int(random.uniform(-self.rateY, -2))
            
        #Hit the Border
        if self.border != None:
            for b in self.border:
                if b[2] == True:
                    #Vertical
                    if b[1] <= (self.position[1] + self.yPlus) or self.position[1] == b[1] or b[1] == (self.position[1] + self.yPlus):
                        self.yPlus = int(random.uniform(2, self.rateY))
                    if b[1] >= (self.position[1] + self.yPlus) or self.position[1] == b[1] or b[1] == (self.position[1] + self.yPlus):
                        self.yPlus = int(random.uniform(2, self.rateY))
                else:
                    #Horizontal
                    if b[0] <= (self.position[0] + self.xPlus) or self.position[0] == b[0] or b[0] == (self.position[0] + self.xPlus):
                        self.xPlus = int(random.uniform(2, self.rateX))
                    if b[0] >= (self.position[0] + self.xPlus) or self.position[0] == b[0] or b[0] == (self.position[0] + self.xPlus):
                        self.xPlus = int(random.uniform(2, self.rateX))
    
class Board():
    def __init__(self, boardX, boardY, Num, infectionPercentage, perPersonSpeedX, perPersonSpeedY, radius, infectionLength, borders = None):
        self.boardSizeX = boardX
        self.boardSizeY = boardY
        self.infectionPercentage = infectionPercentage
        self.radius = radius
        self.infectionLength = infectionLength
        self.borders = borders
        
        self.population = []
        #Add Infected Person
        self.population.append(Person(True, perPersonSpeedX, perPersonSpeedY, self.boardSizeX, self.boardSizeY))
        self.population = self.population + [Person(False, int(random.uniform(1, perPersonSpeedX)), int(random.uniform(1, perPersonSpeedY)), self.boardSizeX, self.boardSizeY, self.borders) for i in range(Num-1)]
        
        self.infected = [0] #Only index values
        self.infectedIndexes = [0]
        self.infectiousPositions = [self.population[0].position]
        self.infectionCount = 1
        self.recoveredCount = 0
        
        self.infectionRecords = [[0,1]]
        
    def move(self):
        for i in range(len(self.population)):
            self.population[i].move()
            
        #Get infectious Positions
        self.infectiousPositions = [self.radiusPositionSpread(self.population[personIndex].position) for personIndex in self.infectedIndexes][0]
        
        #Update Infection
        self.infectionRecords = [[infectionData[0], infectionData[1]+0.1] for infectionData in self.infectionRecords]
        #most Infection end around 2 weeks so we are going to make many people immune after 2 weeks 40% to be immune after 2 weeks
        tempRecords = []
        for i in range(len(self.infectionRecords)):
            if self.infectionRecords[i][1] > self.infectionLength:
                per = random.uniform(0, 1)
                if per < 0.4:
                    tempRecords.append(self.infectionRecords[i])
                else:
                    self.population[self.infectionRecords[i][0]].immune = True
                    self.recoveredCount += 1
            else:
                tempRecords.append(self.infectionRecords[i])
        self.infectionRecords = tempRecords
        
        
        temp = self.population
        
        for x in range(len(temp)):
            if temp[x].infected == False and temp[x].position in self.infectiousPositions and temp[x].immune == False:
                per = random.uniform(0, 1)
                if per < self.infectionPercentage:
                    self.infectionCount += 1
                    self.infectedIndexes.append(x)
                    temp[x].infected = True
                    self.infectionRecords.append([x,1])
            if temp[x].immune == True and temp[x].infected == True:
                temp[x].infected == False
                    
        self.population = temp
    
    def radiusPositionSpread(self, position):
        ans = [position]
        for i in range(self.radius):
            ans.append([position[0] + i, position[1]])
            ans.append([position[0], position[1] + i])
            ans.append([position[0] + i, position[1] + i])
            
            ans.append([position[0] - i, position[1]])
            ans.append([position[0], position[1] - i])
            ans.append([position[0] - i, position[1] - i])
            
            ans.append([position[0] + i, position[1] - i])
            ans.append([position[0] - i, position[1] + i])
        return ans[:]
        
In [4]:
populationSize = 500
InfectionPercentage = 1
xSpeedMax = 6
ySpeedMax = 8
spreadRadius = 10
maxFrames = 1000
infectionLength = 14


def makeData():
    board = Board(defaultX, defaultY, populationSize, InfectionPercentage, xSpeedMax, ySpeedMax, spreadRadius, infectionLength)
    df = pd.DataFrame(columns=['frame', 'x', 'y', 'color', 'totalInfected'])
    
    for frame in range(maxFrames):
        board.move()
        
        xValue = []
        yValue = []
        color = []
        pID = []
        personType = []
        
        for personID in range(len(board.population)):
            if board.population[personID].immune == True:
                xValue.append(board.population[personID].position[0])
                yValue.append(board.population[personID].position[1])
                color.append('green')
                pID.append(personID)
                personType.append('Immune')
            elif board.population[personID].infected == True:
                xValue.append(board.population[personID].position[0])
                yValue.append(board.population[personID].position[1])
                color.append('red')
                pID.append(personID)
                personType.append('Infecious')
            else:
                xValue.append(board.population[personID].position[0])
                yValue.append(board.population[personID].position[1])
                color.append('blue')
                pID.append(personID)
                personType.append('Normal')
        
        
        
        tempDF_UnInfected = pd.DataFrame({
            'frame': frame,
            'id':pID,
            'x': xValue,
            'y': yValue,
            'color': color,
            'totalInfected': board.infectionCount,
            'Recovered': board.recoveredCount,
            'Type': personType
        })
        
        #Add Immune People Before hand
        tempDF_UnInfected = tempDF_UnInfected.append({
            'frame': frame,
            'id':len(pID),
            'x': 10000,
            'y': 10000,
            'color': 'green',
            'totalInfected': board.infectionCount,
            'Recovered': board.recoveredCount,
            'Type': 'Immune'
        }, ignore_index = True)
        
        tempDF_UnInfected = tempDF_UnInfected.sort_values(['id'])
        
        
        df = pd.concat([df, tempDF_UnInfected])
    return df

data = makeData()
data.to_csv('data/simulation1.csv')
D:\Anaconda\envs\Tensorflow2\lib\site-packages\ipykernel_launcher.py:71: FutureWarning:

Sorting because non-concatenation axis is not aligned. A future version
of pandas will change to not sort by default.

To accept the future behavior, pass 'sort=False'.

To retain the current behavior and silence the warning, pass 'sort=True'.


In [5]:
fig = px.scatter(data, x = 'x', y = 'y', animation_frame='frame', animation_group = 'id', color='Type', range_x=[0,defaultX], range_y=[0, defaultY], title='Infections over 10 days')
fig.show()
In [6]:
fig_infections = px.area(data, x='frame', y='totalInfected')
fig_infections.show()
In [7]:
fig_recovered = px.area(data, x='frame', y='Recovered')
fig_recovered.show()

Covid-19 Simulation with Population Density Comparison

Dense Population with No social distance

In [8]:
populationSize = 500
InfectionPercentage = 1
xSpeedMax = 6
ySpeedMax = 8
spreadRadius = 10
maxFrames = 1000
infectionLength = 14
border = [[200, 0, True]]

xValue = 100
yValue = 200

def makeData_Border():
    xValue = 100
    yValue = 200
    board = Board(xValue, yValue, populationSize, InfectionPercentage, xSpeedMax, ySpeedMax, spreadRadius, infectionLength, None)
    df = pd.DataFrame(columns=['frame', 'x', 'y', 'color', 'totalInfected'])
    
    for frame in range(maxFrames):
        board.move()
        
        xValue = []
        yValue = []
        color = []
        pID = []
        personType = []
        
        for personID in range(len(board.population)):
            if board.population[personID].immune == True:
                xValue.append(board.population[personID].position[0])
                yValue.append(board.population[personID].position[1])
                color.append('green')
                pID.append(personID)
                personType.append('Immune')
            elif board.population[personID].infected == True:
                xValue.append(board.population[personID].position[0])
                yValue.append(board.population[personID].position[1])
                color.append('red')
                pID.append(personID)
                personType.append('Infecious')
            else:
                xValue.append(board.population[personID].position[0])
                yValue.append(board.population[personID].position[1])
                color.append('blue')
                pID.append(personID)
                personType.append('Normal')
        
        
        
        tempDF_UnInfected = pd.DataFrame({
            'frame': frame,
            'id':pID,
            'x': xValue,
            'y': yValue,
            'color': color,
            'totalInfected': board.infectionCount,
            'Recovered': board.recoveredCount,
            'Type': personType
        })
        
        #Add Immune People Before hand
        tempDF_UnInfected = tempDF_UnInfected.append({
            'frame': frame,
            'id':len(pID),
            'x': 1000,
            'y': 1000,
            'color': 'green',
            'totalInfected': board.infectionCount,
            'Recovered': board.recoveredCount,
            'Type': 'Immune'
        }, ignore_index = True)
        
        tempDF_UnInfected = tempDF_UnInfected.sort_values(['id'])
        
        
        df = pd.concat([df, tempDF_UnInfected])
    return df

data_border = makeData_Border()
data_border.to_csv('data/simulation1_smallBorder.csv')
D:\Anaconda\envs\Tensorflow2\lib\site-packages\ipykernel_launcher.py:76: FutureWarning:

Sorting because non-concatenation axis is not aligned. A future version
of pandas will change to not sort by default.

To accept the future behavior, pass 'sort=False'.

To retain the current behavior and silence the warning, pass 'sort=True'.


In [9]:
fig_Dense = px.scatter(data_border, x = 'x', y = 'y', animation_frame='frame', animation_group = 'id', color='Type', range_x=[0,xValue], range_y=[0, yValue], title='Infections over 10 days for Dense Population')
fig_Dense.show()
In [10]:
fig_infections_dense = px.area(data_border, x='frame', y='totalInfected')
fig_infections_dense.show()

For not Dense Population and socialy distanced

In [11]:
populationSize = 500
InfectionPercentage = 1
xSpeedMax = 6
ySpeedMax = 8
spreadRadius = 10
maxFrames = 1000
infectionLength = 14
border = [[200, 0, True]]

xValue = 400
yValue = 800

def makeData_Border():
    xValue = 400
    yValue = 800
    board = Board(xValue, yValue, populationSize, InfectionPercentage, xSpeedMax, ySpeedMax, spreadRadius, infectionLength, None)
    df = pd.DataFrame(columns=['frame', 'x', 'y', 'color', 'totalInfected'])
    
    for frame in range(maxFrames):
        board.move()
        
        xValue = []
        yValue = []
        color = []
        pID = []
        personType = []
        
        for personID in range(len(board.population)):
            if board.population[personID].immune == True:
                xValue.append(board.population[personID].position[0])
                yValue.append(board.population[personID].position[1])
                color.append('green')
                pID.append(personID)
                personType.append('Immune')
            elif board.population[personID].infected == True:
                xValue.append(board.population[personID].position[0])
                yValue.append(board.population[personID].position[1])
                color.append('red')
                pID.append(personID)
                personType.append('Infecious')
            else:
                xValue.append(board.population[personID].position[0])
                yValue.append(board.population[personID].position[1])
                color.append('blue')
                pID.append(personID)
                personType.append('Normal')
        
        
        
        tempDF_UnInfected = pd.DataFrame({
            'frame': frame,
            'id':pID,
            'x': xValue,
            'y': yValue,
            'color': color,
            'totalInfected': board.infectionCount,
            'Recovered': board.recoveredCount,
            'Type': personType
        })
        
        #Add Immune People Before hand
        tempDF_UnInfected = tempDF_UnInfected.append({
            'frame': frame,
            'id':len(pID),
            'x': 1000,
            'y': 1000,
            'color': 'green',
            'totalInfected': board.infectionCount,
            'Recovered': board.recoveredCount,
            'Type': 'Immune'
        }, ignore_index = True)
        
        tempDF_UnInfected = tempDF_UnInfected.sort_values(['id'])
        
        
        df = pd.concat([df, tempDF_UnInfected])
    return df

data_border_notDense = makeData_Border()
data_border_notDense.to_csv('data/simulation1_largeBorder.csv')
D:\Anaconda\envs\Tensorflow2\lib\site-packages\ipykernel_launcher.py:76: FutureWarning:

Sorting because non-concatenation axis is not aligned. A future version
of pandas will change to not sort by default.

To accept the future behavior, pass 'sort=False'.

To retain the current behavior and silence the warning, pass 'sort=True'.


In [12]:
fig_notDense = px.scatter(data_border_notDense, x = 'x', y = 'y', animation_frame='frame', animation_group = 'id', color='Type', range_x=[0,xValue], range_y=[0, yValue], title='Infections over 10 days for Dense Population')
fig_notDense.show()
In [13]:
fig_infections_notDense = px.area(data_border_notDense, x='frame', y='totalInfected')
fig_infections_notDense.show()
In [28]:
isoDF = pd.read_csv('data/wikipedia-iso-country-codes.csv')
population_df = pd.read_csv('data/population_by_country_2020.csv')
population_df['Country_ISO'] = [isoDF.loc[isoDF['English short name lower case'] == country]['Alpha-3 code'].values[0] for country in population_df['Country']]
print(population_df.shape)
population_df.head()
(205, 12)
Out[28]:
Country Population (2020) Yearly Change Net Change Density (P/Km) Land Area (Km) Migrants (net) Fert. Rate Med. Age Urban Pop % World Share Country_ISO
0 China 1440297825 0.39% 5540090 153 9388211 -348399.0 1.7 38 61% 18.47% CHN
1 India 1382345085 0.99% 13586631 464 2973190 -532687.0 2.2 28 35% 17.70% IND
2 United States 331341050 0.59% 1937734 36 9147420 954806.0 1.8 38 83% 4.25% USA
3 Indonesia 274021604 1.07% 2898047 151 1811570 -98955.0 2.3 30 56% 3.51% IDN
4 Pakistan 221612785 2.00% 4327022 287 770880 -233379.0 3.6 23 35% 2.83% PAK
In [29]:
population_df_bar = px.bar(population_df.sort_values("Density (P/Km)", ascending=False).head(30), x='Country', y='Density (P/Km)')
population_df_bar.show()
In [30]:
fig_Density = px.choropleth(population_df, locations='Country_ISO', color = 'Density (P/Km)', hover_name='Country', color_continuous_scale='Plasma', 
                            range_color=(0, 400))
fig_Density.show()
In [31]:
isoDF['Country'] = isoDF['English short name lower case']
covid_df = pd.read_csv('data/owid-covid-data.csv')
covid_df['Country'] = covid_df['location']
covid_df = covid_df.groupby(['location']).max()
covid_df = pd.merge(covid_df, isoDF, how='left', on='Country')
covid_df.head()
Out[31]:
iso_code continent date total_cases new_cases new_cases_smoothed total_deaths new_deaths new_deaths_smoothed total_cases_per_million ... handwashing_facilities hospital_beds_per_thousand life_expectancy human_development_index Country English short name lower case Alpha-2 code Alpha-3 code Numeric code ISO 3166-2
0 AFG Asia 9/12/2020 57144.0 1485.0 758.571 2521.0 46.0 25.143 1467.928 ... 37.746 0.50 64.83 0.511 Afghanistan Afghanistan AF AFG 4.0 ISO 3166-2:AF
1 ALB Europe 9/12/2020 128155.0 1239.0 1111.857 2310.0 21.0 20.429 44532.282 ... NaN 2.89 78.57 0.795 Albania Albania AL ALB 8.0 ISO 3166-2:AL
2 DZA Africa 9/12/2020 118378.0 1133.0 1065.429 3126.0 30.0 21.571 2699.548 ... 83.741 1.90 76.88 0.748 Algeria Algeria DZ DZA 12.0 ISO 3166-2:DZ
3 AND Europe 9/12/2020 12497.0 299.0 112.429 120.0 6.0 2.000 161742.057 ... NaN NaN 83.73 0.868 Andorra Andorra AD AND 20.0 ISO 3166-2:AD
4 AGO Africa 9/12/2020 23331.0 355.0 261.857 550.0 12.0 4.571 709.877 ... 26.664 NaN 61.15 0.581 Angola Angola AO AGO 24.0 ISO 3166-2:AO

5 rows × 63 columns

In [32]:
covid_df_bar = px.bar(covid_df.sort_values("total_cases", ascending=False).head(30), x='Country', y='total_cases')
covid_df_bar.show()
In [33]:
fig_covidCases = px.choropleth(covid_df, locations='Alpha-3 code', color = 'total_cases', hover_name='English short name lower case', color_continuous_scale='Plasma', 
                            range_color=(0, 1000000))
fig_covidCases.show()
In [21]:
maskData = pd.read_csv('data/face-covering-policies-covid.csv')
maskData['Country'] = maskData['Entity']
maskData = maskData.groupby(['Entity']).max()
maskData.head()
Out[21]:
Code Day facial_coverings Country
Entity
Afghanistan AFG 2021-04-06 3 Afghanistan
Albania ALB 2021-04-05 4 Albania
Algeria DZA 2021-04-05 3 Algeria
Andorra AND 2021-04-05 3 Andorra
Angola AGO 2021-04-05 4 Angola
In [36]:
masks_bar = px.bar(maskData.sort_values("facial_coverings", ascending=False).head(30), x='Country', y='facial_coverings')
masks_bar.show()
In [22]:
fig_maskPolicy = px.choropleth(maskData, locations='Code', color = 'facial_coverings', hover_name='Country', color_continuous_scale='Plasma', 
                            range_color=(0, 5))
fig_maskPolicy.show()

Check Correlation for Number of Cases and Population Density

In [34]:
correlationDF = pd.merge(covid_df, pd.read_csv('data/population_by_country_2020.csv'), how='left', on='Country')
correlationDF = correlationDF[['Country', 'total_cases', 'Density (P/Km)']]
sns.heatmap(correlationDF.corr(), cmap=sns.diverging_palette(20, 220))
Out[34]:
<matplotlib.axes._subplots.AxesSubplot at 0x121c3c6d0b8>

Find the Basic Reproduction Number for Novel Corona Virus

How?

The daily growth of cumulative number of infections is caculated using the natural LOG scale.

Data: https://www.kaggle.com/fireballbyedimyrnmom/us-counties-covid-19-dataset

In [23]:
covidData = pd.read_csv('data/us-counties.csv')
print(covidData.shape)
covidData.head()
(1193105, 6)
Out[23]:
date county state fips cases deaths
0 2020-01-21 Snohomish Washington 53061.0 1 0.0
1 2020-01-22 Snohomish Washington 53061.0 1 0.0
2 2020-01-23 Snohomish Washington 53061.0 1 0.0
3 2020-01-24 Cook Illinois 17031.0 1 0.0
4 2020-01-24 Snohomish Washington 53061.0 1 0.0

We will the reproductive number for Europe as in Europe there are many developed countries

In [24]:
x = []
y = []
total = 0

for date in covidData['date'].unique().tolist():
    x.append(date)
    y.append(covidData[covidData['date'] == date]['cases'].sum())
    
cumDataEurope = pd.DataFrame({'date':x,'cases':y})
cumDataEurope['date'] = pd.to_datetime(cumDataEurope['date'])
cumDataEurope = cumDataEurope.sort_values('date')
    
casesFig = px.bar(cumDataEurope, x='date', y='cases', title = 'Total Cases USA')
casesFig.show()

We can see December 2020 to Jan 2021 has the highest increase rate therefore we will data from that.

Covid-19 will show symptoms after 14 days therefore, Nov 10th infection numbers will show up Nov 24th.

In [25]:
nov10 = covidData[covidData['date'] == '2020-11-04']['cases'].sum() - covidData[covidData['date'] == '2020-11-03']['cases'].sum()
nov24 = covidData[covidData['date'] == '2020-11-18']['cases'].sum() - covidData[covidData['date'] == '2020-11-17']['cases'].sum()
print("New cases Nov  4th : " + str(nov10))
print("New cases Nov 18th : " + str(nov24))
print(str(nov10) + " people gave covid-19 to " + str(nov24) + ' people')
New cases Nov  4th : 108080
New cases Nov 18th : 172283
108080 people gave covid-19 to 172283 people

With restrictions imposed covid-19 has 1.7 times its infections.

The actual Basic Reprodcution Number is 2.2 which our data with restrictions have shown.

Visualise Reproduction with Infection Numbers For Hypothetical situation

In [26]:
x = []
y = []
t = []
stages = 11
prev = 0
deathRate = 0.02

for i in range(stages):
    x.append(i)
    x.append(i)
    if i == 0:
        y.append(i+1)
        t.append('infection')
        y.append(0)
        t.append('infection')
        prev = i+1
    else:
        prev = 2 * prev
        y.append(prev)
        t.append('infection')
        y.append(int(prev * deathRate))
        t.append('death')

reproductionNum = pd.DataFrame({
    'x':x,
    'y':y,
    'type': t
})

reproductionNumFig = px.line(reproductionNum, x='x', y='y', color='type', title = 'Rproduction of Covid-19')
reproductionNumFig.show()
In [27]:
print("Infections : ", reproductionNum[reproductionNum['type'] == 'infection']['y'].sum()-1)
print("Deaths : ", reproductionNum[reproductionNum['type'] == 'death']['y'].sum())
Infections :  2046
Deaths :  38