COVID19 data 數據集分析 (3) 各個國家數據統計

前言

第一篇文章第二篇文章咱們對line list 數據集進行清洗,以及對文本內容進行詞雲分析。bash

本文中咱們將要對主要的數據集covid_19_data.csv進行清洗和分析。 這個數據集包含了全部受影響的國家的確診,死亡,治癒人數的統計信息。 有一些國家,好比中國,美國,意大利等受疫情影響比較大的國家還有各個省/州的詳細信息。函數

一如既往,問題優先。 今天咱們簡單回答兩個問題:佈局

  • 截止到最近的一天,各個國家的狀況如何?咱們能夠關注前30名。
  • 前30名國家的戰疫歷史趨勢如何?

導入數據

首先導入一些包,其中有兩個不常見的函數模塊,後續咱們會涉及到。post

from datetime import datetime
import matplotlib.pyplot as plt
import pandas as pd
from labellines import labelLines
from matplotlib.dates import date2num
複製代碼

讀取數據,輸出主要信息,這點和前幾篇文章的思路一致。咱們的目的就是養成一些成熟的分析數據的「套路」。優化

covid19_data_file = 'data/COVID_19_data.csv'
covid19_data_df = pd.read_csv(covid19_data_file)

# get numeric statistics
print(covid19_data_df.info())
複製代碼

數據集不大,只有1.5M,由於包含的信息很少。只有簡單的confirmed,deaths,recovered信息。咱們看到有一些Province/State 值缺失。網站

RangeIndex: 24451 entries, 0 to 24450
Data columns (total 8 columns):
 # Column Non-Null Count Dtype 
---  ------           --------------  -----  
 0   SNo              24451 non-null  int64  
 1   ObservationDate  24451 non-null  object 
 2   Province/State   11706 non-null  object 
 3   Country/Region   24451 non-null  object 
 4   Last Update      24451 non-null  object 
 5   Confirmed        24451 non-null  float64
 6   Deaths           24451 non-null  float64
 7   Recovered        24451 non-null  float64
dtypes: float64(3), int64(1), object(4)
memory usage: 1.5+ MB
複製代碼

清理省份/州的信息

輸出前10條數據,咱們能夠看到中國的一些省份的具體信息做爲樣本出如今數據集中,這對於咱們分析數據形成混亂。由於咱們要分析的國家的信息,因此須要對數據進行處理。ui

print(covid19_data_df.head(10))
複製代碼
SNo ObservationDate Province/State  ... Confirmed Deaths  Recovered
0    1      01/22/2020          Anhui  ...       1.0    0.0        0.0
1    2      01/22/2020        Beijing  ...      14.0    0.0        0.0
2    3      01/22/2020      Chongqing  ...       6.0    0.0        0.0
3    4      01/22/2020         Fujian  ...       1.0    0.0        0.0
4    5      01/22/2020          Gansu  ...       0.0    0.0        0.0
5    6      01/22/2020      Guangdong  ...      26.0    0.0        0.0
6    7      01/22/2020        Guangxi  ...       2.0    0.0        0.0
7    8      01/22/2020        Guizhou  ...       1.0    0.0        0.0
8    9      01/22/2020         Hainan  ...       4.0    0.0        0.0
9   10      01/22/2020          Hebei  ...       1.0    0.0        0.0
[10 rows x 8 columns]
複製代碼

思路很簡單,咱們須要將各個省份的天天的數據累加做爲中國的當天的數據,對於其餘國家,若是有具體的省份數據而不是國家的總和樹,咱們也是如此操做。datframe 提供的groupby函數 就是專門作aggreate的。 咱們須要對天天同個國家的全部條目進行求和。groupby的結果是一個MultiIndex 的新Dataframe。咱們能夠直接調用reset_index 將其轉換成兩列。 打印新數據,能夠看到Mainland China 的數據爲535。spa

covid19_country_rows_df = covid19_data_df.groupby(
    ['ObservationDate', 'Country/Region']).sum()
print(covid19_country_rows_df.index)
covid19_country_rows_df.reset_index(
    inplace=True)  # split the index to two columns
print(covid19_country_rows_df.head(5))

複製代碼
MultiIndex([('01/22/2020',            'Hong Kong'),
            ('01/22/2020',                'Japan'),
            ('01/22/2020',                'Macau'),
            ('01/22/2020',       'Mainland China'),
            ('01/22/2020',          'South Korea'),
            ('01/22/2020',               'Taiwan'),
            ('01/22/2020',             'Thailand'),
            ('01/22/2020',                   'US'),
            ('01/23/2020',            'Australia'),
            ('01/23/2020',               'Brazil'),
            ...
            ('05/13/2020', 'United Arab Emirates'),
複製代碼
ObservationDate  Country/Region  SNo  Confirmed  Deaths  Recovered
0      01/22/2020       Hong Kong   13        0.0     0.0        0.0
1      01/22/2020           Japan   36        2.0     0.0        0.0
2      01/22/2020           Macau   21        1.0     0.0        0.0
3      01/22/2020  Mainland China  535      547.0    17.0       28.0
4      01/22/2020     South Korea   38        1.0     0.0        0.0
複製代碼

從新佈局數據

現有的數據佈局並非很理想,由於ObservationDate 是一列,這一列中有不少相同的日期,不方便分析。咱們須要想辦法從新佈局,一個好的佈局方式是日期做爲一個維度,國家做爲另外一個維度,交叉處爲咱們要觀測的數據,好比確診數目(Confirmed)。3d

解決思路是採用pandas 的pivot_table 函數。這裏列出關鍵的參數,index 是咱們最終做爲row 的index的數據,columns 是咱們想把源數據中哪一列的做爲新數據的列(不少列)。value是咱們觀測的值。 爲了方便調用,我這裏寫了一個函數來進行數據轉換和分析。咱們轉換3個透視表,而後對於每一個透視表取最後一行,也就是最新的日期。最後咱們將3個透視表的最新日期的數據都統一到一個數據中。code

def analyze_latest_day(data, top_k=30, sort_by='Deaths',plot_type=None):
    cols = ['Confirmed', 'Deaths', 'Recovered']
    latest_day_all_data = pd.DataFrame([])
    for col in cols:
        all_cols_data = pd.pivot_table(
            data,
            index='ObservationDate',
            columns='Country/Region',
            values=col,
            fill_value=0)
        latest_day_col_data = all_cols_data.iloc[-1, :]
        latest_day_all_data[col] = latest_day_col_data
#### 未完
複製代碼

接下來咱們直接plot latest_day_all_data的數據,採用柱狀圖。這裏有幾個tips:

  • 咱們只plot 前top_k 個國家的數據,這樣圖畫更有條理
  • 咱們對'Confirmed', 'Deaths', 'Recovered'分別標識爲特定的顏色,好比confirmed 橘色(警告色),deaths 黑色(莊重色),recovered(綠色)
  • 顏色代碼能夠從網站輕易copy
# 包含在analyze_latest_day函數中
        latest_day_all_data = latest_day_all_data.sort_values(
            by=sort_by, ascending=False)
        ax = latest_day_all_data.iloc[0:top_k].plot(kind='bar', stacked=False,title='Latest day data Statistics',color = ['#fcbf49', '#03071e', '#02c39a'])
        plt.show()
複製代碼

不得不說,這個圖畫還不錯,可是略顯緊湊。每一個國家對應三根柱子,x座標略顯擁擠。

改進柱狀圖

很容易咱們就想到使用stack類型的柱狀圖。可是咱們不能輕易的把confirmed,deaths 和recovered 堆疊再一塊兒,這樣實際意義沒有參考性。因此咱們須要對數據進行簡單處理,用Unrecovered 來取代Confirmed的信息。

# 包含在analyze_latest_day函數中
        latest_day_all_data['Unrecovered'] = latest_day_all_data['Confirmed'] - \
            latest_day_all_data['Deaths'] - latest_day_all_data['Recovered']
        latest_day_all_data = latest_day_all_data.sort_values(
            by=sort_by, ascending=False)
        latest_day_all_data.drop('Confirmed', axis=1, inplace=True)
        ax = latest_day_all_data.iloc[0:top_k].plot(kind='bar',stacked=True,title='Latest day data Statistics',color = ['#03071e', '#02c39a', '#fcbf49'])
        plt.show()
複製代碼

柱子明顯粗了,並且多了一個信息量(unrecovered/未痊癒/治療中)。

這樣其實咱們第一個問題就有了答案,最近的一天的國家數據一目瞭然。畫面中top_k是按照Confirmed 排序的,咱們也能夠按照Recovered 排序。

各個國家時間線分析

接下來咱們來分析一下各個國家確診數據的歷史信息。前文已經提到咱們能夠經過pivot_table從新佈局數據,這裏咱們先整理出top_k國家的數據。

covid19_country_cols_df = pd.pivot_table(
        data,
        index='ObservationDate',
        columns='Country/Region',
        values=col,
        fill_value=0)
    latest_day_col_data = covid19_country_cols_df.iloc[-1, :]
    latest_day_col_data = latest_day_col_data.sort_values(
        ascending=False)
    top_counties = list(latest_day_col_data.index)[0:top_k]
    top_counties.reverse()  # reverse to add label

    top_k_country_df = covid19_country_cols_df[top_counties]
複製代碼

採用dataframe的plot函數直接預覽。

ax =top_k_country_df.plot(kind='line')
        ax.set_xlabel('date')
        ax.set_ylabel('number of cases')
        ax.set_title(f'number of {col} cases')
        plt.show()
複製代碼

畫面看着不錯,顏色絢爛。可是沒法看到哪一個國家對應哪一個顏色。並且美國(綠色)的扶搖直上,一騎絕塵,其餘國家卻擠在畫面的底部。

下面咱們考慮如何進行改善。

  • 國家分類太多,咱們可不可讓標籤靠近每一個曲線
  • 可讓y軸分佈更彈性化
  • x軸前半部分很單調,由於2月初只有中國和幾個亞洲國家在苦苦掙扎,咱們能夠怎麼優化?

inline標籤

咱們想把標籤放在曲線上,這裏咱們提供兩種方法,第一個中較爲推薦。 採用一個有用的包labellines,能夠解決這個問題。調用函數labelLines便可,其中參數xvals 用於設置標籤放置位置的起止位置。對於x軸爲時間軸的數據,須要輸入datetime格式。參考如下代碼能夠少入坑。

對於y軸的壓縮咱們能夠直接用log scale。

fig, ax = plt.subplots()
        # create x axis label
        time_x = pd.Series(
            pd.to_datetime(
                top_k_country_df.index)).dt.to_pydatetime()
        for col in top_counties:
            ax.plot(time_x, top_k_country_df[col], label=str(col))
        ax.set_yscale('log')
        ax.set_xlabel('date')
        ax.set_ylabel('number of cases')
        ax.set_title(f'number of {col} cases')
        labelLines(
            plt.gca().get_lines(), xvals=(
                date2num(
                    datetime(
                        2020, 3, 10)), date2num(
                            datetime(
                                2020, 5, 13))))
        plt.show()
複製代碼

看起來比剛纔的要人性化一點,標籤都列在曲線上,而且採用一樣的顏色,看起來很像trajectory (彈道)。

壓縮X軸

不少國家都是後來才加入到抗病毒的戰爭中,咱們能夠考慮將x軸變成「加入戰鬥」的時間。定義加入戰鬥能夠從確診數爲0開始。咱們這裏定義爲確診從100 開始,由於最開始大多數國家都是零星的輸入型案例。參考一下代碼,咱們對於每一個國家都只保留確診數據大於100的數據。

start_num = 100
        new_start_df = pd.DataFrame([])
        for col in top_counties:
            new_start_col = top_k_country_df[top_k_country_df[col] > start_num][col].reset_index(drop=True)
            new_start_df[col] = new_start_col

複製代碼

對於plot,咱們不作多的修改,僅僅在線條的末端加上一個箭頭,使其更像彈道(rajectory)。

# check each country increase after 100 confirmed
        start_num = 100
        new_start_df = pd.DataFrame([])
        for col in top_counties:
            new_start_col = top_k_country_df[top_k_country_df[col] > start_num][col].reset_index(drop=True)
            new_start_df[col] = new_start_col
        fig, ax = plt.subplots()
        for col in top_counties:
            plt.plot(new_start_df[col], label=str(col),marker = '>',markevery=[-1])
        ax.set_yscale('symlog')
        ax.set_xlabel('days after first 100 cases')
        ax.set_ylabel('number of cases')
        ax.set_title(f'number of {col} cases')
        labelLines(plt.gca().get_lines(), xvals=(5, 35))
        plt.show()
複製代碼

這個圖看起來更不錯了,這樣咱們能夠清晰的看到各個國家的確診曲線。

一樣的,咱們能夠畫出death 和recovered的曲線。

bonus:尾部標籤

其實,咱們可使用matplotlib庫,進行曲線尾部標註(或者任意位置)。思路就是對plot特定的位置添加text。 代碼與效果以下:

start_num = 100
        new_start_df = pd.DataFrame([])
        for c in top_counties:
            new_start_col = top_k_country_df[top_k_country_df[c] > start_num][c].reset_index(drop=True)
            new_start_df[c] = new_start_col
        fig, ax = plt.subplots()
        for c in top_counties:
            ax.plot(new_start_df[c], label=str(c))
        max_value = new_start_df.max()
        max_index = new_start_df.idxmax()
        for index, value, name in zip(
            max_index.values, max_value.values, list(
                max_value.index)):
            ax.text(
                index+0.5,
                value,
                name,
                size=10,
                ha='left',
                va='center')
            # ax.text(
            # index+5.5,
            # value+3,
            # f'{value:,.0f}',
            # size=10,
            # ha='left',
            # va='center')
        ax.set_yscale('symlog')
        ax.set_xlabel('days after first 100 cases')
        ax.set_ylabel('number of cases')
        ax.set_title(f'number of {col} cases')
        plt.show()
複製代碼

總結

本文接着對COVID19的數據集進行了可視化分析。文中主要介紹了一些數據變形的方法,包括groupy和pivot_table 兩大利器的用法。接着咱們細緻的解決了一些可視化圖畫中的細節問題,讓咱們的畫面更加友好。好比:

  • stack類型的柱狀圖,顏色,數據選取
  • lineplot x軸起始位置的選取
  • y軸的縮放
  • inline(線內)標籤
  • 尾端marker 全部的努力就是爲了讓畫面更清晰的反映更多的信息。

固然了,若是須要進行交互式的查看各個國家的數據,Plotly 必然是更好的選擇。

相關文章
相關標籤/搜索