學習資料科學:Python 網拍資料清洗實戰
Python 網拍資料清洗實戰課程
本課程將從零開始,帶領您學習如何運用 Python 的 pandas
、NumPy
、SciPy
和 re
等套件,清洗並整理網拍資料。您將在本課程中掌握資料清理的完整流程與實用技巧。
1. 準備工作與資料匯入
1.1 環境設定
- 確認 Python 版本:請確認您已安裝 Python 3.6 以上版本。
- 安裝必要套件:使用
pip
安裝pandas
、numpy
、scipy
、matplotlib
、seaborn
、scikit-learn
等套件。
pip install pandas numpy scipy matplotlib seaborn scikit-learn
1.2 匯入套件
在 Python 程式碼中導入課程中將使用的套件。
import pandas as pd
import numpy as np
import scipy.stats as stats
import matplotlib.pyplot as plt
import seaborn as sns
import re
from sklearn.preprocessing import MinMaxScaler, StandardScaler
1.3 資料讀取
使用 pandas
讀取網拍資料。本課程假設資料為 JSON 格式,實際應用時請根據您的資料格式調整。
data = pd.read_json('shopee_data.json')
print(data.head()) # 顯示前幾筆資料
print(data.info()) # 顯示資料的基本資訊
2. 初步資料探索
2.1 資料概覽
初步了解資料的欄位名稱、資料型態、資料量等基本資訊,以掌握資料的大致樣貌。
2.2 缺失值檢查
統計每個欄位的缺失值數量與比例,並透過視覺化方式呈現缺失值分佈。
missing_values = data.isnull().sum()
missing_percentage = (missing_values / len(data)) * 100
print("各欄位缺失值數量:\n", missing_values)
print("\n各欄位缺失值比例:\n", missing_percentage)
sns.heatmap(data.isnull(), cbar=False, cmap='viridis') # 視覺化缺失值
plt.show()
[Image of Heatmap of missing values]
2.3 資料分佈觀察
使用直方圖、箱型圖等視覺化工具,觀察數值型欄位的資料分佈,初步判斷資料的集中趨勢與分散程度。
sns.histplot(data['price'])
plt.title('價格分佈直方圖')
plt.show()
[Image of Histogram of price distribution]
sns.boxplot(y=data['stock'])
plt.title('庫存量箱型圖')
plt.show()
[Image of Boxplot of stock distribution]
3. 缺失值處理
3.1 缺失值處理策略
根據資料特性與分析目的,選擇合適的缺失值處理策略:
-
刪除:
- 移除缺失值過多的列或欄。
- 注意事項:刪除可能損失部分資料資訊,需謹慎評估。
data_cleaned = data.dropna(thresh=len(data.columns) * 0.5) # 移除缺失值超過一半的列
-
填補:
- 使用適當的值填補缺失值,避免資料損失。
- 數值型欄位:
- 平均數 (
mean
) - 中位數 (
median
) - 眾數 (
mode
)
- 平均數 (
- 類別型欄位:
- 眾數 (
mode
) - 固定值 (例如 “未知”、空字串
''
)
- 眾數 (
data['price'] = data['price'].fillna(data['price'].median()) # 使用中位數填補價格 data['stock'] = data['stock'].fillna(data['stock'].mode()[0]) # 使用眾數填補庫存 data['fromAddress'] = data['fromAddress'].fillna('') # 使用空字串填補地址
4. 異常值處理
4.1 異常值檢測方法
-
視覺化方法:
- 箱型圖 (
boxplot
):可視覺化資料分佈,箱鬚外的點可能為異常值。
- 箱型圖 (
-
統計方法:
- Z-score:
- 計算資料點偏離平均數多少個標準差。
- 通常 Z-score 超過 ±3 或 ±2.5 倍標準差的值被視為異常值。
- IQR (四分位距):
- 判斷資料點是否落在 Q1 - 1.5 * IQR 到 Q3 + 1.5 * IQR 的範圍內。
- 落在範圍外的資料點被視為異常值。
def detect_outliers_iqr(df, column): Q1 = df[column].quantile(0.25) Q3 = df[column].quantile(0.75) IQR = Q3 - Q1 lower_bound = Q1 - 1.5 * IQR upper_bound = Q3 + 1.5 * IQR outliers = df[(df[column] < lower_bound) | (df[column] > upper_bound)][column] return outliers, lower_bound, upper_bound price_outliers, price_lower, price_upper = detect_outliers_iqr(data, 'price') print(f"價格異常值數量:{len(price_outliers)}")
- Z-score:
4.2 異常值處理方法
-
刪除:
- 移除異常值所在的資料列。
- 注意事項:與缺失值刪除類似,需謹慎評估資訊損失。
-
截斷 (Capping):
- 將異常值替換為可接受的最大/最小值,使其回歸合理範圍。
def cap_outliers(df, column, lower_bound, upper_bound): df[column] = df[column].clip(lower=lower_bound, upper=upper_bound) return df data = cap_outliers(data, 'price', price_lower, price_upper) # 截斷價格異常值
5. 重複資料處理
5.1 重複資料檢查
-
檢查完全重複的資料列:判斷是否存在完全相同的資料記錄。
-
檢查特定欄位重複的資料列:例如,針對商品標題 (
title
) 檢查是否有重複。duplicates = data.duplicated().sum() title_duplicates = data.duplicated(subset=['title']).sum() print(f"完全重複的資料列數量:{duplicates}") print(f"標題重複的資料列數量:{title_duplicates}")
5.2 重複資料處理
-
移除完全重複的資料列:直接刪除完全相同的資料記錄。
-
移除特定欄位重複的資料列:
- 可選擇保留第一筆 (
'first'
) 或最後一筆 ('last'
) 重複資料,並移除其他重複項。
data = data.drop_duplicates() # 移除完全重複的資料列 data = data.drop_duplicates(subset=['title'], keep='first') # 移除標題重複的資料列,保留第一筆
- 可選擇保留第一筆 (
6. 資料型態一致性
6.1 資料型態檢查
確認每個欄位的資料型態是否符合預期,例如數值欄位是否為數值型態、日期時間欄位是否為日期時間型態等。
6.2 資料型態轉換
進行必要的資料型態轉換,確保資料型態的正確性與一致性。
-
數值型欄位轉換: 使用
pd.to_numeric()
將欄位轉換為數值型 (int
、float
)。 -
日期時間欄位轉換: 使用
pd.to_datetime()
將欄位轉換為日期時間型 (datetime
)。data['price'] = pd.to_numeric(data['price'], errors='coerce') # 將價格轉換為數值型 data['stock'] = pd.to_numeric(data['stock'], errors='coerce') # 將庫存轉換為數值型
7. 正規化處理
7.1 正規化方法
將數值型欄位進行正規化,縮放數值範圍,避免數值大小影響模型分析結果。
-
Min-Max Scaling (最小-最大縮放):
- 將數值縮放到 0 到 1 的範圍。
- 適用於資料分佈非常態,且需要統一數值範圍的場景。
scaler = MinMaxScaler() data[['price', 'stock']] = scaler.fit_transform(data[['price', 'stock']])
-
Z-score Standardization (Z-分數標準化):
- 將數值轉換為平均數為 0,標準差為 1 的標準常態分佈。
- 適用於資料近似常態分佈,且希望消除量綱影響的場景。
scaler = StandardScaler() data[['price', 'stock']] = scaler.fit_transform(data[['price', 'stock']])
8. 文字資料處理 (re
套件)
8.1 正規表達式 (Regular Expression)
學習撰寫基本的正規表達式,用於進行字串的比對、搜尋、取代等操作,以利後續文字資料清理與特徵提取。
8.2 常見的文字清理任務
-
移除特殊字元、HTML 標籤: 清理文字中不必要的符號或標籤。
-
統一大小寫: 將文字統一轉換為大寫或小寫,避免大小寫不一致影響分析。
-
移除多餘的空格: 移除文字中多餘的空白字元。
-
提取關鍵資訊: 使用正規表達式從文字中提取特定資訊,例如品牌名稱、商品規格等。
data['title_clean'] = data['title'].str.replace(r'[^\w\s]', '') # 移除標題中的特殊字元 data['brand'] = data['title'].str.extract(r'【(.*?)】') # 提取品牌名稱 data['title'] = data['title'].apply(lambda x: re.sub(r'【.*?】', '', x).strip() if isinstance(x, str) else x) #移除【XXX專賣】或【XXX】等賣家標記
9. 特徵工程
9.1 從現有欄位衍生新特徵
基於現有欄位,透過程式運算或邏輯判斷,衍生出新的特徵欄位,以提供更多元的分析面向。
-
從標題中提取商品類別: 根據標題文字判斷商品所屬類別。
-
從地址欄位提取地區資訊: 從地址文字中提取縣市、鄉鎮等地區資訊。
-
計算標題長度、描述長度: 計算文字長度,作為商品熱門程度或資訊豐富度的參考。
-
判斷是否為海外商品: 根據地址資訊判斷商品是否為海外商品。
data['is_kitchen_item'] = data['title'].str.contains('刀|鍋|餐具', case=False) # 判斷是否為廚房用品 data['city'] = data['fromAddress'].str.extract(r'(.*?市|.*?縣)') # 提取縣市 data['title_length'] = data['title'].str.len() # 計算標題長度
10. 資料一致性優化
10.1 標準化資料格式
針對資料中格式不一致的欄位進行標準化,提升資料品質與分析準確性。
-
統一地址格式: 例如,將地址中的 “台” 統一替換為 “臺”。
-
標準化標籤:
- 將標籤文字轉換為小寫。
- 移除標籤文字前後的空格。
data['fromAddress'] = data['fromAddress'].str.replace('台', '臺') # 統一地址格式 data['tags'] = data['tags'].apply(lambda x: [tag.lower().strip() for tag in x] if isinstance(x, list) else []) # 標準化標籤
11. 總結與實例
11.1 建立可重複使用的資料清理流程
整合以上所有步驟,將資料清理流程封裝成可重複使用的函數,方便後續資料處理作業。
11.2 應用清理流程於範例資料
使用範例資料,實際執行資料清理流程,展示清理成果。
11.3 儲存清理後的資料
將清理後的資料儲存為 CSV 檔案,方便後續分析或應用。
def clean_data(df):
# 1. 缺失值處理
# ... (請將上述缺失值處理程式碼填入)
# 2. 異常值處理
# ... (請將上述異常值處理程式碼填入)
# 3. 重複資料處理
# ... (請將上述重複資料處理程式碼填入)
# 4. 資料型態一致性
# ... (請將上述資料型態轉換程式碼填入)
# 5. 正規化處理
# ... (請選擇需要的正規化方式,並填入程式碼)
# 6. 文字資料處理
# ... (請將上述文字資料處理程式碼填入)
# 7. 特徵工程
# ... (請將上述特徵工程程式碼填入)
# 8. 資料一致性優化
# ... (請將上述資料一致性優化程式碼填入)
return df
cleaned_data = clean_data(data.copy())
cleaned_data.to_csv('cleaned_data.csv', index=False)
以下是完整的程式碼整合所有步驟的程式碼,並加入必要的註解,使其成為一個可直接執行的 Python 腳本。
import pandas as pd
import numpy as np
import scipy.stats as stats
import matplotlib.pyplot as plt
import seaborn as sns
import re
from sklearn.preprocessing import MinMaxScaler, StandardScaler
def clean_data(df):
"""
完整拍賣資料清理流程函數。
Args:
df: 原始 Pandas DataFrame。
Returns:
cleaned_df: 清理後的 Pandas DataFrame。
"""
print("資料清理流程開始...")
# 1. 缺失值處理
print("\n--- 1. 缺失值處理 ---")
# 檢查缺失值
missing_values = df.isnull().sum()
missing_percentage = (missing_values / len(df)) * 100
print("各欄位缺失值數量:\n", missing_values)
print("\n各欄位缺失值比例:\n", missing_percentage)
# 填補缺失值 (根據先前步驟的策略)
df['price'] = df['price'].fillna(df['price'].median()) # 使用中位數填補價格
df['stock'] = df['stock'].fillna(df['stock'].mode()[0]) # 使用眾數填補庫存
df['fromAddress'] = df['fromAddress'].fillna('') # 使用空字串填補地址
print("缺失值填補完成。")
# 2. 異常值處理
print("\n--- 2. 異常值處理 ---")
# 使用 IQR 方法檢測 'price' 欄位異常值
def detect_outliers_iqr(df, column):
Q1 = df[column].quantile(0.25)
Q3 = df[column].quantile(0.75)
IQR = Q3 - Q1
lower_bound = Q1 - 1.5 * IQR
upper_bound = Q3 + 1.5 * IQR
outliers = df[(df[column] < lower_bound) | (df[column] > upper_bound)][column]
return outliers, lower_bound, upper_bound
price_outliers, price_lower, price_upper = detect_outliers_iqr(df, 'price')
print(f"價格異常值數量:{len(price_outliers)}")
# 截斷 'price' 欄位異常值
def cap_outliers(df, column, lower_bound, upper_bound):
df[column] = df[column].clip(lower=lower_bound, upper=upper_bound)
return df
df = cap_outliers(df, 'price', price_lower, price_upper) # 截斷價格異常值
print("價格異常值截斷處理完成。")
# 3. 重複資料處理
print("\n--- 3. 重複資料處理 ---")
# 檢查重複資料
duplicates = df.duplicated().sum()
title_duplicates = df.duplicated(subset=['title']).sum()
print(f"完全重複的資料列數量:{duplicates}")
print(f"標題重複的資料列數量:{title_duplicates}")
# 移除重複資料
df = df.drop_duplicates() # 移除完全重複的資料列
df = df.drop_duplicates(subset=['title'], keep='first') # 移除標題重複的資料列,保留第一筆
print("重複資料移除完成。")
# 4. 資料型態一致性
print("\n--- 4. 資料型態一致性 ---")
# 資料型態轉換
df['price'] = pd.to_numeric(df['price'], errors='coerce') # 將價格轉換為數值型
df['stock'] = pd.to_numeric(df['stock'], errors='coerce') # 將庫存轉換為數值型
print("資料型態轉換完成。")
# 5. 正規化處理 (Min-Max Scaling 示範)
print("\n--- 5. 正規化處理 (Min-Max Scaling) ---")
scaler = MinMaxScaler()
df[['price', 'stock']] = scaler.fit_transform(df[['price', 'stock']])
print("Min-Max 正規化完成。")
# 6. 文字資料處理
print("\n--- 6. 文字資料處理 ---")
# 文字清理與特徵提取
df['title_clean'] = df['title'].str.replace(r'[^\w\s]', '') # 移除標題中的特殊字元
df['brand'] = df['title'].str.extract(r'【(.*?)】') # 提取品牌名稱
df['title'] = df['title'].apply(lambda x: re.sub(r'【.*?】', '', x).strip() if isinstance(x, str) else x) #移除【XXX專賣】或【XXX】等賣家標記
print("文字資料清理與特徵提取完成。")
# 7. 特徵工程
print("\n--- 7. 特徵工程 ---")
# 衍生新特徵
df['is_kitchen_item'] = df['title'].str.contains('刀|鍋|餐具', case=False) # 判斷是否為廚房用品
df['city'] = df['fromAddress'].str.extract(r'(.*?市|.*?縣)') # 提取縣市
df['title_length'] = df['title'].str.len() # 計算標題長度
print("特徵工程完成。")
# 8. 資料一致性優化
print("\n--- 8. 資料一致性優化 ---")
# 標準化資料格式
df['fromAddress'] = df['fromAddress'].str.replace('台', '臺') # 統一地址格式
df['tags'] = df['tags'].apply(lambda x: [tag.lower().strip() for tag in x] if isinstance(x, list) else []) # 標準化標籤
print("資料一致性優化完成。")
print("\n資料清理流程結束,已回傳 cleaned_df。")
return df
# 範例資料讀取 (請確保 'shopee_data.json' 檔案存在於程式碼相同目錄,或修改路徑)
try:
data = pd.read_json('shopee_data.json')
print("資料讀取成功!")
except FileNotFoundError:
print("錯誤:找不到 'shopee_data.json' 檔案,請確認檔案路徑是否正確。")
data = None # 如果檔案不存在,將 data 設為 None 以避免後續程式碼錯誤
if data is not None: # 確保 data 成功讀取才執行後續程式碼
print("\n--- 原始資料 (前 5 筆) ---")
print(data.head())
print("\n--- 原始資料資訊 ---")
print(data.info())
# 執行資料清理
cleaned_data = clean_data(data.copy()) # 使用 copy() 避免修改到原始 data
print("\n--- 清理後的資料 (前 5 筆) ---")
print(cleaned_data.head())
print("\n--- 清理後的資料資訊 ---")
print(cleaned_data.info())
# 將清理後的資料儲存為 CSV 檔案
cleaned_data.to_csv('cleaned_data.csv', index=False, encoding='utf-8-sig') # 使用 utf-8-sig 編碼避免中文亂碼
print("\n清理後的資料已儲存至 'cleaned_data.csv' 檔案。")
程式碼使用說明:
-
環境準備:
- 請先確保您已安裝 Python 3.6 或以上版本,並已安裝必要的套件,可以使用以下指令安裝:
pip install pandas numpy scipy matplotlib seaborn scikit-learn
- 請確認您的程式碼目錄下有
shopee_data.json
檔案,或是您需要修改程式碼中的檔案路徑以指向您的 JSON 資料檔案。
- 請先確保您已安裝 Python 3.6 或以上版本,並已安裝必要的套件,可以使用以下指令安裝:
-
程式碼執行:
- 將以上程式碼複製並儲存為一個
.py
檔案 (例如data_cleaning.py
)。 - 在終端機或命令提示字元中,使用
python data_cleaning.py
指令執行程式碼。
- 將以上程式碼複製並儲存為一個
-
執行結果:
- 程式碼會先讀取
shopee_data.json
檔案 (如果檔案存在)。 - 接著會依序執行資料清理的各個步驟,並在終端機中印出每個步驟的處理訊息與結果 (例如缺失值數量、異常值數量、重複資料數量等)。
- 最後,清理完成的資料會被儲存為
cleaned_data.csv
檔案,並輸出提示訊息。 - 您可以在程式碼執行目錄下找到
cleaned_data.csv
檔案,此檔案即為清理後的拍賣資料。
- 程式碼會先讀取
注意事項:
- JSON 資料檔案:請務必準備好您的
shopee_data.json
檔案,並確保其格式符合 JSON 格式。如果您的資料格式不是 JSON,您需要修改程式碼中的pd.read_json()
函數來讀取您的資料檔案 (例如使用pd.read_csv()
讀取 CSV 檔案)。 - 欄位名稱:程式碼中的資料清理步驟是基於先前範例資料的欄位名稱 (
price
,stock
,fromAddress
,title
,tags
等) 進行處理的。如果您的資料欄位名稱不同,您需要相應地修改程式碼中的欄位名稱。 - 清理策略調整:程式碼中使用的缺失值填補方式、異常值處理方式、正規化方法、文字清理方式等,都是範例策略,您可以根據您的資料特性與分析目的,調整
clean_data()
函數中的程式碼,以採用更合適的資料清理策略。 - 中文編碼:程式碼在儲存 CSV 檔案時使用了
encoding='utf-8-sig'
,這是為了避免 CSV 檔案中的中文內容在某些軟體 (例如 Excel) 中開啟時出現亂碼問題。如果您沒有中文亂碼問題,也可以使用encoding='utf-8'
或其他編碼方式。