新冠數據整理和簡單分析


最近看了Kaggle上對COVID_19的一些kernal,於是本人在他們的工作基礎上進行了一些進一步的探討,希望對大家有所幫助。下文我將簡單介紹我在時序分析,複雜網絡分析和小波分析三個方面做的一些實驗和對分析結果的理解。

提前準備

使用的工具和包

import numpy as np
import pandas as pd
import plotly.graph_objects as go
import plotly.offline as py
import datashader as ds
from colorcet import fire
import datashader.transfer_functions as tf
from plotly.subplots import make_subplots
import plotly.express as px
import matplotlib.image as mpimg
import pywt

我使用的工具主要包括Numpy, Pandas等基礎數據處理包,同時也使用了Plotly非常炫酷的可視化API,以便做更好的結果呈現。在網絡分析部分,我使用Gephi進行復雜網絡分析和可視化,Pywt包爲使用Python進行小波分析提供了一些必要的方法。

數據來源和讀取

我使用的數據集是由約翰斯·霍普金斯大學(JHU)提供的從2020年1月23日起全球範圍統計的新冠病毒數據集。大家可以通過github直接clone到本地,以便後續的分析。以下是下載地址:
COVID_19數據集

接下來我們將下載好的數據加載進Jupyter lab中。我首先加載的數據是確證病例數。

confirmed = pd.read_csv('/COVID-19/archived_data/archived_time_series/time_series_19-covid-Confirmed_archived_0325.csv')

接着,我們將中國的確診病例數提取出來。

confirmed_China = confirmed[confirmed['Country/Region'] == 'China']
confirmed_China.head(5)

時序分析

中國各省確診時序分析

接着,我們對中國的確診數序列進行一系列的可視化。

py.init_notebook_mode(connected=True)
fig = go.Figure()
for index, row in confirmed_China.iterrows():
    fig.add_trace(go.Scatter(x = row.index[4:], y = list(row)[4:], name = row[0], line=dict(width=4)))
fig.update_layout(title='China Province Confimred Case Number',
                   xaxis_title='Date',
                   yaxis_title='Cases')
py.iplot(fig)

中國各省累計確診數

fig = go.Figure()
for index, row in confirmed_China.iterrows():
    growing_number = []
    for i in range(len(row[4:]) - 1):
        growing_number.append(row[4 + i + 1] - row[4 + i])
    fig.add_trace(go.Scatter(x = row.index[5:], y = growing_number, name = row[0], line=dict(width=4)))
fig.update_layout(title='China Province Confimred Case Growing Number',
                   xaxis_title='Date',
                   yaxis_title='Cases')
py.iplot(fig)

中國各省每日確診數

fig = go.Figure()
for index, row in confirmed_China.iterrows():
    growing_rate = []
    for i in range(len(row[4:]) - 1):
        if row[4 + i] != 0:
            growing_rate.append((row[4 + i + 1] - row[4 + i]) / row[4 + i])
        else:
            growing_rate.append(0)
    fig.add_trace(go.Scatter(x = row.index[5:], y = growing_rate, name = row[0], line=dict(width=4)))
fig.update_layout(title='China Province Confimred Case Growing Rate',
                   xaxis_title='Date',
                   yaxis_title='Cases')
py.iplot(fig)

中國各省份確診增長率

以上我分別繪製了中國各省份累計確診數,每日確診數,確診增長率的折線圖。在第一張圖中,表示湖北的藍紫色折現明顯與其他折現存在很明顯的規律性區別,湖北的確診病例增幅遠高於其他省份,可見在疫情初期湖北省沒有做到有效的防控導致疫情自由爆發(指數級)。但是,除湖北以外的其他省份,幾乎都以類似分式函數的形式在2月15日達到了峯值,這意味着其他省份的疫情實際上都得到了很有效的控制。
圖二是對圖一的一階差分,表示的是每日新增的確診病例數,在2月2日到2月4日前後各省份的每日確診數都開始下降,即累積確診曲線的增長斜率開始減小,由此可見在進行武漢封城等一系列舉措後,各省沒有進一步的輸入病原體(因爲該曲線大體是單峯的),而是在省內進行可控的病毒傳播。另外在3月末出現的曲線波動,我認爲可以歸咎是境外輸入導致。
圖三是對圖一的二階差分,表示的是每日新增確診病例的增長率。可以看到一月中下旬各省份的每日確診增長率就開始快速下降,這進一步的反映了各省內部的防控工作是成功的。

確診地圖可視化

我使用plotly提供的Mapbox繪製函數來完成確診地圖的可視化。

cvs = ds.Canvas(plot_width=1000, plot_height=1000)
agg = cvs.points(confirmed, x='Long', y='Lat')
coords_lat, coords_lon = agg.coords['Lat'].values, agg.coords['Long'].values
coordinates = [[coords_lon[0], coords_lat[0]],
               [coords_lon[-1], coords_lat[0]],
               [coords_lon[-1], coords_lat[-1]],
               [coords_lon[0], coords_lat[-1]]]
img = tf.shade(agg, cmap=fire)[::-1].to_pil()
fig = px.scatter_mapbox(confirmed, lat="Lat", lon="Long", hover_name="Province/State", hover_data=["Country/Region"],
                        color_discrete_sequence=["fuchsia"], zoom=3, height=400)
fig.update_layout(mapbox_style="carto-darkmatter",
                 mapbox_layers = [
                {
                    "sourcetype": "image",
                    "source": img,
                    "coordinates": coordinates
                }]
)
fig.show()

確診病例地圖

從圖上可以看出,美洲和歐洲的情況比亞洲遭。。。。

世界各國確診時序分析

首先,我們讀入一個新的數據表,它提供了更全面的全球疫情數據。

time_series = pd.read_csv('/COVID-19/csse_covid_19_data/csse_covid_19_time_series/time_series_covid19_confirmed_global.csv')

和上文類似的可視化,這裏就不贅述了,直接上代碼。

confirmed_whole = time_series.groupby('Country/Region').sum()
confirmed_whole = confirmed_whole.drop(columns=['Lat', 'Long'])
fig = go.Figure()
for index, row in confirmed_whole.iterrows():
    fig.add_trace(go.Scatter(x = row.index[4:], y = list(row)[4:], name = row.name, line=dict(width=4)))
fig.update_layout(title='Whole World Confimred Case Number',
                   xaxis_title='Date',
                   yaxis_title='Cases')
py.iplot(fig)

各國累積確診數

infection_the_world = confirmed_whole[confirmed_whole.columns[34:]]
fig = make_subplots(rows=2, cols=1, vertical_spacing=0.3, specs=[[{"type": "scatter"}], [{"type": "scatter"}]])
for index, row in infection_the_world.iterrows():
    slope = []
    accelarate = []
    for i in range(len(row) - 1):
        slope.append(row[i + 1] - row[i])
    for j in range(len(slope) - 1):
        accelarate.append(slope[j + 1] - slope[j])
    fig.add_trace(go.Scatter(x = row.index[1:], y = slope, name = row.name, line=dict(width=4)), row=1, col=1)
    fig.add_trace(go.Scatter(x = row.index[2:], y = accelarate, name = row.name, line=dict(width=4)), row=2, col=1)
fig.update_layout(title_text='World Confimred Case Accelaration', height=600)
py.iplot(fig)

各國每日新增和每日新增增長率

從上面的三張圖我們同樣可以解讀許多有價值的信息,但是筆者比較懶,而且每個人對同一件事物的理解也不一樣,這裏就交給大家自行參悟了。

使用關聯網絡分析國家間病毒傳播

國家間如果存在病毒的交叉傳播,那麼它們的序列(確診數列)就會產生較高的相關性。打個比方,如果A國病例數增加,那麼它向B國輸入的感染者的數量就會相應的增加(在不控制的情況下),這些輸入的感染者也會在隨後的一段時間(一週內),表現在B國的序列上,即導致B國序列的同向變化。這個想法是不是合理其實非常有待商榷,但是不妨作爲一種嘗試。

基於DCCA生成去趨勢互相關矩陣

這個方法的數學原理比較複雜,是一種可以反映非平穩時間序列間的相關性的技術。大家感興趣的化,可以砸在Google上查Detrended Cross Correlation Analysis就會出來許多相關的文獻。這裏不贅述了,就直接上代碼。

# Return sliding windows
def sliding_window(xx,k):
    # Function to generate boxes given dataset(xx) and box size (k)
    import numpy as np

    # generate indexes! O(1) way of doing it :)
    idx = np.arange(k)[None, :]+np.arange(len(xx)-k+1)[:, None]
    return xx[idx],idx

def compute_dpcca_others(cdata,k):
    # Input: cdata(nsamples,nvars), k: time scale for dpcca
    # Output: dcca, dpcca, corr, partialCorr
    #
    # Date(last modification): 02/15/2018
    # Author: Jaime Ide ([email protected])
    
    # Code distributed "as is", in the hope that it will be useful, but WITHOUT ANY WARRANTY;
    # without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 
    # See the GNU General Public License for more details.
    
    import numpy as np
    from numpy.matlib import repmat
    
    # Define
    nsamples,nvars = cdata.shape

    # Cummulative sum after removing mean
    #cdata = signal.detrend(cdata,axis=0) # different from only removing the mean...
    cdata = cdata-cdata.mean(axis=0)
    xx = np.cumsum(cdata,axis=0)
    
    F2_dfa_x = np.zeros(nvars)
    allxdif = []
    # Get alldif and F2_dfa
    for ivar in range(nvars): # do for all vars
        xx_swin , idx = sliding_window(xx[:,ivar],k)
        nwin = xx_swin.shape[0]
        b1, b0 = np.polyfit(np.arange(k),xx_swin.T,deg=1) # linear fit (UPDATE if needed)
        
        #x_hat = [[b1[i]*j+b0[i] for j in range(k)] for i in range(nwin)] # Slower version
        x_hatx = repmat(b1,k,1).T*repmat(range(k),nwin,1) + repmat(b0,k,1).T
    
        # Store differences to the linear fit
        xdif = xx_swin-x_hatx
        allxdif.append(xdif)
        # Eq.4
        F2_dfa_x[ivar] = (xdif**2).mean()
    # Get the DCCA matrix
    dcca = np.zeros([nvars,nvars])
    for i in range(nvars): # do for all vars
        for j in range(nvars): # do for all vars
            # Eq.5 and 6
            F2_dcca = (allxdif[i]*allxdif[j]).mean()
            # Eq.1: DCCA
            dcca[i,j] = F2_dcca / np.sqrt(F2_dfa_x[i] * F2_dfa_x[j])   
    
    # Get DPCCA
    C = np.linalg.inv(dcca)
    
    # (Clear but slow version)
    #dpcca = np.zeros([nvars,nvars])
    #for i in range(nvars):
    #    for j in range(nvars):
    #        dpcca[i,j] = -C[i,j]/np.sqrt(C[i,i]*C[j,j])
    
    # DPCCA (oneliner version)
    mydiag = np.sqrt(np.abs(np.diag(C)))
    dpcca = (-C/repmat(mydiag,nvars,1).T)/repmat(mydiag,nvars,1)+2*np.eye(nvars)
    
    # Include correlation and partial corr just for comparison ;)
    # Compute Corr
    corr = np.corrcoef(cdata.T)
    cov = np.cov(cdata.T)
    # Get parCorr
    C0 = np.linalg.inv(cov)
    mydiag = np.sqrt(np.abs(np.diag(C0)))
    parCorr = (-C0/repmat(mydiag,nvars,1).T)/repmat(mydiag,nvars,1)+2*np.eye(nvars)
    
#     return corr,parCorr,dcca,dpcca
    return dcca, dpcca

使用Gephi過濾生成中國各省的相關網絡

# confirmed_China = confirmed_China.set_index('Province/State')
# confirmed_China = confirmed_China.drop(columns=['Country/Region', 'Lat', 'Long'])
Original_Matrix = pd.DataFrame(columns = confirmed_China.index)
for index, row in confirmed_China.iterrows():
    slope = []
    for i in range(len(row) - 1):
        slope.append(row[i + 1] - row[i])
    for j in range(len(slope) - 1):
        Original_Matrix.loc[j, index] = slope[j + 1] - slope[j]
Original_Matrix = Original_Matrix.values
DCCA, DPCCA = compute_dpcca_others(Original_Matrix, 7)
df_DCCA = pd.DataFrame(data=DCCA, index=confirmed_China.index, columns=confirmed_China.index)
df_DCCA.to_csv('Correlation_DCCA_China.csv')

將CSV文件導入Gephi分析,生成出下面的圖片。

中國各省份相關性網絡

使用Gephi過濾生成全球各國相關網絡

Original_Matrix = pd.DataFrame(columns = confirmed_whole.index)
for index, row in confirmed_whole.iterrows():
    slope = []
    for i in range(len(row) - 1):
        slope.append(row[i + 1] - row[i])
    for j in range(len(slope) - 1):
        Original_Matrix.loc[j, index] = slope[j + 1] - slope[j]
Original_Matrix = Original_Matrix.values
DCCA, DPCCA = compute_dpcca_others(Original_Matrix, 7)
df_DCCA = pd.DataFrame(data=DCCA, index=confirmed_whole.index, columns=confirmed_whole.index)
df_DCCA.to_csv('Correlation_DCCA.csv')

全球各國相關網絡

使用小波分析尋找COVID19傳播的規律

特徵提取

A2 = pd.DataFrame(index=confirmed_whole.index, columns=[i for i in range(22)])
D1 = pd.DataFrame(index=confirmed_whole.index, columns=[i for i in range(38)])
D2 = pd.DataFrame(index=confirmed_whole.index, columns=[i for i in range(22)])
Original_Matrix = pd.DataFrame(columns = confirmed_whole.index)
for index, row in confirmed_whole.iterrows():
    slope = []
    for i in range(len(row) - 1):
        slope.append(row[i + 1] - row[i])
    for j in range(len(slope) - 1):
        Original_Matrix.loc[j, index] = slope[j + 1] - slope[j]
for country in Original_Matrix.columns:
    a2, d2, d1 = pywt.wavedec(list(Original_Matrix[country]), 'db4', mode = 'sym', level = 2)
    A2.loc[country] = a2
    D2.loc[country] = d2
    D1.loc[country] = d1

可視化

fig = make_subplots(rows=3, cols=1, vertical_spacing=0.1, specs=[[{"type": "scatter"}], [{"type": "scatter"}], [{"type": "scatter"}]])
for index, item in A2.iterrows():
    fig.add_trace(go.Scatter(y = item, name = item.name, line=dict(width=4)), row=1, col=1)
for index, item in D2.iterrows():
    fig.add_trace(go.Scatter(y = item, name = item.name, line=dict(width=4)), row=2, col=1)
for index, item in D1.iterrows():
    fig.add_trace(go.Scatter(y = item, name = item.name, line=dict(width=4)), row=3, col=1)   
fig.update_layout(title='Wavelets', height=600)
py.iplot(fig)

各國同頻段的小波基分解

將各國時序分爲兩個高頻段和一個低頻段,在第二個高頻段A2處,許多國家的序列表現出了類似的特徵,對這些頻段的分析和提取可以幫助我們爲後續進行解釋和預測提供非常多的幫助。

說明

本文只供學習使用,後續還會繼續更新關於Wavelet分析,複雜網絡分析的後續,也打算加入SIR傳播模型的分析。如果文中有任何問題,希望大家能夠幫忙指出,感謝。

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