第一篇文章和第二篇文章咱們對line list 數據集進行清洗,以及對文本內容進行詞雲分析。bash
本文中咱們將要對主要的數據集covid_19_data.csv進行清洗和分析。 這個數據集包含了全部受影響的國家的確診,死亡,治癒人數的統計信息。 有一些國家,好比中國,美國,意大利等受疫情影響比較大的國家還有各個省/州的詳細信息。函數
一如既往,問題優先。 今天咱們簡單回答兩個問題:佈局
首先導入一些包,其中有兩個不常見的函數模塊,後續咱們會涉及到。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:
# 包含在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()
複製代碼
畫面看着不錯,顏色絢爛。可是沒法看到哪一個國家對應哪一個顏色。並且美國(綠色)的扶搖直上,一騎絕塵,其餘國家卻擠在畫面的底部。
咱們想把標籤放在曲線上,這裏咱們提供兩種方法,第一個中較爲推薦。 採用一個有用的包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軸變成「加入戰鬥」的時間。定義加入戰鬥能夠從確診數爲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的曲線。
其實,咱們可使用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 兩大利器的用法。接着咱們細緻的解決了一些可視化圖畫中的細節問題,讓咱們的畫面更加友好。好比:
固然了,若是須要進行交互式的查看各個國家的數據,Plotly 必然是更好的選擇。