Python 數據分析實戰:Pandas、NumPy、Matplotlib 完整教學
摘要
本教學文章旨在為讀者提供一個全面且實用的 Python 數據分析指南,重點介紹三個核心函式庫:NumPy、Pandas 和 Matplotlib。無論您是數據科學新手還是希望提升技能的開發者,本文都將透過清晰的步驟說明、豐富的程式碼範例和實戰案例,引導您掌握數據處理、分析與視覺化的關鍵技術。我們將深入探討每個函式庫的核心功能、應用場景,並提供常見問題解答與 SEO 優化建議,助您在數據分析領域更上一層樓。
前言
在當今數據驅動的時代,數據分析已成為各行各業不可或缺的技能。Python 以其簡潔的語法和強大的生態系統,迅速崛起成為數據分析領域的首選語言。其中,NumPy 提供了高效的數值運算能力,Pandas 則為數據處理和操作提供了靈活的工具,而 Matplotlib 則能將複雜的數據轉化為直觀的視覺化圖表。本篇文章將帶您從零開始,逐步掌握這三大函式庫的精髓,透過實戰演練,讓您能夠自信地處理和分析真實世界的數據。我們將從 NumPy 的基礎知識開始,逐步深入到 Pandas 的數據處理技巧,最後學習如何使用 Matplotlib 進行數據視覺化,讓數據真正「說話」。
第一章:NumPy 基礎入門:高效數值運算的基石
1.1 什麼是 NumPy?為何在數據分析中不可或缺?
NumPy (Numerical Python) 是 Python 中用於科學計算的核心函式庫,它提供了一個高效的多維陣列物件 ndarray,以及用於處理這些陣列的工具。在數據分析領域,NumPy 之所以不可或缺,主要有以下幾個原因:
NumPy 之所以不可或缺,主要歸因於其在數據處理上的高效性能、記憶體效率以及豐富的數學函數。NumPy 的核心 ndarray 物件是 C 語言實現的,這使得它在處理大量數據時比 Python 原生的列表 (list) 具有顯著的速度優勢,能夠實現向量化操作,避免了 Python 迴圈的性能瓶頸。此外,NumPy 陣列在記憶體中以連續的方式儲存,不僅提高了存取速度,也減少了記憶體開銷,特別是在處理大型數據集時,其記憶體效率遠高於 Python 列表。NumPy 還提供了大量的數學函數,包括線性代數、傅立葉變換、隨機數生成等,這些函數可以直接作用於整個陣列,極大地簡化了數值計算的過程。
簡而言之,NumPy 為 Python 數據分析奠定了高效的數值運算基礎,是 Pandas 和其他科學計算函式庫的底層支撐。
1.2 NumPy 陣列 (ndarray) 的創建與基本操作
NumPy 的核心是 ndarray 物件,它是一個同質的、多維的數據容器。接下來,我們將學習如何創建 ndarray 並進行基本操作。
1.2.1 從 Python 列表創建 ndarray
NumPy 的核心是 ndarray 物件,它是一個同質的、多維的數據容器。創建 ndarray 的最常見方式是將 Python 列表轉換為 NumPy 陣列,所有元素必須是相同類型。此外,NumPy 也提供了 zeros, ones, empty, arange, linspace 等多種函數來快速創建特定形狀和值的陣列。每個 ndarray 都具有其形狀 (shape)、維度 (ndim)、大小 (size) 和數據類型 (dtype) 等屬性,這些屬性提供了陣列的基本資訊。在數據選取方面,NumPy 陣列的索引和切片操作與 Python 列表類似,但功能更強大,支援一維、二維及多維陣列的精確選取。最後,reshape, flatten, transpose 等函數則能方便地進行陣列形狀變換,以適應不同的運算需求,但這些操作不會改變其數據。
以下是相關的程式碼範例:
import numpy as np
# 從一維列表創建陣列
list_1d = [1, 2, 3, 4, 5]
array_1d = np.array(list_1d)
print("一維陣列:", array_1d)
print("類型:", type(array_1d))
# 從二維列表創建陣列
list_2d = [[1, 2, 3], [4, 5, 6]]
array_2d = np.array(list_2d)
print("\n二維陣列:\n", array_2d)
# 創建全零陣列
zeros_array = np.zeros((3, 4)) # 3行4列
print("\n全零陣列:\n", zeros_array)
# 創建全一陣列
ones_array = np.ones((2, 3), dtype=int) # 指定數據類型為整數
print("\n全一陣列:\n", ones_array)
# 創建空陣列 (內容是隨機值,取決於記憶體狀態)
empty_array = np.empty((2, 2))
print("\n空陣列:\n", empty_array)
# 創建等差數列陣列 (類似 Python 的 range)
arange_array = np.arange(0, 10, 2) # 從0到10 (不包含10),步長為2
print("\n等差數列陣列:", arange_array)
# 創建等間隔數列陣列 (指定起始、結束和元素個數)
linspace_array = np.linspace(0, 1, 5) # 從0到1,生成5個等間隔的數
print("\n等間隔數列陣列:", linspace_array)
array_example = np.array([[1, 2, 3], [4, 5, 6]])
print("陣列:\n", array_example)
print("形狀 (shape):", array_example.shape) # (行數, 列數)
print("維度 (ndim):", array_example.ndim) # 陣列的軸數
print("大小 (size):", array_example.size) # 陣列中元素的總數
print("數據類型 (dtype):", array_example.dtype) # 陣列中元素的數據類型
# 一維陣列索引與切片
arr_1d = np.arange(10)
print("一維陣列:", arr_1d)
print("第一個元素:", arr_1d[0])
print("從索引2到5的元素:", arr_1d[2:6])
print("最後一個元素:", arr_1d[-1])
# 二維陣列索引與切片
arr_2d = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
print("\n二維陣列:\n", arr_2d)
print("第二行第一列的元素:", arr_2d[1, 0]) # 等同於 arr_2d[1][0]
print("第一行所有元素:", arr_2d[0, :])
print("所有行第二列的元素:", arr_2d[:, 1])
print("前兩行和後兩列的子陣列:\n", arr_2d[:2, 1:])
arr = np.arange(12)
print("原始陣列:", arr)
# reshape: 將一維陣列變為 3x4 的二維陣列
reshaped_arr = arr.reshape((3, 4))
print("\nreshape 為 3x4:\n", reshaped_arr)
# flatten: 將多維陣列展平為一維陣列
flattened_arr = reshaped_arr.flatten()
print("\nflatten 為一維:", flattened_arr)
# transpose: 轉置陣列 (行列互換)
transposed_arr = reshaped_arr.transpose()
print("\n轉置陣列:\n", transposed_arr)
import numpy as np
# 從一維列表創建陣列
list_1d = [1, 2, 3, 4, 5]
array_1d = np.array(list_1d)
print("一維陣列:", array_1d)
print("類型:", type(array_1d))
# 從二維列表創建陣列
list_2d = [[1, 2, 3], [4, 5, 6]]
array_2d = np.array(list_2d)
print("\n二維陣列:\n", array_2d)
# 創建全零陣列
zeros_array = np.zeros((3, 4)) # 3行4列
print("\n全零陣列:\n", zeros_array)
# 創建全一陣列
ones_array = np.ones((2, 3), dtype=int) # 指定數據類型為整數
print("\n全一陣列:\n", ones_array)
# 創建空陣列 (內容是隨機值,取決於記憶體狀態)
empty_array = np.empty((2, 2))
print("\n空陣列:\n", empty_array)
# 創建等差數列陣列 (類似 Python 的 range)
arange_array = np.arange(0, 10, 2) # 從0到10 (不包含10),步長為2
print("\n等差數列陣列:", arange_array)
# 創建等間隔數列陣列 (指定起始、結束和元素個數)
linspace_array = np.linspace(0, 1, 5) # 從0到1,生成5個等間隔的數
print("\n等間隔數列陣列:", linspace_array)
array_example = np.array([[1, 2, 3], [4, 5, 6]])
print("陣列:\n", array_example)
print("形狀 (shape):", array_example.shape) # (行數, 列數)
print("維度 (ndim):", array_example.ndim) # 陣列的軸數
print("大小 (size):", array_example.size) # 陣列中元素的總數
print("數據類型 (dtype):", array_example.dtype) # 陣列中元素的數據類型
# 一維陣列索引與切片
arr_1d = np.arange(10)
print("一維陣列:", arr_1d)
print("第一個元素:", arr_1d[0])
print("從索引2到5的元素:", arr_1d[2:6])
print("最後一個元素:", arr_1d[-1])
# 二維陣列索引與切片
arr_2d = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
print("\n二維陣列:\n", arr_2d)
print("第二行第一列的元素:", arr_2d[1, 0]) # 等同於 arr_2d[1][0]
print("第一行所有元素:", arr_2d[0, :])
print("所有行第二列的元素:", arr_2d[:, 1])
print("前兩行和後兩列的子陣列:\n", arr_2d[:2, 1:])
arr = np.arange(12)
print("原始陣列:", arr)
# reshape: 將一維陣列變為 3x4 的二維陣列
reshaped_arr = arr.reshape((3, 4))
print("\nreshape 為 3x4:\n", reshaped_arr)
# flatten: 將多維陣列展平為一維陣列
flattened_arr = reshaped_arr.flatten()
print("\nflatten 為一維:", flattened_arr)
# transpose: 轉置陣列 (行列互換)
transposed_arr = reshaped_arr.transpose()
print("\n轉置陣列:\n", transposed_arr)
1.3 NumPy 的廣播 (Broadcasting) 機制
NumPy 的廣播機制允許在不同形狀的陣列之間執行算術運算,而無需顯式地複製數據。這大大提高了運算的效率和程式碼的簡潔性。
NumPy 的廣播機制允許在不同形狀的陣列之間執行算術運算,而無需顯式地複製數據,這大大提高了運算的效率和程式碼的簡潔性。廣播規則主要有三點:如果兩個陣列的維度數不同,那麼維度較小的陣列的形狀會在前面填充 1,直到它們的維度數相同;對於每個維度,如果兩個陣列的長度相同,或者其中一個陣列的長度為 1,那麼它們是兼容的;如果兩個陣列在任何維度上都不兼容,則會引發 ValueError。理解這些規則對於在不同形狀陣列運算中有效應用廣播至關重要。
import numpy as np
a = np.array([1, 2, 3]) # 形狀 (3,)
b = 2 # 純量
print("陣列 a:", a)
print("純量 b:", b)
print("a + b (廣播):", a + b) # b 被廣播成 [2, 2, 2]
matrix = np.array([[1, 2, 3], [4, 5, 6]]) # 形狀 (2, 3)
vector = np.array([10, 20, 30]) # 形狀 (3,)
print("\n矩陣:\n", matrix)
print("向量:", vector)
print("matrix + vector (廣播):\n", matrix + vector) # vector 被廣播成 [[10, 20, 30], [10, 20, 30]]
# 另一個廣播範例
m1 = np.array([[1, 2, 3]]) # 形狀 (1, 3)
m2 = np.array([[10], [20]]) # 形狀 (2, 1)
print("\nm1:\n", m1)
print("m2:\n", m2)
print("m1 + m2 (廣播):\n", m1 + m2)
# m1 被廣播成 [[1, 2, 3], [1, 2, 3]]
# m2 被廣播成 [[10, 10, 10], [20, 20, 20]]
# 結果為 [[11, 12, 13], [21, 22, 23]]
import numpy as np
a = np.array([1, 2, 3]) # 形狀 (3,)
b = 2 # 純量
print("陣列 a:", a)
print("純量 b:", b)
print("a + b (廣播):", a + b) # b 被廣播成 [2, 2, 2]
matrix = np.array([[1, 2, 3], [4, 5, 6]]) # 形狀 (2, 3)
vector = np.array([10, 20, 30]) # 形狀 (3,)
print("\n矩陣:\n", matrix)
print("向量:", vector)
print("matrix + vector (廣播):\n", matrix + vector) # vector 被廣播成 [[10, 20, 30], [10, 20, 30]]
# 另一個廣播範例
m1 = np.array([[1, 2, 3]]) # 形狀 (1, 3)
m2 = np.array([[10], [20]]) # 形狀 (2, 1)
print("\nm1:\n", m1)
print("m2:\n", m2)
print("m1 + m2 (廣播):\n", m1 + m2)
# m1 被廣播成 [[1, 2, 3], [1, 2, 3]]
# m2 被廣播成 [[10, 10, 10], [20, 20, 20]]
# 結果為 [[11, 12, 13], [21, 22, 23]]
1.4 常用數學運算與統計函數
NumPy 提供了豐富的數學運算和統計函數,可以直接應用於陣列。
NumPy 提供了豐富的數學運算和統計函數,可以直接應用於陣列。這些函數包括基本算術運算,如加、減、乘、除,這些運算都是元素級的。此外,NumPy 還提供了多種聚合函數,如 sum, mean, median, std, min, max,用於計算陣列的統計量,並且可以指定 axis 參數來沿著特定軸進行計算。NumPy 也是進行線性代數操作的強大工具,支援點積 (dot) 和矩陣乘法 (@)。在數據篩選方面,NumPy 允許使用條件篩選與布林索引,透過布林陣列來高效地篩選數據,這在數據分析中非常有用。
以下是相關的程式碼範例:
import numpy as np
a = np.array([1, 2, 3])
b = np.array([4, 5, 6])
print("a + b:", a + b)
print("a - b:", a - b)
print("a * b:", a * b) # 元素級乘法
print("a / b:", a / b)
arr = np.array([[1, 2, 3], [4, 5, 6]])
print("陣列:\n", arr)
print("總和:", np.sum(arr))
print("平均值:", np.mean(arr))
print("標準差:", np.std(arr))
print("最大值:", np.max(arr))
print("最小值:", np.min(arr))
# 沿著軸進行計算 (axis=0 表示列,axis=1 表示行)
print("\n沿著列的總和:", np.sum(arr, axis=0))
print("沿著行的平均值:", np.mean(arr, axis=1))
a = np.array([[1, 2], [3, 4]])
b = np.array([[5, 6], [7, 8]])
print("矩陣 a:\n", a)
print("矩陣 b:\n", b)
# 點積 (np.dot(a, b))
print("\n點積 (np.dot(a, b)):\n", np.dot(a, b))
# 矩陣乘法 (Python 3.5+ 支援 @ 運算符)
print("\n矩陣乘法 (a @ b):\n", a @ b)
arr = np.arange(10)
print("原始陣列:", arr)
# 篩選出大於 5 的元素
mask = arr > 5
print("布林遮罩:", mask)
print("大於 5 的元素:", arr[mask])
# 簡寫形式
print("簡寫形式篩選大於 5 的元素:", arr[arr > 5])
# 多重條件篩選
print("大於 3 且小於 7 的元素:", arr[(arr > 3) & (arr < 7)])
import numpy as np
a = np.array([1, 2, 3])
b = np.array([4, 5, 6])
print("a + b:", a + b)
print("a - b:", a - b)
print("a * b:", a * b) # 元素級乘法
print("a / b:", a / b)
arr = np.array([[1, 2, 3], [4, 5, 6]])
print("陣列:\n", arr)
print("總和:", np.sum(arr))
print("平均值:", np.mean(arr))
print("標準差:", np.std(arr))
print("最大值:", np.max(arr))
print("最小值:", np.min(arr))
# 沿著軸進行計算 (axis=0 表示列,axis=1 表示行)
print("\n沿著列的總和:", np.sum(arr, axis=0))
print("沿著行的平均值:", np.mean(arr, axis=1))
a = np.array([[1, 2], [3, 4]])
b = np.array([[5, 6], [7, 8]])
print("矩陣 a:\n", a)
print("矩陣 b:\n", b)
# 點積 (np.dot(a, b))
print("\n點積 (np.dot(a, b)):\n", np.dot(a, b))
# 矩陣乘法 (Python 3.5+ 支援 @ 運算符)
print("\n矩陣乘法 (a @ b):\n", a @ b)
arr = np.arange(10)
print("原始陣列:", arr)
# 篩選出大於 5 的元素
mask = arr > 5
print("布林遮罩:", mask)
print("大於 5 的元素:", arr[mask])
# 簡寫形式
print("簡寫形式篩選大於 5 的元素:", arr[arr > 5])
# 多重條件篩選
print("大於 3 且小於 7 的元素:", arr[(arr > 3) & (arr < 7)])
1.5 實戰範例:創建與操作簡單數據陣列
透過實際範例,我們可以更好地理解 NumPy 的應用。例如,可以利用 NumPy 輕鬆計算學生考試成績的平均分和標準差,快速獲得數據的統計概況。假設我們有一組學生的數學、物理、化學成績數據,NumPy 可以幫助我們計算每門課的平均分、每位學生的總分和平均分,以及找出不及格的成績數量。此外,NumPy 在圖像處理中也扮演著基礎角色,例如對圖像數據進行簡單的像素操作,圖像在電腦中通常表示為多維陣列,每個元素代表一個像素的顏色強度,NumPy 可以實現圖像反轉、亮度調整和區域提取等操作,這展示了其在多維數據處理上的強大能力。
以下是相關的程式碼範例:
import numpy as np
# 學生考試成績 (數學, 物理, 化學)
# 假設有5位學生
scores = np.array([
[85, 90, 78], # 學生1
[72, 88, 95], # 學生2
[90, 75, 80], # 學生3
[60, 70, 65], # 學生4
[92, 85, 88] # 學生5
])
print("學生考試成績:\n", scores)
# 計算每門課的平均分 (沿著 axis=0,即列)
math_avg = np.mean(scores[:, 0])
physics_avg = np.mean(scores[:, 1])
chemistry_avg = np.mean(scores[:, 2])
print("\n數學平均分:", math_avg)
print("物理平均分:", physics_avg)
print("化學平均分:", chemistry_avg)
# 計算每位學生的總分和平均分 (沿著 axis=1,即行)
total_scores = np.sum(scores, axis=1)
student_avg_scores = np.mean(scores, axis=1)
print("\n每位學生的總分:", total_scores)
print("每位學生的平均分:", student_avg_scores)
# 計算所有成績的總平均分
overall_avg = np.mean(scores)
print("\n所有成績的總平均分:", overall_avg)
# 找出成績最高的學生和科目 (簡化處理,找出最大值)
max_score = np.max(scores)
print("最高分:", max_score)
# 找出不及格 (假設60分以下) 的成績數量
fail_count = np.sum(scores < 60)
print("不及格的成績數量:", fail_count)
import numpy as np
# 學生考試成績 (數學, 物理, 化學)
# 假設有5位學生
scores = np.array([
[85, 90, 78], # 學生1
[72, 88, 95], # 學生2
[90, 75, 80], # 學生3
[60, 70, 65], # 學生4
[92, 85, 88] # 學生5
])
print("學生考試成績:\n", scores)
# 計算每門課的平均分 (沿著 axis=0,即列)
math_avg = np.mean(scores[:, 0])
physics_avg = np.mean(scores[:, 1])
chemistry_avg = np.mean(scores[:, 2])
print("\n數學平均分:", math_avg)
print("物理平均分:", physics_avg)
print("化學平均分:", chemistry_avg)
# 計算每位學生的總分和平均分 (沿著 axis=1,即行)
total_scores = np.sum(scores, axis=1)
student_avg_scores = np.mean(scores, axis=1)
print("\n每位學生的總分:", total_scores)
print("每位學生的平均分:", student_avg_scores)
# 計算所有成績的總平均分
overall_avg = np.mean(scores)
print("\n所有成績的總平均分:", overall_avg)
# 找出成績最高的學生和科目 (簡化處理,找出最大值)
max_score = np.max(scores)
print("最高分:", max_score)
# 找出不及格 (假設60分以下) 的成績數量
fail_count = np.sum(scores < 60)
print("不及格的成績數量:", fail_count)
import numpy as np
# 模擬一個 3x3 的灰度圖像 (像素值從 0 到 255)
# 這裡我們用一個簡單的二維陣列表示
image_data = np.array([
[100, 120, 150],
[130, 160, 180],
[110, 140, 170]
], dtype=np.uint8) # 使用 np.uint8 數據類型表示像素值
print("原始圖像數據:\n", image_data)
# 圖像反轉 (像素值取反,例如 255 - 原始值)
inverted_image = 255 - image_data
print("\n反轉後的圖像數據:\n", inverted_image)
# 調整亮度 (例如,所有像素值增加 50,並確保不超過 255)
brightened_image = np.clip(image_data + 50, 0, 255)
print("\n亮度調整後的圖像數據:\n", brightened_image)
# 提取圖像的某個區域 (切片操作)
roi = image_data[0:2, 0:2] # 提取左上角 2x2 區域
print("\n提取的圖像區域 (ROI):\n", roi)
import numpy as np
# 模擬一個 3x3 的灰度圖像 (像素值從 0 到 255)
# 這裡我們用一個簡單的二維陣列表示
image_data = np.array([
[100, 120, 150],
[130, 160, 180],
[110, 140, 170]
], dtype=np.uint8) # 使用 np.uint8 數據類型表示像素值
print("原始圖像數據:\n", image_data)
# 圖像反轉 (像素值取反,例如 255 - 原始值)
inverted_image = 255 - image_data
print("\n反轉後的圖像數據:\n", inverted_image)
# 調整亮度 (例如,所有像素值增加 50,並確保不超過 255)
brightened_image = np.clip(image_data + 50, 0, 255)
print("\n亮度調整後的圖像數據:\n", brightened_image)
# 提取圖像的某個區域 (切片操作)
roi = image_data[0:2, 0:2] # 提取左上角 2x2 區域
print("\n提取的圖像區域 (ROI):\n", roi)
常見錯誤提醒:
- 數據類型不匹配:NumPy 陣列要求所有元素數據類型一致。如果嘗試將不同類型的數據放入陣列,NumPy 會嘗試進行類型轉換,可能導致意外結果或錯誤。
- 形狀不匹配:在進行陣列運算時,如果陣列形狀不滿足廣播規則,會引發
ValueError。務必檢查陣列的shape屬性。 - 修改原始陣列:某些 NumPy 操作會返回新的陣列,而有些則會直接修改原始陣列。在進行操作時,請注意函數的行為,必要時使用
copy()方法來避免意外修改。
第二章:Pandas 數據處理與分析:結構化數據的利器
2.1 什麼是 Pandas?為何是數據分析的核心工具?
Pandas (Python Data Analysis Library) 是一個開源的 Python 函式庫,專為數據操作和分析而設計。它建立在 NumPy 之上,提供了高效、靈活且易於使用的數據結構,使得處理表格型數據變得輕而易舉。Pandas 之所以成為數據分析的核心工具,主要歸因於其以下特性:
Pandas 之所以成為數據分析的核心工具,主要歸因於其強大的數據結構、數據清洗與預處理能力、數據操作與轉換功能以及與其他函式庫的無縫整合。Pandas 引入了兩種核心數據結構:Series (一維帶標籤陣列) 和 DataFrame (二維帶標籤表格數據結構),它們能夠高效地處理各種數據類型,包括數值、字串、時間序列等。它提供了豐富的函數和方法來處理數據中的缺失值、重複值、異常值,以及進行數據類型轉換、數據格式化等操作,是數據清洗階段的利器。此外,Pandas 支援高效的數據篩選、排序、分組聚合、合併、連接等操作,能夠輕鬆地對數據進行重塑和轉換,以滿足不同的分析需求。最重要的是,Pandas 與 NumPy、Matplotlib、Scikit-learn 等科學計算和機器學習函式庫無縫整合,形成了一個強大的數據科學生態系統。
總之,Pandas 極大地簡化了數據的載入、清洗、轉換和分析過程,是任何數據分析師或數據科學家不可或缺的工具。
2.2 Series 與 DataFrame 的創建與基本操作
Pandas 的核心是 Series 和 DataFrame。理解它們的創建和基本操作是掌握 Pandas 的關鍵。
Pandas 的核心是 Series 和 DataFrame。理解它們的創建和基本操作是掌握 Pandas 的關鍵。我們可以從列表、字典或 NumPy 陣列創建 Series 與 DataFrame。DataFrame 具有行索引 (index) 與列 (columns),這使得數據的存取更加直觀。在數據選取方面,Pandas 提供了 loc (基於標籤) 和 iloc (基於整數位置) 兩種主要方式來選取數據。此外,Pandas 也支援方便地添加、修改、刪除列與行,以靈活地操作數據結構。
以下是相關的程式碼範例:
import pandas as pd
import numpy as np
# 從列表創建 Series
s_list = pd.Series([1, 3, 5, np.nan, 6, 8])
print("從列表創建的 Series:\n", s_list)
# 從 NumPy 陣列創建 Series
s_np = pd.Series(np.random.randn(5), index=['a', 'b', 'c', 'd', 'e'])
print("\n從 NumPy 陣列創建的 Series (帶索引):\n", s_np)
# 從字典創建 Series
s_dict = pd.Series({'數學': 90, '物理': 85, '化學': 92})
print("\n從字典創建的 Series:\n", s_dict)
# 從字典創建 DataFrame
data = {
'Name': ['Alice', 'Bob', 'Charlie', 'David'],
'Age': [25, 30, 35, 28],
'City': ['New York', 'Paris', 'London', 'Tokyo']
}
df_dict = pd.DataFrame(data)
print("\n從字典創建的 DataFrame:\n", df_dict)
# 從 NumPy 陣列創建 DataFrame
df_np = pd.DataFrame(np.random.rand(4, 3), columns=['A', 'B', 'C'])
print("\n從 NumPy 陣列創建的 DataFrame:\n", df_np)
df = pd.DataFrame({
'A': [1, 2, 3],
'B': [4, 5, 6],
'C': [7, 8, 9]
}, index=['row1', 'row2', 'row3'])
print("DataFrame:\n", df)
print("\n行索引:", df.index)
print("列標籤:", df.columns)
print("數據值:\n", df.values) # 返回 NumPy 陣列
df = pd.DataFrame({
'col1': [1, 2, 3, 4],
'col2': [5, 6, 7, 8],
'col3': [9, 10, 11, 12]
}, index=['a', 'b', 'c', 'd'])
print("DataFrame:\n", df)
# 使用 loc (基於標籤)
print("\n使用 loc 選取 'b' 行:\n", df.loc['b'])
print("使用 loc 選取 'a' 到 'c' 行和 'col1' 到 'col2' 列:\n", df.loc['a':'c', 'col1':'col2'])
print("使用 loc 選取 'a' 行的 'col1':", df.loc['a', 'col1'])
# 使用 iloc (基於整數位置)
print("\n使用 iloc 選取第 1 行:\n", df.iloc[1]) # 索引從 0 開始
print("使用 iloc 選取第 0 到 2 行和第 0 到 1 列:\n", df.iloc[0:3, 0:2])
print("使用 iloc 選取第 0 行的第 0 列:", df.iloc[0, 0])
# 直接選取列
print("\n選取 'col1' 列:\n", df['col1'])
print("選取多個列:\n", df[['col1', 'col3']])
df = pd.DataFrame({
'Name': ['Alice', 'Bob'],
'Age': [25, 30]
})
print("原始 DataFrame:\n", df)
# 添加新列
df['City'] = ['New York', 'Paris']
print("\n添加 'City' 列後:\n", df)
# 修改列的值
df['Age'] = [26, 31]
print("\n修改 'Age' 列後:\n", df)
# 添加新行 (使用 loc)
df.loc[2] = ['Charlie', 35, 'London'] # 假設索引為 2
print("\n添加新行後:\n", df)
# 刪除列
df_dropped_col = df.drop('City', axis=1) # axis=1 表示刪除列
print("\n刪除 'City' 列後:\n", df_dropped_col)
# 刪除行 (使用 drop)
df_dropped_row = df.drop(0, axis=0) # axis=0 表示刪除行 (索引為 0 的行)
print("\n刪除索引為 0 的行後:\n", df_dropped_row)
import pandas as pd
import numpy as np
# 從列表創建 Series
s_list = pd.Series([1, 3, 5, np.nan, 6, 8])
print("從列表創建的 Series:\n", s_list)
# 從 NumPy 陣列創建 Series
s_np = pd.Series(np.random.randn(5), index=['a', 'b', 'c', 'd', 'e'])
print("\n從 NumPy 陣列創建的 Series (帶索引):\n", s_np)
# 從字典創建 Series
s_dict = pd.Series({'數學': 90, '物理': 85, '化學': 92})
print("\n從字典創建的 Series:\n", s_dict)
# 從字典創建 DataFrame
data = {
'Name': ['Alice', 'Bob', 'Charlie', 'David'],
'Age': [25, 30, 35, 28],
'City': ['New York', 'Paris', 'London', 'Tokyo']
}
df_dict = pd.DataFrame(data)
print("\n從字典創建的 DataFrame:\n", df_dict)
# 從 NumPy 陣列創建 DataFrame
df_np = pd.DataFrame(np.random.rand(4, 3), columns=['A', 'B', 'C'])
print("\n從 NumPy 陣列創建的 DataFrame:\n", df_np)
df = pd.DataFrame({
'A': [1, 2, 3],
'B': [4, 5, 6],
'C': [7, 8, 9]
}, index=['row1', 'row2', 'row3'])
print("DataFrame:\n", df)
print("\n行索引:", df.index)
print("列標籤:", df.columns)
print("數據值:\n", df.values) # 返回 NumPy 陣列
df = pd.DataFrame({
'col1': [1, 2, 3, 4],
'col2': [5, 6, 7, 8],
'col3': [9, 10, 11, 12]
}, index=['a', 'b', 'c', 'd'])
print("DataFrame:\n", df)
# 使用 loc (基於標籤)
print("\n使用 loc 選取 'b' 行:\n", df.loc['b'])
print("使用 loc 選取 'a' 到 'c' 行和 'col1' 到 'col2' 列:\n", df.loc['a':'c', 'col1':'col2'])
print("使用 loc 選取 'a' 行的 'col1':", df.loc['a', 'col1'])
# 使用 iloc (基於整數位置)
print("\n使用 iloc 選取第 1 行:\n", df.iloc[1]) # 索引從 0 開始
print("使用 iloc 選取第 0 到 2 行和第 0 到 1 列:\n", df.iloc[0:3, 0:2])
print("使用 iloc 選取第 0 行的第 0 列:", df.iloc[0, 0])
# 直接選取列
print("\n選取 'col1' 列:\n", df['col1'])
print("選取多個列:\n", df[['col1', 'col3']])
df = pd.DataFrame({
'Name': ['Alice', 'Bob'],
'Age': [25, 30]
})
print("原始 DataFrame:\n", df)
# 添加新列
df['City'] = ['New York', 'Paris']
print("\n添加 'City' 列後:\n", df)
# 修改列的值
df['Age'] = [26, 31]
print("\n修改 'Age' 列後:\n", df)
# 添加新行 (使用 loc)
df.loc[2] = ['Charlie', 35, 'London'] # 假設索引為 2
print("\n添加新行後:\n", df)
# 刪除列
df_dropped_col = df.drop('City', axis=1) # axis=1 表示刪除列
print("\n刪除 'City' 列後:\n", df_dropped_col)
# 刪除行 (使用 drop)
df_dropped_row = df.drop(0, axis=0) # axis=0 表示刪除行 (索引為 0 的行)
print("\n刪除索引為 0 的行後:\n", df_dropped_row)
2.3 數據載入 (CSV, Excel) 與儲存
Pandas 提供了方便的函數來讀取和寫入各種數據格式,其中 CSV 和 Excel 是最常用的。
Pandas 提供了方便的函數來讀取和寫入各種數據格式,其中 CSV 和 Excel 是最常用的。我們可以透過 read_csv 和 read_excel 函數輕鬆載入數據,並使用 to_csv 和 to_excel 函數將處理後的數據導出。在處理文件時,特別是包含中文或其他非 ASCII 字元的數據,編碼問題很常見,通常建議使用 utf-8 編碼來避免亂碼。
以下是相關的程式碼範例:
import pandas as pd
import os
# 創建一個模擬的 CSV 文件
csv_data = """Name,Age,City
Alice,25,New York
Bob,30,Paris
Charlie,35,London
"""
with open(\'sample.csv\', \'w\') as f:
f.write(csv_data)
# 載入 CSV 文件
df_csv = pd.read_csv(\'sample.csv\')
print("從 CSV 載入的 DataFrame:\n", df_csv)
# 創建一個模擬的 Excel 文件 (需要 openpyxl 函式庫)
# 如果沒有安裝,請先運行:pip install openpyxl
excel_data = pd.DataFrame({
\'Product\': [\'A\', \'B\', \'C\'],
\'Price\': [100, 150, 200]
})
excel_data.to_excel(\'sample.xlsx\', index=False)
# 載入 Excel 文件
df_excel = pd.read_excel(\'sample.xlsx\')
print("\n從 Excel 載入的 DataFrame:\n", df_excel)
# 清理創建的測試文件
os.remove(\'sample.csv\')
os.remove(\'sample.xlsx\')
df = pd.DataFrame({
\'Name\': [\'Alice\', \'Bob\'],
\'Score\': [90, 85]
})
# 導出為 CSV 文件 (不包含索引)
df.to_csv(\'output.csv\', index=False)
print("DataFrame 已導出至 output.csv")
# 導出為 Excel 文件 (不包含索引)
df.to_excel(\'output.xlsx\', index=False)
print("DataFrame 已導出至 output.xlsx")
# 清理創建的測試文件
os.remove(\'output.csv\')
os.remove(\'output.xlsx\')
# 創建一個包含中文的 CSV 文件,並指定編碼
chinese_data = """姓名,年齡,城市
小明,28,北京
小紅,24,上海
"""
with open(\'chinese_sample.csv\', \'w\', encoding=\'utf-8\') as f:
f.write(chinese_data)
# 載入時指定編碼
df_chinese = pd.read_csv(\'chinese_sample.csv\', encoding=\'utf-8\')
print("從包含中文的 CSV 載入的 DataFrame:\n", df_chinese)
# 清理創建的測試文件
os.remove(\'chinese_sample.csv\')
import pandas as pd
import os
# 創建一個模擬的 CSV 文件
csv_data = """Name,Age,City
Alice,25,New York
Bob,30,Paris
Charlie,35,London
"""
with open(\'sample.csv\', \'w\') as f:
f.write(csv_data)
# 載入 CSV 文件
df_csv = pd.read_csv(\'sample.csv\')
print("從 CSV 載入的 DataFrame:\n", df_csv)
# 創建一個模擬的 Excel 文件 (需要 openpyxl 函式庫)
# 如果沒有安裝,請先運行:pip install openpyxl
excel_data = pd.DataFrame({
\'Product\': [\'A\', \'B\', \'C\'],
\'Price\': [100, 150, 200]
})
excel_data.to_excel(\'sample.xlsx\', index=False)
# 載入 Excel 文件
df_excel = pd.read_excel(\'sample.xlsx\')
print("\n從 Excel 載入的 DataFrame:\n", df_excel)
# 清理創建的測試文件
os.remove(\'sample.csv\')
os.remove(\'sample.xlsx\')
df = pd.DataFrame({
\'Name\': [\'Alice\', \'Bob\'],
\'Score\': [90, 85]
})
# 導出為 CSV 文件 (不包含索引)
df.to_csv(\'output.csv\', index=False)
print("DataFrame 已導出至 output.csv")
# 導出為 Excel 文件 (不包含索引)
df.to_excel(\'output.xlsx\', index=False)
print("DataFrame 已導出至 output.xlsx")
# 清理創建的測試文件
os.remove(\'output.csv\')
os.remove(\'output.xlsx\')
# 創建一個包含中文的 CSV 文件,並指定編碼
chinese_data = """姓名,年齡,城市
小明,28,北京
小紅,24,上海
"""
with open(\'chinese_sample.csv\', \'w\', encoding=\'utf-8\') as f:
f.write(chinese_data)
# 載入時指定編碼
df_chinese = pd.read_csv(\'chinese_sample.csv\', encoding=\'utf-8\')
print("從包含中文的 CSV 載入的 DataFrame:\n", df_chinese)
# 清理創建的測試文件
os.remove(\'chinese_sample.csv\')
2.4 數據清洗:處理缺失值、重複值與數據類型轉換
數據清洗是數據分析流程中至關重要的一步,它確保了數據的質量和分析結果的準確性。
數據清洗是數據分析流程中至關重要的一步,它確保了數據的質量和分析結果的準確性。Pandas 提供了強大的功能來處理數據中的各種問題,包括檢測缺失值 (isnull, notnull)、處理缺失值 (dropna, fillna)、檢測與處理重複值 (duplicated, drop_duplicates) 以及數據類型轉換 (astype)。透過這些方法,我們可以有效地清理數據,使其符合分析要求。例如,dropna() 可以刪除包含缺失值的行或列,而 fillna() 則可以用指定的值填充缺失值。duplicated() 和 drop_duplicates() 則用於識別和移除重複的數據行。此外,astype() 函數能夠將數據列轉換為正確的數據類型,這對於節省記憶體和確保運算正確性至關重要。
以下是相關的程式碼範例:
import pandas as pd
import numpy as np
df = pd.DataFrame({
'A': [1, 2, np.nan, 4],
'B': [5, np.nan, 7, 8],
'C': [9, 10, 11, 12]
})
print("原始 DataFrame:\n", df)
print("\n檢測缺失值 (isnull):\n", df.isnull())
print("\n檢測非缺失值 (notnull):\n", df.notnull())
print("\n每列的缺失值數量:\n", df.isnull().sum())
# 刪除包含任何缺失值的行
df_dropped_na = df.dropna()
print("刪除包含缺失值的行後:\n", df_dropped_na)
# 刪除所有值都為缺失值的行 (how='all')
df_all_na = pd.DataFrame({
'X': [1, np.nan],
'Y': [np.nan, np.nan]
})
df_dropped_all_na = df_all_na.dropna(how='all')
print("\n刪除所有值都為缺失值的行後:\n", df_dropped_all_na)
# 用特定值填充缺失值
df_filled_zero = df.fillna(0)
print("\n用 0 填充缺失值後:\n", df_filled_zero)
# 用列的平均值填充缺失值
df_filled_mean = df.fillna(df.mean())
print("\n用列平均值填充缺失值後:\n", df_filled_mean)
# 向前填充 (ffill) 或向後填充 (bfill)
df_ffill = df.fillna(method='ffill')
print("\n向前填充缺失值後:\n", df_ffill)
df = pd.DataFrame({
'col1': ['A', 'B', 'A', 'C'],
'col2': [1, 2, 1, 3]
})
print("原始 DataFrame:\n", df)
# 檢測重複行 (預設保留第一個)
print("\n檢測重複行:\n", df.duplicated())
# 刪除重複行 (預設保留第一個)
df_no_duplicates = df.drop_duplicates()
print("\n刪除重複行後:\n", df_no_duplicates)
# 刪除重複行 (保留最後一個)
df_no_duplicates_last = df.drop_duplicates(keep='last')
print("\n刪除重複行 (保留最後一個) 後:\n", df_no_duplicates_last)
# 根據特定列檢測重複值
df_col1_duplicates = df.duplicated(subset=['col1'])
print("\n根據 'col1' 檢測重複值:\n", df_col1_duplicates)
df = pd.DataFrame({
'A': [1, 2, 3],
'B': ['10', '20', '30'],
'C': [1.1, 2.2, 3.3]
})
print("原始 DataFrame 數據類型:\n", df.dtypes)
# 將 'B' 列從字串轉換為整數
df['B'] = df['B'].astype(int)
print("\n轉換 'B' 列為整數後數據類型:\n", df.dtypes)
# 將 'C' 列從浮點數轉換為整數
df['C'] = df['C'].astype(int)
print("\n轉換 'C' 列為整數後數據類型:\n", df.dtypes)
# 嘗試將無法轉換的值轉換為數值會報錯,可以使用 errors='coerce' 處理
df_str_num = pd.DataFrame({'D': ['1', '2', 'abc', '4']})
# df_str_num['D'].astype(int) # 這會報錯
df_str_num['D_numeric'] = pd.to_numeric(df_str_num['D'], errors='coerce')
print("\n包含無法轉換值的列轉換為數值後:\n", df_str_num)
import pandas as pd
import numpy as np
df = pd.DataFrame({
'A': [1, 2, np.nan, 4],
'B': [5, np.nan, 7, 8],
'C': [9, 10, 11, 12]
})
print("原始 DataFrame:\n", df)
print("\n檢測缺失值 (isnull):\n", df.isnull())
print("\n檢測非缺失值 (notnull):\n", df.notnull())
print("\n每列的缺失值數量:\n", df.isnull().sum())
# 刪除包含任何缺失值的行
df_dropped_na = df.dropna()
print("刪除包含缺失值的行後:\n", df_dropped_na)
# 刪除所有值都為缺失值的行 (how='all')
df_all_na = pd.DataFrame({
'X': [1, np.nan],
'Y': [np.nan, np.nan]
})
df_dropped_all_na = df_all_na.dropna(how='all')
print("\n刪除所有值都為缺失值的行後:\n", df_dropped_all_na)
# 用特定值填充缺失值
df_filled_zero = df.fillna(0)
print("\n用 0 填充缺失值後:\n", df_filled_zero)
# 用列的平均值填充缺失值
df_filled_mean = df.fillna(df.mean())
print("\n用列平均值填充缺失值後:\n", df_filled_mean)
# 向前填充 (ffill) 或向後填充 (bfill)
df_ffill = df.fillna(method='ffill')
print("\n向前填充缺失值後:\n", df_ffill)
df = pd.DataFrame({
'col1': ['A', 'B', 'A', 'C'],
'col2': [1, 2, 1, 3]
})
print("原始 DataFrame:\n", df)
# 檢測重複行 (預設保留第一個)
print("\n檢測重複行:\n", df.duplicated())
# 刪除重複行 (預設保留第一個)
df_no_duplicates = df.drop_duplicates()
print("\n刪除重複行後:\n", df_no_duplicates)
# 刪除重複行 (保留最後一個)
df_no_duplicates_last = df.drop_duplicates(keep='last')
print("\n刪除重複行 (保留最後一個) 後:\n", df_no_duplicates_last)
# 根據特定列檢測重複值
df_col1_duplicates = df.duplicated(subset=['col1'])
print("\n根據 'col1' 檢測重複值:\n", df_col1_duplicates)
df = pd.DataFrame({
'A': [1, 2, 3],
'B': ['10', '20', '30'],
'C': [1.1, 2.2, 3.3]
})
print("原始 DataFrame 數據類型:\n", df.dtypes)
# 將 'B' 列從字串轉換為整數
df['B'] = df['B'].astype(int)
print("\n轉換 'B' 列為整數後數據類型:\n", df.dtypes)
# 將 'C' 列從浮點數轉換為整數
df['C'] = df['C'].astype(int)
print("\n轉換 'C' 列為整數後數據類型:\n", df.dtypes)
# 嘗試將無法轉換的值轉換為數值會報錯,可以使用 errors='coerce' 處理
df_str_num = pd.DataFrame({'D': ['1', '2', 'abc', '4']})
# df_str_num['D'].astype(int) # 這會報錯
df_str_num['D_numeric'] = pd.to_numeric(df_str_num['D'], errors='coerce')
print("\n包含無法轉換值的列轉換為數值後:\n", df_str_num)
2.5 數據篩選、排序與分組聚合 (groupby)
這些是 Pandas 中最常用的數據操作,用於從數據中提取有意義的資訊。
Pandas 中最常用的數據操作包括條件篩選、數據排序和分組聚合 (groupby),這些功能用於從數據中提取有意義的資訊。透過布林條件,我們可以靈活地選取符合特定條件的數據。sort_values 和 sort_index 函數則提供了強大的數據排序能力,可以根據單一或多個列進行升序或降序排列。而 groupby 函數是 Pandas 中一個非常強大的功能,它允許我們將數據根據一個或多個鍵進行分組,然後對每個組應用聚合函數,如統計、計數、求和等,從而實現複雜的數據分析。
以下是相關的程式碼範例:
import pandas as pd
df = pd.DataFrame({
'Name': ['Alice', 'Bob', 'Charlie', 'David', 'Eve'],
'Age': [25, 30, 35, 28, 22],
'City': ['New York', 'Paris', 'London', 'New York', 'Paris'],
'Score': [85, 92, 78, 95, 88]
})
print("原始 DataFrame:\n", df)
# 選取 Age 大於 28 的行
df_age_filter = df[df['Age'] > 28]
print("\nAge 大於 28 的行:\n", df_age_filter)
# 選取 City 為 'New York' 的行
df_city_filter = df[df['City'] == 'New York']
print("\nCity 為 'New York' 的行:\n", df_city_filter)
# 多重條件篩選 (Age 大於 25 且 Score 大於 90)
df_multi_filter = df[(df['Age'] > 25) & (df['Score'] > 90)]
print("\nAge 大於 25 且 Score 大於 90 的行:\n", df_multi_filter)
# 使用 isin 篩選多個值
df_isin_filter = df[df['City'].isin(['New York', 'London'])]
print("\nCity 為 'New York' 或 'London' 的行:\n", df_isin_filter)
# 根據 'Age' 列升序排序
df_sorted_age = df.sort_values(by='Age')
print("\n根據 'Age' 升序排序:\n", df_sorted_age)
# 根據 'Age' 降序排序,並在 'Age' 相同時根據 'Score' 升序排序
df_sorted_multi = df.sort_values(by=['Age', 'Score'], ascending=[False, True])
print("\n根據 'Age' 降序,'Score' 升序排序:\n", df_sorted_multi)
# 根據索引排序
df_with_custom_index = df.set_index('Name')
print("\n帶有自定義索引的 DataFrame:\n", df_with_custom_index)
df_sorted_index = df_with_custom_index.sort_index()
print("\n根據索引排序後:\n", df_sorted_index)
df = pd.DataFrame({
'City': ['New York', 'Paris', 'London', 'New York', 'Paris'],
'Product': ['A', 'B', 'A', 'C', 'B'],
'Sales': [100, 150, 120, 200, 180]
})
print("原始 DataFrame:\n", df)
# 根據 'City' 分組,計算每個城市的總銷售額
grouped_by_city = df.groupby('City')['Sales'].sum()
print("\n按城市分組的總銷售額:\n", grouped_by_city)
# 根據 'City' 和 'Product' 分組,計算每個城市每個產品的平均銷售額
grouped_by_city_product = df.groupby(['City', 'Product'])['Sales'].mean()
print("\n按城市和產品分組的平均銷售額:\n", grouped_by_city_product)
# 對多個聚合函數應用
agg_results = df.groupby('City')['Sales'].agg(['sum', 'mean', 'count'])
print("\n按城市分組的多個聚合結果:\n", agg_results)
import pandas as pd
df = pd.DataFrame({
'Name': ['Alice', 'Bob', 'Charlie', 'David', 'Eve'],
'Age': [25, 30, 35, 28, 22],
'City': ['New York', 'Paris', 'London', 'New York', 'Paris'],
'Score': [85, 92, 78, 95, 88]
})
print("原始 DataFrame:\n", df)
# 選取 Age 大於 28 的行
df_age_filter = df[df['Age'] > 28]
print("\nAge 大於 28 的行:\n", df_age_filter)
# 選取 City 為 'New York' 的行
df_city_filter = df[df['City'] == 'New York']
print("\nCity 為 'New York' 的行:\n", df_city_filter)
# 多重條件篩選 (Age 大於 25 且 Score 大於 90)
df_multi_filter = df[(df['Age'] > 25) & (df['Score'] > 90)]
print("\nAge 大於 25 且 Score 大於 90 的行:\n", df_multi_filter)
# 使用 isin 篩選多個值
df_isin_filter = df[df['City'].isin(['New York', 'London'])]
print("\nCity 為 'New York' 或 'London' 的行:\n", df_isin_filter)
# 根據 'Age' 列升序排序
df_sorted_age = df.sort_values(by='Age')
print("\n根據 'Age' 升序排序:\n", df_sorted_age)
# 根據 'Age' 降序排序,並在 'Age' 相同時根據 'Score' 升序排序
df_sorted_multi = df.sort_values(by=['Age', 'Score'], ascending=[False, True])
print("\n根據 'Age' 降序,'Score' 升序排序:\n", df_sorted_multi)
# 根據索引排序
df_with_custom_index = df.set_index('Name')
print("\n帶有自定義索引的 DataFrame:\n", df_with_custom_index)
df_sorted_index = df_with_custom_index.sort_index()
print("\n根據索引排序後:\n", df_sorted_index)
df = pd.DataFrame({
'City': ['New York', 'Paris', 'London', 'New York', 'Paris'],
'Product': ['A', 'B', 'A', 'C', 'B'],
'Sales': [100, 150, 120, 200, 180]
})
print("原始 DataFrame:\n", df)
# 根據 'City' 分組,計算每個城市的總銷售額
grouped_by_city = df.groupby('City')['Sales'].sum()
print("\n按城市分組的總銷售額:\n", grouped_by_city)
# 根據 'City' 和 'Product' 分組,計算每個城市每個產品的平均銷售額
grouped_by_city_product = df.groupby(['City', 'Product'])['Sales'].mean()
print("\n按城市和產品分組的平均銷售額:\n", grouped_by_city_product)
# 對多個聚合函數應用
agg_results = df.groupby('City')['Sales'].agg(['sum', 'mean', 'count'])
print("\n按城市分組的多個聚合結果:\n", agg_results)
2.6 數據合併與連接 (merge, concat)
在實際數據分析中,我們經常需要將來自不同來源的數據集合併起來。
在實際數據分析中,我們經常需要將來自不同來源的數據集合併起來。Pandas 提供了兩種主要的數據合併方式:concat 和 merge。concat 函數主要用於將多個 Series 或 DataFrame 沿著某個軸 (行或列) 堆疊起來,而 merge 函數則用於根據一個或多個共同鍵值將兩個 DataFrame 的行連接起來,類似於資料庫中的 JOIN 操作,支援內連接、左連接、右連接和外連接等多種方式。透過這些功能,我們可以靈活地整合不同數據源,進行更全面的分析。
以下是相關的程式碼範例:
df1 = pd.DataFrame({
'A': ['A0', 'A1'],
'B': ['B0', 'B1']
})
df2 = pd.DataFrame({
'A': ['A2', 'A3'],
'B': ['B2', 'B3']
})
print("df1:\n", df1)
print("\ndf2:\n", df2)
# 沿著行方向連接 (預設 axis=0)
result_rows = pd.concat([df1, df2])
print("\n沿著行方向連接:\n", result_rows)
# 沿著列方向連接 (axis=1)
result_cols = pd.concat([df1, df2], axis=1)
print("\n沿著列方向連接:\n", result_cols)
# 處理索引重複問題 (ignore_index=True)
result_ignore_index = pd.concat([df1, df2], ignore_index=True)
print("\n沿著行方向連接 (忽略索引):\n", result_ignore_index)
import pandas as pd
df_employees = pd.DataFrame({
'employee_id': [1, 2, 3, 4],
'name': ['Alice', 'Bob', 'Charlie', 'David'],
'department_id': [101, 102, 101, 103]
})
df_departments = pd.DataFrame({
'department_id': [101, 102, 104],
'department_name': ['HR', 'IT', 'Marketing']
})
print("df_employees:\n", df_employees)
print("\ndf_departments:\n", df_departments)
# 內連接 (inner join):只保留兩個 DataFrame 中共同的鍵值
inner_join = pd.merge(df_employees, df_departments, on='department_id', how='inner')
print("\n內連接 (inner join):\n", inner_join)
# 左連接 (left join):保留左邊 DataFrame 的所有行,匹配右邊 DataFrame 的行
left_join = pd.merge(df_employees, df_departments, on='department_id', how='left')
print("\n左連接 (left join):\n", left_join)
# 右連接 (right join):保留右邊 DataFrame 的所有行,匹配左邊 DataFrame 的行
right_join = pd.merge(df_employees, df_departments, on='department_id', how='right')
print("\n右連接 (right join):\n", right_join)
# 外連接 (outer join):保留兩個 DataFrame 的所有行,不匹配的用 NaN 填充
outer_join = pd.merge(df_employees, df_departments, on='department_id', how='outer')
print("\n外連接 (outer join):\n", outer_join)
import pandas as pd
# 1月銷售數據
sales_jan = pd.DataFrame({
'Date': ['2026-01-01', '2026-01-02', '2026-01-03'],
'Product_ID': ['P001', 'P002', 'P001'],
'Quantity': [10, 5, 8],
'Price': [50, 120, 50]
})
# 2月銷售數據
sales_feb = pd.DataFrame({
'Date': ['2026-02-01', '2026-02-02', '2026-02-01'],
'Product_ID': ['P002', 'P003', 'P001'],
'Quantity': [7, 12, 15],
'Price': [120, 80, 50]
})
print("1月銷售數據:\n", sales_jan)
print("\n2月銷售數據:\n", sales_feb)
# 使用 concat 合併兩個月的銷售數據
all_sales = pd.concat([sales_jan, sales_feb], ignore_index=True)
print("\n合併後的總銷售數據:\n", all_sales)
# 假設還有一個產品資訊表
product_info = pd.DataFrame({
'Product_ID': ['P001', 'P002', 'P003'],
'Category': ['Electronics', 'Books', 'Clothing']
})
print("\n產品資訊:\n", product_info)
# 將銷售數據與產品資訊合併 (基於 Product_ID)
merged_sales = pd.merge(all_sales, product_info, on='Product_ID', how='left')
print("\n合併產品資訊後的銷售數據:\n", merged_sales)
df1 = pd.DataFrame({
'A': ['A0', 'A1'],
'B': ['B0', 'B1']
})
df2 = pd.DataFrame({
'A': ['A2', 'A3'],
'B': ['B2', 'B3']
})
print("df1:\n", df1)
print("\ndf2:\n", df2)
# 沿著行方向連接 (預設 axis=0)
result_rows = pd.concat([df1, df2])
print("\n沿著行方向連接:\n", result_rows)
# 沿著列方向連接 (axis=1)
result_cols = pd.concat([df1, df2], axis=1)
print("\n沿著列方向連接:\n", result_cols)
# 處理索引重複問題 (ignore_index=True)
result_ignore_index = pd.concat([df1, df2], ignore_index=True)
print("\n沿著行方向連接 (忽略索引):\n", result_ignore_index)
import pandas as pd
df_employees = pd.DataFrame({
'employee_id': [1, 2, 3, 4],
'name': ['Alice', 'Bob', 'Charlie', 'David'],
'department_id': [101, 102, 101, 103]
})
df_departments = pd.DataFrame({
'department_id': [101, 102, 104],
'department_name': ['HR', 'IT', 'Marketing']
})
print("df_employees:\n", df_employees)
print("\ndf_departments:\n", df_departments)
# 內連接 (inner join):只保留兩個 DataFrame 中共同的鍵值
inner_join = pd.merge(df_employees, df_departments, on='department_id', how='inner')
print("\n內連接 (inner join):\n", inner_join)
# 左連接 (left join):保留左邊 DataFrame 的所有行,匹配右邊 DataFrame 的行
left_join = pd.merge(df_employees, df_departments, on='department_id', how='left')
print("\n左連接 (left join):\n", left_join)
# 右連接 (right join):保留右邊 DataFrame 的所有行,匹配左邊 DataFrame 的行
right_join = pd.merge(df_employees, df_departments, on='department_id', how='right')
print("\n右連接 (right join):\n", right_join)
# 外連接 (outer join):保留兩個 DataFrame 的所有行,不匹配的用 NaN 填充
outer_join = pd.merge(df_employees, df_departments, on='department_id', how='outer')
print("\n外連接 (outer join):\n", outer_join)
import pandas as pd
# 1月銷售數據
sales_jan = pd.DataFrame({
'Date': ['2026-01-01', '2026-01-02', '2026-01-03'],
'Product_ID': ['P001', 'P002', 'P001'],
'Quantity': [10, 5, 8],
'Price': [50, 120, 50]
})
# 2月銷售數據
sales_feb = pd.DataFrame({
'Date': ['2026-02-01', '2026-02-02', '2026-02-01'],
'Product_ID': ['P002', 'P003', 'P001'],
'Quantity': [7, 12, 15],
'Price': [120, 80, 50]
})
print("1月銷售數據:\n", sales_jan)
print("\n2月銷售數據:\n", sales_feb)
# 使用 concat 合併兩個月的銷售數據
all_sales = pd.concat([sales_jan, sales_feb], ignore_index=True)
print("\n合併後的總銷售數據:\n", all_sales)
# 假設還有一個產品資訊表
product_info = pd.DataFrame({
'Product_ID': ['P001', 'P002', 'P003'],
'Category': ['Electronics', 'Books', 'Clothing']
})
print("\n產品資訊:\n", product_info)
# 將銷售數據與產品資訊合併 (基於 Product_ID)
merged_sales = pd.merge(all_sales, product_info, on='Product_ID', how='left')
print("\n合併產品資訊後的銷售數據:\n", merged_sales)
以下是相關的程式碼範例:
import pandas as pd
df1 = pd.DataFrame({'A': ['A0', 'A1'], 'B': ['B0', 'B1']})
df2 = pd.DataFrame({'A': ['A2', 'A3'], 'B': ['B2', 'B3']})
print("df1:\n", df1)
print("\ndf2:\n", df2)
# 沿著行方向連接 (預設 axis=0)
result_rows = pd.concat([df1, df2])
print("\n沿著行方向連接:\n", result_rows)
# 沿著列方向連接 (axis=1)
result_cols = pd.concat([df1, df2], axis=1)
print("\n沿著列方向連接:\n", result_cols)
# 處理索引重複問題 (ignore_index=True)
result_ignore_index = pd.concat([df1, df2], ignore_index=True)
print("\n沿著行方向連接 (忽略索引):\n", result_ignore_index)
import pandas as pd
df_employees = pd.DataFrame({
'employee_id': [1, 2, 3, 4],
'name': ['Alice', 'Bob', 'Charlie', 'David'],
'department_id': [101, 102, 101, 103]
})
df_departments = pd.DataFrame({
'department_id': [101, 102, 104],
'department_name': ['HR', 'IT', 'Marketing']
})
print("df_employees:\n", df_employees)
print("\ndf_departments:\n", df_departments)
# 內連接 (inner join):只保留兩個 DataFrame 中共同的鍵值
inner_join = pd.merge(df_employees, df_departments, on='department_id', how='inner')
print("\n內連接 (inner join):\n", inner_join)
# 左連接 (left join):保留左邊 DataFrame 的所有行,匹配右邊 DataFrame 的行
left_join = pd.merge(df_employees, df_departments, on='department_id', how='left')
print("\n左連接 (left join):\n", left_join)
# 右連接 (right join):保留右邊 DataFrame 的所有行,匹配左邊 DataFrame 的行
right_join = pd.merge(df_employees, df_departments, on='department_id', how='right')
print("\n右連接 (right join):\n", right_join)
# 外連接 (outer join):保留兩個 DataFrame 的所有行,不匹配的用 NaN 填充
outer_join = pd.merge(df_employees, df_departments, on='department_id', how='outer')
print("\n外連接 (outer join):\n", outer_join)
import pandas as pd
# 1月銷售數據
sales_jan = pd.DataFrame({
'Date': ['2026-01-01', '2026-01-02', '2026-01-03'],
'Product_ID': ['P001', 'P002', 'P001'],
'Quantity': [10, 5, 8],
'Price': [50, 120, 50]
})
# 2月銷售數據
sales_feb = pd.DataFrame({
'Date': ['2026-02-01', '2026-02-02', '2026-02-01'],
'Product_ID': ['P002', 'P003', 'P001'],
'Quantity': [7, 12, 15],
'Price': [120, 80, 50]
})
print("1月銷售數據:\n", sales_jan)
print("\n2月銷售數據:\n", sales_feb)
# 使用 concat 合併兩個月的銷售數據
all_sales = pd.concat([sales_jan, sales_feb], ignore_index=True)
print("\n合併後的總銷售數據:\n", all_sales)
# 假設還有一個產品資訊表
product_info = pd.DataFrame({
'Product_ID': ['P001', 'P002', 'P003'],
'Category': ['Electronics', 'Books', 'Clothing']
})
print("\n產品資訊:\n", product_info)
# 將銷售數據與產品資訊合併 (基於 Product_ID)
merged_sales = pd.merge(all_sales, product_info, on='Product_ID', how='left')
print("\n合併產品資訊後的銷售數據:\n", merged_sales)
import pandas as pd
df1 = pd.DataFrame({'A': ['A0', 'A1'], 'B': ['B0', 'B1']})
df2 = pd.DataFrame({'A': ['A2', 'A3'], 'B': ['B2', 'B3']})
print("df1:\n", df1)
print("\ndf2:\n", df2)
# 沿著行方向連接 (預設 axis=0)
result_rows = pd.concat([df1, df2])
print("\n沿著行方向連接:\n", result_rows)
# 沿著列方向連接 (axis=1)
result_cols = pd.concat([df1, df2], axis=1)
print("\n沿著列方向連接:\n", result_cols)
# 處理索引重複問題 (ignore_index=True)
result_ignore_index = pd.concat([df1, df2], ignore_index=True)
print("\n沿著行方向連接 (忽略索引):\n", result_ignore_index)
import pandas as pd
df_employees = pd.DataFrame({
'employee_id': [1, 2, 3, 4],
'name': ['Alice', 'Bob', 'Charlie', 'David'],
'department_id': [101, 102, 101, 103]
})
df_departments = pd.DataFrame({
'department_id': [101, 102, 104],
'department_name': ['HR', 'IT', 'Marketing']
})
print("df_employees:\n", df_employees)
print("\ndf_departments:\n", df_departments)
# 內連接 (inner join):只保留兩個 DataFrame 中共同的鍵值
inner_join = pd.merge(df_employees, df_departments, on='department_id', how='inner')
print("\n內連接 (inner join):\n", inner_join)
# 左連接 (left join):保留左邊 DataFrame 的所有行,匹配右邊 DataFrame 的行
left_join = pd.merge(df_employees, df_departments, on='department_id', how='left')
print("\n左連接 (left join):\n", left_join)
# 右連接 (right join):保留右邊 DataFrame 的所有行,匹配左邊 DataFrame 的行
right_join = pd.merge(df_employees, df_departments, on='department_id', how='right')
print("\n右連接 (right join):\n", right_join)
# 外連接 (outer join):保留兩個 DataFrame 的所有行,不匹配的用 NaN 填充
outer_join = pd.merge(df_employees, df_departments, on='department_id', how='outer')
print("\n外連接 (outer join):\n", outer_join)
import pandas as pd
# 1月銷售數據
sales_jan = pd.DataFrame({
'Date': ['2026-01-01', '2026-01-02', '2026-01-03'],
'Product_ID': ['P001', 'P002', 'P001'],
'Quantity': [10, 5, 8],
'Price': [50, 120, 50]
})
# 2月銷售數據
sales_feb = pd.DataFrame({
'Date': ['2026-02-01', '2026-02-02', '2026-02-01'],
'Product_ID': ['P002', 'P003', 'P001'],
'Quantity': [7, 12, 15],
'Price': [120, 80, 50]
})
print("1月銷售數據:\n", sales_jan)
print("\n2月銷售數據:\n", sales_feb)
# 使用 concat 合併兩個月的銷售數據
all_sales = pd.concat([sales_jan, sales_feb], ignore_index=True)
print("\n合併後的總銷售數據:\n", all_sales)
# 假設還有一個產品資訊表
product_info = pd.DataFrame({
'Product_ID': ['P001', 'P002', 'P003'],
'Category': ['Electronics', 'Books', 'Clothing']
})
print("\n產品資訊:\n", product_info)
# 將銷售數據與產品資訊合併 (基於 Product_ID)
merged_sales = pd.merge(all_sales, product_info, on='Product_ID', how='left')
print("\n合併產品資訊後的銷售數據:\n", merged_sales)
2.7 實戰範例:使用真實數據集進行數據清洗與初步分析
為了更貼近真實情境,我們將使用一個虛擬的電商訂單數據集來實際演練 Pandas 的數據清洗與初步分析流程。這個範例將涵蓋數據導入、缺失值與重複值的處理、數據類型轉換,以及最終的初步分析,例如計算總銷售額、找出暢銷產品等,完整地展示一個小型數據分析專案的生命週期。
import pandas as pd
import numpy as np
# 創建一個模擬的電商訂單數據集
data = {
'OrderID': [1001, 1002, 1003, 1004, 1005, 1006, 1007, 1008, 1009, 1010],
'CustomerID': [1, 2, 1, 3, 2, 4, 1, 5, 3, 2],
'OrderDate': ['2026-01-01', '2026-01-01', '2026-01-02', '2026-01-02', '2026-01-03', '2026-01-03', '2026-01-04', '2026-01-04', '2026-01-05', '2026-01-05'],
'Product': ['Laptop', 'Mouse', 'Keyboard', 'Laptop', 'Monitor', 'Mouse', 'Laptop', 'Keyboard', 'Monitor', 'Mouse'],
'Quantity': [1, 2, 1, 1, 1, 1, 2, 1, 1, 3],
'UnitPrice': [1200, 25, 75, 1200, 200, 25, 1200, 75, 200, 25],
'Discount': [0.1, 0, 0, 0.1, 0, 0, 0.1, 0, 0, 0],
'PaymentMethod': ['Credit Card', 'PayPal', 'Credit Card', 'PayPal', 'Credit Card', 'Cash', 'Credit Card', 'PayPal', 'Cash', 'PayPal'],
'ShippingCost': [10, 5, 8, 10, 12, 5, 10, 8, 12, 5]
}
df_orders = pd.DataFrame(data)
# 引入一些缺失值和重複值來模擬真實數據
df_orders.loc[2, 'Quantity'] = np.nan # 模擬缺失值
df_orders.loc[5, 'Product'] = np.nan # 模擬缺失值
df_orders.loc[8, 'CustomerID'] = np.nan # 模擬缺失值
df_orders = pd.concat([df_orders, df_orders.iloc[[0]]], ignore_index=True) # 模擬重複行
print("原始電商訂單數據:\n", df_orders)
print("\n原始數據資訊:")
df_orders.info()
# 1. 數據清洗:處理缺失值
# 檢查缺失值
print("\n缺失值統計:\n", df_orders.isnull().sum())
# 針對 Quantity 和 CustomerID 填充缺失值 (例如,Quantity 填充中位數,CustomerID 填充眾數)
# 注意:這裡為了簡化,直接填充一個固定值,實際應用中應根據業務邏輯處理
df_orders['Quantity'].fillna(df_orders['Quantity'].median(), inplace=True)
df_orders['CustomerID'].fillna(df_orders['CustomerID'].mode()[0], inplace=True)
# 針對 Product 填充缺失值 (例如,填充 'Unknown')
df_orders['Product'].fillna('Unknown', inplace=True)
print("\n填充缺失值後的數據:\n", df_orders)
print("\n填充缺失值後缺失值統計:\n", df_orders.isnull().sum())
# 2. 數據清洗:處理重複值
print("\n重複行數量:", df_orders.duplicated().sum())
df_orders.drop_duplicates(inplace=True)
print("\n刪除重複行後的數據:\n", df_orders)
# 3. 數據類型轉換
# 將 'OrderDate' 轉換為日期時間類型
df_orders['OrderDate'] = pd.to_datetime(df_orders['OrderDate'])
print("\n轉換 'OrderDate' 類型後的數據資訊:")
df_orders.info()
# 4. 創建新特徵:計算總金額
df_orders['TotalPrice'] = df_orders['Quantity'] * df_orders['UnitPrice'] * (1 - df_orders['Discount'])
print("\n添加 'TotalPrice' 列後的數據:\n", df_orders)
# 5. 初步分析:總銷售額
total_sales_amount = df_orders['TotalPrice'].sum()
print(f"\n總銷售額:{total_sales_amount:.2f}")
# 6. 初步分析:暢銷產品
bestselling_products = df_orders.groupby('Product')['Quantity'].sum().sort_values(ascending=False)
print("\n暢銷產品 (按數量):\n", bestselling_products)
# 7. 初步分析:每月銷售趨勢 (假設數據跨越多月)
# 從 OrderDate 提取月份
df_orders['Month'] = df_orders['OrderDate'].dt.month
monthly_sales = df_orders.groupby('Month')['TotalPrice'].sum()
print("\n每月銷售額:\n", monthly_sales)
# 8. 初步分析:不同支付方式的銷售額
payment_method_sales = df_orders.groupby('PaymentMethod')['TotalPrice'].sum().sort_values(ascending=False)
print("\n不同支付方式的銷售額:\n", payment_method_sales)
# 常見錯誤提醒:
# * **數據類型不匹配**:在進行數值運算前,確保相關列的數據類型是數值型。例如,從 CSV 讀取的數字可能被識別為字串,需要使用 `astype()` 或 `pd.to_numeric()` 轉換。
# * **索引錯亂**:在 `concat` 或 `merge` 操作後,如果沒有正確處理索引,可能會導致索引重複或錯亂。使用 `ignore_index=True` 或 `reset_index()` 可以解決此問題。
# * **原地修改 (inplace=True)**:許多 Pandas 方法有 `inplace` 參數。當 `inplace=True` 時,操作會直接修改原始 DataFrame,而不返回新的 DataFrame。這可以節省記憶體,但也可能導致意外的副作用,因此使用時需謹慎。如果希望保留原始 DataFrame,則不要使用 `inplace=True`,而是將結果賦值給一個新的變數。
# * **鏈式賦值警告 (SettingWithCopyWarning)**:當對 DataFrame 的一個「視圖」進行修改時,Pandas 可能會發出 `SettingWithCopyWarning`。這通常發生在篩選後直接賦值。為避免此問題,應明確使用 `.loc` 或 `.iloc` 進行賦值,或使用 `.copy()` 創建一個副本再操作。
import pandas as pd
import numpy as np
# 創建一個模擬的電商訂單數據集
data = {
'OrderID': [1001, 1002, 1003, 1004, 1005, 1006, 1007, 1008, 1009, 1010],
'CustomerID': [1, 2, 1, 3, 2, 4, 1, 5, 3, 2],
'OrderDate': ['2026-01-01', '2026-01-01', '2026-01-02', '2026-01-02', '2026-01-03', '2026-01-03', '2026-01-04', '2026-01-04', '2026-01-05', '2026-01-05'],
'Product': ['Laptop', 'Mouse', 'Keyboard', 'Laptop', 'Monitor', 'Mouse', 'Laptop', 'Keyboard', 'Monitor', 'Mouse'],
'Quantity': [1, 2, 1, 1, 1, 1, 2, 1, 1, 3],
'UnitPrice': [1200, 25, 75, 1200, 200, 25, 1200, 75, 200, 25],
'Discount': [0.1, 0, 0, 0.1, 0, 0, 0.1, 0, 0, 0],
'PaymentMethod': ['Credit Card', 'PayPal', 'Credit Card', 'PayPal', 'Credit Card', 'Cash', 'Credit Card', 'PayPal', 'Cash', 'PayPal'],
'ShippingCost': [10, 5, 8, 10, 12, 5, 10, 8, 12, 5]
}
df_orders = pd.DataFrame(data)
# 引入一些缺失值和重複值來模擬真實數據
df_orders.loc[2, 'Quantity'] = np.nan # 模擬缺失值
df_orders.loc[5, 'Product'] = np.nan # 模擬缺失值
df_orders.loc[8, 'CustomerID'] = np.nan # 模擬缺失值
df_orders = pd.concat([df_orders, df_orders.iloc[[0]]], ignore_index=True) # 模擬重複行
print("原始電商訂單數據:\n", df_orders)
print("\n原始數據資訊:")
df_orders.info()
# 1. 數據清洗:處理缺失值
# 檢查缺失值
print("\n缺失值統計:\n", df_orders.isnull().sum())
# 針對 Quantity 和 CustomerID 填充缺失值 (例如,Quantity 填充中位數,CustomerID 填充眾數)
# 注意:這裡為了簡化,直接填充一個固定值,實際應用中應根據業務邏輯處理
df_orders['Quantity'].fillna(df_orders['Quantity'].median(), inplace=True)
df_orders['CustomerID'].fillna(df_orders['CustomerID'].mode()[0], inplace=True)
# 針對 Product 填充缺失值 (例如,填充 'Unknown')
df_orders['Product'].fillna('Unknown', inplace=True)
print("\n填充缺失值後的數據:\n", df_orders)
print("\n填充缺失值後缺失值統計:\n", df_orders.isnull().sum())
# 2. 數據清洗:處理重複值
print("\n重複行數量:", df_orders.duplicated().sum())
df_orders.drop_duplicates(inplace=True)
print("\n刪除重複行後的數據:\n", df_orders)
# 3. 數據類型轉換
# 將 'OrderDate' 轉換為日期時間類型
df_orders['OrderDate'] = pd.to_datetime(df_orders['OrderDate'])
print("\n轉換 'OrderDate' 類型後的數據資訊:")
df_orders.info()
# 4. 創建新特徵:計算總金額
df_orders['TotalPrice'] = df_orders['Quantity'] * df_orders['UnitPrice'] * (1 - df_orders['Discount'])
print("\n添加 'TotalPrice' 列後的數據:\n", df_orders)
# 5. 初步分析:總銷售額
total_sales_amount = df_orders['TotalPrice'].sum()
print(f"\n總銷售額:{total_sales_amount:.2f}")
# 6. 初步分析:暢銷產品
bestselling_products = df_orders.groupby('Product')['Quantity'].sum().sort_values(ascending=False)
print("\n暢銷產品 (按數量):\n", bestselling_products)
# 7. 初步分析:每月銷售趨勢 (假設數據跨越多月)
# 從 OrderDate 提取月份
df_orders['Month'] = df_orders['OrderDate'].dt.month
monthly_sales = df_orders.groupby('Month')['TotalPrice'].sum()
print("\n每月銷售額:\n", monthly_sales)
# 8. 初步分析:不同支付方式的銷售額
payment_method_sales = df_orders.groupby('PaymentMethod')['TotalPrice'].sum().sort_values(ascending=False)
print("\n不同支付方式的銷售額:\n", payment_method_sales)
# 常見錯誤提醒:
# * **數據類型不匹配**:在進行數值運算前,確保相關列的數據類型是數值型。例如,從 CSV 讀取的數字可能被識別為字串,需要使用 `astype()` 或 `pd.to_numeric()` 轉換。
# * **索引錯亂**:在 `concat` 或 `merge` 操作後,如果沒有正確處理索引,可能會導致索引重複或錯亂。使用 `ignore_index=True` 或 `reset_index()` 可以解決此問題。
# * **原地修改 (inplace=True)**:許多 Pandas 方法有 `inplace` 參數。當 `inplace=True` 時,操作會直接修改原始 DataFrame,而不返回新的 DataFrame。這可以節省記憶體,但也可能導致意外的副作用,因此使用時需謹慎。如果希望保留原始 DataFrame,則不要使用 `inplace=True`,而是將結果賦值給一個新的變數。
# * **鏈式賦值警告 (SettingWithCopyWarning)**:當對 DataFrame 的一個「視圖」進行修改時,Pandas 可能會發出 `SettingWithCopyWarning`。這通常發生在篩選後直接賦值。為避免此問題,應明確使用 `.loc` 或 `.iloc` 進行賦值,或使用 `.copy()` 創建一個副本再操作。
第三章:Matplotlib 數據視覺化:讓數據說故事
3.1 什麼是 Matplotlib?為何視覺化如此重要?
Matplotlib 是 Python 中最基礎且廣泛使用的 2D 繪圖函式庫,它能夠生成各種靜態、動態、互動式的圖表。對於數據分析而言,數據視覺化扮演著至關重要的角色:
對於數據分析而言,數據視覺化扮演著至關重要的角色,主要體現在三個方面:首先,透過圖表,我們可以洞察數據中的模式、趨勢、異常值和關係,這些資訊往往難以從原始數據或表格中直接看出;其次,視覺化是將複雜數據分析結果傳達給非技術人員最有效的方式,清晰的圖表能夠幫助決策者快速理解數據背後的含義,從而溝通結果;最後,基於視覺化洞察,我們可以做出更明智、更數據驅動的決策,進而支持決策。
Matplotlib 的 pyplot 模組提供了一個類似 MATLAB 的繪圖介面,使得繪圖過程直觀且易於上手。
3.2 基本圖表類型與繪製
Matplotlib 支援多種圖表類型,能夠滿足不同的數據視覺化需求。本節將介紹幾種最常用的基本圖表及其繪製方法,包括用於趨勢分析的折線圖 (Line Plot)、探索數據關係的散佈圖 (Scatter Plot)、比較類別數據的長條圖 (Bar Plot)、概覽數據分佈的直方圖 (Histogram),以及展示比例構成的圓餅圖 (Pie Chart)。透過這些圖表,我們可以有效地將數據轉化為直觀的視覺資訊。
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
# 設置中文顯示,避免亂碼 (適用於大多數作業系統)
plt.rcParams["font.sans-serif"] = ["Arial Unicode MS"] # 或者其他支持中文的字體,如 SimHei
plt.rcParams["axes.unicode_minus"] = False # 解決負號顯示問題
# 數據準備
x = np.linspace(0, 10, 100)
y1 = np.sin(x)
y2 = np.cos(x)
# 折線圖 (Line Plot):趨勢分析
plt.figure(figsize=(8, 4))
plt.plot(x, y1, label=\'sin(x)\', color=\'blue\', linestyle=\'-\'')
plt.plot(x, y2, label=\'cos(x)\', color=\'red\', linestyle=\'--\')
plt.title(\'正弦與餘弦函數圖\')
plt.xlabel(\'X 軸\')
plt.ylabel(\'Y 軸\')
plt.legend()
plt.grid(True)
plt.show()
# 散佈圖 (Scatter Plot):關係探索
np.random.seed(42)
num_points = 50
data_x = np.random.rand(num_points) * 10
data_y = np.random.rand(num_points) * 10 + data_x * 0.5 + np.random.randn(num_points) * 2
plt.figure(figsize=(8, 6))
plt.scatter(data_x, data_y, c=\'green\', alpha=0.7, edgecolors=\'w\', s=50)
plt.title(\'隨機數據散佈圖\')
plt.xlabel(\'特徵 X\')
plt.ylabel(\'特徵 Y\')
plt.grid(True)
plt.show()
# 長條圖 (Bar Plot):類別比較
categories = [\'A\', \'B\', \'C\', \'D\']
values = [23, 45, 56, 12]
plt.figure(figsize=(7, 5))
plt.bar(categories, values, color=\'skyblue\')
plt.title(\'各類別銷售額\')
plt.xlabel(\'類別\')
plt.ylabel(\'銷售額\')
plt.show()
# 直方圖 (Histogram):分佈概覽
dist_data = np.random.randn(1000) # 1000 個標準常態分佈隨機數
plt.figure(figsize=(8, 5))
plt.hist(dist_data, bins=30, color=\'purple\', alpha=0.7)
plt.title(\'數據分佈直方圖\')
plt.xlabel(\'數值\')
plt.ylabel(\'頻率\')
plt.grid(axis=\'y\', alpha=0.75)
plt.show()
# 圓餅圖 (Pie Chart):比例展示
labels = [\'Apple\', \'Banana\', \'Cherry\', \'Date\]
sizes = [15, 30, 45, 10]
explode = (0, 0.1, 0, 0) # 突出顯示第二個切片
plt.figure(figsize=(6, 6))
plt.pie(sizes, explode=explode, labels=labels, autopct=\'%1.1f%%\', shadow=True, startangle=90)
plt.axis(\'equal\') # 確保圓餅圖是圓形的
plt.title(\'水果銷售佔比\')
plt.show()
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
# 設置中文顯示,避免亂碼 (適用於大多數作業系統)
plt.rcParams["font.sans-serif"] = ["Arial Unicode MS"] # 或者其他支持中文的字體,如 SimHei
plt.rcParams["axes.unicode_minus"] = False # 解決負號顯示問題
# 數據準備
x = np.linspace(0, 10, 100)
y1 = np.sin(x)
y2 = np.cos(x)
# 折線圖 (Line Plot):趨勢分析
plt.figure(figsize=(8, 4))
plt.plot(x, y1, label=\'sin(x)\', color=\'blue\', linestyle=\'-\'')
plt.plot(x, y2, label=\'cos(x)\', color=\'red\', linestyle=\'--\')
plt.title(\'正弦與餘弦函數圖\')
plt.xlabel(\'X 軸\')
plt.ylabel(\'Y 軸\')
plt.legend()
plt.grid(True)
plt.show()
# 散佈圖 (Scatter Plot):關係探索
np.random.seed(42)
num_points = 50
data_x = np.random.rand(num_points) * 10
data_y = np.random.rand(num_points) * 10 + data_x * 0.5 + np.random.randn(num_points) * 2
plt.figure(figsize=(8, 6))
plt.scatter(data_x, data_y, c=\'green\', alpha=0.7, edgecolors=\'w\', s=50)
plt.title(\'隨機數據散佈圖\')
plt.xlabel(\'特徵 X\')
plt.ylabel(\'特徵 Y\')
plt.grid(True)
plt.show()
# 長條圖 (Bar Plot):類別比較
categories = [\'A\', \'B\', \'C\', \'D\']
values = [23, 45, 56, 12]
plt.figure(figsize=(7, 5))
plt.bar(categories, values, color=\'skyblue\')
plt.title(\'各類別銷售額\')
plt.xlabel(\'類別\')
plt.ylabel(\'銷售額\')
plt.show()
# 直方圖 (Histogram):分佈概覽
dist_data = np.random.randn(1000) # 1000 個標準常態分佈隨機數
plt.figure(figsize=(8, 5))
plt.hist(dist_data, bins=30, color=\'purple\', alpha=0.7)
plt.title(\'數據分佈直方圖\')
plt.xlabel(\'數值\')
plt.ylabel(\'頻率\')
plt.grid(axis=\'y\', alpha=0.75)
plt.show()
# 圓餅圖 (Pie Chart):比例展示
labels = [\'Apple\', \'Banana\', \'Cherry\', \'Date\]
sizes = [15, 30, 45, 10]
explode = (0, 0.1, 0, 0) # 突出顯示第二個切片
plt.figure(figsize=(6, 6))
plt.pie(sizes, explode=explode, labels=labels, autopct=\'%1.1f%%\', shadow=True, startangle=90)
plt.axis(\'equal\') # 確保圓餅圖是圓形的
plt.title(\'水果銷售佔比\')
plt.show()
3.3 圖表客製化:提升視覺表達力
Matplotlib 提供了豐富的選項來客製化圖表,使其更具可讀性和專業性。這些客製化選項包括調整線條顏色、樣式、寬度,添加標題、軸標籤、圖例,設置軸範圍和刻度,以及添加網格線和文字註釋等。透過精細地調整這些視覺元素,我們可以有效地提升圖表的視覺表達力,更清晰地傳達數據背後的資訊。
import matplotlib.pyplot as plt
import numpy as np
plt.rcParams["font.sans-serif"] = ["Arial Unicode MS"]
plt.rcParams["axes.unicode_minus"] = False
x = np.arange(0, 10, 0.1)
y = np.exp(-x) * np.sin(x * 3)
plt.figure(figsize=(10, 6))
plt.plot(x, y, color=\'#FF5733\', linestyle=\'-.\', linewidth=2, marker=\'o\', markersize=6, markevery=10, label=\'衰減正弦波\')
# 添加標題和軸標籤
plt.title(\'衰減正弦波示例圖\', fontsize=16, fontweight=\'bold\')
plt.xlabel(\'時間 (秒)\' , fontsize=12)
plt.ylabel(\'振幅\', fontsize=12)
# 設置圖例
plt.legend(loc=\'upper right\', fontsize=10)
# 設置軸範圍
plt.xlim(0, 9) # X 軸從 0 到 9
plt.ylim(-0.5, 1.0) # Y 軸從 -0.5 到 1.0
# 設置刻度
plt.xticks(np.arange(0, 10, 1)) # X 軸刻度間隔為 1
plt.yticks(np.arange(-0.5, 1.1, 0.25)) # Y 軸刻度間隔為 0.25
# 添加網格線
plt.grid(True, linestyle=\'--\', alpha=0.6)
# 添加文字註釋
plt.text(2, 0.8, \'峰值點\', fontsize=10, color=\'darkblue\')
plt.annotate(\'局部最小值\', xy=(5.5, -0.4), xytext=(6.5, -0.2),
arrowprops=dict(facecolor=\'black\', shrink=0.05), fontsize=10)
# 調整邊距
plt.tight_layout()
plt.show()
import matplotlib.pyplot as plt
import numpy as np
plt.rcParams["font.sans-serif"] = ["Arial Unicode MS"]
plt.rcParams["axes.unicode_minus"] = False
x = np.arange(0, 10, 0.1)
y = np.exp(-x) * np.sin(x * 3)
plt.figure(figsize=(10, 6))
plt.plot(x, y, color=\'#FF5733\', linestyle=\'-.\', linewidth=2, marker=\'o\', markersize=6, markevery=10, label=\'衰減正弦波\')
# 添加標題和軸標籤
plt.title(\'衰減正弦波示例圖\', fontsize=16, fontweight=\'bold\')
plt.xlabel(\'時間 (秒)\' , fontsize=12)
plt.ylabel(\'振幅\', fontsize=12)
# 設置圖例
plt.legend(loc=\'upper right\', fontsize=10)
# 設置軸範圍
plt.xlim(0, 9) # X 軸從 0 到 9
plt.ylim(-0.5, 1.0) # Y 軸從 -0.5 到 1.0
# 設置刻度
plt.xticks(np.arange(0, 10, 1)) # X 軸刻度間隔為 1
plt.yticks(np.arange(-0.5, 1.1, 0.25)) # Y 軸刻度間隔為 0.25
# 添加網格線
plt.grid(True, linestyle=\'--\', alpha=0.6)
# 添加文字註釋
plt.text(2, 0.8, \'峰值點\', fontsize=10, color=\'darkblue\')
plt.annotate(\'局部最小值\', xy=(5.5, -0.4), xytext=(6.5, -0.2),
arrowprops=dict(facecolor=\'black\', shrink=0.05), fontsize=10)
# 調整邊距
plt.tight_layout()
plt.show()
中文字體顯示問題與解決方案
在 Matplotlib 中直接使用中文字符作為標題或標籤時,可能會出現亂碼 (方塊)。這是因為 Matplotlib 預設使用的字體不包含中文字符。解決方案是指定一個支持中文的字體,例如透過設置全局字體 plt.rcParams["font.sans-serif"] 為系統中已安裝的中文通用字體 (如 Windows 的 SimHei、macOS 的 PingFang HK 或 Arial Unicode MS,以及 Linux 的 WenQuanYi Zen Hei 或 Arial Unicode MS),並設置 plt.rcParams["axes.unicode_minus"] = False 來解決負號顯示問題。以下是相關的程式碼範例:
import matplotlib.pyplot as plt
# 解決方案:設置全局字體
# 方法一:手動指定字體文件路徑 (如果知道字體文件在哪裡)
# plt.rcParams["font.sans-serif"] = ["/path/to/your/chinese_font.ttf"]
# 方法二:指定系統中已安裝的中文通用字體 (推薦)
# Windows: SimHei, Microsoft YaHei
# macOS: PingFang HK, Arial Unicode MS
# Linux: WenQuanYi Zen Hei, Arial Unicode MS
plt.rcParams["font.sans-serif"] = ["Arial Unicode MS"] # 這裡以 Arial Unicode MS 為例,它在多數系統上都存在
plt.rcParams["axes.unicode_minus"] = False # 解決負號顯示問題
plt.figure(figsize=(6, 4))
plt.plot([1, 2, 3], [4, 5, 6])
plt.title(\'中文標題示例\')
plt.xlabel(\'X 軸標籤\')
plt.ylabel(\'Y 軸標籤\')
plt.show()
import matplotlib.pyplot as plt
# 解決方案:設置全局字體
# 方法一:手動指定字體文件路徑 (如果知道字體文件在哪裡)
# plt.rcParams["font.sans-serif"] = ["/path/to/your/chinese_font.ttf"]
# 方法二:指定系統中已安裝的中文通用字體 (推薦)
# Windows: SimHei, Microsoft YaHei
# macOS: PingFang HK, Arial Unicode MS
# Linux: WenQuanYi Zen Hei, Arial Unicode MS
plt.rcParams["font.sans-serif"] = ["Arial Unicode MS"] # 這裡以 Arial Unicode MS 為例,它在多數系統上都存在
plt.rcParams["axes.unicode_minus"] = False # 解決負號顯示問題
plt.figure(figsize=(6, 4))
plt.plot([1, 2, 3], [4, 5, 6])
plt.title(\'中文標題示例\')
plt.xlabel(\'X 軸標籤\')
plt.ylabel(\'Y 軸標籤\')
plt.show()
3.4 多圖繪製:子圖 (subplot) 的應用
當需要比較多個圖表或在同一畫布上展示不同視角的數據時,Matplotlib 的子圖 (subplot) 功能非常有用。它允許我們在一個圖形視窗中創建多個坐標系,每個坐標系都可以獨立繪製圖表。主要有兩種創建子圖的方法:plt.subplot() 和 plt.subplots()。plt.subplot() 採用網格佈局參數 (行數、列數、子圖索引) 來指定當前繪製的子圖,而 plt.subplots() 則會返回一個 Figure 物件和一個 Axes 物件陣列,這種面向對象的方法在處理複雜佈局時更為靈活和推薦。
import matplotlib.pyplot as plt
import numpy as np
plt.rcParams["font.sans-serif"] = ["Arial Unicode MS"]
plt.rcParams["axes.unicode_minus"] = False
# 數據準備
x = np.linspace(0, 2 * np.pi, 400)
y_sin = np.sin(x)
y_cos = np.cos(x)
y_tan = np.tan(x)
y_exp = np.exp(-x)
# 方法一:使用 plt.subplot()
plt.figure(figsize=(12, 8))
plt.subplot(2, 2, 1) # 2行2列,第一個子圖
plt.plot(x, y_sin, color=\'blue\')
plt.title(\'正弦函數\')
plt.grid(True)
plt.subplot(2, 2, 2) # 2行2列,第二個子圖
plt.plot(x, y_cos, color=\'red\')
plt.title(\'餘弦函數\')
plt.grid(True)
plt.subplot(2, 2, 3) # 2行2列,第三個子圖
plt.plot(x, y_tan, color=\'green\')
plt.title(\'正切函數\')
plt.ylim(-5, 5) # 限制 Y 軸範圍以更好地顯示正切函數
plt.grid(True)
plt.subplot(2, 2, 4) # 2行2列,第四個子圖
plt.plot(x, y_exp, color=\'purple\')
plt.title(\'指數衰減函數\')
plt.grid(True)
plt.tight_layout() # 自動調整子圖參數,使之填充整個圖像區域
plt.show()
# 方法二:使用 plt.subplots() (推薦,更面向對象)
fig, axes = plt.subplots(nrows=2, ncols=2, figsize=(12, 8))
# axes 是一個 2x2 的 NumPy 陣列,包含每個子圖的 Axes 物件
axes[0, 0].plot(x, y_sin, color=\'blue\')
axes[0, 0].set_title(\'正弦函數\')
axes[0, 0].grid(True)
axes[0, 1].plot(x, y_cos, color=\'red\')
axes[0, 1].set_title(\'餘弦函數\')
axes[0, 1].grid(True)
axes[1, 0].plot(x, y_tan, color=\'green\')
axes[1, 0].set_title(\'正切函數\')
axes[1, 0].set_ylim(-5, 5)
axes[1, 0].grid(True)
axes[1, 1].plot(x, y_exp, color=\'purple\')
axes[1, 1].set_title(\'指數衰減函數\')
axes[1, 1].grid(True)
fig.suptitle(\'多個數學函數子圖示例\', fontsize=16, fontweight=\'bold\') # 整個圖的總標題
plt.tight_layout(rect=[0, 0.03, 1, 0.95]) # 調整佈局,為總標題留出空間
plt.show()
import matplotlib.pyplot as plt
import numpy as np
plt.rcParams["font.sans-serif"] = ["Arial Unicode MS"]
plt.rcParams["axes.unicode_minus"] = False
# 數據準備
x = np.linspace(0, 2 * np.pi, 400)
y_sin = np.sin(x)
y_cos = np.cos(x)
y_tan = np.tan(x)
y_exp = np.exp(-x)
# 方法一:使用 plt.subplot()
plt.figure(figsize=(12, 8))
plt.subplot(2, 2, 1) # 2行2列,第一個子圖
plt.plot(x, y_sin, color=\'blue\')
plt.title(\'正弦函數\')
plt.grid(True)
plt.subplot(2, 2, 2) # 2行2列,第二個子圖
plt.plot(x, y_cos, color=\'red\')
plt.title(\'餘弦函數\')
plt.grid(True)
plt.subplot(2, 2, 3) # 2行2列,第三個子圖
plt.plot(x, y_tan, color=\'green\')
plt.title(\'正切函數\')
plt.ylim(-5, 5) # 限制 Y 軸範圍以更好地顯示正切函數
plt.grid(True)
plt.subplot(2, 2, 4) # 2行2列,第四個子圖
plt.plot(x, y_exp, color=\'purple\')
plt.title(\'指數衰減函數\')
plt.grid(True)
plt.tight_layout() # 自動調整子圖參數,使之填充整個圖像區域
plt.show()
# 方法二:使用 plt.subplots() (推薦,更面向對象)
fig, axes = plt.subplots(nrows=2, ncols=2, figsize=(12, 8))
# axes 是一個 2x2 的 NumPy 陣列,包含每個子圖的 Axes 物件
axes[0, 0].plot(x, y_sin, color=\'blue\')
axes[0, 0].set_title(\'正弦函數\')
axes[0, 0].grid(True)
axes[0, 1].plot(x, y_cos, color=\'red\')
axes[0, 1].set_title(\'餘弦函數\')
axes[0, 1].grid(True)
axes[1, 0].plot(x, y_tan, color=\'green\')
axes[1, 0].set_title(\'正切函數\')
axes[1, 0].set_ylim(-5, 5)
axes[1, 0].grid(True)
axes[1, 1].plot(x, y_exp, color=\'purple\')
axes[1, 1].set_title(\'指數衰減函數\')
axes[1, 1].grid(True)
fig.suptitle(\'多個數學函數子圖示例\', fontsize=16, fontweight=\'bold\') # 整個圖的總標題
plt.tight_layout(rect=[0, 0.03, 1, 0.95]) # 調整佈局,為總標題留出空間
plt.show()
3.5 實戰範例:將 Pandas 處理後的數據進行視覺化
結合 Pandas 強大的數據處理能力和 Matplotlib 豐富的視覺化功能,我們可以從原始數據中提取有價值的洞察,並將其以直觀、易於理解的圖表形式呈現。本節將透過具體範例,展示如何利用這兩大工具,從數據清洗、轉換到最終的視覺化呈現,完成一個完整的數據分析流程,從而幫助讀者更好地理解數據背後的意義和趨勢。
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
plt.rcParams["font.sans-serif"] = ["Arial Unicode MS"]
plt.rcParams["axes.unicode_minus"] = False
# 模擬電商銷售數據
data = {
\'OrderDate\': pd.to_datetime([\'2026-01-01\', \'2026-01-05\', \'2026-01-10\', \'2026-01-15\', \'2026-01-20\',
\'2026-02-01\', \'2026-02-08\', \'2026-02-15\', \'2026-02-20\', \'2026-02-25\',
\'2026-03-01\', \'2026-03-05\', \'2026-03-10\', \'2026-03-15\', \'2026-03-20\']),
\'ProductCategory\': [\'Electronics\', \'Books\', \'Electronics\', \'Clothing\', \'Books\',
\'Electronics\', \'Clothing\', \'Books\', \'Electronics\', \'Clothing\',
\'Books\', \'Electronics\', \'Clothing\', \'Books\', \'Electronics\'],
\'SalesAmount\': np.random.randint(50, 500, size=15)
}
df_sales = pd.DataFrame(data)
print("原始銷售數據:\n", df_sales)
# 範例一:繪製每月銷售額折線圖
# 提取月份並計算每月總銷售額
df_sales[\'Month\'] = df_sales[\'OrderDate\'].dt.to_period(\'M\')
monthly_sales = df_sales.groupby(\'Month\')[\'SalesAmount\'].sum().reset_index()
monthly_sales[\'Month\'] = monthly_sales[\'Month\'].astype(str) # 轉換為字串以便繪圖
plt.figure(figsize=(10, 6))
plt.plot(monthly_sales[\'Month\'], monthly_sales[\'SalesAmount\'], marker=\'o\', linestyle=\'-\'', color=\'skyblue\')
plt.title(\'2026 年每月銷售額趨勢\', fontsize=16)
plt.xlabel(\'月份\', fontsize=12)
plt.ylabel(\'銷售額\', fontsize=12)
plt.grid(True, linestyle=\'--\', alpha=0.7)
plt.xticks(rotation=45) # 旋轉 X 軸標籤,避免重疊
plt.tight_layout()
plt.show()
# 範例二:展示不同產品類別的銷售佔比圓餅圖
category_sales = df_sales.groupby(\'ProductCategory\')[\'SalesAmount\'].sum()
plt.figure(figsize=(8, 8))
plt.pie(category_sales, labels=category_sales.index, autopct=\'%1.1f%%\', startangle=90,
colors=plt.cm.Paired.colors, wedgeprops={\'edgecolor\': \'black\', \'linewidth\': 1})
plt.axis(\'equal\')
plt.title(\'產品類別銷售佔比\', fontsize=16)
plt.show()
# 範例三:分析產品類別的銷售額長條圖
plt.figure(figsize=(10, 6))
category_sales.plot(kind=\'bar\', color=\'lightcoral\', edgecolor=\'black\')
plt.title(\'各產品類別總銷售額\', fontsize=16)
plt.xlabel(\'產品類別\', fontsize=12)
plt.ylabel(\'總銷售額\', fontsize=12)
plt.xticks(rotation=45)
plt.grid(axis=\'y\', linestyle=\'--\', alpha=0.7)
plt.tight_layout()
plt.show()
# 常見錯誤提醒:
在 Matplotlib 繪圖過程中,有幾個常見錯誤需要特別注意:首先是**中文字體亂碼**問題,務必設置支持中文的字體,否則標題和標籤會顯示為方塊;其次是**圖表元素重疊**,當數據點過多或標籤過長時,圖表元素可能會重疊,此時可以使用 `plt.xticks(rotation=...)` 旋轉 X 軸標籤,或調整 `figsize` 增大圖表尺寸,或使用 `plt.tight_layout()` 自動調整佈局;再者是**數據解讀偏差**,視覺化只是工具,最終的解讀仍需結合領域知識,例如折線圖的趨勢可能受季節性影響,圓餅圖的比例可能因數據量大小而有不同意義,應避免僅憑圖表表面現象做出結論;最後是**選擇不合適的圖表類型**,不同的數據類型和分析目的需要不同的圖表,例如比較類別數據用長條圖,展示趨勢用折線圖,查看分佈用直方圖,展示佔比用圓餅圖,選擇錯誤的圖表會誤導讀者。
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
plt.rcParams["font.sans-serif"] = ["Arial Unicode MS"]
plt.rcParams["axes.unicode_minus"] = False
# 模擬電商銷售數據
data = {
\'OrderDate\': pd.to_datetime([\'2026-01-01\', \'2026-01-05\', \'2026-01-10\', \'2026-01-15\', \'2026-01-20\',
\'2026-02-01\', \'2026-02-08\', \'2026-02-15\', \'2026-02-20\', \'2026-02-25\',
\'2026-03-01\', \'2026-03-05\', \'2026-03-10\', \'2026-03-15\', \'2026-03-20\']),
\'ProductCategory\': [\'Electronics\', \'Books\', \'Electronics\', \'Clothing\', \'Books\',
\'Electronics\', \'Clothing\', \'Books\', \'Electronics\', \'Clothing\',
\'Books\', \'Electronics\', \'Clothing\', \'Books\', \'Electronics\'],
\'SalesAmount\': np.random.randint(50, 500, size=15)
}
df_sales = pd.DataFrame(data)
print("原始銷售數據:\n", df_sales)
# 範例一:繪製每月銷售額折線圖
# 提取月份並計算每月總銷售額
df_sales[\'Month\'] = df_sales[\'OrderDate\'].dt.to_period(\'M\')
monthly_sales = df_sales.groupby(\'Month\')[\'SalesAmount\'].sum().reset_index()
monthly_sales[\'Month\'] = monthly_sales[\'Month\'].astype(str) # 轉換為字串以便繪圖
plt.figure(figsize=(10, 6))
plt.plot(monthly_sales[\'Month\'], monthly_sales[\'SalesAmount\'], marker=\'o\', linestyle=\'-\'', color=\'skyblue\')
plt.title(\'2026 年每月銷售額趨勢\', fontsize=16)
plt.xlabel(\'月份\', fontsize=12)
plt.ylabel(\'銷售額\', fontsize=12)
plt.grid(True, linestyle=\'--\', alpha=0.7)
plt.xticks(rotation=45) # 旋轉 X 軸標籤,避免重疊
plt.tight_layout()
plt.show()
# 範例二:展示不同產品類別的銷售佔比圓餅圖
category_sales = df_sales.groupby(\'ProductCategory\')[\'SalesAmount\'].sum()
plt.figure(figsize=(8, 8))
plt.pie(category_sales, labels=category_sales.index, autopct=\'%1.1f%%\', startangle=90,
colors=plt.cm.Paired.colors, wedgeprops={\'edgecolor\': \'black\', \'linewidth\': 1})
plt.axis(\'equal\')
plt.title(\'產品類別銷售佔比\', fontsize=16)
plt.show()
# 範例三:分析產品類別的銷售額長條圖
plt.figure(figsize=(10, 6))
category_sales.plot(kind=\'bar\', color=\'lightcoral\', edgecolor=\'black\')
plt.title(\'各產品類別總銷售額\', fontsize=16)
plt.xlabel(\'產品類別\', fontsize=12)
plt.ylabel(\'總銷售額\', fontsize=12)
plt.xticks(rotation=45)
plt.grid(axis=\'y\', linestyle=\'--\', alpha=0.7)
plt.tight_layout()
plt.show()
# 常見錯誤提醒:
在 Matplotlib 繪圖過程中,有幾個常見錯誤需要特別注意:首先是**中文字體亂碼**問題,務必設置支持中文的字體,否則標題和標籤會顯示為方塊;其次是**圖表元素重疊**,當數據點過多或標籤過長時,圖表元素可能會重疊,此時可以使用 `plt.xticks(rotation=...)` 旋轉 X 軸標籤,或調整 `figsize` 增大圖表尺寸,或使用 `plt.tight_layout()` 自動調整佈局;再者是**數據解讀偏差**,視覺化只是工具,最終的解讀仍需結合領域知識,例如折線圖的趨勢可能受季節性影響,圓餅圖的比例可能因數據量大小而有不同意義,應避免僅憑圖表表面現象做出結論;最後是**選擇不合適的圖表類型**,不同的數據類型和分析目的需要不同的圖表,例如比較類別數據用長條圖,展示趨勢用折線圖,查看分佈用直方圖,展示佔比用圓餅圖,選擇錯誤的圖表會誤導讀者。
第四章:綜合實戰案例:從數據到洞察的完整旅程
在本章中,我們將透過一個綜合實戰案例,演示如何將 NumPy、Pandas 和 Matplotlib 這三大 Python 數據分析函式庫結合應用,從原始數據中提取有價值的洞察。我們將以一個虛擬的「城市天氣數據集」為例,詳細展示數據載入、清洗、分析到視覺化的完整流程,幫助讀者融會貫通前述知識,並將其應用於實際問題解決中。
4.1 案例背景與數據準備
假設我們有一個包含多個城市每日天氣資訊的數據集,其中包括日期、城市、平均溫度、最高溫度、最低溫度和降水量。我們的目標是透過分析這些數據,找出潛在的天氣模式和趨勢,例如不同城市的溫度差異、降水量的季節性變化等。為了演示這個過程,我們將首先創建一個模擬的數據集。
import pandas as pd
import numpy as np
# 創建模擬的城市天氣數據集
data = {
\"Date\": pd.to_datetime([
\"2026-01-01\", \"2026-01-01\", \"2026-01-01\",
\"2026-01-02\", \"2026-01-02\", \"2026-01-02\",
\"2026-01-03\", \"2026-01-03\", \"2026-01-03\",
\"2026-01-04\", \"2026-01-04\", \"2026-01-04\",
\"2026-01-05\", \"2026-01-05\", \"2026-01-05\",
\"2026-01-06\", \"2026-01-06\", \"2026-01-06\",
\"2026-01-07\", \"2026-01-07\", \"2026-01-07\",
\"2026-01-08\", \"2026-01-08\", \"2026-01-08\",
\"2026-01-09\", \"2026-01-09\", \"2026-01-09\",
\"2026-01-10\", \"2026-01-10\", \"2026-01-10\",
]),
\"City\": [
\"Taipei\", \"Kaohsiung\", \"Taichung\",
\"Taipei\", \"Kaohsiung\", \"Taichung\",
\"Taipei\", \"Kaohsiung\", \"Taichung\",
\"Taipei\", \"Kaohsiung\", \"Taichung\",
\"Taipei\", \"Kaohsiung\", \"Taichung\",
\"Taipei\", \"Kaohsiung\", \"Taichung\",
\"Taipei\", \"Kaohsiung\", \"Taichung\",
\"Taipei\", \"Kaohsiung\", \"Taichung\",
\"Taipei\", \"Kaohsiung\", \"Taichung\",
\"Taipei\", \"Kaohsiung\", \"Taichung\",
],
\"AvgTemp\": [
18, 22, 20, 19, 23, 21, 17, 21, 19, 16, 20, 18, 15, 19, 17,
14, 18, 16, 13, 17, 15, 12, 16, 14, 11, 15, 13, 10, 14, 12
],
\"MaxTemp\": [
22, 26, 24, 23, 27, 25, 21, 25, 23, 20, 24, 22, 19, 23, 21,
18, 22, 20, 17, 21, 19, 16, 20, 18, 15, 19, 17, 14, 18, 16
],
\"MinTemp\": [
15, 19, 17, 16, 20, 18, 14, 18, 16, 13, 17, 15, 12, 16, 14,
11, 15, 13, 10, 14, 12, 9, 13, 11, 8, 12, 10, 7, 11, 9
],
\"Precipitation\": [
5, 0, 2, 10, 0, 5, 0, 0, 0, 15, 0, 8, 20, 0, 10,
0, 0, 0, 5, 0, 2, 0, 0, 0, 10, 0, 5, 0, 0, 0
]
}
df_weather = pd.DataFrame(data)
# 引入一些缺失值來模擬真實數據
df_weather.loc[3, \"AvgTemp\"] = np.nan
df_weather.loc[7, \"Precipitation\"] = np.nan
df_weather.loc[12, \"MaxTemp\"] = np.nan
print(\"原始天氣數據:\\n\", df_weather.head())
print(\"\\n數據資訊:\")
df_weather.info()
import pandas as pd
import numpy as np
# 創建模擬的城市天氣數據集
data = {
\"Date\": pd.to_datetime([
\"2026-01-01\", \"2026-01-01\", \"2026-01-01\",
\"2026-01-02\", \"2026-01-02\", \"2026-01-02\",
\"2026-01-03\", \"2026-01-03\", \"2026-01-03\",
\"2026-01-04\", \"2026-01-04\", \"2026-01-04\",
\"2026-01-05\", \"2026-01-05\", \"2026-01-05\",
\"2026-01-06\", \"2026-01-06\", \"2026-01-06\",
\"2026-01-07\", \"2026-01-07\", \"2026-01-07\",
\"2026-01-08\", \"2026-01-08\", \"2026-01-08\",
\"2026-01-09\", \"2026-01-09\", \"2026-01-09\",
\"2026-01-10\", \"2026-01-10\", \"2026-01-10\",
]),
\"City\": [
\"Taipei\", \"Kaohsiung\", \"Taichung\",
\"Taipei\", \"Kaohsiung\", \"Taichung\",
\"Taipei\", \"Kaohsiung\", \"Taichung\",
\"Taipei\", \"Kaohsiung\", \"Taichung\",
\"Taipei\", \"Kaohsiung\", \"Taichung\",
\"Taipei\", \"Kaohsiung\", \"Taichung\",
\"Taipei\", \"Kaohsiung\", \"Taichung\",
\"Taipei\", \"Kaohsiung\", \"Taichung\",
\"Taipei\", \"Kaohsiung\", \"Taichung\",
\"Taipei\", \"Kaohsiung\", \"Taichung\",
],
\"AvgTemp\": [
18, 22, 20, 19, 23, 21, 17, 21, 19, 16, 20, 18, 15, 19, 17,
14, 18, 16, 13, 17, 15, 12, 16, 14, 11, 15, 13, 10, 14, 12
],
\"MaxTemp\": [
22, 26, 24, 23, 27, 25, 21, 25, 23, 20, 24, 22, 19, 23, 21,
18, 22, 20, 17, 21, 19, 16, 20, 18, 15, 19, 17, 14, 18, 16
],
\"MinTemp\": [
15, 19, 17, 16, 20, 18, 14, 18, 16, 13, 17, 15, 12, 16, 14,
11, 15, 13, 10, 14, 12, 9, 13, 11, 8, 12, 10, 7, 11, 9
],
\"Precipitation\": [
5, 0, 2, 10, 0, 5, 0, 0, 0, 15, 0, 8, 20, 0, 10,
0, 0, 0, 5, 0, 2, 0, 0, 0, 10, 0, 5, 0, 0, 0
]
}
df_weather = pd.DataFrame(data)
# 引入一些缺失值來模擬真實數據
df_weather.loc[3, \"AvgTemp\"] = np.nan
df_weather.loc[7, \"Precipitation\"] = np.nan
df_weather.loc[12, \"MaxTemp\"] = np.nan
print(\"原始天氣數據:\\n\", df_weather.head())
print(\"\\n數據資訊:\")
df_weather.info()
4.2 數據清洗與預處理
在進行數據分析之前,數據清洗與預處理是不可或缺的步驟。這包括處理數據中的缺失值、異常值,以及進行必要的數據類型轉換,確保數據的品質和一致性,為後續的分析奠定堅實的基礎。
# 檢查缺失值
print(\"\\n缺失值統計:\\n\", df_weather.isnull().sum())
# 處理缺失值:
# AvgTemp, MaxTemp, MinTemp 可以用該城市該日期的平均值或前一天的值填充
# 這裡簡化處理,直接用列的平均值填充數值型缺失值
df_weather[\"AvgTemp\"] = df_weather[\"AvgTemp\"].fillna(df_weather[\"AvgTemp\"].mean())
df_weather[\"MaxTemp\"] = df_weather[\"MaxTemp\"].fillna(df_weather[\"MaxTemp\"].mean())
# Precipitation 缺失值可以考慮填充 0 (假設缺失代表無降水)
df_weather[\"Precipitation\"] = df_weather[\"Precipitation\"].fillna(0)
print(\"\\n填充缺失值後的數據:\\n\", df_weather.isnull().sum())
# 數據類型轉換:\"Date\" 列已經在創建時轉換為 datetime 類型
# 創建新特徵:從日期中提取月份和年份
df_weather[\"Month\"] = df_weather[\"Date\"].dt.month
df_weather[\"Year\"] = df_weather[\"Date\"].dt.year
print(\"\\n添加月份和年份後的數據:\\n\", df_weather.head())
# 檢查缺失值
print(\"\\n缺失值統計:\\n\", df_weather.isnull().sum())
# 處理缺失值:
# AvgTemp, MaxTemp, MinTemp 可以用該城市該日期的平均值或前一天的值填充
# 這裡簡化處理,直接用列的平均值填充數值型缺失值
df_weather[\"AvgTemp\"] = df_weather[\"AvgTemp\"].fillna(df_weather[\"AvgTemp\"].mean())
df_weather[\"MaxTemp\"] = df_weather[\"MaxTemp\"].fillna(df_weather[\"MaxTemp\"].mean())
# Precipitation 缺失值可以考慮填充 0 (假設缺失代表無降水)
df_weather[\"Precipitation\"] = df_weather[\"Precipitation\"].fillna(0)
print(\"\\n填充缺失值後的數據:\\n\", df_weather.isnull().sum())
# 數據類型轉換:\"Date\" 列已經在創建時轉換為 datetime 類型
# 創建新特徵:從日期中提取月份和年份
df_weather[\"Month\"] = df_weather[\"Date\"].dt.month
df_weather[\"Year\"] = df_weather[\"Date\"].dt.year
print(\"\\n添加月份和年份後的數據:\\n\", df_weather.head())
4.3 數據分析與探索
數據清洗與預處理完成後,我們便可以開始進行探索性數據分析 (EDA)。這個階段的目標是透過統計摘要和初步視覺化,揭示數據中的模式、趨勢、異常值和關係,從而對數據有更深入的理解,並為後續的建模或更深入的分析提供方向。
# 計算各城市平均溫度、總降水量
city_summary = df_weather.groupby(\"City\").agg(
AvgTemp_Mean=(\"AvgTemp\", \"mean\"),
Total_Precipitation=(\"Precipitation\", \"sum\")
).reset_index()
print(\"\\n各城市天氣摘要:\\n\", city_summary)
# 分析不同月份的溫度趨勢 (這裡只有一個月,但邏輯適用於多月數據)
monthly_avg_temp = df_weather.groupby(\"Month\")[\"AvgTemp\"].mean().reset_index()
print(\"\\n每月平均溫度:\\n\", monthly_avg_temp)
# 找出最高溫和最低溫的城市與日期
max_temp_record = df_weather.loc[df_weather[\"MaxTemp\"] == df_weather[\"MaxTemp\"].max()]
min_temp_record = df_weather.loc[df_weather[\"MinTemp\"] == df_weather[\"MinTemp\"].min()]
print(\"\\n最高溫紀錄:\\n\", max_temp_record)
print(\"\\n最低溫紀錄:\\n\", min_temp_record)
# 計算各城市平均溫度、總降水量
city_summary = df_weather.groupby(\"City\").agg(
AvgTemp_Mean=(\"AvgTemp\", \"mean\"),
Total_Precipitation=(\"Precipitation\", \"sum\")
).reset_index()
print(\"\\n各城市天氣摘要:\\n\", city_summary)
# 分析不同月份的溫度趨勢 (這裡只有一個月,但邏輯適用於多月數據)
monthly_avg_temp = df_weather.groupby(\"Month\")[\"AvgTemp\"].mean().reset_index()
print(\"\\n每月平均溫度:\\n\", monthly_avg_temp)
# 找出最高溫和最低溫的城市與日期
max_temp_record = df_weather.loc[df_weather[\"MaxTemp\"] == df_weather[\"MaxTemp\"].max()]
min_temp_record = df_weather.loc[df_weather[\"MinTemp\"] == df_weather[\"MinTemp\"].min()]
print(\"\\n最高溫紀錄:\\n\", max_temp_record)
print(\"\\n最低溫紀錄:\\n\", min_temp_record)
4.4 數據視覺化呈現
數據分析的最終目標之一是將複雜的數據和分析結果以直觀、易於理解的視覺形式呈現。透過 Matplotlib,我們可以創建多種圖表,將前一階段的數據分析與探索的成果清晰地展示出來,幫助我們更好地理解數據背後的模式、趨勢和關係,並有效地將這些洞察傳達給他人。
import matplotlib.pyplot as plt
plt.rcParams[\"font.sans-serif\"] = [\"Arial Unicode MS\"]
plt.rcParams[\"axes.unicode_minus\"] = False
# 繪製各城市月平均溫度折線圖 (這裡只有一個月,但可以展示城市間對比)
plt.figure(figsize=(10, 6))
for city in df_weather[\"City\"].unique():
city_data = df_weather[df_weather[\"City\"] == city]
plt.plot(city_data[\"Date\"], city_data[\"AvgTemp\"], label=city, marker=\'o\')
plt.title(\"各城市平均溫度趨勢 (2026 年 1 月)\", fontsize=16)
plt.xlabel(\"日期\", fontsize=12)
plt.ylabel(\"平均溫度 (°C)\", fontsize=12)
plt.legend()
plt.grid(True, linestyle=\'--\', alpha=0.7)
plt.xticks(rotation=45)
plt.tight_layout()
plt.show()
# 繪製城市總降水量長條圖
plt.figure(figsize=(8, 5))
plt.bar(city_summary[\"City\"], city_summary[\"Total_Precipitation\"], color=[\"skyblue\", \"lightcoral\", \"lightgreen\"])
plt.title(\"各城市總降水量 (2026 年 1 月)\", fontsize=16)
plt.xlabel(\"城市\", fontsize=12)
plt.ylabel(\"總降水量 (mm)\", fontsize=12)
plt.grid(axis=\'y\', linestyle=\'--\', alpha=0.7)
plt.tight_layout()
plt.show()
# 繪製溫度與降水量的散佈圖,探索潛在關係
plt.figure(figsize=(10, 6))
plt.scatter(df_weather[\"AvgTemp\"], df_weather[\"Precipitation\"],
c=df_weather[\"Month\"], cmap=\'viridis\', alpha=0.7, s=50)
plt.title(\"平均溫度與降水量關係圖 (按月份著色)\", fontsize=16)
plt.xlabel(\"平均溫度 (°C)\", fontsize=12)
plt.ylabel(\"降水量 (mm)\", fontsize=12)
plt.colorbar(label=\"月份\")
plt.grid(True, linestyle=\'--\', alpha=0.7)
plt.tight_layout()
plt.show()
import matplotlib.pyplot as plt
plt.rcParams[\"font.sans-serif\"] = [\"Arial Unicode MS\"]
plt.rcParams[\"axes.unicode_minus\"] = False
# 繪製各城市月平均溫度折線圖 (這裡只有一個月,但可以展示城市間對比)
plt.figure(figsize=(10, 6))
for city in df_weather[\"City\"].unique():
city_data = df_weather[df_weather[\"City\"] == city]
plt.plot(city_data[\"Date\"], city_data[\"AvgTemp\"], label=city, marker=\'o\')
plt.title(\"各城市平均溫度趨勢 (2026 年 1 月)\", fontsize=16)
plt.xlabel(\"日期\", fontsize=12)
plt.ylabel(\"平均溫度 (°C)\", fontsize=12)
plt.legend()
plt.grid(True, linestyle=\'--\', alpha=0.7)
plt.xticks(rotation=45)
plt.tight_layout()
plt.show()
# 繪製城市總降水量長條圖
plt.figure(figsize=(8, 5))
plt.bar(city_summary[\"City\"], city_summary[\"Total_Precipitation\"], color=[\"skyblue\", \"lightcoral\", \"lightgreen\"])
plt.title(\"各城市總降水量 (2026 年 1 月)\", fontsize=16)
plt.xlabel(\"城市\", fontsize=12)
plt.ylabel(\"總降水量 (mm)\", fontsize=12)
plt.grid(axis=\'y\', linestyle=\'--\', alpha=0.7)
plt.tight_layout()
plt.show()
# 繪製溫度與降水量的散佈圖,探索潛在關係
plt.figure(figsize=(10, 6))
plt.scatter(df_weather[\"AvgTemp\"], df_weather[\"Precipitation\"],
c=df_weather[\"Month\"], cmap=\'viridis\', alpha=0.7, s=50)
plt.title(\"平均溫度與降水量關係圖 (按月份著色)\", fontsize=16)
plt.xlabel(\"平均溫度 (°C)\", fontsize=12)
plt.ylabel(\"降水量 (mm)\", fontsize=12)
plt.colorbar(label=\"月份\")
plt.grid(True, linestyle=\'--\', alpha=0.7)
plt.tight_layout()
plt.show()
4.5 總結與洞察
透過上述分析,我們可以從「城市天氣數據集」中得出以下洞察:
透過上述分析,我們可以從「城市天氣數據集」中得出以下洞察:首先,溫度趨勢顯示在 2026 年 1 月份,各城市的平均溫度呈現逐漸下降的趨勢,這符合冬季的氣候特徵,其中台北的溫度相對較低,高雄則相對較高;其次,降水分佈方面,台北的總降水量明顯高於高雄和台中,這可能與其地理位置和氣候模式有關,且降水事件似乎在某些特定日期較為集中;最後,溫度與降水關係的散佈圖顯示,在較低的平均溫度下,降水量有時會較高,這可能暗示著冬季降雨或濕冷天氣的發生,然而這也需要進一步的數據和更長時間的觀察來驗證。
如何將這些洞察應用於實際決策:
這些洞察可以應用於實際決策:例如,在城市規劃方面,根據降水數據,城市規劃者可以評估不同城市的排水系統需求;在旅遊業方面,旅遊業者可以根據溫度趨勢和降水模式,為不同城市制定季節性的旅遊推廣策略;在農業方面,農民可以根據氣溫和降水預測,調整作物種植和灌溉計劃。
常見錯誤與解決方案:
在數據分析過程中,也應注意常見錯誤與解決方案:首先是數據偏見,本案例數據量較小且僅限於一個月,可能存在偏見,在實際應用中應使用更大量、更長時間跨度的數據來確保分析結果的可靠性;其次是過度擬合,在進行更複雜的預測模型時,應注意避免過度擬合,確保模型在未見數據上的泛化能力,這通常需要使用交叉驗證等技術;最後是忽略領域知識,數據分析不僅僅是技術操作,更需要結合領域知識來解釋數據背後的含義,例如理解台灣冬季的東北季風對降水的影響,有助於更準確地解讀天氣數據。
這個綜合實戰案例展示了 NumPy、Pandas 和 Matplotlib 如何協同工作,幫助我們從原始數據中發現有意義的資訊,並將其轉化為可操作的洞察。這正是數據分析的魅力所在。
結論
本教學文章全面介紹了 Python 數據分析的三大基石:NumPy、Pandas 和 Matplotlib。透過學習 NumPy 的高效數值運算、Pandas 強大的數據處理能力,以及 Matplotlib 靈活的數據視覺化技巧,您已具備處理和分析結構化數據的核心技能。數據分析是一個不斷學習和實踐的過程,鼓勵您將所學應用於更多真實世界的數據集,持續探索和提升。掌握這些工具,您將能夠更好地理解數據、從中提取價值,並為決策提供有力支持。
常見問題 (FAQ)
- NumPy 和 Pandas 有什麼區別?我應該先學哪個? NumPy 主要處理數值陣列運算,提供底層高效計算的能力,是許多科學計算庫的基礎。而 Pandas 則建立在 NumPy 之上,提供了更高級的數據結構,如 Series 和 DataFrame,以及更豐富的數據處理工具,使其更適合處理表格型數據。對於初學者而言,建議先學習 NumPy 的基礎知識,理解其陣列操作的原理,再深入學習 Pandas,這樣能更好地掌握數據處理的精髓。
- Matplotlib 和 Seaborn 哪個更好? Matplotlib 是 Python 中最基礎且功能強大的繪圖庫,提供了高度的客製化能力,可以繪製各種靜態、動態和互動式圖表。而 Seaborn 則是建立在 Matplotlib 之上的高級數據視覺化庫,它提供了更美觀的預設樣式和更高級的統計圖表功能,能夠更方便地繪製複雜的統計圖表。通常情況下,兩者會結合使用,Matplotlib 負責底層的圖表控制和細節調整,而 Seaborn 則用於快速生成美觀且具有統計意義的圖表。因此,沒有絕對的「更好」,而是根據具體需求選擇或結合使用。
- 如何選擇合適的數據分析工具? 選擇合適的數據分析工具主要取決於任務需求、數據類型和個人偏好。Python 及其生態系統 (NumPy, Pandas, Matplotlib) 適用於通用數據分析、機器學習和自動化任務,具有高度靈活性和擴展性。R 語言則在統計分析、學術研究和數據視覺化方面擁有強大優勢。對於小型數據集和快速探索性分析,Excel 仍是一個便捷的選擇。而 SQL 則是用於數據庫查詢和管理的核心工具。在實際工作中,數據分析師通常會根據專案需求,靈活運用多種工具。
- 如何處理大型數據集以提高 Pandas 效能?
處理大型數據集時,為了提高 Pandas 的效能,可以採取多種策略:首先,優化數據類型,例如使用更小的整數或浮點數類型,可以顯著減少記憶體佔用;其次,對於非常大的文件,可以使用
chunksize分塊讀取,避免一次性載入所有數據導致記憶體溢出;再者,應避免在 DataFrame 上進行迭代,盡量使用 Pandas 內建的向量化操作,這會比 Python 迴圈快得多;最後,對於超大型數據集,可以考慮使用 Dask 或 PySpark 等分散式計算框架,它們能夠將數據處理任務分發到多個核心或機器上並行執行。 - 學習 Python 數據分析的下一步是什麼? 在掌握了 NumPy、Pandas 和 Matplotlib 的基礎後,學習 Python 數據分析的下一步可以朝多個方向發展:您可以學習更多高級視覺化庫,例如 Seaborn 和 Plotly,以創建更具吸引力和互動性的圖表;深入統計學和機器學習,掌握 Scikit-learn 等庫的使用,進行更複雜的數據建模和預測;學習數據庫操作,例如使用 SQLAlchemy 與各種數據庫進行交互;最重要的是,積極參與數據分析專案,將所學知識應用於真實世界的數據,累積實戰經驗,不斷提升解決問題的能力。
- 如何將 Matplotlib 圖表保存為高解析度圖片?
要將 Matplotlib 圖表保存為高解析度圖片,可以使用
plt.savefig()函數。其中,dpi參數用於控制圖片的解析度,例如設置dpi=300可以獲得 300 DPI 的圖片,而bbox_inches=\'tight\'參數則可以確保圖表邊界緊湊,避免出現多餘的空白邊緣。例如:plt.savefig(\'filename.png\', dpi=300, bbox_inches=\'tight\')。 - Python 數據分析的學習路徑建議? 對於 Python 數據分析的學習路徑,建議從Python 基礎開始,掌握基本的語法和程式設計概念。接著,深入學習NumPy以理解高效的數值運算,然後是Pandas,掌握其強大的數據處理和操作能力。在數據處理的基礎上,學習Matplotlib/Seaborn進行數據視覺化。隨後,建議補充統計學基礎知識,這對於理解數據背後的意義至關重要。進一步可以學習機器學習基礎,例如使用 Scikit-learn 進行模型建立。最後,也是最重要的一步,是透過實戰專案來鞏固所學,將理論知識應用於實際問題解決中,不斷累積經驗。
- 在數據分析中,如何確保數據的準確性和可靠性? 確保數據的準確性和可靠性是數據分析成功的關鍵。這需要多方面的努力:首先,必須對數據來源進行可靠性驗證,了解數據的採集方式、潛在偏差和限制;其次,在數據清洗階段必須保持嚴謹,仔細處理缺失值、異常值和重複值,確保數據的完整性和一致性;再者,可以透過交叉驗證和多種分析方法的比較來驗證分析結果的穩健性;最後,進行結果的合理性檢查並結合領域知識,判斷分析結果是否符合常識和業務邏輯,避免得出錯誤的結論。
SEO 優化與相關工具推薦
長尾關鍵字建議
為了提升文章的搜尋引擎優化 (SEO) 效果,建議包含以下長尾關鍵字:Python 數據分析教學、Pandas 數據處理實戰、NumPy 陣列操作教學、Matplotlib 數據視覺化教學、Python 數據科學入門指南、數據分析師必備 Python 技能、Python 數據分析範例程式碼、Python 數據分析工具比較、如何用 Python 進行數據清洗、Python 數據視覺化圖表類型。
內部連結建議
為了增加文章的內部連結,提升網站的 SEO 表現和用戶體驗,建議在文章中適當位置加入以下內部連結:建議連結至「Python 程式設計基礎教學」以提供讀者 Python 基礎知識的補充;建議連結至「機器學習入門與 Scikit-learn 實戰」以引導讀者進一步學習數據建模;建議連結至「數據庫與 SQL 基礎教學」以擴展讀者在數據庫操作方面的知識。
相關工具推薦
除了 NumPy、Pandas 和 Matplotlib 之外,還有一些相關工具可以極大地提升數據分析的效率和體驗:Jupyter Notebook / JupyterLab 提供了一個互動式數據分析環境,方便程式碼、輸出和說明文字的整合;VS Code 是一個強大的程式碼編輯器,支援 Python 開發和 Jupyter Notebook;Seaborn 是基於 Matplotlib 的高級數據視覺化庫,能夠提供更美觀的統計圖表;Scikit-learn 是一個廣泛使用的機器學習庫,提供了多種機器學習演算法;Google Colab 則是一個基於雲端的 Jupyter Notebook 環境,無需本地配置即可運行 Python 程式碼,非常適合學習和快速原型開發。
實用性說明
為了確保文章的實用性,本教學文章在設計上特別強調以下幾點:首先,每個主要概念都將搭配具體的實際範例和程式碼,展示如何應用 NumPy、Pandas 和 Matplotlib 解決實際問題;其次,對於複雜的操作,將提供清晰的步驟說明,確保讀者能夠按部就班地學習和實踐;最後,在各章節和實戰案例中,將指出新手常犯的常見錯誤,並提供解決方案,幫助讀者避免踩坑,提升學習效率。
