{ "cells": [ { "cell_type": "markdown", "metadata": { "id": "FViVRqT3LI7U" }, "source": [ "## $\\Large{Pandas\\; Tutorial\\; (part2)}$\n", "\n", "已經習慣了Pandas的操作了嗎? 在這個部分,我們會介紹一些更實用的功能,例如如何進行一些基本的描述性統計、製作樞紐分析表、以及畫統計圖表。在講解完後你會發現光使用Pandas套件就可以完成幾乎所有在資料探索上會使用到的功能,可以感受到Pandas的厲害之處了嗎? 就讓我們繼續看下去吧。\n", "\n", "## [Panda官方手冊](https://pandas.pydata.org/pandas-docs/stable/#0)" ] }, { "cell_type": "markdown", "metadata": { "id": "M8gyDp0ILI7m" }, "source": [ "### 本章節內容大綱\n", "* [在DataFrame中進行數值統計](#在DataFrame中進行數值統計)\n", " - [加總、計數、以及其他統計量](#加總、計數、以及其他統計量)\n", " - [遺漏值](#遺漏值)\n", "* [DataFrame進階用法](#DataFrame進階用法)\n", " - [apply功能的使用](#apply功能的使用)\n", " - [群組(groupby)的使用](#群組的使用)\n", " - [pivot_table的使用](#pivot_table的使用)\n", "* [使用DataFrame繪製統計圖](#使用DataFrame繪製統計圖)" ] }, { "cell_type": "markdown", "metadata": { "id": "MlGfXjSyLI7q" }, "source": [ "## 載入套件" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "Zl6_16BsLI7u" }, "outputs": [], "source": [ "import pandas as pd\n", "import numpy as np\n", "\n", "%matplotlib inline" ] }, { "cell_type": "markdown", "metadata": { "id": "q-7HXbdzLI70" }, "source": [ "## 載入第二部分使用到的資料集" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "KuAFooz-LI72" }, "outputs": [], "source": [ "# 載入範例的資料集,這次我們使用的是鐵達尼號的資料,後續在機器學習領域上會很常見到這份資料集\n", "df = pd.read_csv('https://github.com/TA-aiacademy/course_3.0/releases/download/Python/titanic_train.csv')\n", "df.head()" ] }, { "cell_type": "markdown", "metadata": { "id": "GMtzVNneLI74" }, "source": [ "\n", "## 在DataFrame中進行數值統計\n", "\n", "在資料分析的過程中,我們會希望將整份資料或特定欄位做一個整體趨勢的說明,而不是直接把所有資料湊到別人的鼻子下請他們自己看,如何用簡單的方式描述資料我們得利用到統計學上的一些統計量。別擔心,很多其實我們應該已經耳熟能詳了,更進階的統計概念我們也會在之後的統計學單元傳授給大家。現在就讓我們先來看一下如何對dataframe這類的資料做數值統計吧。" ] }, { "cell_type": "markdown", "metadata": { "id": "LDW1aVm9LI78" }, "source": [ "\n", "- ### 加總、計數、以及其他統計量\n", "\n", "如同numpy中的np.sum(), np.mean(),在Pandas中我們也使用類似的方法做計算。" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "6up21MAaLI7-" }, "outputs": [], "source": [ "# 先觀察一下前五筆資料\n", "df.head()" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "8_qlklhzLI8B" }, "outputs": [], "source": [ "# 計算Survived的平均次數\n", "df['Survived'].mean()" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "7f_di_oOLI8D" }, "outputs": [], "source": [ "# 計算Fare的最大值\n", "df['Fare'].max()" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "yaZawqWWLI8E" }, "outputs": [], "source": [ "# 若欄位是字串之類的類別變項,我們可以使用.value_counts()觀察各個類別的出現次數\n", "df['Sex'].value_counts()" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "5IznOITILI8G" }, "outputs": [], "source": [ "# 若不想一個一個欄位觀察,直接使用df.mean()會呈現所有pandas覺得可以做平均數的欄位平均\n", "# 需要注意的是有些以數值做編碼的類別變項(ex. Pclass)也會被當作連續變項來看待因而納入\n", "df.mean()" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "qKGDQQeBLI8H" }, "outputs": [], "source": [ "# 更簡單的做法是使用df.describe(),pandas會自動將特定類型的欄位做一個摘要\n", "# 在連續型欄位上會呈現較常用的一些統計量\n", "df.describe(include='number')" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "y5eQ5XBfLI8I" }, "outputs": [], "source": [ "# 在類別型欄位會呈現計數、有幾個類別、以及出現最多次類別的頻率等資訊\n", "df.describe(include='object')" ] }, { "cell_type": "markdown", "metadata": { "id": "_rstvMWcLI8J" }, "source": [ "\n", "- ### 遺漏值\n", "\n", "除了上面提到的數值、類別、時間之外,後續大家還會遇到的特殊資料型態是**遺漏資料**,意思是我們並不知道某些資料在某些欄位的數值,會出現這種情況的原因也有非常多種(比較常見的是在資料收集上出現問題、或是在概念上本來某些資料就不會有這個欄位的數值等等)。在Numpy與Pandas兩個套件中,遺漏資料常以**NaN**表示,意思是Not a Number,接下來讓我們示範一下如何找到資料中的遺漏值以及做一些處理。" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "eDuftTMPLI8M" }, "outputs": [], "source": [ "# 使用df.info()觀察資料整體狀況\n", "df.info()" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "5S3JeEHco7Cd" }, "outputs": [], "source": [ "df['Age'].isna()" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "ynRjMuhhLI8N" }, "outputs": [], "source": [ "# 使用isna()方法判斷Age是否為遺漏值,且挑出Age為遺漏值的前5筆資料\n", "df[df['Age'].isna()].head()" ] }, { "cell_type": "markdown", "metadata": { "id": "uI2Bah1XLI8O" }, "source": [ "從資訊中我們發現有部分欄位的資料筆數並非891筆,這代表資料中有一些遺漏的資料,例如Age欄位只有714筆資料,而Cabin欄位甚至只有204筆資料,另外觀察前五筆資料也可以明顯看到Cabin中有些欄位顯示NaN,這就代表在這筆資料的這個欄位的數值為遺漏值。\n", "\n", "了解到資料中有遺漏後又該怎麼處理呢?最常使用的方式是直接刪除遺漏資料或是補值,以下我們來示範應該如何做這些處理。" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "HD-WTVkPLI8R" }, "outputs": [], "source": [ "# 只要任何一個欄位有遺漏資料,使用dropna就會將其剔除\n", "delete_na_df = df.dropna()\n", "\n", "# 使用shape觀察資料筆數可以發現完全無遺漏值的資料只剩下183筆\n", "print(delete_na_df.shape)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "iF4ls7xELI8T" }, "outputs": [], "source": [ "# 先複製一份以免改動到原本資料\n", "fill_na_df = df.copy()\n", "\n", "# 事先計算年齡的平均\n", "mean_age = df['Age'].mean()\n", "\n", "# 使用fillna方法將遺漏值取代為特定的數值\n", "fill_na_df['Age'].fillna(value=mean_age, inplace=True)\n", "\n", "# 再以info看一次資料的狀況,Age欄位現在是不是沒有遺漏值了?\n", "fill_na_df.info()" ] }, { "cell_type": "markdown", "metadata": { "id": "HzP1z_YtLI8U" }, "source": [ "---\n", "\n", "## DataFrame進階用法\n", "\n", "在第一部分時我們介紹了如何在dataframe中挑選、排序、增加/刪除欄位、以及合併兩個或多個資料表,然而Pandas的功能還萬萬不只這樣。這個部份我們將會額外介紹三個好用的方法,分別為apply, groupby, 以及pivot_table。" ] }, { "cell_type": "markdown", "metadata": { "id": "FFXI8Ax4LI8V" }, "source": [ "\n", "- ### apply功能的使用\n", "\n", "相對而言apply的使用方式並沒有那麼的直觀,但你可以想像它是另一種形式的迴圈,它可以依照每個小分組(通常是一欄或一列,但我們也可以搭配著groupby使用)重複地執行同一個函數,例如在以下的例子當中,我們使用apply去計算Age與Fare的平均數是多少,然而apply更強大的功能是可以放入自己定義的函數做運算。" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "12bmAWwLLI8W" }, "outputs": [], "source": [ "# 挑選出Age與Fare欄位並且用apply計算每個欄位的平均\n", "# 還記得axis的用法嗎? axis=0代表我們將第一個軸(row)的數值視為同一群,第二個軸(column)的數值則分開計算\n", "df[['Age', 'Fare']].apply(np.mean, axis=0)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "yly5VuBcLI8X" }, "outputs": [], "source": [ "# 挑選出Age與Fare欄位並且以apply計算每筆資料的平均\n", "# 在此我們設定axis=1去計算每筆資料在這兩個欄位的平均,雖然這個數值在解釋上並無任何意義\n", "df[['Age', 'Fare']].apply(np.mean, axis=1).head()" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "4yABDJYNLI8Y" }, "outputs": [], "source": [ "# 我們也可以自行定義一個函數並且使用apply去做計算\n", "# 在此我們使用年齡與性別去決定每個乘客的身分\n", "\n", "def get_identity(df):\n", " if df['Sex'] == 'male':\n", " if df['Age'] < 18:\n", " return 'Child'\n", " else:\n", " return 'Man'\n", " elif df['Sex'] == 'female':\n", " if df['Age'] < 18:\n", " return 'Child'\n", " else:\n", " return 'Woman'\n", "\n", "\n", "# 依據每個row執行get_identity這個函數,並將結果儲存至新的identity欄位\n", "df['identity'] = df.apply(get_identity, axis=1)\n", "\n", "# 觀察前20筆資料的結果\n", "df.head(20)" ] }, { "cell_type": "markdown", "metadata": { "id": "yLq-gnzeLI8Z" }, "source": [ "\n", "- ### 群組的使用\n", "\n", "在比較進階的資料處理上,我們會希望針對每個不同的類別分別了解他們的特性,例如希望依據縣市計算出各區域的人民平均月薪,這個時候groupby就可以派上用場。在此我們同樣以鐵達尼號的乘客資料進行示範。" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "CcP3CcclLI8Z" }, "outputs": [], "source": [ "# 依照上船港口(Embarked)欄位做群組,挑選出票價並且計算平均\n", "df.groupby(by='Embarked')['Fare'].mean()" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "pAVl5Xt1LI8c" }, "outputs": [], "source": [ "# 依照上船港口(Embarked)與性別(Sex)計算各欄位的資料數量(如果欄位內沒有遺漏值的話可以將這個數量作為人次來看)\n", "df.groupby(by=['Embarked', 'Sex']).count()" ] }, { "cell_type": "markdown", "metadata": { "id": "5y9oLDVHLI8d" }, "source": [ "除了直接使用groupby搭配常見的統計值之外,我們同樣也可以在groupby後加上apply做運算,此時apply會將指定欄位中類別相同的資料視為同一組做一次運算。" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "bNf3rKDzLI8d" }, "outputs": [], "source": [ "# 自定義計算資料中遺漏值總數的函數\n", "\n", "def total_na_number(df):\n", " return df.isna().sum()\n", "\n", "\n", "# 依據艙等(Pclass)欄位將資料分組,並分別計算各欄位的遺漏數總數\n", "df.groupby(by='Pclass').apply(total_na_number)" ] }, { "cell_type": "markdown", "metadata": { "id": "pVquASA9LI8e" }, "source": [ "\n", "- ### pivot_table的使用\n", "\n", "在這邊要講的最後一個函數叫做pivot_table,在功能上對應的是excel中的樞紐分析表,因此它可以彈性地以你想要的方式合併不同組別的資料並且整理成新的表格樣式。" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "ieev70LqLI8f" }, "outputs": [], "source": [ "# 以pivot_table方法將資料做總別,設定性別(Sex)的各個類別為列、艙等(Pclass)的各個類別為欄、並且計算每個組別下票價(Fare)的平均。\n", "df.pivot_table(values='Fare', index='Sex', columns='Pclass', aggfunc='mean')" ] }, { "cell_type": "markdown", "metadata": { "id": "mIT4SzxBLI8f" }, "source": [ "---\n", "\n", "## 使用DataFrame繪製統計圖\n", "\n", "雖然用統計量去描述資料是一件非常方便的事情,但由於數字非常抽象,有時卻又會很難了解資料比較詳細的狀況。還有沒有甚麼辦法可以讓我們瞭解資料呢? 這時候資料的視覺化就派上用場了,在後面一個單元我們將會教大家如何使用另外一個matplotlib套件對圖形做更細部的調整,然而在此我們先來看看Pandas套件內所提供的繪圖函數吧。" ] }, { "cell_type": "markdown", "metadata": { "id": "xQZKSo_ALI8g" }, "source": [ "- ### 連續變項的統計圖\n", "\n", "在觀察單一連續變項的資料上,我們常使用直方圖、機率密度函數圖、以及盒型圖做繪製,以下我們分別呈現如何使用pandas繪製這三種統計圖。" ] }, { "cell_type": "markdown", "metadata": { "id": "QfordqewLI8h" }, "source": [ "#### 直方圖\n", "\n", "直方圖會把資料依據數值大小分成幾個組別,再將各組的資料筆數繪製成圖,這也是大家日常生活中最常見的統計圖之一。" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "SEgjJQVILI8h" }, "outputs": [], "source": [ "# 以直方圖繪製資料中年齡的分布\n", "df['Age'].hist()" ] }, { "cell_type": "markdown", "metadata": { "id": "iIJWLDxoLI8i" }, "source": [ "#### 機率密度函數圖\n", "\n", "有別於將資料做離散化的分組,機率密度函數圖假設資料在各個數值上的出現機率是連續的,並且嘗試透過手上的資料估計最可能的出現機率密度為何,因此在縱軸上將會從出現的頻率改為機率密度的數值。然而在使用情境上大致與直方圖相同,都是描述資料的分布狀況。" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "DZVSeD2YLI8j" }, "outputs": [], "source": [ "# 以機率密度函數圖繪製資料中年齡的分布\n", "df['Age'].plot.kde()" ] }, { "cell_type": "markdown", "metadata": { "id": "ATIGN0vqLI8j" }, "source": [ "#### 盒型圖\n", "\n", "又被稱為箱型圖,圖中的幾個端點都具有統計意義,因此除了資料分佈之外也可以快速得到幾個關鍵的統計量與資料中的極端值。也因為箱型圖在視覺上非常簡潔,在做多個欄位的分布比較時很被用到。\n", "\n", "" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "kf_UCA4SLI8k" }, "outputs": [], "source": [ "# 將資料中年齡的分布以盒型圖繪製\n", "df['Age'].plot.box()" ] }, { "cell_type": "markdown", "metadata": { "id": "Ru_hsMKoLI8k" }, "source": [ "- ### 類別變項的統計圖\n", "\n", "由於類別變項無法用連續的數值表示,上述所提到的三個圖形都不適合做類別變項的探索。若要了解類別變項的資料分布狀況,我們會使用長條圖或是圓餅圖做視覺上的呈現。" ] }, { "cell_type": "markdown", "metadata": { "id": "E7Zsnq5PLI8l" }, "source": [ "#### 長條圖\n", "\n", "長條圖與直方圖長相類似,因此很容易被搞錯。兩者差異在於直方圖呈現的是連續型的資料,因此圖形會相互連接;而長條圖呈現的是類別型的資料,因此類別與類別之間將會有間隔。" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "StU7Bv1SLI8l" }, "outputs": [], "source": [ "# 繪製上船港口(Embarked)的長條圖\n", "# 首先需要計算每個類別的出現次數\n", "embarked_counts = df['Embarked'].value_counts()\n", "\n", "# 再將出現次數繪製長條圖\n", "embarked_counts.plot.bar()" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "Nd7Rb6K3o7Cl" }, "outputs": [], "source": [ "embarked_counts" ] }, { "cell_type": "markdown", "metadata": { "id": "fBjxQNzgLI8m" }, "source": [ "#### 圓餅圖\n", "\n", "相較於呈現出現次數,圓餅圖呈現的是各類別的相對比例,由於它在視覺上相對美觀因此常被使用,然而圓餅圖有許多缺點,例如類別之間微小的比例差異難以直接從圖形呈現、或是不適合用於呈現多個類別的比例狀況,因此在使用上必須小心謹慎。" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "lpbVhVDGLI8n" }, "outputs": [], "source": [ "# 繪製上船港口(Embarked)的圓餅圖\n", "# 首先需要計算每個類別的出現次數\n", "embarked_counts = df['Embarked'].value_counts()\n", "\n", "# 再將出現次數繪製圓餅圖\n", "embarked_counts.plot.pie()" ] }, { "cell_type": "markdown", "metadata": { "id": "4AlVmptnLI8n" }, "source": [ "- ### 觀察欄位之間的關聯\n", "\n", "除了了解單一變項的資料分布狀況外,我們也會觀察不同欄位之間的關係,同樣地,圖形的選擇也會依照欄位的性質而有所不同,在此我們針對散佈圖與群組盒型圖兩種圖表作介紹。" ] }, { "cell_type": "markdown", "metadata": { "id": "KWHxL7RSLI8o" }, "source": [ "#### 散佈圖\n", "\n", "散佈圖適合的欄位類型為兩個連續的變項,我們可以以直接的方式觀察兩個欄位數值之間的關聯。" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "aSHbZKdtLI8o" }, "outputs": [], "source": [ "# 針對年齡(Age)與票價(Fare)繪製散佈圖\n", "df.plot.scatter(x='Age', y='Fare')" ] }, { "cell_type": "markdown", "metadata": { "id": "4yG5ReivLI8p" }, "source": [ "#### 群組盒型圖\n", "\n", "若兩個欄位之一為類別型的變項,我們常會以群組盒型圖作資料分布的比較。" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "YD4gFneTLI8p" }, "outputs": [], "source": [ "# 針對票價(Fare)與艙等(Pclass)繪製群組盒型圖\n", "df.boxplot(column='Fare', by='Pclass')" ] }, { "cell_type": "markdown", "metadata": { "id": "8ybLQw0eLI8p" }, "source": [ "## Pandas小結\n", "\n", "希望在這兩個部份的教學後,大家可以對如何在python中處理表格類型資料有一個初步的了解。然而Pandas套件仍然有其他強大的功能,在此無法一一講解,後續大家在實務上遇到問題時不妨多參考官方文件的介紹、或是搜尋各大論壇大家對於特定功能的討論或解答唷。\n", "\n", "- [Pandas官方文件](https://pandas.pydata.org/pandas-docs/stable/index.html)" ] } ], "metadata": { "colab": { "name": "Pandas_part2.ipynb", "provenance": [] }, "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.7.10" } }, "nbformat": 4, "nbformat_minor": 0 }