[2020年冠狀病毒肺炎 - 武漢加油] 使用迴歸模型和LSTM預測確診病人數目

【2月7日更新模型】

針對之前的內容,有讀者提出:

1.由於在疫情出現之後,全國各地對確診或疑似患者進行隔離,阻止了部分病毒的傳播,因此相較於指數擬合,Logistic增長模型更適合此次新型冠狀病毒感染人數預測。

2.能夠預測一個月之後的感染人數嗎?(如果使用指數擬合當然是不行啦,我之所以使用指數擬合是因爲在1月底發現當時的確診曲線和指數函數很接近,因此簡單的試了一下)

如果綜合考慮以上兩個問題,且不使用複雜的傳染病模型,結合文章python實現logistic增長模型擬合2019-nCov確診人數,我發現可以嘗試一下使用邏輯斯蒂增長模型(Logistic growth model)做個預測,因此本次更新一下使用邏輯斯蒂函數預測的結果。事先聲明,logistic增長模型也並不能準確地預估未來的確診人數。

另外:我還發現了很多優秀的更復雜的預測模型,一併分享給大家:武漢肺炎傳播的數學模型大全!

在高中生物中有提到過,種羣的增長曲線有分"J"型和"S"型,其中"J"型爲理想型,實際中種羣的數量不可能一直像指數函數那樣,而是一個S型曲線,如下圖所示。

這個函數的曲線和機器學習中的Sigmoid函數f(x)=\frac{1}{1+e^{-x}}極其相似,其公式都有點像,logistic增長函數爲:

P(t) = \frac{KP_0e^{rt}}{K+P_0(e^{rt} - 1)}

其中K爲環境最大容量,P0爲初始容量,r爲增長速率,r越大則增長越快(即更快的逼近上限)。

該模型的微分式是:\frac{dx}{dt}=rx(1-x)

預估結果如下(預計到2月15左右新增開始明顯減少),如果需要詳細的代碼,見[2020年冠狀病毒肺炎 - 武漢加油] 使用Logistic增長模型預測確診病人數目

(2月7日更新 完)

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

(原文)

此次冠狀病毒來勢洶洶,如何通過數學模型預測肺炎的傳播呢?有兩個大的方向:

  1. 利用歷史數據對未來的感染人數做預測:這是一個較爲簡單的預測模型,可以有以下幾種思路:1. 利用歷史數據進行擬合,得到擬合曲線後預測未來幾天可能的感染數,但效果一般都不是太好。2. 使用時間序列模型如ARIMA對未來幾天的感染人數進行預測。3. 使用神經網絡如LSTM訓練預測模型。
  2. 根據冠狀病毒的傳播特性以及統計數據建立傳染病模型:這是一個較爲複雜的問題,不過目前已經有了很多傳染病模型如SIS、SIR等,可以結合此次肺炎疫情傳播特性、現有樣本數估計出一個大概的參數,建立適當的數學模型。這種模型能夠較爲精準的預估疫情的發展趨勢。

在這裏我只簡單的嘗試解決第一個問題:對數據進行擬合併對未來的感染人數進行預測。關於第二個傳染病模型可以參看b站上畢導THU的科普視頻簡單算算,你宅在家裏究竟能爲抗擊肺炎疫情做出多大貢獻?以及一個寫的很棒的帶有可交互的的傳染病模型文章Going-Critical(純英文警告!)。

📕 正文


1 使用多項式迴歸以及指數迴歸對確診人數進行預測

我嘗試分別使用多項式函數和指數函數對歷史數據進行擬合,並對預測結果進行比較:發現指數函數擬合得到的預測結果偏高多項式擬合的結果較爲合理。由於官方平臺公佈的數據也在實時更新,因此預測結果僅作參考!

三個模型給出的1月30日確診感染數量將在:9,900-11,000之間,由於大家都在竭盡全力搶救治療以及對患者進行隔離,實際的確診數量應該會低於理論值,我預測明天公佈的感染數量應該在9,300至10,500之間。

數據來源:騰訊新型冠狀肺炎病情實時跟蹤

數據爬取日期:2020年1月30日 09點

下圖是使用指數函數對歷史數據進行擬合得到的後兩天確診數量預測結果(官方數據公佈會有延後,如1月29的數據會在1月30日才公佈,因此只有1月31號官方纔會公佈1月30的確診數量):

下圖是使用四次函數對歷史數據進行擬合得到的後兩天確診數量預測結果:

下圖是使用三次函數對歷史數據進行擬合得到的後兩天確診數量預測結果:

源碼:

from scipy.optimize import curve_fit
import urllib
import json
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
import pandas as pd
import plotly.express as px
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import PolynomialFeatures
from sklearn import linear_model
import scipy as sp
from scipy.stats import norm

def date_encode(date):
    # '01.24' -> 1 * 100 + 24 = 124
    d = date.split('.')
    month, day = int(d[0]), int(d[1])
    return 100 * month + day

def date_decode(date):
    # 124 -> '01.24'
    return '{}.{}'.format(str(date // 100), str(date % 100))
    
def sequence_analyse(data):
    date_list, confirm_list, dead_list, heal_list, suspect_list = [], [], [], [], []
    data.sort(key = lambda x: date_encode(x['date']))
    for day in data:
        date_list.append(date_encode(day['date']))
        confirm_list.append(int(day['confirm']))
        dead_list.append(int(day['dead']))
        heal_list.append(int(day['heal']))
        suspect_list.append(int(day['suspect']))
    return pd.DataFrame({
        'date': date_list, 
        'confirm': confirm_list, 
        'dead': dead_list,
        'heal': heal_list,
        'suspect': suspect_list
    })

def get_date_list(month):
    month_day = [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
    ans = []
    for i in range(1, month_day[month - 1] + 1):
        if month == 1 and i < 13:
            continue
        ans.append(100 * month + i)
    return np.array(ans)


url = 'https://view.inews.qq.com/g2/getOnsInfo?name=wuwei_ww_cn_day_counts'
response = urllib.request.urlopen(url)
json_data = response.read().decode('utf-8').replace('\n','')
data = json.loads(json_data)
data = json.loads(data['data'])

df = sequence_analyse(data)
x, y = df['date'].values[:-1], df['confirm'].values[:-1]
x_idx = list(np.arange(len(x)))

def func(x, a, b, c):
    return a * np.exp(b * x) + c

def f_3(x, A, B, C, D):  
    return A*x*x*x + B*x*x + C*x + D

def f_4(x, A, B, C, D, E):
    return A*x*x*x*x + B*x*x*x + C*x*x + D*x + E

plt.figure(figsize=(15,8))
plt.scatter(x, y, color='purple', marker='x', label="History data")
plt.plot(x, y, color='gray', label="History curve")
popt, pcov = curve_fit(func, x_idx, y)

test_x =  x_idx + [i + 2 for i in x_idx[-2:]]
label_x = np.array(test_x) + 113
test_y = [func(i, popt[0],popt[1],popt[2]) for i in test_x]
plt.plot(label_x, test_y, 'g--', label="Fitting curve")
plt.title("{:.4}·e^{:.4}+({:.4})".format(popt[0], popt[1], popt[2]), loc="center", pad=-40)
plt.scatter(label_x[-2:], test_y[-2:], marker='x', color="red", linewidth=7, label="Predicted data")
plt.xticks(label_x, [date_decode(i) for i in label_x])
plt.ylim([-500, test_y[-1] + 2000])
plt.legend()

for i in range(len(x)):
    plt.text(x[i], test_y[i] + 200, y[i], ha='center', va='bottom', fontsize=12, color='red')
for a, b in zip(label_x, test_y):
    plt.text(a, b + 800, int(b), ha='center', va='bottom', fontsize=12)

 

2 使用LSTM對未來幾天的確診人數進行預測

我嘗試使用LSTM進行預測,但無奈當前的數據太少,而且數據本身只有一個遞增的趨勢,並不能提供太多的信息。因此預測效果並不理想,因此淺嘗輒止了~

(武漢加油!)

發佈了129 篇原創文章 · 獲贊 118 · 訪問量 22萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章