全國新型肺炎累計感染數預測

全國新型肺炎累計感染數預測

摘要

春節無所事事,做了篇關於武漢肺炎的預測模型,預測標的是全國累計感染病例,模型採用2020-01-21到2020-01-29的數據作爲訓練集,利用皮爾生長曲線,結合貝葉斯估計,對當前的全國累計感染肺炎數據做出預測。結論:預測2020-01-30的全國累計感染新型肺炎總數爲10459例;2020-02-11號預測感染人數達到了最大值,即預測當體全國累計感染人數爲42526例;每日新增人數在2月3日達到了最大,即預測那一天新增4379例感染者,後期的新增感染病例逐步降低,直到爲0。

1.1 累計感染者基本模型

病毒感染可看做是病毒這種生物繁衍的一種,描述生物繁衍的模型常用的有皮爾模型和Gompertz模型,這裏假設真實感染者數量是一個皮爾過程,可以用一個生長曲線來描述,原因:

  • 新型肺炎的初始階段,感染人數少,但增長較快,此時由於對該病情的瞭解不深,預防措施較少,因此,一旦暴露在有病毒的環境下,
    受感染的機率就較大,也就是此時的感染率最高。

  • 後期人們逐步認識到病情的嚴重性,各種預防措施使得即使暴露在病毒的環境中,受感染的機率也低於初期,因爲,中期傳播感染率
    較低,但是由於人口基數較大,因此,此時的感染量是最大的。

  • 末期病毒得到控制或感染趨於飽和,此時感染絕對數量達到最大,但幾乎不會再產生新的感染病例,隨着治療出院人口的增加,淨感染人數爲負的狀態。

假設yty_t表示當日的總累計感染人數,該變量服從如下過程:

dydt=γy(1yk)(1.1){dy\over dt}=\gamma·y(1-{y\over k})\tag {1.1}
其中γ\gamma表示增長率kk表示在給定的資源稟賦下,生物羣體能達到的最大量,也稱爲承載力tt表示時間。上述方程式描述生物羣體單位時間增加量的建模過程,即可以理解爲單位時間內的新增病例等於羣體按一定增長率γ\gamma增長,而後又受制於資源的約束,越接近於羣體最大上限,
增加量越小,用yk{y\over k}表示接近羣體潛在最大上限,增長量跟這個指標呈反比,這裏假設線性關係,所以該增量分解了兩個部分,一個部分是自然增長,另一個部分是資源約束下,導致一部分增長無效,即需要扣除的增長部分。
方程(1)給出y的初始值可以求出微分方程的解:
yt=k1+(y0k1)eγt(1.2)y_t =\frac {k}{1+({y_0\over k}-1)e^{-\gamma·t}} \tag{1.2}
其中y0y_0表示yy的初始值。生長曲線大致的樣子如下:
圖1 生長曲線

1.2 擴展模型

由於現實情況比較複雜,受諸多因素的影響,新型肺炎的傳播可以從兩個角度擴散,我稱之爲“寬度”和“深度”,寬度可以理解爲具有傳播能力的個人組成的集體,這個集體平均每日新增密切接觸者人數,深度可以理解爲每百位密切接觸者被感染者比例,肺炎發展的不同時期所採取的措施,通過影響深度或者寬度,進而來影響最終疾病的傳播速度和數量,比如通過戴口罩可以降低被感染機率,通過躲在屋裏不出來,可以降低新增密切接觸者人數,這些舉措都在影響着方程中的參數γ\gammakk,因此,這裏我們把這兩個參數內生化,把他們看做是隨着某些因素在變化的過程,這些因素比如每百人開口罩人數比例,平均每人在公共場所的時間,交通人流等等,由於數據不可得,把γ\gamma看做是時間tt的函數,而承載力k的放鬆方式我們採用另外一種方式,這裏暫且不討論;
希望擴展模型後,模型能體現出如下特點:

  • 隨着時間的增加,新型肺炎的增長率是在下降的,如圖2所示;
    在這裏插入圖片描述
  • 隨着時間的增加,承載力是在下降的,如果是自然狀態下,假設病毒來了,人們沒有應對的行爲,任憑病毒肆意自然傳播,那麼承載力應該是固定不變的,比如14億人口,最終可能會感染到一定比例後,達到最大上限;由於人們的干擾行爲導致承載力也是在下降的,因此,這裏希望模型具有如下特點;如圖所示承載力隨時間下降,而累積被感染人數隨時間遞增,最終二者交匯在一起,感染人數達到上限,病毒傳播也告一段落;
    在這裏插入圖片描述
  • 利用專家或者現有的研究成果修正模型參數。

1.2.1 拓展後的模型形式

爲了實現上述對模型的要求,我們對模型做了如下拓展:
{yt=k(change_point,γ)1+(y0k1)eγt+ϵtγ(t)=11+eβt+α(1.3) \left \{ \begin{array}{c} y_t =\frac {k(change\_point,\gamma)}{1+({y_0\over k}-1)e^{-\gamma·t}} + \epsilon_t \\ \gamma(t)={1\over{1+ e^{\beta·t+\alpha}}} \tag{1.3} \end{array} \right.
公式(1.3)式相對公式(1.1)有三處改變:

  • 增加了隨機干擾項ϵt\epsilon_t,通常假設ϵtiidN(0,σ2)\epsilon_t\sim iidN(0,\sigma^2)
  • γ\gamma與時間tt的關係用一個logistic函數來表示,這是因爲我們希望γ\gamma是隨時間衰減,且希望在不同時間段具有不同的衰減強度,該函數可以實現我的這種想法;
  • kk看做是一個變量,引入一個叫做change_pointchange\_point的變量,將承載力kk看做是增長率γ\gammachange_pointchange\_point的函數。先給出change_point(拐點)的定義,拐點就是增長量出現下降的點,也是上述模型中,一階導數達到最大的點,總之,看到拐點了,就認爲每日新增感染者開始逐步減小了。具體可參考文獻【1】。當已知增長率和拐點,則可以推出承載力kk,已知承載力kk,則可以推出拐點,因此,這裏我們把承載力kk看做是拐點。

到此爲止,仍需要解決的問題有:

  • 怎麼利用現有的武漢肺炎研究的先驗信息問題;
  • 參數估計問題。

1.2.2 模型的進一步拓展——貝葉斯方法

首先,先探討方程(1.3)的參數估計問題,方程含有kk,γ,β,α,τ\gamma,\beta,\alpha,\tau 5個參數,且方程式非線性方程,相對來講這個非線性方程比較簡單,可以在假設β,α\beta,\alpha是給定常數的情況下,把非線性方程轉化爲線性方程,即轉化爲可以用最小二乘法或最大似然法求解其他參數,然後將上述解作爲迭代法的初始值,用迭代法就可以求出所有參數的值,但是,這往往需要有較多的觀測值,實際情況,我們從國家衛健委公佈的數據,長度不過10天左右,那麼有僅有的幾個數據,來估計模型的參數,隨機性太大,得出的參數太不靠靠,另外,採用這種方式,我們就很難將現有的研究成果綜合到模型中去了,因此,這裏拋棄這種解題思路,轉爲利用貝葉斯法。

所謂的貝葉斯法,這裏是把模型中所涉及到的參數都給隨機化,都把他們看做是服從某種分佈的隨機變量,或者某些隨機變量的函數。這裏大致把思路說下,細節不詳講,假設每天累計病患數服從某個分佈,每日政府官網報告的數據,就是這個分佈的一個採樣,該分佈函數記作p()p(每天累計病患數),每天的觀測值,是在一定的參數條件下的觀測結果(雖然參數是多少我們不知道,但他們確實以一定形式存在),即

p()=p()p()p()p(每天累計病患數|參數空間)={p(參數空間|每天累計病患數)·p(每天累計病患數)\over p(參數空間)}
其中p()p(每天累計病患數∣參數空間)p()p()p(參數空間|每天累計病患數)·p(每天累計病患數)有相同的單調性,當用前者構建似然函數時,就可以用後者代替子似然函數中的位置,進而求出參數的後驗分佈。
那麼接下來將解決第一個問題,即我們把上面涉及到的5個參數,都把它們看做是服從某種分佈的隨機變量,具體分佈函數形式怎樣,我們將在下面的章節來分別構建,這就是參數的先驗分佈問題。

1.3 參變量的先驗分佈形式

1.3.1 承載力kk(change_point)的先驗分佈

從數學上知道拐點和承載力可以相互轉換,那我們爲什麼要用拐點而不用承載力呢,這是因爲關於承載力很難找到相關的論斷(除去一些預測模型做的預測),而大家似乎對拐點有比較多的意見,這爲蒐集主觀信息提供了便利的條件。從當前的新聞信息我梳理了如下幾條先驗證信息:

  • 國家對武漢新型肺炎的管控措施強度時間點————武漢嚴格出入嚴格管控:1月21日
  • 新型肺炎的潛伏期時間長度;最長14天,主要集中在前3-7天
  • 專家給出的拐點出現的位置爲正月15號左右。

在1月21號之前,還存在如下問題:

  • 武漢公佈的數據失真,政府還有點遮遮掩掩的態度;
  • 1月21號前後是春運高峯期;
  • 民衆普遍感知較弱,以我爲例,只覺得當時還很遙遠;

由於重視程度不夠,又趕上春運,加上潛伏期兩週,所以,即使後來做了管控,依舊會出現一波病毒爆發增長期,時間最長兩週左右,這兩週內,將最大出現增長率的最大峯值,同時,由於嚴控的實施,政策效應其效果要等到前期潛伏病患告一段落,結合着專家觀點,預計自1月21號其,向後推兩週,也就是2月4號,將很可能達到增長的** 拐點**,我們這裏假定2月3號,2月4號,2月5號將出現增長量的拐點,稱這些天爲重點關注天,把拐點出現在哪一天看做是一個概率分佈,我們給出如下的先驗分佈:
拐點的先驗分佈:假設分段均勻分佈,且根據先驗信息,推斷拐點出現在大概率出現在2月3號-2月5號之間,出現在這區間之前的概率和這區間之後的概率假設相等,具體是多少呢,把給定的時間劃分爲3部分,第一部分就是重點關注天之前的部分,第二部分就是重點關注天,第三部分就是重點關注天之後的部分,每一部分的概率相等,即爲1/3,而每一部分又包含了若干天,每一部分的每一天假設概率也相等,比如重點關注天爲2月3號,2月4號,2月5號,我們假設這三天等概率,而這一部分的總概率佔1/3,則每一天佔1/3*1/3=0.1111。
拐點的分佈是一個分段函數,我將會引入另一個隨機變量來表示拐點,即把拐點看做:
{change_point=f(ρ)ρU(0,100)(1.4) \left \{ \begin{array}{c} change\_point=f(\rho) \\ \rho\sim U(0,100) \tag{1.4} \end{array} \right.
下面是利用Buffon投針原理,用ρ\rho 實現change_pointchange\_point的代碼:

#!/usr/bin/env python
# coding: utf-8
import pymc as pm
import pandas as pd
import numpy as np
import matplotlib as mpl
# mpl.use('agg')
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
import seaborn as sns # box -plot
from pathlib import Path
import pdb
import pickle
p = Path().resolve()
import warnings

warnings.filterwarnings('ignore')
print("this path:",p)
# 初始化,給出all_range和all_labels
range1 = np.arange(0,100,(100-0)/3).tolist()
range1.append(100)
# 這裏可以通過設置不同的劃分區間,來控制轉變點的先驗證分佈
print(range1)
all_range = []
all_labels = []
range_num = {0:12,1:3,2:85}
labels = {0:[1,12],1:[13,15],2:[16,100]}
for i in range(len(range1)-1):
    start,end = [range1[i],range1[i+1]]
    sub_range_num = range_num[i]
    sub_range = np.arange(start,end,(end-start)/sub_range_num).tolist()
    sub_range.append(end)
    all_range.extend(sub_range)
    all_labels.extend(range(labels[i][0],labels[i][1]+1))


# 第一步,定義拐點:change_point
rho = pm.Uniform("rho", lower=0, upper=100)
@pm.deterministic
def change_point(rho=rho):
    u= pd.cut([rho],np.unique(all_range),labels=all_labels)[0]
    return int(u)

1.3.2 增長率γ\gamma的先驗分佈

從公式(1.3)中我們假設γ\gammaα,β\alpha,\beta 的函數,只需構建α,β\alpha,\beta的先驗分佈,就可以得到 γ\gamma的先驗分佈,我們假設 γ\gamma是一個兩參變量的logistic函數,那麼且限定了它的承載力,即最大值爲1,最小值0,也就是說我們的γ\gamma取值在(0,1)之間,首先來看這個取值範圍是否合理,γ\gamma表示增長率,現有的數據表示1個人平均感染2-4個人,這個是較高的估計,而保守估計是在1-2個人之間,然後,考慮到發病率和發病時間,這些人並不是同一天一起發病,而是一段時間逐步發病,我們前面有潛伏期和發病期的時間,潛伏期最大不超過14天,而一般在3-7日出現感染症狀,這樣來看,假設平均感染率爲7天,那麼其實平均到每一天的感染人數不足一個人,因此,從整個感染羣體的增長來看,增長率小於1是合理的,並且我們預測的是新增感染數量,而不是治癒數量,因此,不低於0也合理;
選擇logistic函數還有另一個好處是,可以通過參變量α,β\alpha,\beta 來得到不同單調性,不同增長率的函數,這裏我們假設α,β\alpha,\beta的先驗分佈爲分佈,初始值爲接近於0的常數:

{γ(t)=11+eβt+ααN(0,0.001)βN(0.001,0.001)(1.5) \left \{ \begin{array}{c} \gamma(t)={1\over{1+ e^{\beta·t+\alpha}}} \\ \alpha\sim N(0,0.001) \\ \beta\sim N(0.001,0.001) \tag{1.5} \end{array} \right.

# 第二步,定義gamma
beta = pm.Normal("beta",0.001,0.001,value=0.1)
alpha = pm.Normal("alpha",0,0.001,value=0.0)

# 第三步,定義似然函數的標準差參數先驗分佈
@pm.deterministic
def gamma(t=t,alpha=alpha,beta=beta):
    return 1.0 / (1.0 + np.exp(beta*t*0.1+alpha))

1.3.3 增長率ϵ\epsilon的先驗分佈

ϵ\epsilon是隨機干擾項,一般假設獨立同分布,且0均值,這裏我們把方差作爲一個參數,這個參數的分佈是(0,100)上的均勻分佈;即
{ϵN(0,1τ)τU(0,1)(1.6) \left \{ \begin{array}{c} \epsilon\sim N(0,{1\over{\tau}}) \\ \tau\sim U(0,1) \\ \tag{1.6} \end{array} \right.
以下是代碼實現:

std = pm.Uniform("std",0,100,trace=False)
@pm.deterministic
def prec(U=std):
    return 1.0/U**2

1.3.4 觀測值的先驗分佈(似然函數)

我們假設觀測值是正太分佈,其均值就是我們建模給出的yty_t,其方差就是我們給定的隨機干擾項的方程,即:
obsN(yt,1τ)(1.7) obs\sim N(y_t,{1\over{\tau}}) \tag{1.7}
下面是實現的似然函數代碼:

# 依賴的函數
def infections(gamma, change_point, y0, t):
    """
    求生物羣體大小
    """
    all_y = [y0]
    all_k = [y0 * (1 + np.exp(change_point * gamma[0]*1.0**0))]
    all_delta_y = [y0]
    last_y = y0
    for i in t[1:]:
        # 最大承載力不能低於前一期的生物總數
        new_k = y0 * (1 + np.exp(change_point * gamma[i]*1.0**i))
        k = max(new_k, all_y[i - 1])
        y_i = k / (1 + (k/y0-1)*np.exp(-gamma[i]*i*1.0**i))

        delta_y = max(y_i - all_y[i - 1], 0)
        last_y = last_y + delta_y
        if k < last_y:
            k = last_y
        all_delta_y.append(delta_y)
        all_y.append(last_y)
        all_k.append(k)
    return np.cumsum(all_delta_y), all_k

def gamma_f(t, alpha, beta):
    "用logistic函數來表示增長率的變化過程"
    return 1.0 / (1.0 + np.exp(beta*t+alpha))
t = range(0,8)
date = pd.date_range(start='21/1/2020', periods=100)
result = pd.read_excel("確診病例數據.xlsx")
Y = result["累計"].values[-8:]
y0=Y[0]
def f(gamma=gamma, change_point=change_point, y0=y0,t=t):
    y, _ = infections(gamma=gamma, change_point=change_point, y0=y0,t=t)
    return y
mean = pm.Lambda("mean",f)

# 第四步,給定觀測值模型,似然函數
obs = pm.Normal("obs",mean,prec,value=Y,observed=True)

到此,我們完成了所有參變量的隨機化,並給出了先驗分佈,下面解決第二個問題,估計參數問題。

1.3.5 參數估計

由於分佈太複雜,我們用MCMC方法來求參數的後驗分佈。這裏用2020-01-21到2020-01-28日全國累計被感染新型肺炎的總數作爲觀測數據,數據來源:國家衛健委http://www.nhc.gov.cn/

日期 全國累計感染總數(例)
2020-01-21 440
2020-01-22 571
2020-01-23 830
2020-01-24 1287
2020-01-25 1975
2020-01-26 2744
2020-01-27 4515
2020-01-28 5974
2020-01-29 7711

其中21-28日作爲樣本內擬合參數的數據,29日作爲樣本外檢驗的數據。擬合模型的代碼如下:

# 第五步,MCMC求後驗分佈
model = [obs,mean,prec,gamma,change_point,beta,alpha,rho]
mcmc = pm.MCMC(model)
mcmc.sample(100000,80000)
print("done!")

2.1 模型結果分析

2.1.1 拐點的後驗分佈

結論: 通過上述的模型擬合,我們來看下change_point的後驗分佈,在構造拐點的先驗分佈時,我們假定它是服從均勻分佈的,只是這個均勻分佈是個分段函數。下面給出了先驗分佈和後驗分佈的對比圖,從圖中可以看出(上面是先驗分佈,下面是後驗分佈),經過實際數據的“修正”,有些日期是拐點的概率爲0,而有日期(我們假設的重點關注天)是拐點的概率變大了,說明實際數據更加支持了我們的主觀猜測。
從數據來看,2月3日 出現拐點的概率最大,概率值爲25.86%2月2日-2月5日出現拐點的總概率爲80%,說明拐點大概率在這個時間區間出現,而我們在前期,根據推測大致給出的時間是2月3日到2月5日,時間區間大致吻合。
在這裏插入圖片描述
下面給出是拐點概率前10個最大值:

日期 是拐點的概率 (%)
2月3日 25.86
2月2日 22.76
2月4日 17.56
2月5日 12.87
2月6日 0.445
3月16日 0.40
2月8日 0.38
4月28日 0.37
2月26日 0.35
2月19日 0.34
2月2日 22.76
2月2日 22.76
# 拐點的先驗分佈:利用蒙特卡洛法,從自己定義的分段概率分佈中抽樣10000次,統計各個值出現的頻率
# 理論上出現13,14,15的概率爲1/9=0.11111
all_test_result = []
# 蒙特卡洛模擬,生成1萬次隨機
for i in range(10000): 
    rho.random()
    all_test_result.append(change_point.value)
test = pd.Series(all_test_result)
prior_distribution = test.value_counts()/len(test)
prior_distribution.index = [(date[0]+pd.Timedelta(days=i)).strftime("%Y-%m-%d") for i in prior_distribution.index]
prior_distribution.sort_index(inplace=True)
plt.subplot(211)
prior_distribution.plot(kind='bar',rot='90',figsize=(24,8),title='prior of $change point$',grid=False,fontsize=14,label="Probability")
plt.legend(loc='best')

# 拐點的後驗分佈
change_point_samples = mcmc.trace("change_point")[:]
posterior = pd.Series(change_point_samples)
frequence = posterior.value_counts()/len(posterior)
frequence.index = [date[0]+pd.Timedelta(days=i) for i in frequence.index]
frequence = frequence.reindex(date, fill_value=0)
frequence.index = [str(i) for i in frequence.index.date]
frequence.sort_index(inplace=True)
plt.subplot(212)
frequence.plot(kind='bar',rot='90',figsize=(24,8),title='posterior of $change point$',grid=False,fontsize=14,label="Probability")
plt.legend(loc='best')
plt.subplots_adjust(hspace=0.8)
plt.show()
print("拐點出現位置的概率分佈top10:\n{}".format(frequence.sort_values(ascending=False).head(10)))

2.2.2 增長率γ\gamma的後驗分佈

增長率γ\gammaα\alphaβ\beta的非線性函數,我們假設α\alphaβ\beta是正態的,具體γ\gamma的分佈是什麼分佈不清楚,我們只能抽樣,不清楚他的分佈函數的具體形態,由於增長率γ\gamma依賴於兩個參變量,先看這兩個參變量的分佈.。
γ\gamma的後驗分佈:
在這裏插入圖片描述
α\alpha的後驗分佈:
在這裏插入圖片描述
β\beta的後驗分佈:

在這裏插入圖片描述

2.2.3 ρ\rho的後驗分佈

在先驗分佈中,我們假設ρ\rho是[0,100]上的均勻分佈,我們來看下它的後驗分佈變成什麼樣子。
在這裏插入圖片描述

2.3 樣本外預測——未來的累計感染肺炎數量預測

有了上述模型的參數估計,下面來預測全國肺炎感染累計總數量,一次完整的時間序列預測,需要知道ρ\rho的一個標量值、β\beta的一個標量值、α\alpha的一個標量值,和t的一個時間序列值,上述採樣,我們已經實現了這些參數的採樣序列,那麼就可以根據這些參數來做預測了。

下面定義預測模型,由於參數是採樣得到,因此,要遍歷這些採樣得到的參數,每一組參數都會產生一組預測值。先把預測值保存下來,預測模型定義如下:

def forecast(t, change_point_samples, alpha_samples, beta_samples, y0=440):
    all_forecast = []
    all_max_value = []
    for i in range(len(change_point_samples)):
        gamma = gamma_f(t, alpha_samples[i], beta_samples[i])
        change_point = change_point_samples[i]
        forecast,max_value = infections(gamma=gamma, change_point=change_point, y0=y0, t=t)
        all_max_value.append(max_value)
        all_forecast.append(forecast)
    return all_forecast,all_max_value
forecast_date_range = range(0,100)

下面將調用模型,預測預測2020年1月21號-2020年4月29號的數據,其中1月21號-1月28號是樣本內數據,參與了模型參數的擬合,all_forecast表示所有參數的預測值集合,all_forecast_max_value承載力預測集合:

all_forecast,all_forecast_max_value = forecast(t=forecast_date_range,change_point_samples=change_point_samples,alpha_samples=alpha_samples,beta_samples=beta_samples,y0=440)

2.2.1 預測結果分析

2.2.1.1 每天全國累計感染者的預測值

由於我們進行了20000次採樣,也就是說我們有2萬組模型參數,只是有些參數出現的概率高有些參數出現的概率低,首先來看下預測矩陣的大小,是一個20000*100的矩陣,因爲我們從1/21日開始(包含)向外預測,共預測100步,所以時間序列長度爲100,而我們的採樣爲2萬次,所以有2萬個這種時間序列,如下圖:
在這裏插入圖片描述
下面將進行每個日期預測值分佈進行統計,由於皮爾模型對超參敏感,如果按照估計出來的超參的分佈,進行採樣,然後再統計預測值的分佈是有很大問題的,舉例說明一下,如果參數值取兩個值比如0和1,該參數的後驗分佈是假設取到0的概率爲80%,取到1的概率爲20%,假設取到0時,預測值爲2,而取到1時,預測值爲2000000,如果真實參數值爲0,即此時的預測值0,那麼顯然我們不能用採樣出來的參數對應的預測值做均值來作爲模型最終預測值,因爲我們求均值時,裏面混入了2000000這個很大的值,會把我們的預測值偏向於這個小概率參數的預測。
所以,我們要先對參數做一遍過濾,過濾的方法就是樣本內的擬合度和樣本外的預測精度,當參數所對應的的模型對樣本內和樣本外都有一定的預測精度,我們才把這個參數作爲備選,這裏我給的樣本內時間點爲1月26日,1月27日,1月28日,樣本外的測試集我給的是1月29日,根據對這幾天的預測綜合誤差,選出有效參數,再統計這些有效參數對應的預測值。這裏我選擇模型的原則是綜合誤差用mape定義,要求綜合誤差在2.5%以內;

date = pd.date_range(start='21/1/2020', periods=100)
result = pd.read_excel("確診病例數據.xlsx")
obs_value = result["累計"].values[-9:]
# 測試集的誤差權重較大,賦值爲70%的權重,樣本內的誤差佔比爲30%
pd_forecast["mape"] = (0.7*abs(pd_forecast[[date[8]]]/obs_value[-1]-1).values + 0.1*abs(pd_forecast[[date[7]]]/obs_value[-2]-1).values + 0.1*abs(pd_forecast[[date[6]]]/obs_value[-3]-1).values+ 0.1*abs(pd_forecast[[date[5]]]/obs_value[-4]-1).values)
# pd_forecast.head(10)
sub_forecast = pd_forecast.loc[pd_forecast["mape"]<0.025]
# 通過選擇
print("選擇結果:",sub_forecast.shape)
sub_forecast = sub_forecast.T
sub_forecast = sub_forecast.drop(['mape'], axis=0)
sub_forecast.head(5)

爲了給出每日累計感染者預測總數量,這裏用所有參數預測的中位數作爲預測值,命名爲total number,每日增長量命名爲growth,從預測數據來看2020-02-11號預測感染人數達到了最大值,即全國累計感染人數爲42526例,後續就不在增加了。而每日新增人數在2月3日達到了最大,即預測那一天新增4379例感染者。下圖全國累計的感染數預測,和增量預測。
在這裏插入圖片描述
每日的預測值如下:
在這裏插入圖片描述

2.2.1.2 每天全國累計感染者的預測值預測區間

這裏沒有給出置信區間,因爲每一天的預測中,均含有異常值,有時異常值比例很高,會導致置信區間變形,需要去除異常值,去除異常值後,在給出置信區間,由於時間有限,這個工作就不做了,直接給出了剔除異常值後的所有預測值構成的預測區間。

在這裏插入圖片描述

# 求每天的預測分佈
series_forecast = pd.DataFrame(sub_forecast.values.flatten(),index=np.array([[i.strftime("%Y-%m-%d")]*sub_forecast.shape[1] for i in sub_forecast.index]).flatten()).reset_index()
series_forecast.rename(columns={0:"forecast","index":"date"},inplace=True)
series_forecast
plt.close("all")
plt.figure(2211)
forecast_set = series_forecast.iloc[0:22*sub_forecast.shape[1],:].boxplot(column="forecast", by="date",figsize=(12,8),showfliers = False,showmeans=False, patch_artist=True,meanline=True,return_type='dict')
plt.xticks(rotation=45,fontsize=15)
forecast_medians = [i.get_ydata()[0]for i in forecast_set["forecast"]["medians"]]  # 與自己求的mid值是一樣的
# plt.plot(forecast_medians[0:21],'-or')
plt.show()

3.1 模型總結

1.模型完成於2020年1月28日,編輯工作完成與2020年1月29日,所以排版比較醜陋,模型假設受感染人數符合皮爾生長曲線特徵,因此用皮爾曲線進行預測,在建模時,將增長率和承載力看做隨時間變化的過程,這是考慮到隨着實際情況,隨着人們的干預,受感染過程並非一個自然增長,而是受控增長,這也是很多不瞭解國內情況的一批專家所忽略的,這也是我爲什麼要做這個預測的原因。

2.因爲國內正值春節,春節的客流量預計未來幾天會逐步增加,這個對模型的潛在影響並沒有體現出來,可能會導致模型預測不準的以一個原因。

3.模型考慮了專家的意見,在針對拐點先驗分佈的假設上,綜合的專家的意見,以及大致的病毒傳播時間,給出了一個不是很嚴格的推理,並沒有做嚴格的論證,還是有帶改進的地方。

4.最後想要說的是,要堅定信心,不要做一個無知無畏的人,也不要如驚弓之鳥,具體怎麼做呢,。。。,活一天賺一天吧。

模擬擬合的全部代碼

後期其他代碼將上傳到githup

# 初始化,給出all_range和all_labels
range1 = np.arange(0,100,(100-0)/3).tolist()
range1.append(100)
# 這裏可以通過設置不同的劃分區間,來控制轉變點的先驗證分佈
print(range1)
all_range = []
all_labels = []
range_num = {0:12,1:3,2:85}
labels = {0:[1,12],1:[13,15],2:[16,100]}
for i in range(len(range1)-1):
    start,end = [range1[i],range1[i+1]]
    sub_range_num = range_num[i]
    sub_range = np.arange(start,end,(end-start)/sub_range_num).tolist()
    sub_range.append(end)
    all_range.extend(sub_range)
    all_labels.extend(range(labels[i][0],labels[i][1]+1))


# 第一步,定義拐點:change_point
rho = pm.Uniform("rho", lower=0, upper=100)
@pm.deterministic
def change_point(rho=rho):
    u= pd.cut([rho],np.unique(all_range),labels=all_labels)[0]
    return int(u)

# 第二步,定義gamma
beta = pm.Normal("beta",0.001,0.001,value=0.1)
alpha = pm.Normal("alpha",0,0.001,value=0.0)

# 第三步,定義似然函數的標準差參數先驗分佈
@pm.deterministic
def gamma(t=t,alpha=alpha,beta=beta):
    return 1.0 / (1.0 + np.exp(beta*t*0.1+alpha))

std = pm.Uniform("std",0,100,trace=False)
@pm.deterministic
def prec(U=std):
    return 1.0/U**2

# 第四步,定義似然函數的均值先驗分佈

t = range(0,8)
date = pd.date_range(start='21/1/2020', periods=100)
result = pd.read_excel("確診病例數據.xlsx")
Y = result["累計"].values[-8:]
y0=Y[0]
def f(gamma=gamma, change_point=change_point, y0=y0,t=t):
    y, _ = infections(gamma=gamma, change_point=change_point, y0=y0,t=t)
    return y
mean = pm.Lambda("mean",f)

# 第四步,給定觀測值模型,似然函數
obs = pm.Normal("obs",mean,prec,value=Y,observed=True)
model = [obs,mean,prec,gamma,change_point,beta,alpha,rho]

# 第五步,MCMC求後驗分佈
mcmc = pm.MCMC(model)
mcmc.sample(100000,80000)
print("done!")

參考文獻:
[1]: Cameron Davidson-Pilon,貝葉斯方法 概率編程與貝葉斯推斷[M],…
[2]: 魏宗舒等,概率論與數理統計教程[M],…
[3]: 皮爾曲線模型的推廣及其應用,https://wenku.baidu.com/view/8b3362afd1f34693daef3ee6.html
[4]: Logistic曲線與Gompertz曲線的比較研究,https://www.docin.com/p-944041461.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章