從 pandas 開始 Python 與資料科學之旅
仿效 Tidyverse 的學習模式
我們介紹過從 Tidyverse 中的 dplyr 與 ggplot2 套件開始學習 R 語言而非傳統 Base R First 的方式;這樣的學習模式假使套用在 Python 中的話,不從變數類型、資料結構、流程控制、迴圈、自訂函數與類別談起,又該如何開始學習 Python 與資料科學?
我很快就聯想到 pandas 套件,pandas 取名自 pan(el)-da(ta)-s,也與套件主要提供的三個資料結構:Panel
、DataFrame
與 Series
相呼應,她的 GitHub repository 是如此介紹:
Flexible and powerful data analysis / manipulation library for Python, providing labeled data structures similar to R data.frame objects, statistical functions, and much more.
有別於 dplyr、ggplot2 等 Tidyverse 套件各司其職的分工,pandas 自己就能處理載入、整理與視覺化等常見的資料應用。讓我們同樣以 gapminder 作為範例資料集(若您對 gapminder 資料集不熟悉,可以參考這兩篇文章的其中一篇 Tidyverse:R 語言學習之旅的新起點或 R 語言動態視覺化的 Hello World),來對照練習 dplyr 與 ggplot2 的功能。
這篇文章所使用的程式與圖形都可以在這個 Notebook 找到。
成為 DataInPoint 的贊助者
資料載入
pandas 可以支援多種文字、二進位檔案與資料庫的資料載入,常見的 txt、csv、excel 試算表、MySQL 或 PostgreSQL 都難不倒,如果對詳細的清單有興趣,可以參考 pandas 0.21.0 documentation。
我們已經將 R 語言的 gapminder 資料匯出成為常見的 csv 文字檔與 excel 試算表儲存在雲端,打開 Jupyter Notebook 先將 csv 文字檔載入:
import pandas as pd# 讀入 csv 文字檔
csv_file = "https://storage.googleapis.com/learn_pd_like_tidyverse/gapminder.csv"
gapminder = pd.read_csv(csv_file)
print(type(gapminder))
gapminder.head()
再試一下將 excel 試算表載入:
# 讀入 excel 試算表
xlsx_file = "https://storage.googleapis.com/learn_pd_like_tidyverse/gapminder.xlsx"
gapminder = pd.read_excel(xlsx_file)
print(type(gapminder))
gapminder.head()
pandas 有一些好用的屬性與方法可以快速暸解一個 DataFrame 的外觀與內容:
df.shape
:這個 DataFrame 有幾列有幾欄df.columns
:這個 DataFrame 的變數資訊df.index
:這個 DataFrame 的列索引資訊df.info()
:關於 DataFrame 的詳細資訊df.describe()
:關於 DataFrame 各數值變數的描述統計
資料整理
dplyr 的基本功能是六個能與 SQL 查詢語法相互呼應的函數:
filter()
函數:SQL 查詢中的where
描述select()
函數:SQL 查詢中的select
描述mutate()
函數:SQL 查詢中的衍生欄位描述arrange()
函數:SQL 查詢中的order by
描述summarise()
函數:SQL 查詢中的聚合函數描述group_by()
函數:SQL 查詢中的group by
描述
撰寫布林判斷條件將符合條件的觀測值從資料框中篩選出,實踐 filter()
函數的功能,例如選出臺灣:
gapminder[gapminder['country'] == 'Taiwan']
如果有多個條件,可以使用 |
或 &
符號連結,例如選出 2007 年的亞洲國家:
gapminder[gapminder[(gapminder['year'] == 2007) & (gapminder['continent'] == 'Asia')]]
用 list
標註變數名稱可以將變數從資料框中選出,實踐 select()
函數的功能,例如選出 country 與 continent 變數:
gapminder[['country', 'continent']]
如果只選一個變數且沒有以 list 標註,同樣能選出變數,但是型別會變為 Series
:
country = gapminder['country']
print(type(country))
直接撰寫衍生公式並為變數命名即可實踐 mutate()
函數的功能,搭配 apply()
與 lambda 函數將公式應用到每一個觀測值,例如新增一個 country_abb
變數擷取原本 country
變數的前三個英文字母:
gapminder['country_abb'] = gapminder['country'].apply(lambda x: x[:3])
gapminder
呼叫 DataFrame 不同的聚合函數針對欄位計算,實踐 summarise()
函數的功能,例如計算 2007 年全球人口總數:
gapminder[gapminder['year'] == 2007][['pop']].sum()
或者計算 2007 年全球的平均壽命、平均財富:
gapminder[gapminder['year'] == 2007][['lifeExp', 'gdpPercap']].mean()
最後是呼叫 DataFrame 的 groupby
方法實踐 group_by()
函數的功能,例如計算 2007 年各洲人口總數:
gapminder[gapminder['year'] == 2007].groupby(by = 'continent')['pop'].sum()
或者計算 2007 年各洲平均壽命、平均財富:
gapminder[gapminder['year'] == 2007].groupby(by = 'continent')[['lifeExp', 'gdpPercap']].mean()
資料視覺化
Python 視覺化的基石是 Matplotlib 套件的 pyplot,她的繪圖哲學是將圖形的元素,例如座標軸、線、點或者文字用不同的方法一一拼湊起來,優點是繪圖的彈性非常高,缺點則是對於初學者的門檻略高。為了解決這個問題,pandas 套件將 matplotlib.pyplot 的基礎圖形包裝起來成為一個方法,讓使用者只要呼叫 df.plot()
就能夠便利地繪圖,可以選擇的圖形種類相當豐富,只要指定 kind =
參數即可:
- ‘line’ : 線圖(預設)
- ‘bar’ : 垂直長條圖
- ‘barh’ : 水平長條圖
- ‘hist’ : 直方圖
- ‘box’ : 盒鬚圖
- ‘scatter’ : 散佈圖
- ‘hexbin’ : hexbin plot
- …etc.
在作圖之前我們載入 matplotlib.pyplot 與 seaborn,前者是繪圖的基礎套件,後者是讓圖形的樣式美觀:
import matplotlib.pyplot as plt
import seaborn as sns
視覺化時間與數值:線圖
將臺灣資料篩選出來並繪製從 1952 年至 2007 年的人口變化:
gapminder_twn = gapminder[gapminder['country'] == 'Taiwan']
gapminder_twn[['year', 'pop']].plot(kind = 'line', x = 'year', y = 'pop', title = 'Pop vs. Year in Taiwan', legend = False)
plt.show()
或者將中國、日本、南韓與臺灣資料篩選出來並繪製從 1952 年至 2007 年的平均壽命變化:
gapminder_northasia = gapminder.loc[gapminder['country'].isin(['China', 'Japan', 'Korea, Rep.', 'Taiwan'])]
gapminder_northasia_pivot = gapminder_northasia.pivot_table(values = 'lifeExp', columns = 'country', index = 'year')
gapminder_northasia_pivot.plot(title = 'Life Expectancies in North Asia')
plt.show()
視覺化數值的分佈:直方圖、盒鬚圖
將 2007 年資料篩選出來並以三個子圖(subplots)繪製人口數、平均壽命與人均所得的直方圖:
gapminder_2007 = gapminder[gapminder['year'] == 2007]
gapminder_2007[['pop', 'gdpPercap', 'lifeExp']].hist(bins = 15)
plt.show()
或者繪製人均所得的直方圖:
gapminder_2007[['gdpPercap']].plot(kind = 'hist', title = 'GDP Per Capita in 2007', legend = False, bins = 15)
plt.show()
或者將人均所得直方圖依照不同洲別以不同顏色繪製:
gapminder_continent_pivot = gapminder_2007.pivot_table(values = 'gdpPercap', columns = 'continent', index = 'country')
gapminder_continent_pivot.plot(kind = 'hist', alpha=0.5, bins = 20, title = 'GDP Per Capita by Continent')
plt.show()
或者依照不同洲別,將人均所得以盒鬚圖繪製:
gapminder_continent_pivot.plot(kind = 'box', title = 'GDP Per Capita by Continent')
plt.show()
視覺化相關性:散佈圖、hexbin plot
繪製 2007 年各國人均所得與平均壽命的散佈圖:
gapminder_2007.plot(kind = 'scatter', x = 'gdpPercap', y = 'lifeExp', title = 'Wealth vs. Health in 2007')
plt.show()
或改以 hexbin plot 呈現:
gapminder_2007.plot(kind = 'hexbin', x = 'gdpPercap', y = 'lifeExp', title = 'Wealth vs. Health in 2007', gridsize = 20)
plt.show()
視覺化排名:長條圖
繪製 2007 年各洲的人口總數:
summarized_df = gapminder[gapminder['year'] == 2007].groupby(by = 'continent')['pop'].sum()
summarized_df.plot(kind = 'bar', rot = 0)
plt.show()
或者繪製 2007 年各洲平均壽命、平均財富:
summarized_df = gapminder[gapminder['year'] == 2007].groupby(by = 'continent')[['lifeExp', 'gdpPercap']].mean()
summarized_df.plot(kind = 'barh', subplots = True, layout = (1, 2), sharex = False, sharey = True, legend = False)
plt.show()
Series 與 Panel
pandas 除了提供 DataFrame
這個資料結構,尚有 Series
與 Panel
兩種資料結構;只要單選 DataFrame
之中的單一變數且不要以 list 標註就可以獲得 Series
:
country = gapminder['country']
type(country)
Series
還可以再拆分為 index
與 values
兩個部分,其中 values
就是一個 Numpy 的 ndarray
:
print(country.values)
print(type(country.values))
如此一來就順利掌握住這些資料結構的關係;一個 DataFrame
可以解構為多個 Series
,一個 Series
可以再解構為 ndarray
,ndarray
可以再解構取得之中的數字、布林或文字。
而 Panel
則是能儲存多個 DataFrame
資料結構,例如可以將原本的 gapminder 依照年份拆開,一個年份的資料用一個 DataFrame
儲存,然後將 12 個 DataFrame
都儲存到一個 Panel
物件之中:
df_grouped = gapminder.groupby(['year'])
df_dict = {}
for i in range(1952, 2011, 5):
df_dict[i] = df_grouped.get_group(i).reset_index(drop = True)
gapminder_panel = pd.Panel(df_dict)
gapminder_panel
練習:整理 gapminder.org 的資料
接著我們利用 gapminder.org 提供的資料來練習使用 pandas 做資料載入與整理合併,最後輸出更貼近 Hans Rosling(1948–2017) 所展示的資料;首先從網站將人口數、平均壽命與平均財富三個 excel 試算表下載,先觀察一下資料外觀:
接著我們會將三個 excel 試算表載入、轉換為長表格、合併、然後寫出。
載入
gapminder.org 提供的 excel 試算表都有多個工作表,我們只需要名稱為 Data 的工作表,載入成 DataFrame
並存入一個 list
:
def get_data(url_list):
df_list = []
for url in url_list:
df_list.append(pd.read_excel(url, sheetname = 'Data'))
return df_listurl_list = ['https://storage.googleapis.com/learn_pd_like_tidyverse/indicator_gapminder_population.xlsx', 'https://storage.googleapis.com/learn_pd_like_tidyverse/indicator_gapminder_gdp_per_capita_ppp.xlsx', 'https://storage.googleapis.com/learn_pd_like_tidyverse/indicator_life_expectancy_at_birth.xlsx']
wide_df_list = get_data(url_list)
wide_df_list[0].head()
wide_df_list[1].head()
wide_df_list[2].head()
轉換為長表格
接著利用 pd.melt()
方法將 list 中的寬表格轉換為長表格:
def get_long_df(wide_df_list):
long_df_list = []
source_var = ['Total population', 'GDP per capita', 'Life expectancy']
renamed_var = ['pop', 'gdpPercap', 'lifeExp']
for (i, old_var, new_var) in zip(range(3), source_var, renamed_var):
df = pd.melt(wide_df_list[i], id_vars = [old_var])
df.columns = ['country', 'year', new_var]
long_df_list.append(df)
return long_df_listlong_df_list = get_long_df(wide_df_list)
long_df_list[0].head()
long_df_list[1].head()
long_df_list[2].head()
合併並移除遺漏值
最後利用 pd.merge()
方法將 list 中三個長表格合併、用 drop.na()
移除有遺漏的觀測值、依年份與國名排序(sort_values
)最後重設索引值(reset_index
)。
merged_df = pd.merge(long_df_list[0], long_df_list[1], on = ['country', 'year'])
merged_df = pd.merge(merged_df, long_df_list[2], on = ['country', 'year'])
merged_df = merged_df.dropna()
merged_df = merged_df.sort_values(['year', 'country'])
merged_df = merged_df.reset_index(drop = True)
merged_df.head()
寫出
與資料載入相互呼應,pandas 可以支援多種文字、二進位檔案與資料庫的資料寫出,常見的 txt、csv、excel 試算表、MySQL 或 PostgreSQL 都難不倒,如果對詳細的清單有興趣,可以參考 pandas 0.21.0 documentation;我們最後將整理合併完成的資料寫出為 csv 與 excel 試算表,通常寫出時都會將索引值移除。
merged_df.to_csv('gapminder.csv', index = False)
merged_df.to_excel('gapminder.xlsx', index = False)
如果您喜歡這篇文章,請多按下方的「拍手」圖像幾次、分享到社群網站、成為我們的贊助者以及訂閱 DataInPoint 的新文章!
延伸閱讀
除了 pandas 官方文件中的 10 Minutes to pandas,DataCamp 上有非常多 pandas 課程,其中我特別喜歡 Daniel Chen 所講授的 Cleaning Data in Python,課程深入淺出,可以讓初學者對 pandas 有一定程度的理解。