# Python 資料分析 ###### tags: `Python/SQL商業資料分析` `資料分析` `Python` `視覺化` [TOC] # Numpy 資料分析工具基礎 ## Python 資料科學工具概論 在 Python 套件生態系中:`Numpy`、`Pandas`、`Matplotlib`、`Scipy` 以及 `scikit-learn` 是常見用來進行資料分析和機器學習、資料科學應用的重要套件。 - **Numpy**:Numpy 是許多 Python 資料科學套件的基礎,例如:Pandas。讓開發者可以很容易建立向量、矩陣進行高效率的大量運算 - **Pandas**:一個 Python 用來資料處理的工具,可以讀取各種檔案轉成欄列式資料格式,進而過濾或是進行資料前處理 - **Matplotlib**:一個 Python 用來資訊視覺化產生圖表的套件,可以搭配 Pandas 一起使用進行探索式資料分析(EDA) - **Scipy**:讓開發者可以透過 Python 進行科學運算的套件,例如:傅利葉轉換等 - **scikit-learn**:是一個 Python 用來進行機器學習的套件,內建許多好用的工具及經典的機器學習演算法 ## Numpy 基礎介紹 Numpy 可以產生一維、二維陣列進行向量和矩陣運算,其在大量運算時有非常優異的效能。 其中 `Numpy` 中最重要的就是 `ndarray` 物件和 `Python` 原生的 `list` 主要差異在於: - ndarray 是由相同資料型別的元素所組成(若為物件陣列則例外),list 元素可以是不同資料型別 - ndarray 建立時大小為固定,若更改大小則為新創建的 ndarray Numpy 可以提供高效率大量矩陣運算 - 向量:向量為一維陣列,通常代表速度等有方向性的數據。在數學中索引是由 1 開始,但在電腦科學的程式世界中是由 0 開始 - 矩陣:矩陣為二維陣列,由橫的列(row)和直的欄(column)所組成,矩陣運算常用於多變數的運算或是線性代數等運算。 ## Numpy 特性 `Numpy` 所建立的 `ndarray` 內元素為相同資料型別,例如:int、float 等。 使用 `numpy` 創建陣列: ```python # 引入套件,使用 as 代表別名,可以讓我們少打一點字 import numpy as np # 陣列元素可以使用 list 或 tuple 傳入 A = np.array([1, 2, 3]) B = np.array((1, 2, 3)) # 印出結果 print(A) print(B) # 印出型別 print(type(A)) print(type(B)) # 更改資料 A[0] = 12 B[0] = 7 # 印出更改結果 print(A) print(B) ``` # Numpy 基本操作 ## 建立一維陣列 透過 `np.array` 和一維的串列我們可以很容易建立一維陣列。 ```python # 引入套件,使用 as 代表別名,可以讓我們少打一點字 import numpy as np # 陣列元素可以使用 list 或 tuple 傳入 array_1 = np.array([1, 2, 3]) ``` 除了自行指定元素外,也可以透過內建函式建立陣列,例如:`np.zeros()` 會建立指定數量的 0 元素,而 `np.ones()` 則會建立指定數量 1 元素: ```python # 引入套件,使用 as 代表別名,可以讓我們少打一點字 import numpy as np print(np.zeros(3)) print(np.ones(10)) # 執行結果: [0. 0. 0.] [1. 1. 1. 1. 1. 1. 1. 1. 1. 1.] ``` ## 索引取值 陣列部分和 `list` 幾乎一樣,透過從 0 開始的 `index` 以及 `slicing` 切片我們可以取出指定的元素內容。 ```python # 引入套件,使用 as 代表別名,可以讓我們少打一點字 import numpy as np # 陣列元素可以使用 list 或 tuple 傳入 array_1 = np.array([1, 2, 3]) print(array_1[0]) # 取 1 到 3(不含)元素 print(array_1[1:3]) # 透過 mask 布林遮罩可以決定要取出哪些符合條件的元素陣列 # 符合的只有 index 2,元素為 3 的值 mask = (array_1 % 3 == 0) print(array_1[mask]) #執行結果: 1 [2 3] [3] ``` ## 一維陣列運算 I 以下建立兩個一維陣列進行運算: A = [123] B = [456] ### 加法/減法 ```python # 引入套件,使用 as 代表別名,可以讓我們少打一點字 import numpy as np # 陣列元素可以使用 list 或 tuple 傳入 A = np.array([1, 2, 3]) B = np.array([4, 5, 6]) result_1 = A + B result_2 = A - B print(result_1) print(result_2) #執行結果: [5 7 9] [-3 -3 -3] ``` ## 一維陣列運算 II ### 乘法/除法 ```python # 引入套件,使用 as 代表別名,可以讓我們少打一點字 import numpy as np # 陣列元素可以使用 list 或 tuple 傳入 A = np.array([1, 2, 3]) B = np.array([4, 5, 6]) result_1 = A * B result_2 = A / B print(result_1) print(result_2) #執行結果: [ 4 10 18] [ 0.25 0.4 0.5 ] ``` ## 二維陣列運算 I ```python # 引入套件,使用 as 代表別名,可以讓我們少打一點字 import numpy as np # 陣列元素可以使用 list 或 tuple 傳入 A = np.array([[1, 2, 3], [4, 5, 6]]) B = np.array([[7, 8, 9], [1, 2, 3]]) result_1 = A + B result_2 = A - B # 元素乘積為每一個元素相乘 result_3 = A * B # 除法 result_4 = A / B print(result_1) print(result_2) print(result_3) print(result_4) #執行結果 [[ 8 10 12] [ 5 7 9]] [[-6 -6 -6] [ 3 3 3]] [[ 7 16 27] [ 4 10 18]] [[0.14285714 0.25 0.33333333] [4. 2.5 2. ]] ``` ## 二維陣列運算 II ### 轉置矩陣 轉置矩陣會將欄和列互換。 ```python # 引入套件,使用 as 代表別名,可以讓我們少打一點字 import numpy as np # 陣列元素可以使用 list 或 tuple 傳入 A = np.array([[1, 2, 3], [4, 5, 6]]) print(A.T) #執行結果: [[1 4] [2 5] [3 6]] ``` ## Numpy 函式操作 I 在 Numpy 中有許多函式工具,以下舉比較常見的:內積、外積在 Numpy 中如何使用。 A ⎡ 1 2 3 ⎤ ⎣ 4 5 6 ⎦ B ⎡ 7 8 9 ⎤ ⎣ 1 2 3 ⎦ C ⎡ 7 8 9 ⎤ ⎢ 1 2 3 ⎥ ⎣ 1 2 3 ⎦ ### 矩陣點積 矩陣點積需要**第一個欄數等於第二個矩陣列數**。我們建立 C 範例矩陣與其相乘。 ```python # 引入套件,使用 as 代表別名,可以讓我們少打一點字 import numpy as np # 陣列元素可以使用 list 或 tuple 傳入 A = np.array([[1, 2, 3], [4, 5, 6]]) C = np.array([[7, 8, 9], [1, 2, 3], [1, 2, 3]]) # 注意:矩陣乘法需要 第一個欄數等於第二個矩陣列數 result_1 = A.dot(C) print(result_1) #執行結果: [[12 18 24] [39 54 69]] ``` ## Numpy 函式操作 II ### 內積(inner) ```python # 引入套件,使用 as 代表別名,可以讓我們少打一點字 import numpy as np # 陣列元素可以使用 list 或 tuple 傳入 A = np.array([[1, 2, 3], [4, 5, 6]]) B = np.array([[7, 8, 9], [1, 2, 3]]) result_1 = np.inner(A, B) print(result_1) #執行結果: [[ 50 14] [122 32]] ``` ### 外積(outer) ![](https://i.imgur.com/dlGWOsk.png) ```python # 引入套件,使用 as 代表別名,可以讓我們少打一點字 import numpy as np # 陣列元素可以使用 list 或 tuple 傳入 A = np.array([[1, 2, 3], [4, 5, 6]]) B = np.array([[7, 8, 9], [1, 2, 3]]) result_1 = np.outer(A, B) print(result_1) #執行結果: [[ 7 8 9 1 2 3] [14 16 18 2 4 6] [21 24 27 3 6 9] [28 32 36 4 8 12] [35 40 45 5 10 15] [42 48 54 6 12 18]] ``` ![](https://i.imgur.com/WRp3gqb.png) # Pandas 套件基礎 `Pandas` 是一個 Python 用來資料處理的工具,可以讀取各種檔案轉成欄列式資料格式,進而過濾或是進行資料前處理(將資料整理好方便後續資料分析使用)。 `Pandas` 是做為資料處理和資料分析一個好用的工具,其主要資料結構有包含:`Series` 物件和 `DataFrame` 物件。其中 `DataFrame` 就類似我們在使用的 Excel 試算表一樣,由欄列所組成的表格結構。 由於 `Pandas` 本身基於 Numpy 所以在使用大量資料運算時效能表現也優於原生的 Python 資料結構,所以是常用將資料載入進行資料分析的好用工具。 - **Series** 物件 Series 為索引標籤和實際值的陣列組合 - **DataFrame** 物件 DataFrame 類似試算表和關聯式資料庫資料表欄列結構,每一欄是固定資料型別但不同欄可以儲存不同的資料型別 用以下語法在 Anaconda Prompt 終端機下安裝: ```text pip install pandas ``` ![](https://i.imgur.com/1RvBxkM.png) ## 建立 Series 物件 透過 `list` 當作參數可以將 list 轉換成 `Series` 物件。 ```python # 引入 pandas 套件,使用別名 pd 可以少打字 import pandas as pd # 建立 Series 物件,傳入 list 當作參數 series_1 = pd.Series([12, 34, 41, 3]) print(series_1) #執行結果: 0 12 1 34 2 41 3 3 dtype: int64 ``` **建立 Series 索引值**(預設為 0, 1, 2...,但可以透過 index 屬性更改): ```python # 引入 pandas 套件,使用別名 pd 可以少打字 import pandas as pd # 建立 Series 物件並設定 index 索引 grades = pd.Series([60, 77, 92, 43], index=['小明', '小王', '小華', '小光']) print(grades) #執行結果: 小明 60 小王 77 小華 92 小光 43 dtype: int64 ``` **使用索引取值:** ```python # 引入 pandas 套件,使用別名 pd 可以少打字 import pandas as pd # 建立 Series 物件,傳入 list 當作參數 series_1 = pd.Series([12, 34, 41, 3]) print(series_1[0]) print(series_1[1:3]) #執行結果: 12 1 34 2 41 dtype: int64 ``` **使用自定義索引取值:** ```python # 引入 pandas 套件,使用別名 pd 可以少打字 import pandas as pd # 建立 Series 物件並設定 index 索引 grades = pd.Series([60, 77, 92, 43], index=['小明', '小王', '小華', '小光']) print(grades['小王']) #執行結果: 77 ``` ## 建立 DataFrame 物件 `DataFrame` 是 `Pandas` 最重要的資料結構,基本上我們使用 `Pandas` 進行資料分析和操作大部分都是在使用 `DataFrame`。 ```python # 引入 pandas 套件,使用別名 pd 可以少打字 import pandas as pd # 準備傳入 data = { 'name': ['王小明', '張小華', '廖丁丁', '許小光'], 'email': ['min@gmail.com', 'hchang@gmail.com', 'laioding@gmail.com', 'hsulight@gmail.com'], 'grades': [60, 77, 92, 43] } # 建立 DataFrame 物件 student_df = pd.DataFrame(data) print(student_df) #執行結果: name email grades 0 王小明 min@gmail.com 60 1 張小華 hchang@gmail.com 77 2 廖丁丁 laioding@gmail.com 92 3 許小光 hsulight@gmail.com 43 ``` 也可以自行定義 index: ```python # 引入 pandas 套件,使用別名 pd 可以少打字 import pandas as pd # 準備傳入 DataFrame 的資料 data = { 'name': ['王小明', '張小華', '廖丁丁', '許小光'], 'email': ['min@gmail.com', 'hchang@gmail.com', 'laioding@gmail.com', 'hsulight@gmail.com'], 'grades': [60, 77, 92, 43] } # 建立 DataFrame 物件 student_df = pd.DataFrame(data, index=['one', 'second', 'third','fourth']) print(student_df) ``` ## 列出資料訊息 透過 `DataFrame` 函式我們可以很容易一窺整個資料集的統計數據和資料內容。 ```python # 引入 pandas 套件,使用別名 pd 可以少打字 import pandas as pd data = { 'name': ['王小明', '張小華', '廖丁丁', '許小光'], 'email': ['min@gmail.com', 'hchang@gmail.com', 'laioding@gmail.com', 'hsulight@gmail.com'], 'grades': [60, 77, 92, 43] } # 建立 DataFrame 物件 student_df = pd.DataFrame(data) # 列出欄位資料型別等資訊 print(student_df.info()) # 列出統計資訊 print(student_df.describe()) ``` 執行結果: ```python <class 'pandas.core.frame.DataFrame'> RangeIndex: 4 entries, 0 to 3 Data columns (total 3 columns): name 4 non-null object email 4 non-null object grades 4 non-null int64 dtypes: int64(1), object(2) memory usage: 176.0+ bytes None grades count 4.000000 mean 68.000000 std 21.181753 min 43.000000 25% 55.750000 50% 68.500000 75% 80.750000 max 92.000000 ``` 列出 DataFrame 的 index/columns: ```python # 列出 DataFrame 的 index/columns print(student_df.index) print(student_df.columns) #執行結果: RangeIndex(start=0, stop=4, step=1) Index(['name', 'email', 'grades'], dtype='object') ``` 印出頭尾指定幾筆資料: ```python # 印出頭尾指定幾筆資料 print(student_df.head(2)) print(student_df.tail(2)) #執行結果: name email grades 0 王小明 min@gmail.com 60 1 張小華 hchang@gmail.com 77 name email grades 2 廖丁丁 laioding@gmail.com 92 3 許小光 hsulight@gmail.com 43 ``` ## Pandas DataFrame 基本操作概論 由於 `DataFrame` 是 Pandas 最常使用的資料結構物件,所以我們接下來會主要學習如何操作 `DataFrame` 資料結構物件。 ![](https://i.imgur.com/xBXyemh.png) ### 讀取資料 在 Pandas 中主要可以使用 `read_json(檔案名稱)`、`read_csv(檔案名稱)`、`read_html(檔案名稱)` 來讀取檔案。 - **Step1. 下載政府開放資料檔案** 在[政府開放資料(Open Data)網站](https://data.gov.tw/)中我們可以下載許多生活上的開放資料來進行資料分析學習使用。 在這邊我們使用 `大專院校校別學生數` 中的 `大專院校校別學生數` 資料。[下載網址](https://data.gov.tw/dataset/6231)。 ![](https://i.imgur.com/3zyw6AC.png) 載檔案後將資料檔案放入和程式同一個專案工作資料夾下。 - **Step2. 使用 Pandas 載入檔案** ```python import pandas as pd df = pd.read_csv('108_student.csv') ``` - **Step3. 列出資料相關資訊** ```python import pandas as pd df = pd.read_csv('108_student.csv') # 印出索引和欄位相關資訊 print(df.info()) # 印出所有資料 print(df) ``` - **Step4. 列出前後筆資料** ```python import pandas as pd df = pd.read_csv('108_student.csv') # 印出頭 5 筆資料 print(df.head(5)) # 印出尾 5 筆資料 print(df.tail(5)) ``` ### DataFrame 查詢 在 `DataFrame` 中若要取值可以使用常用的 `loc`、`iloc` 方法,以下分別介紹兩者使用方式。 - loc 語法使用方式:`loc(列索引範圍, [欄位名稱])` ```python import pandas as pd df = pd.read_csv('108_student.csv') # 印出 index 2-10 資料的學校代碼、學校名稱和總計欄位 print(df.loc[2:10, ['學校代碼', '學校名稱', '總計']]) #執行結果: 學校代碼 學校名稱 總計 2 1 國立政治大學 9585 3 1 國立政治大學 1787 4 2 國立清華大學 1656 5 2 國立清華大學 5190 6 2 國立清華大學 8500 7 2 國立清華大學 39 8 2 國立清華大學 1293 9 3 國立臺灣大學 3433 10 3 國立臺灣大學 10335 ``` - iloc 語法使用方式:`iloc(列索引範圍(不含結束), 欄位索引範圍])` ```python import pandas as pd df = pd.read_csv('108_student.csv') # 印出 index 0-2 資料的 0:學校代碼、1: 學校名稱和 4:總計欄位 print(df.iloc[0:3, [0, 1, 4]] #執行結果: 學校代碼 學校名稱 總計 0 1 國立政治大學 995 1 1 國立政治大學 4023 2 1 國立政治大學 9585 ``` ### DataFrame 過濾 在取值時有時會希望透過設立條件可以過濾出我們想要看的資料,此時可以使用過濾語法,選定欄位和條件進行過濾: ```python import pandas as pd df = pd.read_csv('108_student.csv') # 過濾取出欄位總計 大於 20000 的資料 filtered_df = df[df['總計'] > 20000] print(filtered_df) #執行結果: 學校代碼 學校名稱 日間∕進修別 等級別 總計 男生計 女生計 一年級男生 一年級女生 二年級男生 二年級女生 ... 四年級女生 五年級男生 五年級女生 六年級男生 六年級女生 七年級男生 七年級女生 延修生男生 延修生女生 縣市名稱 體系別 267 1005 淡江大學 D 日 B 學士 20273 10310 9963 2471 2333 2462 2394 ... 2458 31 28 0 0 0 0 746 371 01 新北市 1 一般 273 1006 中國文化大學 D 日 B 學士 20411 9930 10481 2368 2426 2178 2425 ... 2465 33 27 0 0 0 0 1037 872 30 臺北市 1 一般 [2 rows x 25 columns] ``` 只取:`'學校名稱', '日間∕進修別', '等級別', '總計', '男生計', '女生計' 欄位)`: ```python # 取出所有列 index 欄位為 學校名稱', '日間∕進修別', '等級別', '總計', '男生計', '女生計' 資料 print(filtered_df.loc[:, ['學校名稱', '日間∕進修別', '等級別', '總計', '男生計', '女生計']]) #執行結果: 學校名稱 日間∕進修別 等級別 總計 男生計 女生計 267 淡江大學 D 日 B 學士 20273 10310 9963 273 中國文化大學 D 日 B 學士 20411 9930 10481 ``` ### DataFrame 更新索引 在 DataFrame 中列為預設 index 索引(0, 1, 2, 3...),若希望使用某一個欄位(column)設為 index 的話可以使用 `set_index` 方法進行設定。 ![](https://i.imgur.com/fseIFU5.png) ![](https://i.imgur.com/B0ZueGl.png) ### DataFrame 排序 在 DataFrame 中可以使用 `sort_values` 進行特定欄位排序 **(ascending=True 為由小到大,False 為由大到小排序)** ```python import pandas as pd df = pd.read_csv('108_student.csv') print(df.sort_values(['總計'], ascending=False).loc[:, ['學校名稱', '總計']]) ``` 執行結果(可以看到單用總計來排序的學校名稱結果): ```python 53 學校名稱 總計 273 中國文化大學 20411 267 淡江大學 20273 251 輔仁大學 17528 229 國立高雄科技大學 17287 324 銘傳大學 17200 .. ... ... 197 國立高雄餐旅大學 1 561 東南科技大學 1 13 國立臺灣大學 1 88 國立臺灣科技大學 1 213 國立臺中科技大學 1 [794 rows x 2 columns] ``` 另外也可以透過列的 `index` 來排序: ```python print(df.sort_index(ascending=False).loc[:, ['學校名稱', '總計']]) ``` ### DataFrame 群組 若希望像是 `SQL Group By` 語法一樣,針對特定欄位進行群組化,我們可以使用 groupby(['欄位名稱']),同時可以使用 sum() 彙總函式搭配排序來呈現資料: ```python print(df.groupby(['學校名稱']).sum().sort_values('總計', ascending=False)) ``` 執行結果: ```python 總計 男生計 女生計 一年級男生 一年級女生 二年級男生 二年級女生 三年級男生 三年級女生 \ 學校名稱 國立臺灣大學 31945 19027 12918 5383 3628 5159 3463 3565 2530 國立高雄科技大學 27302 17528 9774 4769 2740 4696 2469 3700 2162 輔仁大學 25709 10707 15002 2666 3816 2582 3774 2352 3323 中國文化大學 25186 12235 12951 2933 3078 2614 2992 2508 2696 淡江大學 24635 12765 11870 3254 2889 3171 2956 2681 2675 ... ... ... ... ... ... ... ... ... ... 馬偕醫學院 684 237 447 44 121 52 120 44 79 蘭陽技術學院 667 373 294 27 20 92 70 71 54 臺灣觀光學院 608 283 325 84 75 43 57 37 48 國立臺灣戲曲學院 595 200 395 44 100 49 92 44 96 法鼓文理學院 305 98 207 33 53 25 59 17 41 四年級男生 四年級女生 五年級男生 五年級女生 六年級男生 六年級女生 七年級男生 七年級女生 延修生男生 延修生女生 學校名稱 國立臺灣大學 2940 2162 494 259 413 186 344 138 729 552 國立高雄科技大學 3388 2113 176 47 71 9 55 5 673 229 輔仁大學 2253 3280 97 86 80 80 31 23 646 620 中國文化大學 2551 2858 198 179 154 107 99 50 1178 991 淡江大學 2620 2757 92 65 46 37 32 13 869 478 ... ... ... ... ... ... ... ... ... ... ... 馬偕醫學院 46 82 28 20 18 22 2 0 3 3 蘭陽技術學院 114 87 54 45 0 0 0 0 15 18 臺灣觀光學院 62 88 9 16 0 0 0 0 48 41 國立臺灣戲曲學院 48 96 0 0 0 0 0 0 15 11 法鼓文理學院 19 46 3 3 1 3 0 0 0 2 [152 rows x 19 columns] ``` ### DataFrame 刪除欄位 若希望刪除特定欄位,可以使用 `drop([欄位], axis=指定欄或列)` 方法(axis=1 為欄),指定要刪除的欄位。 ```python # 引入 pandas 套件,使用別名 pd 可以少打字 import pandas as pd # 準備傳入 DataFrame 的資料 data_1 = { 'name': ['王小明', '張小華', '廖丁丁', '許小光'], 'email': ['min@gmail.com', 'hchang@gmail.com', 'laioding@gmail.com', 'hsulight@gmail.com'], 'grades': [60, 77, 92, 43] } # 建立 DataFrame 物件 student_df_1 = pd.DataFrame(data_1) print(student_df_1) # 使用 drop 指定欄位,記得要給定 axis=1 為欄。若 axis=0 為代表列 student_df_1 = student_df_1.drop(['grades'], axis=1) print(student_df_1) ``` 執行結果: ```python name email grades 0 王小明 min@gmail.com 60 1 張小華 hchang@gmail.com 77 2 廖丁丁 laioding@gmail.com 92 3 許小光 hsulight@gmail.com 43 name email 0 王小明 min@gmail.com 1 張小華 hchang@gmail.com 2 廖丁丁 laioding@gmail.com 3 許小光 hsulight@gmail.com ``` ### 充填 NA/NaN 值 有時建立或讀取進來的資料值中會有 `NA/NaN` 值(可能是資料遺漏或是沒有值),此時我們可以使用 `DataFrame.fillna` 來填充 NA/NaN 值,例如改為 0 等。 使用 Numpy 和 Pandas 建立一個有 NA/NaN 值的 DataFrame: ```python # 引入 pandas 套件,使用別名 pd 可以少打字 import pandas as pd import numpy as np df = pd.DataFrame([[np.nan, 2, np.nan, 0], [3, 4, np.nan, 1], [np.nan, np.nan, np.nan, 5], [np.nan, 3, np.nan, 4]], columns=list('ABCD')) ``` ```python! #執行結果: df A B C D 0 NaN 2.0 NaN 0 1 3.0 4.0 NaN 1 2 NaN NaN NaN 5 3 NaN 3.0 NaN 4 ``` 使用 `fillna` 將 NA/NaN 轉為指定數字,`fillna` 會回傳轉換結果 ```python # 將 df 更新為 NA/NaN 轉為 0 的 DataFrame,也可以自行指定為其他數值 df = df.fillna(0) A B C D 0 0.0 2.0 0.0 0 1 3.0 4.0 0.0 1 2 0.0 0.0 0.0 5 3 0.0 3.0 0.0 4 ``` ### 合併與更新物件 若要合併不同的 DataFrame 我們可以使用列合併 `concat()` 或是欄位合併 `merge()`。 以下先建立學習資料: - 使用 `concat` 列合併資料 ```python # 引入 pandas 套件,使用別名 pd 可以少打字 import pandas as pd # 準備傳入 DataFrame 的資料 data_1 = { 'name': ['王小明', '張小華', '廖丁丁', '許小光'], 'email': ['min@gmail.com', 'hchang@gmail.com', 'laioding@gmail.com', 'hsulight@gmail.com'], 'grades': [60, 77, 92, 43] } data_2 = { 'name': ['黃明明', '汪新新', '鮑呱呱', '江組組'], 'email': ['ww@gmail.com', 'cc@gmail.com', 'bb@gmail.com', 'ee@gmail.com'], 'grades': [70, 17, 32, 43] } # 建立 DataFrame 物件 student_df_1 = pd.DataFrame(data_1) student_df_2 = pd.DataFrame(data_2) print(student_df_1) print(student_df_2) ``` ```python student_fg_3 = pd.concat([student_df_1, student_df_2]) print(student_fg_3) #若有 ignore_index 則列的 index 會重新排列: student_fg_3 = pd.concat([student_df_1, student_df_2], ignore_index=True) print(student_fg_3) ``` - 使用 `merge` 列合併資料 ```python # 引入 pandas 套件,使用別名 pd 可以少打字 import pandas as pd # 準備傳入 DataFrame 的資料 data_1 = { 'name': ['王小明', '張小華', '廖丁丁', '許小光'], 'email': ['min@gmail.com', 'hchang@gmail.com', 'laioding@gmail.com', 'hsulight@gmail.com'], 'grades': [60, 77, 92, 43] } data_2 = { 'name': ['王小明', '張小華', '廖丁丁', '許小光'], 'age': [19, 20, 32, 43] } # 建立 DataFrame 物件 student_df_1 = pd.DataFrame(data_1) student_df_2 = pd.DataFrame(data_2) print(student_df_1) print(student_df_2) ``` ```python student_fg_3 = pd.merge(student_df_1, student_df_2) print(student_fg_3) ``` ### 輸出資料 在 Pandas 中同樣可以使用 `DataFrame.to_csv(檔案名稱)`、`DataFrame.to_json(檔案名稱)`、`DataFrame.to_excel(檔案名稱)` 和 `DataFrame.to_html(檔案名稱)`將資料轉成檔案。 ```python # 引入 pandas 套件,使用別名 pd 可以少打字 import pandas as pd data = { 'name': ['王小明', '張小華', '廖丁丁', '許小光'], 'email': ['min@gmail.com', 'hchang@gmail.com', 'laioding@gmail.com', 'hsulight@gmail.com'], 'grades': [60, 77, 92, 43] } # 建立 DataFrame 物件 student_df = pd.DataFrame(data) # 將 DataFrame 轉成 CSV 檔案 print(student_df.to_csv('student_demo.csv')) ``` # Python 資料視覺化基礎 - `Numpy`:Numpy 是許多 Python 資料科學套件的基礎 - 例如:Pandas。讓開發者可以很容易建立向量、矩陣進行高效率的大量運算 - `Pandas`:用來資料處理的工具,可以讀取各種檔案轉成欄列式資料格式,進而過濾或進行資料前處理 - `Matplotlib`:用來資訊視覺化產生圖表的套件,可以搭配 Pandas 一起使用進行探索式資料分析 - `Scipy`:讓開發者可以透過 Python 進行科學運算的套件,例如:傅利葉轉換等 - `scikit-learn`:是一個 Python 用來進行機器學習的套件,內建許多工具及機器學習演算法 ## Matplotlib 圖表繪製初體驗 ```python pip install matplotlib ``` 使用 matplotlib 套件的 pyplot 模組: ```python # 使用別名 plt 可以替代少打字 matplotlib.pyplot,別名是可以自己命名 import matplotlib.pyplot as plt ``` ## 認識圖表專有名詞 在開始繪製圖表前我們先認識一下圖表一些專有名詞: 1. `figure`: 代表整個面板 2. `axes`: 代表整個圖表 3. `axis`: 代表軸 4. `legend`: 代表圖示說明 5. `label`: 代表軸標頭(有 xlabel 和 ylabel) 6. `title`: 代表圖表標頭 ![](https://i.imgur.com/KAZEHBP.jpg) ## 長條圖 繪製長條圖 `bar(x, y)`: ![](https://i.imgur.com/wAyelCd.png) ```python import matplotlib.pyplot as plt # X 軸 stock_list = ['2031', '2341', '2342', '2345'] # Y 軸 volumes = [23341, 412221, 41907, 3115987] # 設定圖表類型和資料 plt.bar(stock_list, volumes) # 顯示圖表 plt.show() # 若需要可以將圖表存為圖片 plt.savefig('plot.png') ``` ## 折線圖 繪製折線圖 `plot(x, y)`: ![](https://i.imgur.com/TQqpenV.png) ```python import matplotlib.pyplot as plt # X 軸 stock_list = ['11/1', '11/2', '11/3', '11/4'] # Y 軸 prices = [23, 41, 41, 3] plt.plot(stock_list, prices) plt.show() ``` ## 圓餅圖 繪製圓餅圖 `pie(values, labels=labels)`(lables 標籤設定名稱): ![](https://i.imgur.com/bji4QKR.png) ```python import matplotlib.pyplot as plt # X 軸 stock_list = ['2031', '2341', '2342', '2345'] # Y 軸 volumes = [23341, 412221, 41907, 3115987] plt.pie(volumes, labels=stock_list) plt.show() ``` ## 整合 Pandas 和 Matplotlib 進行圖表繪製 事實上,`Matplotlib` 和 `Pandas` 可以一起搭配(也可以只單獨使用 Matplotlib),使用進行資料的圖表繪製。 `DataFrame.plot()` 是 `Matplotlib` 用來繪圖的方法,可以將 DataFrame 的資料轉換成資訊視覺化的圖表,而 **`kind` 參數則可以決定是要長條圖、圓餅圖,更多圖形類型**可以[參考官方文件](https://pandas.pydata.org/pandas-docs/version/0.23.4/generated/pandas.DataFrame.plot.html)。 > ‘折線圖 line’ : line plot (default 預設沒有設定) ‘柱狀圖 bar’ : vertical bar plot ‘橫向柱狀圖 barh’ : horizontal bar plot ‘直方圖 hist’ : histogram ‘箱形圖 box’ : boxplot ‘圓餅圖 pie’ : pie plot ‘散佈圖 scatter’ : scatter plot ```python # df 為 DataFrame df.plot(kind='bar') plt.show() ``` ## 常見繪圖錯誤處理 ### 資料型別錯誤 繪製圖表時出現 `pandas DataFrame TypeError: no numeric data to plot error `錯誤訊息。這代表有可能**繪製的資料中有不是數值的值導致無法繪製**。所以確認 X 和Y 軸使用的資料欄位資料型別是否正確。 印出所有欄位資料型別(type): ```python print(df.dtypes) ``` 將欄位值的資料型別透過 astype 轉換為數值型別: ```python df = df['更改的欄位'].astype('float') ``` # 股價走勢折線圖 ```python import pandas as pd import matplotlib.pyplot as plt # 讀取 CSV 資料 df = pd.read_csv('performance.csv') # 選取所有列的 date, year_revenue 欄資料 data = df.loc[:, ['date', 'year_revenue']] # 將 date 設為 index,要當作 X 軸使用 data = data.set_index('date') print('data', data) # 產生 line chart data.plot(kind='line') # 設定圖表標頭 plt.title('stock performance') # 顯示圖表 plt.show() ``` ![](https://i.imgur.com/yt5UZIl.png) # 股票公司營收長條圖 ```python import pandas as pd import matplotlib.pyplot as plt # 讀取 CSV 資料 df = pd.read_csv('performance.csv') data = df.loc[:, ['date', 'year_revenue']] data = data.set_index('date') fig = data.plot(kind='bar').get_figure() plt.title('stock performance') fig.savefig('plot.png') ``` ![](https://i.imgur.com/pNZD7lh.png) # 範例實作 ## Step 1. 規劃目標並準備資料 目標:我們希望透過長條圖顯示桃園地區結婚對數,了解結婚分布狀況。 資料集使用政府開放資料中[ 108 年度桃園地區結婚對數資料集](https://data.gov.tw/dataset/27396),我們下載 .csv 檔案後更改檔案名稱為: marriage.csv。 ## Step 2. 取出要顯示圖表的資料 ```python import pandas as pd # 讀入資料集檔案 df = pd.read_csv('marriage.csv') # loc 取值方式為 [索引, [欄位]],取 月份區域別 出來當作 index data = df.loc[:, ['月份區域別', '一月']] print(data) # 原本 index 為 0, 1, 2, 3...12,改為 月份區域別,這樣 x 軸標籤就會改為 月份區域別 data = data.set_index('月份區域別') print(data) ``` ## Step 3. 引入 matplotlib 套件繪製長條圖 於程式最上方引入 `matplotlib` 套件並使用別名繪製長條圖: ```python import matplotlib.pyplot as plt # 指定中文字體的設定 plt.rcParams['font.sans-serif'] = ['SimHei'] plt.rcParams['axes.unicode_minus'] = False # kind 定義圖表類型。plot 函式會取 column 值代表 y 軸的值,index 索引代表 x 軸 data.plot(kind='bar') # 顯示圖表 plt.show() ``` 完整程式碼: ```python import pandas as pd import matplotlib.pyplot as plt # 指定中文字體的設定 plt.rcParams['font.sans-serif'] = ['SimHei'] plt.rcParams['axes.unicode_minus'] = False df = pd.read_csv('marriage.csv') # loc 取值方式為 [索引, [欄位]],取 月份區域別 出來當作 index data = df.loc[:, ['月份區域別', '一月']] print(data) # 原本 index 為 0, 1, 2, 3...12,改為 月份區域別,這樣 x 軸標籤就會改為 月份區域別 data = data.set_index('月份區域別') print(data) # plot 函式會取 column 值代表 y 軸的值,index 索引代表 x 軸 data.plot(kind='bar') # 設定標頭和字體 plt.title('一月份各區域結婚數') # 設定 x 軸標頭和字體 plt.xlabel('區域別') # 設定 y 軸標頭和字體 plt.ylabel('結婚數') # 顯示圖表 plt.show() ``` ## 使用程式中引入外部載入中文字體方式 若需要使用設定載入字體物件方式(每次寫程式都需要載入字體物件),可以參考以下程式碼。 **如何在 replit 上引入外部字體:** 1. 首先下載欲使用字體,這裡使用 NotoSansCJK-Black.ttc 2. 進入 `replit` 中點選左方上傳檔案,將下載的字體上傳上去 3. 接著將範例程式碼輸入 main.py 內: ```python import pandas as pd import matplotlib.pyplot as plt from matplotlib.font_manager import FontProperties # 讀入資料集檔案 df = pd.read_csv('marriage.csv') # loc 取值方式為 [索引, [欄位]],取 月份區域別 出來當作 index data = df.loc[:, ['月份區域別', '一月']] print(data) # 原本 index 為 0, 1, 2, 3...12,改為 月份區域別,這樣 x 軸標籤就會改為 月份區域別 data = data.set_index('月份區域別') print(data) # plot 函式會取 column 值代表 y 軸的值,index 索引代表 x 軸 axes = data.plot(kind='bar') # 引入字體當案,以下為將 NotoSansCJK-Black.ttc 字體檔案和程式放在同一個資料夾中 myfont = FontProperties(fname=r'NotoSansCJK-Black.ttc') # 設定標頭和字體 plt.title('一月份各區域結婚數',fontproperties=myfont) # 設定 x 軸標頭和字體 plt.xlabel('區域別',fontproperties=myfont) # 設定 y 軸標頭和字體 plt.ylabel('結婚數', fontproperties=myfont) # 設定右上角說明圖示字體 plt.legend(prop=myfont) # 使用 for 迴圈一一取出 x 軸標籤 label 設定字體,若 y 軸有中文字也是類似使用方式 get_yticklabels for label in axes.get_xticklabels(): label.set_fontproperties(myfont) # 顯示圖表 plt.show() ``` # 延伸閱讀 - [SQL 資料庫](https://hackmd.io/fQbgqRSYTDi2Ouis0fE3rg?view) - [Python 網頁爬蟲](https://hackmd.io/JOc4g8AjSZiokS6vZckFSw?view) - [Python 基礎語法](https://hackmd.io/4FH3w4_pQP6_dsandd45LA) - [Python 進階語法使用](https://hackmd.io/QYVYsxE8QyWNsnIhukt-2Q) - [Python 資料科學與探索式資料分析](https://hackmd.io/qSceMWZWQcWsMIA9QiO1Nw?view) - [營收與使用者行為資料分析專案](https://hackmd.io/i6kRZN8JQsq57uDrKeKoLQ) - [Python專案實作 資料分析與爬蟲](https://hackmd.io/oh18KsFvSxe5Eh3ECHDJOA?view)