[2020年冠狀病毒肺炎 - 武漢加油] 使用Logistic增長模型預測確診病人數目

在前一段時間,我腦子一熱使用指數迴歸以及多項式迴歸對新型冠狀病毒2019-nCov的感染人數進行預測。後來發現不行啊,感染人數不可能一直上漲啊,總得有停止上漲的時候啊!在多名網友的提醒下,在參考了邢翔瑞大佬的博客之後,我痛定思痛,嘗試使用Logistic增長模型對感染人數進行預測,結果如下圖(下圖是直接使用擬合得到的r值,爲0.303,經過簡單的測試,這個模型預測的數量偏小,如果有大佬對此比較瞭解能否指點一二):

2月9日預測結果(看最近幾天的預測結果已經很接近了):

2月8日預測結果:

2月7日預測結果:

 

我們在高中生物中有學過,種羣的增長曲線有分"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)

 

如果看不懂上面的內容,直接複製粘貼代碼運行一下吧~(其實和之前的思路一樣,使用最小二乘法擬合數據,只是擬合的函數換了而已)

from scipy.optimize import curve_fit
import urllib
import json
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
import time


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(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(target_month = 3):
    """
    得到從1月13日到month月最後一天的所有日期列表
    """
    month_day = [0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
    cur_month, cur_day = 1, 13
    ans = []
    while cur_month <= target_month:
        while cur_day <= month_day[cur_month]:
            d = "0" + str(cur_day) if cur_day < 10 else str(cur_day)
            ans += [str(cur_month) + "/" + d]
            cur_day += 1
        cur_day = 1
        cur_month += 1
    return ans


def logistic_function(t, K, P0, r):
    t0 = 0
    exp = np.exp(r * (t - t0))
    return (K * exp * P0) / (K + (exp - 1) * P0)

def predict():
    # 預測未來天數
    predict_days = 20
    
    # 獲取實時數據
    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)
    date, confirm = df['date'].values, df['confirm'].values
    x = np.arange(len(confirm))
    date_labels = get_date_list(4)

    # 用最小二乘法估計擬合
    popt, pcov = curve_fit(logistic_function, x, confirm)
    print(popt)

    #近期情況預測
    predict_x = list(x) + [x[-1] + i for i in range(1, 1 + predict_days)]
    predict_x = np.array(predict_x)
    predict_y = logistic_function(predict_x, popt[0], popt[1], popt[2])

    #繪圖
    plt.figure(figsize=(15, 8))
    plt.plot(x, confirm, 's',label="confimed infected number")
    plt.plot(predict_x, predict_y, 's',label="predicted infected number")
    plt.xticks(predict_x, date_labels[:len(predict_x) + 1], rotation=90)
    plt.yticks(rotation=90)

    plt.suptitle("Logistic Fitting Curve for 2019-nCov infected numbers(Max = {},  r={:.3})".format(int(popt[0]), popt[2]), fontsize=16, fontweight="bold")
    plt.title("Predict time:{}".format(time.strftime("%Y-%m-%d", time.localtime())), fontsize=16)
    plt.xlabel('date', fontsize=14)
    plt.ylabel('infected number', fontsize=14)
    plt.plot()

predict()

 

參考:

python 對於任意數據和曲線進行擬合併求出函數表達式的三種方案

python實現logistic增長模型擬合2019-nCov確診人數2月1日更新

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