A Time Series Analysis to Predict the End of COVID-19 in Canada.
By: Masoud Kameli
Tags: Canada, Predictions, Covid 19, Machine Learning, Time Series Analysis

Machine Learning and Prediction

Disclaimer: This article is not a medical suggestion and should not be referenced to for any medical advice. Any figures, numbers and dates provided in this section are just assumptions based on a Machine Learning algorithm. No decision shall be made based on those predictions.

Prophet was released in 2017 for Python and R by Facebook, and gave data scientists around the world a new tools for time series analysis. This model uses time series and daily observations to make predictions based on periodical or non-periodical changepoints. It also can detect a general trend in the observed data while including the effect of holidays and weekdays. FBprophet is mostly being used in stock market and business analyses. If you want more information, please, feel free to check reference section for a link to its full introduction on GitHub.

That being said, we must decide on what our imports should be. Should it be the cumulative or the daily data? Should it be the all data or just an slice of it after an specific date?

As I mentioned, trend and change over points are keys in this algorithm. If we import a rising wave, we will get a similar rising wave based on detected changepoints. Similarly, if we import a falling trend with ups and downs, it will output a similar trend by trying to detect reasons for those peaks and troughs during next months or weeks. So, the imported data should defiantly be the daily cases and not the cumulative numbers.

As we can see on the previous figure, Canada’s number of confirmed cases is falling around the last week of April. I decided on an exact date by using rolling average method and set that date as my entry point.

# Grab an slice of our main DataFrame:

df = Countries['Canada']['Confirmed'] # find the index of maximum value:

index_max = Countries.diff()['Canada']['Confirmed'].rolling(window=7).mean().idxmax() df = df.reset_index() # Use rolling average to find the date with maximum number of cases

df['Rolling'] = df['Confirmed'].rolling(window=7).mean() # for modelling we only use the data after the index_max date: dfT = df[df['Time']>=index_max]

dfT=dfT.dropna()

Please note that the column to predicted must be named ‘y’ and time series must be named ‘ds’ if you want to use Prophet.

dfT.rename(columns={"Time":'ds','Confirmed':'y'},inplace=True)

Before any further steps, we have to decide on changepoint scaler for fbprophet. It’s a value in our model which decides on bias-variance tradeoff. ChangePoint Scaler is a value between zero and one; higher values can lead to over-fitting while lower values can cause under fitting. If this subject is not familiar to you, you can refer to this article for more explanation.

The default changepoint scaler is set to 0.05, which gives acceptable results, but I ran a loop to find the best value for more accuracy in modelling this specific data set; the best number is something around 0.09.

Different Changepoint Scalers and their errors as we continue predicting

In this point, many may run the model and enjoy the results, but let’s test it beforehand. Before running the model, we can split our data to two train and test part to see how it is performing on our test data.

# First, predict 10 days to see how model react:

dfTrain = dfT[:len(dfT)-10] # create an instance of fbprophet and fit the training data:

import fbprophet

mymodel = fbprophet.Prophet(changepoint_prior_scale=0.09)

mymodel.fit(dfTrain) # make an empty dataframe with future dates:

pre_dfT = mymodel.make_future_dataframe(periods=10,freq='D') pre_dfT = mymodel.predict(pre_dfT) # calculate the error for each test day:

pre_dfT.set_index('ds',inplace=True)

dfT.set_index('ds',inplace=True)

Merged_df = pd.DataFrame([pre_dfT['yhat'],dfT['y']]).transpose()[:37]

Merged_df['ERROR'] = (Merged_df['yhat'] - Merged_df['y'])/Merged_df['y'] * 100

Error for each day of test split

As we can see, the model performed pretty well; our mean error for all test days are around 1.5 percent. That is to say, our model predicted the exact number of cases with only 15 people error out for every 1000 cases.

(abs(Merged_df['ERROR'])/Merged_df['y']*100).mean()

After making sure that our model’s accuracy is in an acceptable range, we can repeat the procedure on all of the data to see if the trend doesn’t change, when it ends and how many people will get infected.

We can repeat the procedure for predicting the number of deaths:

With the benefit of FBprophet, we can investigate the trend in general or in the scale of weeks.

mymodel.plot_components(pre_dfT)

General trend and weekly trend of COVID-19 confirmed cases

However, at first, the above figure may intuitively seem wrong - because there shouldn’t be any difference in number of infected people in different days of a week, I believe this presents some facts about the tests results and its procedures.

After some research I found out in Canada due to high volume of requests for coronavirus test, results may take up to 3 or 5 working days. On the other hand, many people book a test in the beginning of the week because they may not be allowed to go out on weekends due to the social distancing regulations in Canada. As a result, we get more number of infected people in Thursdays and Fridays.