R 語言統計分析入門:數據讀取、清理與基礎統計
摘要
本篇文章旨在為統計分析初學者提供一份全面的 R 語言入門指南。我們將從 R 語言與 RStudio 的環境建置開始,逐步深入探討數據讀取、數據清理與基礎統計分析的核心概念與實作技巧。透過豐富的程式碼範例、詳細的步驟說明以及常見錯誤提醒,讀者將能夠掌握使用 R 語言進行數據處理與初步分析的關鍵能力。文章內容涵蓋 CSV、Excel 等常見數據格式的讀取,缺失值、異常值與重複值的處理,以及描述性統計與基礎推論性統計的應用。此外,我們也將提供 SEO 優化建議、長尾關鍵字、內部連結策略與相關工具推薦,協助讀者不僅學會 R 語言,更能提升數據分析專案的效率與品質。
前言
在當今數據驅動的時代,統計分析已成為各行各業不可或缺的技能。無論是學術研究、市場分析、生物醫學還是社會科學,數據分析都能提供深入的洞察力,輔助決策制定。而在眾多統計分析工具中,R 語言以其強大的統計功能、豐富的套件生態系統以及卓越的數據可視化能力,成為了數據科學家、統計學家和研究人員的首選工具之一 [1]。
對於初學者而言,R 語言的學習曲線可能看似陡峭,但只要掌握正確的學習方法和實作技巧,便能逐步駕馭這項強大的工具。本教學文章將聚焦於 R 語言統計分析的入門環節,特別是數據的「讀取」、「清理」與「基礎統計分析」。這三個環節是任何數據分析專案的基石,數據的品質與分析的準確性息息相關。一個乾淨、結構良好的數據集,是得出可靠分析結果的前提。
本文的目標讀者是那些對統計分析有興趣,但對 R 語言或程式設計經驗較少的初學者。我們將透過清晰的解釋、實用的程式碼範例和逐步的指導,幫助讀者建立扎實的 R 語言統計分析基礎。文章中也將穿插 SEO 優化建議,包括長尾關鍵字的使用、內部連結的建立,以及相關工具的推薦,旨在提升文章的搜尋引擎可見度與實用價值。
讓我們一同踏上 R 語言統計分析的學習之旅,解鎖數據背後的奧秘!
第一章:R 語言與 RStudio 環境建置
1.1 R 語言的優勢
R 語言不僅僅是一個統計軟體,它更是一個功能強大的程式語言和數據分析環境。其主要優勢包括:
- 開源免費:R 語言是開源軟體,任何人都可以免費使用、修改和分發,這使得它在全球範圍內擁有龐大的用戶社群和開發者支持。
- 強大的統計功能:R 內建了大量的統計函數,涵蓋了從描述性統計到高級推論性統計、機器學習等各個領域。幾乎所有最新的統計方法都會在 R 中實現。
- 豐富的套件生態系統:R 的核心功能可以透過 CRAN (Comprehensive R Archive Network) 上數以萬計的套件進行擴展。這些套件提供了特定領域的分析工具、數據處理功能和可視化選項,例如
tidyverse系列套件(dplyr,ggplot2,tidyr等)極大地簡化了數據操作和可視化過程。 - 卓越的數據可視化:R 語言擁有世界一流的數據可視化能力,尤其是
ggplot2套件,能夠創建出美觀且具有高度客製化的統計圖表,幫助分析師更好地理解和呈現數據。 - 跨平台兼容性:R 語言可以在 Windows、macOS 和 Linux 等多種作業系統上運行。
1.2 R 與 RStudio 的安裝步驟
為了更高效地使用 R 語言,我們通常會搭配整合開發環境 (IDE) RStudio。RStudio 提供了一個友善的圖形使用者介面,集成了程式碼編輯器、控制台、環境視窗、檔案管理和繪圖視窗等功能,極大地提升了 R 語言的開發體驗。以下是安裝 R 和 RStudio 的步驟:
-
安裝 R 語言:
- 前往 CRAN 官方網站。
- 選擇離您最近的 CRAN Mirror(鏡像站點)。
- 根據您的作業系統(Windows, macOS, Linux)下載並安裝最新版本的 R。按照安裝程式的指示完成安裝。
-
安裝 RStudio Desktop:
- 前往 RStudio 官方網站。
- 選擇免費的 RStudio Desktop 版本下載。
- 根據您的作業系統下載並安裝 RStudio。安裝過程通常非常簡單,只需按照指示點擊「下一步」即可。
1.3 RStudio 介面介紹
成功安裝 R 和 RStudio 後,打開 RStudio,您會看到一個分為四個主要窗格的介面:
- 左上角:原始碼編輯器 (Source Editor):您可以在這裡編寫、編輯和保存 R 程式碼(
.R檔案)。您可以選取程式碼行並按下Ctrl + Enter(Windows/Linux) 或Cmd + Enter(macOS) 來執行程式碼。 - 左下角:控制台 (Console):這是 R 語言的命令列介面。當您執行程式碼時,結果會顯示在這裡。您也可以直接在此輸入 R 命令並立即執行。
- 右上角:環境 (Environment) / 歷史 (History) / 連接 (Connections) / 教學 (Tutorial):
- 環境 (Environment):顯示當前 R 會話中所有已載入的數據集、變數和函數。這是一個非常方便的功能,可以讓您一目瞭然地查看工作空間中的所有對象。
- 歷史 (History):記錄您在控制台中執行過的所有命令。
- 連接 (Connections):用於連接各種數據庫。
- 教學 (Tutorial):提供互動式教學。
- 右下角:檔案 (Files) / 繪圖 (Plots) / 套件 (Packages) / 說明 (Help) / 檢視器 (Viewer):
- 檔案 (Files):一個檔案瀏覽器,用於管理您的專案檔案。
- 繪圖 (Plots):所有生成的圖形都會顯示在這裡。
- 套件 (Packages):顯示所有已安裝和已載入的 R 套件。您可以在這裡安裝新套件或載入已安裝的套件。
- 說明 (Help):提供 R 函數和套件的說明文件。
- 檢視器 (Viewer):用於顯示本地網頁內容或 HTML 小部件。
熟悉 RStudio 介面將有助於您更高效地進行數據分析工作。
第二章:數據讀取
數據讀取是數據分析的第一步,也是確保後續分析順利進行的基礎。R 語言支援多種數據格式的讀取,本章將詳細介紹最常見的幾種數據格式及其在 R 中的讀取方法。
2.1 常見數據格式及其特性
在數據分析中,我們經常會遇到以下幾種數據格式,了解它們的特性有助於選擇正確的讀取方法:
- CSV (Comma Separated Values):
- 特性:最常見的純文字數據格式,數據欄位之間以逗號分隔。每行代表一條記錄,每列代表一個欄位。易於讀取和交換,幾乎所有數據處理軟體都支援。
- 應用場景:小型到中型數據集,跨平台數據交換。
- Excel (XLSX/XLS):
- 特性:Microsoft Excel 的電子表格格式,廣泛用於數據儲存和管理。通常包含多個工作表,可以儲存多種數據類型(文字、數字、日期等),並支援格式化。
- 應用場景:辦公環境中的數據管理,包含多個相關數據表。
- TXT (Text File):
- 特性:純文字檔案,數據欄位可能以空格、Tab 鍵或其他符號分隔。比 CSV 更通用,但需要明確指定分隔符。
- 應用場景:日誌文件、自定義格式數據。
- SPSS (SAV):
- 特性:SPSS 統計軟體的專有數據格式,常用於社會科學研究。除了數據本身,還包含變數標籤、值標籤和缺失值定義等元數據。
- 應用場景:社會科學、市場研究等領域的數據交換。
- JSON (JavaScript Object Notation):
- 特性:一種輕量級的數據交換格式,基於 JavaScript 的物件表示法。常用於網頁應用程式和 API 數據傳輸,結構化程度高,易於人機讀取。
- 應用場景:Web API 數據、非結構化或半結構化數據。
- XML (Extensible Markup Language):
- 特性:一種標記語言,用於儲存和傳輸結構化數據。比 JSON 更為冗長,但提供了更強大的結構定義能力。
- 應用場景:複雜的數據交換、配置文件。
2.2 R 內建函數與套件:讀取不同數據格式
R 語言提供了多種內建函數和強大的第三方套件來讀取這些不同格式的數據。選擇合適的工具可以大大提高數據讀取的效率和準確性。
| 數據格式 | 常用函數/套件 | 說明 |
|---|---|---|
| CSV | read.csv() (base R), read_csv() (readr) | read.csv() 是 R 基礎包中的函數,功能全面但對於大型文件可能較慢。read_csv() 來自 tidyverse 系列的 readr 套件,通常更快、更智能,能自動推斷數據類型,並以 tibble 格式讀取,這在 tidyverse 工作流中更為方便。 |
| Excel | read_excel() (readxl) | 讀取 Excel 檔案 (.xlsx, .xls)。readxl 套件是處理 Excel 文件的首選,它能很好地處理不同版本和複雜結構的 Excel 文件。 |
| TXT | read.table() (base R), read_delim() (readr) | read.table() 是基礎 R 函數,用於讀取一般文字檔案,可指定分隔符號。read_delim() 來自 readr 套件,提供更具彈性的分隔符號指定方式,並能處理各種複雜的文字文件格式。 |
| SPSS | read_sav() (haven) | 讀取 SPSS 數據檔案 (.sav)。haven 套件是專門為導入統計軟體(如 SPSS, SAS, Stata)數據而設計的,它能很好地保留原始數據中的標籤和缺失值定義。 |
| JSON | fromJSON() (jsonlite) | 從 JSON 檔案或字串讀取數據。jsonlite 套件提供了高效且易於使用的函數來解析 JSON 數據,並將其轉換為 R 中的列表或數據框。 |
| XML | xmlParse(), xmlToDataFrame() (XML) | 讀取 XML 檔案。XML 套件提供了處理 XML 數據的強大功能,但使用起來可能相對複雜。 |
2.3 實際範例:逐步讀取不同格式的數據
在進行數據讀取之前,建議先設定工作目錄。這樣 R 就能直接在該目錄下尋找檔案,而無需指定完整的檔案路徑。您可以使用 setwd() 函數設定工作目錄,或在 RStudio 中透過 Session -> Set Working Directory -> Choose Directory... 來設定。為了演示,我們假設所有數據文件都位於當前工作目錄下。
# 設定工作目錄 (請替換為您的實際路徑,例如:"C:/Users/YourUser/Documents/R_Project")
# setwd("~/Documents/R_Data_Analysis")
# 檢查當前工作目錄,確保檔案路徑正確
getwd()
# 設定工作目錄 (請替換為您的實際路徑,例如:"C:/Users/YourUser/Documents/R_Project")
# setwd("~/Documents/R_Data_Analysis")
# 檢查當前工作目錄,確保檔案路徑正確
getwd()
2.3.1 讀取 CSV 檔案
CSV 檔案是最常見的數據格式之一。我們將創建一個模擬的 students.csv 檔案來演示讀取過程。
# 創建一個模擬的 students.csv 檔案
csv_content <- "Name,Age,Major,Score\nAlice,20,Computer Science,85\nBob,21,Mathematics,92\nCharlie,20,Physics,78\nDavid,22,Computer Science,90"
writeLines(csv_content, "students.csv")
# 使用 read.csv() (base R) 讀取 CSV 檔案
students_csv_base <- read.csv("students.csv")
# 查看數據結構 (str: structure)
str(students_csv_base)
# 查看前幾行數據 (head)
head(students_csv_base)
# 查看數據摘要 (summary)
summary(students_csv_base)
# 使用 read_csv() (readr 套件) 讀取 CSV 檔案
# 首先,確保 readr 套件已安裝並載入
# install.packages("readr") # 如果尚未安裝,請先執行此行
library(readr)
students_csv_readr <- read_csv("students.csv")
str(students_csv_readr)
head(students_csv_readr)
summary(students_csv_readr)
# 比較 read.csv 和 read_csv 的輸出,read_csv 通常會自動將字串識別為字符型,而不是因子型,且返回 tibble 格式。
# 創建一個模擬的 students.csv 檔案
csv_content <- "Name,Age,Major,Score\nAlice,20,Computer Science,85\nBob,21,Mathematics,92\nCharlie,20,Physics,78\nDavid,22,Computer Science,90"
writeLines(csv_content, "students.csv")
# 使用 read.csv() (base R) 讀取 CSV 檔案
students_csv_base <- read.csv("students.csv")
# 查看數據結構 (str: structure)
str(students_csv_base)
# 查看前幾行數據 (head)
head(students_csv_base)
# 查看數據摘要 (summary)
summary(students_csv_base)
# 使用 read_csv() (readr 套件) 讀取 CSV 檔案
# 首先,確保 readr 套件已安裝並載入
# install.packages("readr") # 如果尚未安裝,請先執行此行
library(readr)
students_csv_readr <- read_csv("students.csv")
str(students_csv_readr)
head(students_csv_readr)
summary(students_csv_readr)
# 比較 read.csv 和 read_csv 的輸出,read_csv 通常會自動將字串識別為字符型,而不是因子型,且返回 tibble 格式。
read.csv() 常用參數說明:
file:要讀取的檔案路徑。header:邏輯值,指示檔案的第一行是否包含變數名稱 (預設為TRUE)。sep:分隔符號 (預設為"")。對於 CSV 檔案,通常是""。dec:小數點符號 (預設為".")。na.strings:指定哪些字串應被視為缺失值 (NA)。encoding:指定檔案編碼,處理中文亂碼時常用 (例如"UTF-8","BIG5")。
read_csv() 常用參數說明:
file:要讀取的檔案路徑。col_names:邏輯值或字符向量,指示是否包含欄位名稱或提供新的欄位名稱。col_types:指定每列的數據類型,可以手動控制數據讀取。locale:用於指定編碼、日期格式等。
2.3.2 讀取 Excel 檔案
Excel 檔案在商業和研究中非常普遍。我們將創建一個模擬的 grades.xlsx 檔案來演示讀取過程。
# 創建一個模擬的 grades.xlsx 檔案 (需要 openxlsx 套件來寫入 Excel)
# install.packages("openxlsx") # 如果尚未安裝,請先執行此行
library(openxlsx)
grades_data <- data.frame(
ID = c(1, 1, 2, 2),
Subject = c("Math", "English", "Math", "English"),
Grade = c("A", "B", "B", "A")
)
write.xlsx(grades_data, "grades.xlsx", sheetName = "Sheet1", rowNames = FALSE)
# 使用 read_excel() (readxl 套件) 讀取 Excel 檔案
# 首先,確保 readxl 套件已安裝並載入
# install.packages("readxl") # 如果尚未安裝,請先執行此行
library(readxl)
grades_excel <- read_excel("grades.xlsx", sheet = "Sheet1")
str(grades_excel)
head(grades_excel)
summary(grades_excel)
# 如果 Excel 檔案有多個工作表,您可以透過 sheet 參數指定要讀取的工作表名稱或索引。
# 例如:read_excel("grades.xlsx", sheet = 2) 或 read_excel("grades.xlsx", sheet = "第二個工作表名稱")
# 創建一個模擬的 grades.xlsx 檔案 (需要 openxlsx 套件來寫入 Excel)
# install.packages("openxlsx") # 如果尚未安裝,請先執行此行
library(openxlsx)
grades_data <- data.frame(
ID = c(1, 1, 2, 2),
Subject = c("Math", "English", "Math", "English"),
Grade = c("A", "B", "B", "A")
)
write.xlsx(grades_data, "grades.xlsx", sheetName = "Sheet1", rowNames = FALSE)
# 使用 read_excel() (readxl 套件) 讀取 Excel 檔案
# 首先,確保 readxl 套件已安裝並載入
# install.packages("readxl") # 如果尚未安裝,請先執行此行
library(readxl)
grades_excel <- read_excel("grades.xlsx", sheet = "Sheet1")
str(grades_excel)
head(grades_excel)
summary(grades_excel)
# 如果 Excel 檔案有多個工作表,您可以透過 sheet 參數指定要讀取的工作表名稱或索引。
# 例如:read_excel("grades.xlsx", sheet = 2) 或 read_excel("grades.xlsx", sheet = "第二個工作表名稱")
read_excel() 常用參數說明:
path:要讀取的 Excel 檔案路徑。sheet:要讀取的工作表名稱或索引 (預設為第一個工作表)。range:指定要讀取的儲存格範圍 (例如"A1:C10")。col_names:邏輯值或字符向量,指示是否包含欄位名稱或提供新的欄位名稱。col_types:指定每列的數據類型。
2.3.3 讀取 TXT 檔案
TXT 檔案是通用的文字檔案,分隔符號可能多樣。我們將創建一個模擬的 data.txt 檔案來演示讀取過程。
# 創建一個模擬的 data.txt 檔案 (Tab 分隔)
txt_content <- "ID\tValue1\tValue2\n1\t10.5\t20\n2\t12.3\t22\n3\t11.8\t21"
writeLines(txt_content, "data.txt")
# 使用 read.table() (base R) 讀取 Tab 分隔的 TXT 檔案
data_txt_base <- read.table("data.txt", header = TRUE, sep = "\t")
str(data_txt_base)
head(data_txt_base)
# 使用 read_delim() (readr 套件) 讀取 TXT 檔案
library(readr)
data_txt_readr <- read_delim("data.txt", delim = "\t")
str(data_txt_readr)
head(data_txt_readr)
# 如果分隔符是空格,則可以省略 sep 參數或設定 sep = " "。
# 例如:read.table("space_separated.txt", header = TRUE)
# 創建一個模擬的 data.txt 檔案 (Tab 分隔)
txt_content <- "ID\tValue1\tValue2\n1\t10.5\t20\n2\t12.3\t22\n3\t11.8\t21"
writeLines(txt_content, "data.txt")
# 使用 read.table() (base R) 讀取 Tab 分隔的 TXT 檔案
data_txt_base <- read.table("data.txt", header = TRUE, sep = "\t")
str(data_txt_base)
head(data_txt_base)
# 使用 read_delim() (readr 套件) 讀取 TXT 檔案
library(readr)
data_txt_readr <- read_delim("data.txt", delim = "\t")
str(data_txt_readr)
head(data_txt_readr)
# 如果分隔符是空格,則可以省略 sep 參數或設定 sep = " "。
# 例如:read.table("space_separated.txt", header = TRUE)
read.table() 常用參數說明:
file:要讀取的檔案路徑。header:邏輯值,指示檔案的第一行是否包含變數名稱 (預設為FALSE)。sep:分隔符號 (預設為空白字元,即一個或多個空格、Tab、換行符等)。quote:指定引號字元。fill:邏輯值,如果行長度不一致,是否用空值填充 (預設為FALSE)。
read_delim() 常用參數說明:
file:要讀取的檔案路徑。delim:分隔符號,可以是任何字串。- 其他參數與
read_csv()類似。
2.3.4 讀取 SPSS 檔案
SPSS 檔案通常包含豐富的元數據。haven 套件是處理這類檔案的最佳選擇。
# 創建一個模擬的 survey.sav 檔案 (需要 haven 套件來寫入 SPSS 格式)
# install.packages("haven") # 如果尚未安裝,請先執行此行
library(haven)
survey_data <- data.frame(
Age = c(25, 30, 35),
Gender = factor(c("Male", "Female", "Male"), levels = c("Male", "Female"), labels = c("男性", "女性")),
Satisfaction = labelled(c(1, 2, 1), labels = c(`不滿意` = 1, `滿意` = 2))
)
write_sav(survey_data, "survey.sav")
# 使用 read_sav() (haven 套件) 讀取 SPSS 檔案
library(haven)
survey_spss <- read_sav("survey.sav")
str(survey_spss)
head(survey_spss)
summary(survey_spss)
# 注意:haven 套件會盡可能保留 SPSS 檔案中的標籤和缺失值定義,這對於後續分析非常有用。
# 創建一個模擬的 survey.sav 檔案 (需要 haven 套件來寫入 SPSS 格式)
# install.packages("haven") # 如果尚未安裝,請先執行此行
library(haven)
survey_data <- data.frame(
Age = c(25, 30, 35),
Gender = factor(c("Male", "Female", "Male"), levels = c("Male", "Female"), labels = c("男性", "女性")),
Satisfaction = labelled(c(1, 2, 1), labels = c(`不滿意` = 1, `滿意` = 2))
)
write_sav(survey_data, "survey.sav")
# 使用 read_sav() (haven 套件) 讀取 SPSS 檔案
library(haven)
survey_spss <- read_sav("survey.sav")
str(survey_spss)
head(survey_spss)
summary(survey_spss)
# 注意:haven 套件會盡可能保留 SPSS 檔案中的標籤和缺失值定義,這對於後續分析非常有用。
read_sav() 常用參數說明:
file:要讀取的 SPSS 檔案路徑。encoding:指定檔案編碼。user_na:邏輯值,是否將 SPSS 的用戶定義缺失值轉換為 R 的NA(預設為TRUE)。
2.3.5 讀取 JSON 檔案
JSON 檔案在 Web 數據抓取和 API 交互中非常常見。jsonlite 套件提供了高效的 JSON 解析功能。
# 創建一個模擬的 config.json 檔案
json_content <- "{\n \"setting1\": \"value1\",\n \"setting2\": 123,\n \"options\": [\n {\"id\": 1, \"name\": \"Option A\"},\n {\"id\": 2, \"name\": \"Option B\"}\n ]\n}"
writeLines(json_content, "config.json")
# 使用 fromJSON() (jsonlite 套件) 讀取 JSON 檔案
# 首先,確保 jsonlite 套件已安裝並載入
# install.packages("jsonlite") # 如果尚未安裝,請先執行此行
library(jsonlite)
config_json <- fromJSON("config.json")
str(config_json)
print(config_json)
# fromJSON() 會將 JSON 數據轉換為 R 中的列表或數據框,具體取決於 JSON 結構。
# 如果 JSON 是一個物件陣列,它會嘗試轉換為數據框。
# 創建一個模擬的 config.json 檔案
json_content <- "{\n \"setting1\": \"value1\",\n \"setting2\": 123,\n \"options\": [\n {\"id\": 1, \"name\": \"Option A\"},\n {\"id\": 2, \"name\": \"Option B\"}\n ]\n}"
writeLines(json_content, "config.json")
# 使用 fromJSON() (jsonlite 套件) 讀取 JSON 檔案
# 首先,確保 jsonlite 套件已安裝並載入
# install.packages("jsonlite") # 如果尚未安裝,請先執行此行
library(jsonlite)
config_json <- fromJSON("config.json")
str(config_json)
print(config_json)
# fromJSON() 會將 JSON 數據轉換為 R 中的列表或數據框,具體取決於 JSON 結構。
# 如果 JSON 是一個物件陣列,它會嘗試轉換為數據框。
fromJSON() 常用參數說明:
txt:要解析的 JSON 字串或檔案路徑。simplifyVector:邏輯值,是否將 JSON 陣列簡化為 R 向量或數據框 (預設為TRUE)。flatten:邏輯值,是否將嵌套的 JSON 物件扁平化為單一數據框的列 (預設為FALSE)。
2.4 常見錯誤與解決方案 (數據讀取)
數據讀取過程中可能會遇到各種問題,以下是一些常見錯誤及其解決方案:
- 檔案路徑錯誤:
- 錯誤訊息:
Error in file(file, "rt") : cannot open the connection或No such file or directory。 - 原因:這通常是因為檔案不存在、檔案名稱或路徑不正確,或者 R 沒有權限訪問該路徑。
- 解決方案:
- 檢查檔案名稱和副檔名:確保檔案名稱拼寫正確,且副檔名與實際檔案類型匹配 (例如
.csv,.xlsx)。 - 檢查檔案路徑:確保提供的路徑是絕對路徑或相對於當前工作目錄的相對路徑。使用
getwd()檢查當前工作目錄,並使用list.files()查看當前目錄下的檔案。 - 設定正確的工作目錄:使用
setwd("您的檔案路徑")或 RStudio 的Session -> Set Working Directory。 - 權限問題:確保 R 有讀取該檔案的權限。
- 檢查檔案名稱和副檔名:確保檔案名稱拼寫正確,且副檔名與實際檔案類型匹配 (例如
- 錯誤訊息:
- 編碼問題:
- 錯誤訊息:讀取中文數據時出現亂碼,例如顯示為
<?>或其他不可讀字符。 - 原因:檔案的編碼格式與 R 讀取時使用的編碼不一致。
- 解決方案:嘗試在讀取函數中指定
encoding參數。常見的中文編碼有"UTF-8"和"BIG5"(繁體中文) 或"GBK"(簡體中文)。例如:read.csv("file.csv", encoding = "UTF-8")。
- 錯誤訊息:讀取中文數據時出現亂碼,例如顯示為
- 分隔符號不正確:
- 錯誤訊息:數據讀取後所有內容都在一列中,或者數據被錯誤地分割。
- 原因:
sep或delim參數設定不正確,與原始檔案的實際分隔符號不符。 - 解決方案:打開原始檔案檢查其分隔符號(逗號、Tab、分號、空格等),並在函數中正確指定。例如,Tab 分隔使用
sep = "\t"或delim = "\t"。
- 欄位名稱問題:
- 錯誤訊息:第一行數據被當作欄位名稱,或者欄位名稱包含特殊字元導致問題。
- 原因:
header參數設定錯誤,或原始欄位名稱不符合 R 的命名規範。 - 解決方案:
- 如果檔案沒有標題行,設定
header = FALSE。讀取後可以手動為欄位命名。 - 對於特殊字元,R 會自動轉換(例如將空格替換為點),但最好在原始數據中避免使用特殊字元或在讀取後進行清理。
- 如果檔案沒有標題行,設定
- 套件未安裝或未載入:
- 錯誤訊息:
Error in library(readxl) : there is no package called \'readxl\'。 - 原因:您嘗試使用的函數來自一個尚未安裝或載入的套件。
- 解決方案:請確保您已使用
install.packages("套件名稱")安裝了所需的套件,並使用library(套件名稱)載入。例如:install.packages("readxl")後再library(readxl)。
- 錯誤訊息:
- 數據類型推斷錯誤:
- 錯誤訊息:某些列被錯誤地識別為字符型而不是數值型,或日期被識別為字符型。
- 原因:R 或
readr在自動推斷數據類型時可能出錯,尤其是在數據中存在混合類型或特殊符號時。 - 解決方案:使用
col_types參數手動指定每列的數據類型,或在讀取後使用as.numeric(),as.Date(),as.factor()等函數進行轉換。
第三章:數據清理
數據清理是數據分析流程中至關重要的一步,它確保了數據的品質和分析結果的可靠性。真實世界的數據往往充滿了缺失值、異常值、重複值和不一致的格式。本章將詳細介紹如何在 R 語言中進行數據清理,並提供實用的程式碼範例。
3.1 數據清理的重要性與原則
數據清理不僅僅是修正錯誤,更是對數據進行深入理解的過程。其重要性體現在:
- 提升分析準確性:不乾淨的數據會導致錯誤的統計結果和誤導性的結論。例如,缺失值會影響平均數的計算,異常值會扭曲相關性分析。
- 確保模型穩定性與效能:機器學習模型對數據品質非常敏感。清理後的數據能提高模型的訓練效率、預測準確性和泛化能力。
- 提高數據可用性與可解釋性:清理後的數據更易於理解和使用,方便後續的分析、可視化和報告撰寫。
- 節省時間與資源:雖然數據清理本身耗時,但從長遠來看,它能避免因數據問題而重複分析、修正錯誤或重新收集數據所花費的更多時間和資源。
數據清理的原則:
- 理解數據來源:了解數據是如何收集、儲存和傳輸的,這有助於預測可能出現的問題。
- 系統性檢查:對數據進行全面的檢查,包括缺失值、異常值、重複值、數據類型、格式一致性等。
- 記錄清理過程:詳細記錄每一步清理操作,包括原因、方法和結果,以便追溯和重現。
- 迭代與驗證:數據清理不是一次性任務,可能需要多次迭代。在清理後,應重新檢查數據,驗證清理效果。
- 保持原始數據:在進行任何清理操作之前,最好備份原始數據,以防萬一需要回溯。
3.2 缺失值處理 (Missing Values)
缺失值 (Missing Values),通常表示為 NA (Not Available),是數據集中常見的問題。它們可能是由於數據採集失敗、用戶未填寫、數據損壞等原因造成。處理缺失值的方法有很多種,選擇哪種方法取決於缺失值的數量、類型、分佈以及分析目的。
3.2.1 檢測缺失值
在處理缺失值之前,首先需要識別它們。R 提供了多種函數來檢測缺失值。
# 創建一個包含缺失值的數據框範例
df_missing <- data.frame(
ID = 1:5,
Value1 = c(10, 12, NA, 15, 18), # 數值型缺失
Value2 = c(TRUE, NA, FALSE, TRUE, FALSE), # 邏輯型缺失
Category = c("A", "B", "A", NA, "C"), # 字符型缺失
Date = as.Date(c("2023-01-01", NA, "2023-01-03", "2023-01-04", "2023-01-05")) # 日期型缺失
)
print("原始數據框 (含缺失值):")
print(df_missing)
# 檢查每個元素是否為缺失值,返回一個邏輯矩陣
is.na(df_missing)
# 計算每個欄位的缺失值數量
colSums(is.na(df_missing))
# 計算總缺失值數量
sum(is.na(df_missing))
# 檢查哪些列包含缺失值
names(df_missing)[colSums(is.na(df_missing)) > 0]
# 檢查哪些行包含缺失值
which(!complete.cases(df_missing)) # complete.cases() 返回沒有 NA 的行
# 使用 dplyr 統計缺失值
library(dplyr)
df_missing %>%
summarise_all(~sum(is.na(.)))
# 創建一個包含缺失值的數據框範例
df_missing <- data.frame(
ID = 1:5,
Value1 = c(10, 12, NA, 15, 18), # 數值型缺失
Value2 = c(TRUE, NA, FALSE, TRUE, FALSE), # 邏輯型缺失
Category = c("A", "B", "A", NA, "C"), # 字符型缺失
Date = as.Date(c("2023-01-01", NA, "2023-01-03", "2023-01-04", "2023-01-05")) # 日期型缺失
)
print("原始數據框 (含缺失值):")
print(df_missing)
# 檢查每個元素是否為缺失值,返回一個邏輯矩陣
is.na(df_missing)
# 計算每個欄位的缺失值數量
colSums(is.na(df_missing))
# 計算總缺失值數量
sum(is.na(df_missing))
# 檢查哪些列包含缺失值
names(df_missing)[colSums(is.na(df_missing)) > 0]
# 檢查哪些行包含缺失值
which(!complete.cases(df_missing)) # complete.cases() 返回沒有 NA 的行
# 使用 dplyr 統計缺失值
library(dplyr)
df_missing %>%
summarise_all(~sum(is.na(.)))
3.2.2 處理缺失值的方法
-
刪除含有缺失值的觀測值 (列或行):
- 刪除整行 (Listwise Deletion):如果缺失值數量不多,且刪除後不會損失過多重要資訊,這是最簡單且直接的方法。但如果缺失值分佈廣泛,可能會導致大量數據丟失,影響樣本代表性。
R# 刪除所有含有任何缺失值的行# 刪除所有含有任何缺失值的行
df_na_omit <- na.omit(df_missing) print("刪除所有含缺失值的行:") print(df_na_omit)
僅刪除特定欄位含有缺失值的行 (使用 dplyr 的 drop_na())
library(dplyr)
df_na_drop_value1 <- df_missing %>%
drop_na(Value1) # 僅刪除 Value1 欄位有 NA 的行
print("僅刪除 Value1 欄位含缺失值的行:")
print(df_na_drop_value1)
```
* **刪除整列**:通常不建議,除非整列數據幾乎都是缺失值,或者該列對分析不重要。否則會丟失該變數的所有資訊。
library(dplyr)
df_na_drop_value1 <- df_missing %>%
drop_na(Value1) # 僅刪除 Value1 欄位有 NA 的行
print("僅刪除 Value1 欄位含缺失值的行:")
print(df_na_drop_value1)
```
* **刪除整列**:通常不建議,除非整列數據幾乎都是缺失值,或者該列對分析不重要。否則會丟失該變數的所有資訊。
-
填補缺失值 (Imputation):
- 使用固定值填補:例如用 0、特定標籤(如
"Unknown"或"Missing")來填補。這種方法簡單,但可能會引入偏差,特別是當固定值與實際數據分佈不符時。
R# 使用 0 填補 Value1 的缺失值 df_fill_zero <- df_missing df_fill_zero$Value1[is.na(df_fill_zero$Value1)] <- 0 print("Value1 欄位用 0 填補:") print(df_fill_zero) # 使用 "Unknown" 填補 Category 的缺失值 df_fill_unknown <- df_missing df_fill_unknown$Category[is.na(df_fill_unknown$Category)] <- "Unknown" print("Category 欄位用 \"Unknown\" 填補:") print(df_fill_unknown)# 使用 0 填補 Value1 的缺失值 df_fill_zero <- df_missing df_fill_zero$Value1[is.na(df_fill_zero$Value1)] <- 0 print("Value1 欄位用 0 填補:") print(df_fill_zero) # 使用 "Unknown" 填補 Category 的缺失值 df_fill_unknown <- df_missing df_fill_unknown$Category[is.na(df_fill_unknown$Category)] <- "Unknown" print("Category 欄位用 \"Unknown\" 填補:") print(df_fill_unknown)- 使用統計量填補:對於數值型數據,平均數 (mean)、中位數 (median) 或眾數 (mode) 是常見選擇;對於類別型數據,眾數更合適。這種方法比固定值填補更合理,但會低估變異性。
R# 使用 Value1 的平均數填補缺失值 df_fill_mean <- df_missing mean_value1 <- mean(df_fill_mean$Value1, na.rm = TRUE) # na.rm = TRUE 忽略 NA 計算平均數 df_fill_mean$Value1[is.na(df_fill_mean$Value1)] <- mean_value1 print("Value1 欄位用平均數填補:") print(df_fill_mean) # 使用 Category 的眾數填補缺失值 (需要自定義函數或使用套件) # 這裡我們手動計算眾數 get_mode <- function(v) { uniqv <- unique(v) uniqv[which.max(tabulate(match(v, uniqv)))] } mode_category <- get_mode(df_missing$Category[!is.na(df_missing$Category)]) df_fill_mode <- df_missing df_fill_mode$Category[is.na(df_fill_mode$Category)] <- mode_category print("Category 欄位用眾數填補:") print(df_fill_mode)# 使用 Value1 的平均數填補缺失值 df_fill_mean <- df_missing mean_value1 <- mean(df_fill_mean$Value1, na.rm = TRUE) # na.rm = TRUE 忽略 NA 計算平均數 df_fill_mean$Value1[is.na(df_fill_mean$Value1)] <- mean_value1 print("Value1 欄位用平均數填補:") print(df_fill_mean) # 使用 Category 的眾數填補缺失值 (需要自定義函數或使用套件) # 這裡我們手動計算眾數 get_mode <- function(v) { uniqv <- unique(v) uniqv[which.max(tabulate(match(v, uniqv)))] } mode_category <- get_mode(df_missing$Category[!is.na(df_missing$Category)]) df_fill_mode <- df_missing df_fill_mode$Category[is.na(df_fill_mode$Category)] <- mode_category print("Category 欄位用眾數填補:") print(df_fill_mode)- 使用預測模型填補:更複雜但通常更準確的方法,例如使用迴歸模型、K-近鄰 (KNN) 或機器學習模型預測缺失值。這通常需要
mice(Multivariate Imputation by Chained Equations) 或VIM(Visualization and Imputation of Missing Values) 等套件。這種方法能更好地保留數據的潛在結構。
R# 概念說明:使用 mice 套件進行多重填補 # install.packages("mice") # 如果尚未安裝,請先執行此行 # library(mice) # imputed_data <- mice(df_missing, m = 5, maxit = 50, meth = \'pmm\', seed = 500) # m=5 表示生成 5 個填補數據集 # completed_data <- complete(imputed_data, 1) # 選擇第一個填補數據集 # print(completed_data)# 概念說明:使用 mice 套件進行多重填補 # install.packages("mice") # 如果尚未安裝,請先執行此行 # library(mice) # imputed_data <- mice(df_missing, m = 5, maxit = 50, meth = \'pmm\', seed = 500) # m=5 表示生成 5 個填補數據集 # completed_data <- complete(imputed_data, 1) # 選擇第一個填補數據集 # print(completed_data) - 使用固定值填補:例如用 0、特定標籤(如
-
tidyr套件的應用:tidyr套件提供了drop_na()和fill()等函數,讓缺失值處理更加簡潔,尤其是在tidyverse工作流中。Rlibrary(tidyr) # 刪除所有含有缺失值的行 df_tidyr_drop <- df_missing %>% drop_na() print("使用 tidyr::drop_na() 刪除缺失值:") print(df_tidyr_drop) # 使用前一個有效值填補缺失值 (適用於時間序列數據等,例如將 NA 填補為上一個觀測值) df_tidyr_fill_down <- df_missing %>% fill(Value1, .direction = "down") print("使用 tidyr::fill() 向下填補 Value1:") print(df_tidyr_fill_down) # 使用後一個有效值填補缺失值 df_tidyr_fill_up <- df_missing %>% fill(Value1, .direction = "up") print("使用 tidyr::fill() 向上填補 Value1:") print(df_tidyr_fill_up)library(tidyr) # 刪除所有含有缺失值的行 df_tidyr_drop <- df_missing %>% drop_na() print("使用 tidyr::drop_na() 刪除缺失值:") print(df_tidyr_drop) # 使用前一個有效值填補缺失值 (適用於時間序列數據等,例如將 NA 填補為上一個觀測值) df_tidyr_fill_down <- df_missing %>% fill(Value1, .direction = "down") print("使用 tidyr::fill() 向下填補 Value1:") print(df_tidyr_fill_down) # 使用後一個有效值填補缺失值 df_tidyr_fill_up <- df_missing %>% fill(Value1, .direction = "up") print("使用 tidyr::fill() 向上填補 Value1:") print(df_tidyr_fill_up)
3.3 異常值檢測與處理 (Outliers)
異常值 (Outliers) 是數據集中與其他觀測值顯著不同的數據點。它們可能是數據輸入錯誤、測量誤差,也可能是真實但極端的事件。異常值會對統計分析結果產生重大影響,例如扭曲平均數、標準差、相關係數,甚至影響模型訓練。因此,識別和適當處理異常值是數據清理的重要環節。
3.3.1 檢測異常值
-
視覺化方法:
- 箱形圖 (Box Plot):最常用的異常值檢測工具。箱形圖會顯示數據的四分位數 (Q1, Q3) 和中位數,並將超出 1.5 倍 IQR (Interquartile Range, 四分位距 = Q3 - Q1) 範圍的數據點標記為異常值。這是一種非參數方法,對數據分佈沒有嚴格要求。
R# 創建一個包含異常值的數據範例 data_outlier <- c(10, 12, 15, 16, 18, 20, 22, 25, 50, 8, 100, 5) # 繪製箱形圖 boxplot(data_outlier, main = "Box Plot for Outlier Detection", ylab = "Value", col = "lightblue") # 箱形圖中超出鬍鬚(whiskers)的點即為異常值。# 創建一個包含異常值的數據範例 data_outlier <- c(10, 12, 15, 16, 18, 20, 22, 25, 50, 8, 100, 5) # 繪製箱形圖 boxplot(data_outlier, main = "Box Plot for Outlier Detection", ylab = "Value", col = "lightblue") # 箱形圖中超出鬍鬚(whiskers)的點即為異常值。- 直方圖 (Histogram) / 密度圖 (Density Plot):觀察數據分佈,極端值可能在分佈的兩端形成孤立的條形或峰值。
Rhist(data_outlier, main = "Histogram for Outlier Detection", xlab = "Value", col = "lightgreen") plot(density(data_outlier), main = "Density Plot for Outlier Detection")hist(data_outlier, main = "Histogram for Outlier Detection", xlab = "Value", col = "lightgreen") plot(density(data_outlier), main = "Density Plot for Outlier Detection")- 散點圖 (Scatter Plot):對於多變數數據,散點圖可以幫助識別在多個維度上異常的數據點。
-
統計方法:
- Z-score (標準分數):對於服從常態分佈的數據,Z-score 可以衡量數據點與平均值的距離,以標準差為單位。通常,Z-score 超過 ±2 或 ±3 的數據點被視為異常值。注意:Z-score 對於非常態分佈或存在極端異常值的數據可能不適用,因為平均數和標準差本身會被異常值影響。
R# 計算 Z-score z_scores <- scale(data_outlier) # scale() 函數會將數據標準化,即計算 Z-score print("Z-scores:") print(z_scores) # 找出 Z-score 絕對值超過 2 的異常值 (可調整閾值,例如 2 或 3) outliers_z <- data_outlier[abs(z_scores) > 2] print("Z-score 異常值 (閾值 > 2):") print(outliers_z)# 計算 Z-score z_scores <- scale(data_outlier) # scale() 函數會將數據標準化,即計算 Z-score print("Z-scores:") print(z_scores) # 找出 Z-score 絕對值超過 2 的異常值 (可調整閾值,例如 2 或 3) outliers_z <- data_outlier[abs(z_scores) > 2] print("Z-score 異常值 (閾值 > 2):") print(outliers_z)- IQR 方法:基於箱形圖的原理,將超出
Q1 - 1.5 * IQR或Q3 + 1.5 * IQR範圍的數據點視為異常值。這是一種穩健的方法,不受極端值影響。
R# 計算 IQR Q1 <- quantile(data_outlier, 0.25) Q3 <- quantile(data_outlier, 0.75) IQR_val <- Q3 - Q1 # 定義異常值上下限 lower_bound <- Q1 - 1.5 * IQR_val upper_bound <- Q3 + 1.5 * IQR_val # 找出異常值 outliers_iqr <- data_outlier[data_outlier < lower_bound | data_outlier > upper_bound] print("IQR 異常值:") print(outliers_iqr)# 計算 IQR Q1 <- quantile(data_outlier, 0.25) Q3 <- quantile(data_outlier, 0.75) IQR_val <- Q3 - Q1 # 定義異常值上下限 lower_bound <- Q1 - 1.5 * IQR_val upper_bound <- Q3 + 1.5 * IQR_val # 找出異常值 outliers_iqr <- data_outlier[data_outlier < lower_bound | data_outlier > upper_bound] print("IQR 異常值:") print(outliers_iqr)outliers套件:提供了一些專門用於異常值檢測的函數,例如grubbs.test()(用於檢測單個異常值) 和rosner.test()(用於檢測多個異常值)。
R# 概念說明:使用 outliers 套件 # install.packages("outliers") # 如果尚未安裝,請先執行此行 # library(outliers) # grubbs.test(data_outlier)# 概念說明:使用 outliers 套件 # install.packages("outliers") # 如果尚未安裝,請先執行此行 # library(outliers) # grubbs.test(data_outlier)
3.3.2 處理異常值的方法
處理異常值需要謹慎,因為不當處理可能會導致數據失真或重要資訊丟失。處理方法應根據異常值的來源、數量和對分析的影響來決定。
-
刪除異常值:
- 適用情況:如果異常值是明顯的數據輸入錯誤、測量誤差,且數量不多,對分析結果影響較大,可以直接刪除。
- 缺點:可能導致數據丟失,特別是當異常值數量較多時。
R# 刪除 IQR 方法檢測到的異常值 data_no_outliers <- data_outlier[data_outlier >= lower_bound & data_outlier <= upper_bound] print("刪除異常值後的數據:") print(data_no_outliers)# 刪除 IQR 方法檢測到的異常值 data_no_outliers <- data_outlier[data_outlier >= lower_bound & data_outlier <= upper_bound] print("刪除異常值後的數據:") print(data_no_outliers) -
轉換異常值 (Transformation):
- 適用情況:當數據分佈嚴重偏斜,導致出現許多極端值時,可以對數據進行轉換(例如對數轉換
log()、平方根轉換sqrt()、倒數轉換1/x),使其分佈更接近常態,從而減少異常值的影響。這有助於滿足某些統計模型的常態性假設。 - 缺點:轉換後的數據解釋性可能降低。
R# 對數轉換範例 (假設數據為正數) data_log_transformed <- log(data_outlier) hist(data_log_transformed, main = "Log Transformed Data", xlab = "Log(Value)", col = "lightcoral")# 對數轉換範例 (假設數據為正數) data_log_transformed <- log(data_outlier) hist(data_log_transformed, main = "Log Transformed Data", xlab = "Log(Value)", col = "lightcoral") - 適用情況:當數據分佈嚴重偏斜,導致出現許多極端值時,可以對數據進行轉換(例如對數轉換
-
替換異常值 (Winsorization / Capping):
- 適用情況:將異常值替換為鄰近的有效值、平均數、中位數或上下限值。例如,將所有超出上限的數據點替換為上限值,將所有低於下限的數據點替換為下限值 (稱為 Winsorization 或 Capping)。
- 優點:保留了所有觀測值,避免了數據丟失。
- 缺點:可能會壓縮數據的變異性。
R# 將超出 IQR 上下限的異常值替換為上下限值 data_winsorized <- data_outlier data_winsorized[data_winsorized < lower_bound] <- lower_bound data_winsorized[data_winsorized > upper_bound] <- upper_bound print("替換異常值後的數據 (Winsorized):") print(data_winsorized)# 將超出 IQR 上下限的異常值替換為上下限值 data_winsorized <- data_outlier data_winsorized[data_winsorized < lower_bound] <- lower_bound data_winsorized[data_winsorized > upper_bound] <- upper_bound print("替換異常值後的數據 (Winsorized):") print(data_winsorized) -
將異常值視為單獨類別:
- 適用情況:如果異常值代表了真實但極端的事件,且這些事件本身具有研究價值,可以將其標記為一個單獨的類別進行分析,而不是簡單地刪除或修改。
- 優點:保留了異常值的資訊,並允許對其進行專門分析。
3.4 數據類型轉換 (Data Type Conversion)
正確的數據類型對於統計分析至關重要。R 語言中的基本數據類型包括數值型 (numeric)、整數型 (integer)、字符型 (character)、邏輯型 (logical) 和因子型 (factor)。錯誤的數據類型會導致計算錯誤或無法進行某些分析。例如,將數字儲存為字符型會阻止您進行數學運算。
# 創建一個數據框範例,其中包含需要轉換的數據類型
df_types <- data.frame(
ID = c("1", "2", "3", "4"), # 應為數值型
Age = c(25, 30, 22, 28), # 已是數值型
Gender = c("Male", "Female", "Male", "Female"), # 應為因子型
IsStudent = c(T, F, T, F), # 已是邏輯型
Score = c("85.5", "90", "78.2", "91"), # 應為數值型
EnrollDate = c("2023-01-01", "2023-02-15", "2023-03-10", "2023-04-20") # 應為日期型
)
print("原始數據結構:")
str(df_types)
# 將 ID 從字符型轉換為數值型
df_types$ID <- as.numeric(df_types$ID)
# 將 Gender 從字符型轉換為因子型 (類別變數)。因子型在統計建模中非常重要。
df_types$Gender <- as.factor(df_types$Gender)
# 將 Score 從字符型轉換為數值型
df_types$Score <- as.numeric(df_types$Score)
# 將 EnrollDate 從字符型轉換為日期型
df_types$EnrollDate <- as.Date(df_types$EnrollDate)
print("轉換後的數據結構:")
str(df_types)
print("轉換後的數據:")
print(df_types)
# 創建一個數據框範例,其中包含需要轉換的數據類型
df_types <- data.frame(
ID = c("1", "2", "3", "4"), # 應為數值型
Age = c(25, 30, 22, 28), # 已是數值型
Gender = c("Male", "Female", "Male", "Female"), # 應為因子型
IsStudent = c(T, F, T, F), # 已是邏輯型
Score = c("85.5", "90", "78.2", "91"), # 應為數值型
EnrollDate = c("2023-01-01", "2023-02-15", "2023-03-10", "2023-04-20") # 應為日期型
)
print("原始數據結構:")
str(df_types)
# 將 ID 從字符型轉換為數值型
df_types$ID <- as.numeric(df_types$ID)
# 將 Gender 從字符型轉換為因子型 (類別變數)。因子型在統計建模中非常重要。
df_types$Gender <- as.factor(df_types$Gender)
# 將 Score 從字符型轉換為數值型
df_types$Score <- as.numeric(df_types$Score)
# 將 EnrollDate 從字符型轉換為日期型
df_types$EnrollDate <- as.Date(df_types$EnrollDate)
print("轉換後的數據結構:")
str(df_types)
print("轉換後的數據:")
print(df_types)
dplyr 套件的 mutate() 函數:在 tidyverse 工作流中,mutate() 函數是進行數據類型轉換的常用工具,它允許您在數據框中創建新列或修改現有列。
library(dplyr)
df_types_mutate <- df_types %>%
mutate(
ID = as.numeric(ID),
Gender = as.factor(Gender),
Score = as.numeric(Score),
EnrollDate = as.Date(EnrollDate)
)
print("使用 dplyr::mutate() 轉換後的數據結構:")
str(df_types_mutate)
library(dplyr)
df_types_mutate <- df_types %>%
mutate(
ID = as.numeric(ID),
Gender = as.factor(Gender),
Score = as.numeric(Score),
EnrollDate = as.Date(EnrollDate)
)
print("使用 dplyr::mutate() 轉換後的數據結構:")
str(df_types_mutate)
3.5 重複值處理 (Duplicate Values)
重複值 (Duplicate Values) 是指數據集中完全相同或部分相同的觀測值。重複值會導致分析結果偏誤,例如高估樣本量、影響統計量的計算。因此,識別並處理重複值是數據清理的必要步驟。
# 創建一個包含重複值的數據框範例
df_duplicates <- data.frame(
ID = c(1, 2, 3, 1, 4, 2),
Name = c("Alice", "Bob", "Charlie", "Alice", "David", "Bob"),
Score = c(85, 90, 78, 85, 92, 90)
)
print("原始數據框 (含重複值):")
print(df_duplicates)
# 檢測重複行:duplicated() 函數會標記除第一次出現以外的所有重複行
duplicated_rows <- duplicated(df_duplicates)
print("重複行的邏輯向量:")
print(duplicated_rows)
# 顯示重複的行 (即除了第一次出現的重複行)
df_duplicates[duplicated_rows, ]
# 刪除重複行 (保留第一次出現的行)
df_unique <- df_duplicates[!duplicated_rows, ]
print("刪除重複行後的數據 (保留第一次出現):")
print(df_unique)
# 使用 `dplyr::distinct()` 刪除重複行 (更簡潔)
library(dplyr)
df_distinct <- df_duplicates %>% distinct()
print("使用 dplyr::distinct() 刪除重複行:")
print(df_distinct)
# 根據特定欄位刪除重複行 (例如,只考慮 ID 和 Name,如果 ID 和 Name 相同則視為重複)
# .keep_all = TRUE 會保留所有其他欄位
df_distinct_id_name <- df_duplicates %>% distinct(ID, Name, .keep_all = TRUE)
print("根據 ID 和 Name 刪除重複行:")
print(df_distinct_id_name)
# 創建一個包含重複值的數據框範例
df_duplicates <- data.frame(
ID = c(1, 2, 3, 1, 4, 2),
Name = c("Alice", "Bob", "Charlie", "Alice", "David", "Bob"),
Score = c(85, 90, 78, 85, 92, 90)
)
print("原始數據框 (含重複值):")
print(df_duplicates)
# 檢測重複行:duplicated() 函數會標記除第一次出現以外的所有重複行
duplicated_rows <- duplicated(df_duplicates)
print("重複行的邏輯向量:")
print(duplicated_rows)
# 顯示重複的行 (即除了第一次出現的重複行)
df_duplicates[duplicated_rows, ]
# 刪除重複行 (保留第一次出現的行)
df_unique <- df_duplicates[!duplicated_rows, ]
print("刪除重複行後的數據 (保留第一次出現):")
print(df_unique)
# 使用 `dplyr::distinct()` 刪除重複行 (更簡潔)
library(dplyr)
df_distinct <- df_duplicates %>% distinct()
print("使用 dplyr::distinct() 刪除重複行:")
print(df_distinct)
# 根據特定欄位刪除重複行 (例如,只考慮 ID 和 Name,如果 ID 和 Name 相同則視為重複)
# .keep_all = TRUE 會保留所有其他欄位
df_distinct_id_name <- df_duplicates %>% distinct(ID, Name, .keep_all = TRUE)
print("根據 ID 和 Name 刪除重複行:")
print(df_distinct_id_name)
3.6 數據重塑與轉換 (Data Reshaping and Transformation)
數據重塑 (Data Reshaping) 是指改變數據集的結構,使其更適合特定的分析或可視化需求。tidyr 套件提供了強大的函數來實現數據的寬格式 (wide format) 和長格式 (long format) 之間的轉換。理解這兩種格式對於數據處理和建模至關重要。
- 寬格式 (Wide Format):每個觀測值(例如一個人)佔一行,每個變數佔一列。當變數數量較少,但每個變數有多個測量值時,寬格式可能較為直觀。
- 長格式 (Long Format):每個觀測值(例如一個人)可能佔多行,其中一列表示測量類型,另一列表示測量值。長格式通常更適合於
ggplot2等可視化工具和許多統計模型。
3.6.1 寬格式轉長格式 (pivot_longer())
當多個欄位代表同一類型的測量值時,適合轉換為長格式。例如,將不同年份的銷售額欄位轉換為一個「年份」欄位和一個「銷售額」欄位。
# 創建一個寬格式數據框範例 (不同年份的銷售額)
df_wide <- data.frame(
Region = c("North", "South", "East"),
Sales_2020 = c(100, 150, 120),
Sales_2021 = c(120, 160, 130),
Sales_2022 = c(130, 170, 140)
)
print("原始寬格式數據:")
print(df_wide)
library(tidyr)
# 將 Sales_2020, Sales_2021, Sales_2022 轉換為長格式
# cols:指定要轉換的列
# names_to:新創建的列,用於儲存原始列的名稱 (例如 "Sales_2020" 中的 "2020")
# values_to:新創建的列,用於儲存原始列的值 (例如銷售額)
df_long <- df_wide %>%
pivot_longer(cols = starts_with("Sales_"), # 選擇所有以 "Sales_" 開頭的列
names_to = "Year_Raw", # 暫時儲存原始列名
values_to = "Sales")
print("轉換為長格式 (初步):")
print(df_long)
# 進一步清理 Year_Raw 欄位,提取年份數字並轉換為數值型
df_long <- df_long %>%
mutate(Year = as.numeric(gsub("Sales_", "", Year_Raw))) %>% # 使用 gsub 移除 "Sales_" 前綴
select(-Year_Raw) # 移除原始的 Year_Raw 欄位
print("轉換為長格式 (最終):")
print(df_long)
# 創建一個寬格式數據框範例 (不同年份的銷售額)
df_wide <- data.frame(
Region = c("North", "South", "East"),
Sales_2020 = c(100, 150, 120),
Sales_2021 = c(120, 160, 130),
Sales_2022 = c(130, 170, 140)
)
print("原始寬格式數據:")
print(df_wide)
library(tidyr)
# 將 Sales_2020, Sales_2021, Sales_2022 轉換為長格式
# cols:指定要轉換的列
# names_to:新創建的列,用於儲存原始列的名稱 (例如 "Sales_2020" 中的 "2020")
# values_to:新創建的列,用於儲存原始列的值 (例如銷售額)
df_long <- df_wide %>%
pivot_longer(cols = starts_with("Sales_"), # 選擇所有以 "Sales_" 開頭的列
names_to = "Year_Raw", # 暫時儲存原始列名
values_to = "Sales")
print("轉換為長格式 (初步):")
print(df_long)
# 進一步清理 Year_Raw 欄位,提取年份數字並轉換為數值型
df_long <- df_long %>%
mutate(Year = as.numeric(gsub("Sales_", "", Year_Raw))) %>% # 使用 gsub 移除 "Sales_" 前綴
select(-Year_Raw) # 移除原始的 Year_Raw 欄位
print("轉換為長格式 (最終):")
print(df_long)
3.6.2 長格式轉寬格式 (pivot_wider())
當您需要將某個類別變數的不同值轉換為單獨的欄位時,適合轉換為寬格式。例如,將不同科目的成績從長格式轉換為每個科目一個欄位的寬格式。
# 創建一個長格式數據框範例 (學生各科成績)
df_long_grades <- data.frame(
Student = c("A", "A", "B", "B", "C", "C"),
Subject = c("Math", "English", "Math", "English", "Math", "English"),
Grade = c(90, 85, 75, 88, 95, 80)
)
print("原始長格式數據:")
print(df_long_grades)
# 將 Subject 轉換為寬格式
# names_from:指定包含新列名稱的列 (例如 "Math", "English")
# values_from:指定包含新列值的列 (例如成績)
df_wide_grades <- df_long_grades %>%
pivot_wider(names_from = Subject,
values_from = Grade)
print("轉換為寬格式:")
print(df_wide_grades)
# 創建一個長格式數據框範例 (學生各科成績)
df_long_grades <- data.frame(
Student = c("A", "A", "B", "B", "C", "C"),
Subject = c("Math", "English", "Math", "English", "Math", "English"),
Grade = c(90, 85, 75, 88, 95, 80)
)
print("原始長格式數據:")
print(df_long_grades)
# 將 Subject 轉換為寬格式
# names_from:指定包含新列名稱的列 (例如 "Math", "English")
# values_from:指定包含新列值的列 (例如成績)
df_wide_grades <- df_long_grades %>%
pivot_wider(names_from = Subject,
values_from = Grade)
print("轉換為寬格式:")
print(df_wide_grades)
3.7 數據驗證 (Data Validation)
數據驗證是數據清理的最後一步,旨在確保數據符合預期的規則和約束。這包括檢查數據範圍、格式、一致性等。
- 範圍檢查:確保數值型數據在合理範圍內 (例如年齡不能為負數,分數不能超過 100)。
- 格式檢查:確保日期、電話號碼等數據符合特定格式。
- 一致性檢查:檢查不同欄位之間的邏輯關係 (例如出生日期不能晚於入學日期)。
- 唯一性檢查:確保主鍵或識別符是唯一的。
# 範例:檢查 Age 欄位是否為正數
if (any(df_types_mutate$Age < 0)) {
warning("Age 欄位存在負數值!")
}
# 範例:檢查 Score 欄位是否在 0-100 之間
if (any(df_types_mutate$Score < 0 | df_types_mutate$Score > 100)) {
warning("Score 欄位存在超出 0-100 範圍的值!")
}
# 範例:檢查 ID 欄位是否唯一
if (any(duplicated(df_types_mutate$ID))) {
warning("ID 欄位存在重複值!")
}
# 範例:檢查 Age 欄位是否為正數
if (any(df_types_mutate$Age < 0)) {
warning("Age 欄位存在負數值!")
}
# 範例:檢查 Score 欄位是否在 0-100 之間
if (any(df_types_mutate$Score < 0 | df_types_mutate$Score > 100)) {
warning("Score 欄位存在超出 0-100 範圍的值!")
}
# 範例:檢查 ID 欄位是否唯一
if (any(duplicated(df_types_mutate$ID))) {
warning("ID 欄位存在重複值!")
}
3.8 實際範例:逐步清理客戶數據集
讓我們透過一個綜合範例來演示數據清理的流程。假設我們有一個模擬的客戶數據集 customer_data.csv,其中包含各種數據質量問題。
CustomerID,Name,Age,Gender,Income,EnrollDate,ProductPreference
1,Alice,28,Female,50000,2022-01-15,Electronics
2,Bob,NA,Male,60000,2022-02-20,Books
3,Charlie,35,Female,75000,2022-03-10,Electronics
4,David,28,Male,50000,2022-01-15,Electronics
5,Eve,40,Female,NA,2022-04-05,Clothing
6,Frank,30,Male,65000,2022-05-12,Books
7,Grace,25,Female,55000,2022-06-18,NA
8,Heidi,NA,Female,70000,2022-07-22,Electronics
9,Ivan,32,Male,62000,2022-08-01,Books
10,Judy,28,Female,50000,2022-01-15,Electronics
11,Karl,150,Male,1000000,2023-01-01,Luxury
12,Linda,-5,Female,30000,2023-02-01,Books
CustomerID,Name,Age,Gender,Income,EnrollDate,ProductPreference
1,Alice,28,Female,50000,2022-01-15,Electronics
2,Bob,NA,Male,60000,2022-02-20,Books
3,Charlie,35,Female,75000,2022-03-10,Electronics
4,David,28,Male,50000,2022-01-15,Electronics
5,Eve,40,Female,NA,2022-04-05,Clothing
6,Frank,30,Male,65000,2022-05-12,Books
7,Grace,25,Female,55000,2022-06-18,NA
8,Heidi,NA,Female,70000,2022-07-22,Electronics
9,Ivan,32,Male,62000,2022-08-01,Books
10,Judy,28,Female,50000,2022-01-15,Electronics
11,Karl,150,Male,1000000,2023-01-01,Luxury
12,Linda,-5,Female,30000,2023-02-01,Books
清理步驟:
- 讀取數據
- 檢查數據結構與摘要
- 處理重複值
- 處理缺失值
- 處理異常值 (以 Age 和 Income 為例)
- 數據類型轉換
- 數據驗證
# 1. 讀取數據
library(readr)
library(dplyr)
library(tidyr)
# 創建 customer_data.csv 檔案
customer_csv_content <- "CustomerID,Name,Age,Gender,Income,EnrollDate,ProductPreference\n1,Alice,28,Female,50000,2022-01-15,Electronics\n2,Bob,NA,Male,60000,2022-02-20,Books\n3,Charlie,35,Female,75000,2022-03-10,Electronics\n4,David,28,Male,50000,2022-01-15,Electronics\n5,Eve,40,Female,NA,2022-04-05,Clothing\n6,Frank,30,Male,65000,2022-05-12,Books\n7,Grace,25,Female,55000,2022-06-18,NA\n8,Heidi,NA,Female,70000,2022-07-22,Electronics\n9,Ivan,32,Male,62000,2022-08-01,Books\n10,Judy,28,Female,50000,2022-01-15,Electronics\n11,Karl,150,Male,1000000,2023-01-01,Luxury\n12,Linda,-5,Female,30000,2023-02-01,Books"
writeLines(customer_csv_content, "customer_data.csv")
customer_data <- read_csv("customer_data.csv")
print("原始數據:")
print(customer_data)
# 2. 檢查數據結構與摘要
print("原始數據結構:")
str(customer_data)
print("原始數據摘要:")
summary(customer_data)
# 3. 處理重複值 (CustomerID, Name, EnrollDate, ProductPreference 都相同才算重複)
print("處理重複值前總行數:")
print(nrow(customer_data))
customer_data_unique <- customer_data %>%
distinct(CustomerID, Name, EnrollDate, ProductPreference, .keep_all = TRUE)
print("處理重複值後總行數:")
print(nrow(customer_data_unique))
print(customer_data_unique)
# 4. 處理缺失值
print("缺失值數量 (處理前):")
colSums(is.na(customer_data_unique))
# 對 Age 欄位使用中位數填補
median_age <- median(customer_data_unique$Age, na.rm = TRUE)
customer_data_cleaned <- customer_data_unique %>%
mutate(Age = replace_na(Age, median_age))
# 對 Income 欄位使用平均數填補
mean_income <- mean(customer_data_cleaned$Income, na.rm = TRUE)
customer_data_cleaned <- customer_data_cleaned %>%
mutate(Income = replace_na(Income, mean_income))
# 對 ProductPreference 欄位使用眾數填補
get_mode <- function(v) {
uniqv <- unique(v)
uniqv[which.max(tabulate(match(v, uniqv)))]
}
mode_preference <- get_mode(customer_data_cleaned$ProductPreference[!is.na(customer_data_cleaned$ProductPreference)])
customer_data_cleaned <- customer_data_cleaned %>%
mutate(ProductPreference = replace_na(ProductPreference, mode_preference))
print("缺失值數量 (處理後):")
colSums(is.na(customer_data_cleaned))
print("處理缺失值後的數據:")
print(customer_data_cleaned)
# 5. 處理異常值 (以 Age 和 Income 為例,使用 IQR 方法)
# 處理 Age 異常值
Q1_age <- quantile(customer_data_cleaned$Age, 0.25)
Q3_age <- quantile(customer_data_cleaned$Age, 0.75)
IQR_age <- Q3_age - Q1_age
lower_bound_age <- Q1_age - 1.5 * IQR_age
upper_bound_age <- Q3_age + 1.5 * IQR_age
print("Age 異常值 (原始值):")
print(customer_data_cleaned$Age[customer_data_cleaned$Age < lower_bound_age | customer_data_cleaned$Age > upper_bound_age])
# 將 Age 異常值替換為上下限值 (Winsorization)
customer_data_cleaned$Age[customer_data_cleaned$Age < lower_bound_age] <- lower_bound_age
customer_data_cleaned$Age[customer_data_cleaned$Age > upper_bound_age] <- upper_bound_age
# 處理 Income 異常值
Q1_income <- quantile(customer_data_cleaned$Income, 0.25)
Q3_income <- quantile(customer_data_cleaned$Income, 0.75)
IQR_income <- Q3_income - Q1_income
lower_bound_income <- Q1_income - 1.5 * IQR_income
upper_bound_income <- Q3_income + 1.5 * IQR_income
print("Income 異常值 (原始值):")
print(customer_data_cleaned$Income[customer_data_cleaned$Income < lower_bound_income | customer_data_cleaned$Income > upper_bound_income])
# 將 Income 異常值替換為上下限值
customer_data_cleaned$Income[customer_data_cleaned$Income < lower_bound_income] <- lower_bound_income
customer_data_cleaned$Income[customer_data_cleaned$Income > upper_bound_income] <- upper_bound_income
print("處理異常值後的數據:")
print(customer_data_cleaned)
# 6. 數據類型轉換
customer_data_final <- customer_data_cleaned %>%
mutate(
Gender = as.factor(Gender),
ProductPreference = as.factor(ProductPreference),
EnrollDate = as.Date(EnrollDate) # 將日期字串轉換為日期格式
)
print("最終清理後的數據結構:")
str(customer_data_final)
print("最終清理後的數據:")
print(customer_data_final)
# 7. 數據驗證 (額外檢查)
print("數據驗證結果:")
# 檢查 Age 是否在合理範圍 (例如 18-99)
if (any(customer_data_final$Age < 18 | customer_data_final$Age > 99)) {
warning("Age 欄位存在超出 18-99 範圍的值!")
} else {
print("Age 欄位在合理範圍內。")
}
# 檢查 Income 是否為正數
if (any(customer_data_final$Income < 0)) {
warning("Income 欄位存在負數值!")
} else {
print("Income 欄位為正數。")
}
# 檢查 CustomerID 是否唯一
if (any(duplicated(customer_data_final$CustomerID))) {
warning("CustomerID 欄位存在重複值!")
} else {
print("CustomerID 欄位唯一。")
}
# 1. 讀取數據
library(readr)
library(dplyr)
library(tidyr)
# 創建 customer_data.csv 檔案
customer_csv_content <- "CustomerID,Name,Age,Gender,Income,EnrollDate,ProductPreference\n1,Alice,28,Female,50000,2022-01-15,Electronics\n2,Bob,NA,Male,60000,2022-02-20,Books\n3,Charlie,35,Female,75000,2022-03-10,Electronics\n4,David,28,Male,50000,2022-01-15,Electronics\n5,Eve,40,Female,NA,2022-04-05,Clothing\n6,Frank,30,Male,65000,2022-05-12,Books\n7,Grace,25,Female,55000,2022-06-18,NA\n8,Heidi,NA,Female,70000,2022-07-22,Electronics\n9,Ivan,32,Male,62000,2022-08-01,Books\n10,Judy,28,Female,50000,2022-01-15,Electronics\n11,Karl,150,Male,1000000,2023-01-01,Luxury\n12,Linda,-5,Female,30000,2023-02-01,Books"
writeLines(customer_csv_content, "customer_data.csv")
customer_data <- read_csv("customer_data.csv")
print("原始數據:")
print(customer_data)
# 2. 檢查數據結構與摘要
print("原始數據結構:")
str(customer_data)
print("原始數據摘要:")
summary(customer_data)
# 3. 處理重複值 (CustomerID, Name, EnrollDate, ProductPreference 都相同才算重複)
print("處理重複值前總行數:")
print(nrow(customer_data))
customer_data_unique <- customer_data %>%
distinct(CustomerID, Name, EnrollDate, ProductPreference, .keep_all = TRUE)
print("處理重複值後總行數:")
print(nrow(customer_data_unique))
print(customer_data_unique)
# 4. 處理缺失值
print("缺失值數量 (處理前):")
colSums(is.na(customer_data_unique))
# 對 Age 欄位使用中位數填補
median_age <- median(customer_data_unique$Age, na.rm = TRUE)
customer_data_cleaned <- customer_data_unique %>%
mutate(Age = replace_na(Age, median_age))
# 對 Income 欄位使用平均數填補
mean_income <- mean(customer_data_cleaned$Income, na.rm = TRUE)
customer_data_cleaned <- customer_data_cleaned %>%
mutate(Income = replace_na(Income, mean_income))
# 對 ProductPreference 欄位使用眾數填補
get_mode <- function(v) {
uniqv <- unique(v)
uniqv[which.max(tabulate(match(v, uniqv)))]
}
mode_preference <- get_mode(customer_data_cleaned$ProductPreference[!is.na(customer_data_cleaned$ProductPreference)])
customer_data_cleaned <- customer_data_cleaned %>%
mutate(ProductPreference = replace_na(ProductPreference, mode_preference))
print("缺失值數量 (處理後):")
colSums(is.na(customer_data_cleaned))
print("處理缺失值後的數據:")
print(customer_data_cleaned)
# 5. 處理異常值 (以 Age 和 Income 為例,使用 IQR 方法)
# 處理 Age 異常值
Q1_age <- quantile(customer_data_cleaned$Age, 0.25)
Q3_age <- quantile(customer_data_cleaned$Age, 0.75)
IQR_age <- Q3_age - Q1_age
lower_bound_age <- Q1_age - 1.5 * IQR_age
upper_bound_age <- Q3_age + 1.5 * IQR_age
print("Age 異常值 (原始值):")
print(customer_data_cleaned$Age[customer_data_cleaned$Age < lower_bound_age | customer_data_cleaned$Age > upper_bound_age])
# 將 Age 異常值替換為上下限值 (Winsorization)
customer_data_cleaned$Age[customer_data_cleaned$Age < lower_bound_age] <- lower_bound_age
customer_data_cleaned$Age[customer_data_cleaned$Age > upper_bound_age] <- upper_bound_age
# 處理 Income 異常值
Q1_income <- quantile(customer_data_cleaned$Income, 0.25)
Q3_income <- quantile(customer_data_cleaned$Income, 0.75)
IQR_income <- Q3_income - Q1_income
lower_bound_income <- Q1_income - 1.5 * IQR_income
upper_bound_income <- Q3_income + 1.5 * IQR_income
print("Income 異常值 (原始值):")
print(customer_data_cleaned$Income[customer_data_cleaned$Income < lower_bound_income | customer_data_cleaned$Income > upper_bound_income])
# 將 Income 異常值替換為上下限值
customer_data_cleaned$Income[customer_data_cleaned$Income < lower_bound_income] <- lower_bound_income
customer_data_cleaned$Income[customer_data_cleaned$Income > upper_bound_income] <- upper_bound_income
print("處理異常值後的數據:")
print(customer_data_cleaned)
# 6. 數據類型轉換
customer_data_final <- customer_data_cleaned %>%
mutate(
Gender = as.factor(Gender),
ProductPreference = as.factor(ProductPreference),
EnrollDate = as.Date(EnrollDate) # 將日期字串轉換為日期格式
)
print("最終清理後的數據結構:")
str(customer_data_final)
print("最終清理後的數據:")
print(customer_data_final)
# 7. 數據驗證 (額外檢查)
print("數據驗證結果:")
# 檢查 Age 是否在合理範圍 (例如 18-99)
if (any(customer_data_final$Age < 18 | customer_data_final$Age > 99)) {
warning("Age 欄位存在超出 18-99 範圍的值!")
} else {
print("Age 欄位在合理範圍內。")
}
# 檢查 Income 是否為正數
if (any(customer_data_final$Income < 0)) {
warning("Income 欄位存在負數值!")
} else {
print("Income 欄位為正數。")
}
# 檢查 CustomerID 是否唯一
if (any(duplicated(customer_data_final$CustomerID))) {
warning("CustomerID 欄位存在重複值!")
} else {
print("CustomerID 欄位唯一。")
}
3.9 常見錯誤與解決方案 (數據清理)
數據清理是一個容易出錯的環節,以下是一些常見錯誤及其解決方案:
- 忽略缺失值:
- 錯誤:直接進行分析,導致結果不準確或模型崩潰。
- 解決方案:務必在分析前檢查並處理缺失值。根據缺失值的性質和數量,選擇刪除、填補或更複雜的處理方法。
- 不當處理異常值:
- 錯誤:盲目刪除異常值可能導致重要資訊丟失,或者替換方式不合理導致數據失真。
- 解決方案:應先理解異常值的來源和性質(是錯誤還是真實的極端值),再決定處理方式。可以嘗試多種方法(刪除、轉換、替換、單獨分析),並評估其對分析結果的影響。
- 數據類型不匹配:
- 錯誤:例如將字符型數據用於數值計算,或將日期數據視為普通字符。
- 解決方案:務必在分析前將數據轉換為正確的類型。使用
str()函數檢查數據結構,並使用as.numeric(),as.Date(),as.factor()等函數進行轉換。
- 未考慮數據分佈:
- 錯誤:在填補缺失值或處理異常值時,未考慮數據的分佈特性,例如對偏態分佈的數據使用平均數填補。
- 解決方案:對於偏態分佈的數據,中位數可能比平均數更適合填補缺失值。在處理異常值時,對數轉換等方法可能更有效。始終先可視化數據分佈。
- 過度清理:
- 錯誤:過度清理可能導致數據失真或重要模式被移除,使得分析結果無法反映真實情況。
- 解決方案:數據清理應有目的性,並在清理前後進行驗證。每次清理操作都應有明確的理由,並記錄下來。在不確定時,保守處理或嘗試多種清理方案。
- 未備份原始數據:
- 錯誤:直接在原始數據上進行清理操作,一旦出錯難以恢復。
- 解決方案:在進行任何清理操作之前,始終備份原始數據。可以在 R 中創建數據框的副本,例如
df_cleaned <- df_original。
- 未記錄清理過程:
- 錯誤:清理過程不透明,難以追溯、重現或與他人協作。
- 解決方案:詳細記錄每一步清理操作,包括使用的函數、參數、原因和預期結果。這對於確保分析的透明度和可重現性至關重要。
第四章:基礎統計分析
數據清理完成後,我們就可以開始進行基礎統計分析,以了解數據的概況和潛在模式。本章將介紹描述性統計、數據可視化以及基礎推論性統計,幫助您從數據中提取有價值的資訊。
4.1 描述性統計 (Descriptive Statistics)
描述性統計 (Descriptive Statistics) 用於總結和描述數據集的特徵,幫助我們快速了解數據的分佈、集中趨勢和離散程度。它是任何數據分析的起點。
4.1.1 集中趨勢測量
集中趨勢測量描述了數據集的中心位置。
- 平均數 (Mean):所有數據點的總和除以數據點的數量。易受異常值影響,適用於對稱分佈的數值型數據。
- R 函數:
mean()
- R 函數:
- 中位數 (Median):將數據排序後,位於中間位置的數值。不受異常值影響,適用於偏態分佈的數值型數據。
- R 函數:
median()
- R 函數:
- 眾數 (Mode):數據集中出現頻率最高的數值。適用於類別型數據,R 基礎包中沒有直接的眾數函數,需要自定義或使用套件。
- R 函數:通常需要自定義函數,例如
names(sort(table(x), decreasing = TRUE))[1]。
- R 函數:通常需要自定義函數,例如
4.1.2 離散趨勢測量
離散趨勢測量描述了數據點的分散程度。
- 全距 (Range):最大值與最小值之差。簡單易懂,但只考慮了兩個極端值,對異常值敏感。
- R 函數:
range()(返回最大值和最小值),max() - min()
- R 函數:
- 四分位數 (Quartiles):將數據分為四等份的三個點 (Q1, Q2/中位數, Q3)。Q1 是第 25 百分位數,Q3 是第 75 百分位數。
- R 函數:
quantile(x, probs = c(0.25, 0.75))
- R 函數:
- 四分位距 (Interquartile Range, IQR):Q3 與 Q1 之差,表示中間 50% 數據的範圍。不受異常值影響,是衡量數據離散程度的穩健指標。
- R 函數:
IQR()
- R 函數:
- 方差 (Variance):數據點與平均值之間離散程度的平均平方差。單位是原始數據單位的平方,解釋性較差。
- R 函數:
var()
- R 函數:
- 標準差 (Standard Deviation):方差的平方根,與原始數據單位一致,更易於解釋。常用於衡量數據的波動性。
- R 函數:
sd()
- R 函數:
4.1.3 頻率分佈
頻率分佈描述了數據集中每個類別或數值區間出現的次數或比例。
- 頻率表 (Frequency Table):顯示每個類別或數值區間出現的次數和相對頻率。適用於類別型數據。
- R 函數:
table()(次數),prop.table(table())(相對頻率)
- R 函數:
4.2 常用函數與實作 (描述性統計)
R 語言提供了許多內建函數來計算描述性統計量。我們將使用之前清理好的 customer_data_final 數據集進行演示。
# 使用之前清理好的 customer_data_final 數據集
print("最終清理後的數據:")
print(customer_data_final)
# 數據摘要 (對所有數值型欄位提供最小值、Q1、中位數、平均數、Q3、最大值)
summary(customer_data_final)
# 計算特定欄位的平均數、中位數、標準差
mean_age <- mean(customer_data_final$Age)
median_income <- median(customer_data_final$Income)
sd_age <- sd(customer_data_final$Age)
print(paste("平均年齡:", round(mean_age, 2)))
print(paste("中位數收入:", round(median_income, 2)))
print(paste("年齡標準差:", round(sd_age, 2)))
# 計算類別變數的頻率表
table_gender <- table(customer_data_final$Gender)
print("性別頻率表:")
print(table_gender)
# 計算相對頻率
prop_table_gender <- prop.table(table_gender)
print("性別相對頻率表:")
print(prop_table_gender)
table_product <- table(customer_data_final$ProductPreference)
print("產品偏好頻率表:")
print(table_product)
# 使用 `dplyr` 和 `skimr` 套件進行更詳細的描述性統計
# install.packages("skimr") # 如果尚未安裝,請先執行此行
library(skimr)
# skim() 函數提供了一個簡潔且全面的數據摘要,包括缺失值、完整性、數據類型、描述性統計量和迷你直方圖。
print("使用 skimr::skim() 進行數據摘要:")
customer_data_final %>% skim()
# 按組別進行描述性統計 (例如,按性別查看收入分佈)
print("按性別分組的收入描述性統計:")
customer_data_final %>%
group_by(Gender) %>%
summarise(
Count = n(), # 計算每個組別的觀測值數量
Mean_Income = mean(Income),
Median_Income = median(Income),
SD_Income = sd(Income),
Min_Income = min(Income),
Max_Income = max(Income),
IQR_Income = IQR(Income)
)
# 使用之前清理好的 customer_data_final 數據集
print("最終清理後的數據:")
print(customer_data_final)
# 數據摘要 (對所有數值型欄位提供最小值、Q1、中位數、平均數、Q3、最大值)
summary(customer_data_final)
# 計算特定欄位的平均數、中位數、標準差
mean_age <- mean(customer_data_final$Age)
median_income <- median(customer_data_final$Income)
sd_age <- sd(customer_data_final$Age)
print(paste("平均年齡:", round(mean_age, 2)))
print(paste("中位數收入:", round(median_income, 2)))
print(paste("年齡標準差:", round(sd_age, 2)))
# 計算類別變數的頻率表
table_gender <- table(customer_data_final$Gender)
print("性別頻率表:")
print(table_gender)
# 計算相對頻率
prop_table_gender <- prop.table(table_gender)
print("性別相對頻率表:")
print(prop_table_gender)
table_product <- table(customer_data_final$ProductPreference)
print("產品偏好頻率表:")
print(table_product)
# 使用 `dplyr` 和 `skimr` 套件進行更詳細的描述性統計
# install.packages("skimr") # 如果尚未安裝,請先執行此行
library(skimr)
# skim() 函數提供了一個簡潔且全面的數據摘要,包括缺失值、完整性、數據類型、描述性統計量和迷你直方圖。
print("使用 skimr::skim() 進行數據摘要:")
customer_data_final %>% skim()
# 按組別進行描述性統計 (例如,按性別查看收入分佈)
print("按性別分組的收入描述性統計:")
customer_data_final %>%
group_by(Gender) %>%
summarise(
Count = n(), # 計算每個組別的觀測值數量
Mean_Income = mean(Income),
Median_Income = median(Income),
SD_Income = sd(Income),
Min_Income = min(Income),
Max_Income = max(Income),
IQR_Income = IQR(Income)
)
4.3 數據可視化 (Data Visualization)
數據可視化是理解數據和呈現分析結果的強大工具。它能將複雜的數據模式以直觀、易懂的方式呈現出來,幫助我們發現趨勢、異常和關係。ggplot2 是 R 語言中最受歡迎且功能強大的可視化套件,基於「圖形語法」(Grammar of Graphics) 理念,能夠創建出美觀且高度客製化的統計圖表。
# install.packages("ggplot2") # 如果尚未安裝,請先執行此行
library(ggplot2)
# 1. 直方圖 (Histogram):查看數值型變數的分佈
# 範例:查看 Age 的分佈
ggplot(customer_data_final, aes(x = Age)) + # aes() 定義美學映射,x 軸為 Age
geom_histogram(binwidth = 5, fill = "steelblue", color = "black", alpha = 0.7) + # geom_histogram 繪製直方圖,binwidth 設定分組寬度
labs(title = "客戶年齡分佈", x = "年齡", y = "頻率") + # labs 設定圖表標題和軸標籤
theme_minimal() # theme_minimal() 應用簡潔主題
# 2. 箱形圖 (Box Plot):查看數值型變數在不同類別下的分佈,常用於異常值檢測
# 範例:查看不同性別的 Income 分佈
ggplot(customer_data_final, aes(x = Gender, y = Income, fill = Gender)) + # x 軸為 Gender (類別), y 軸為 Income (數值), fill 根據 Gender 填充顏色
geom_boxplot(alpha = 0.8) + # geom_boxplot 繪製箱形圖
labs(title = "不同性別的收入分佈", x = "性別", y = "收入") +
theme_minimal() +
theme(legend.position = "none") # 隱藏圖例
# 3. 長條圖 (Bar Chart):查看類別型變數的頻率分佈
# 範例:查看產品偏好分佈
ggplot(customer_data_final, aes(x = ProductPreference, fill = ProductPreference)) + # x 軸為 ProductPreference, fill 根據 ProductPreference 填充顏色
geom_bar(stat = "count", alpha = 0.8) + # geom_bar 繪製長條圖,stat="count" 表示計算每個類別的數量
labs(title = "客戶產品偏好", x = "產品偏好", y = "客戶數量") +
theme_minimal() +
theme(axis.text.x = element_text(angle = 45, hjust = 1)) # 旋轉 x 軸標籤以避免重疊
# 4. 散點圖 (Scatter Plot):查看兩個數值型變數之間的關係
# 範例:查看 Age 與 Income 之間的關係,並按 Gender 區分顏色
ggplot(customer_data_final, aes(x = Age, y = Income, color = Gender)) + # x 軸為 Age, y 軸為 Income, color 根據 Gender 區分顏色
geom_point(alpha = 0.7, size = 3) + # geom_point 繪製散點圖,alpha 設定透明度,size 設定點的大小
labs(title = "年齡與收入的散點圖", x = "年齡", y = "收入") +
theme_minimal()
# 5. 密度圖 (Density Plot):查看數值型變數的分佈,特別適合比較不同組別的分佈
# 範例:比較不同性別的 Age 分佈
ggplot(customer_data_final, aes(x = Age, fill = Gender)) +
geom_density(alpha = 0.6) + # geom_density 繪製密度圖
labs(title = "不同性別的年齡密度分佈", x = "年齡", y = "密度") +
theme_minimal()
# install.packages("ggplot2") # 如果尚未安裝,請先執行此行
library(ggplot2)
# 1. 直方圖 (Histogram):查看數值型變數的分佈
# 範例:查看 Age 的分佈
ggplot(customer_data_final, aes(x = Age)) + # aes() 定義美學映射,x 軸為 Age
geom_histogram(binwidth = 5, fill = "steelblue", color = "black", alpha = 0.7) + # geom_histogram 繪製直方圖,binwidth 設定分組寬度
labs(title = "客戶年齡分佈", x = "年齡", y = "頻率") + # labs 設定圖表標題和軸標籤
theme_minimal() # theme_minimal() 應用簡潔主題
# 2. 箱形圖 (Box Plot):查看數值型變數在不同類別下的分佈,常用於異常值檢測
# 範例:查看不同性別的 Income 分佈
ggplot(customer_data_final, aes(x = Gender, y = Income, fill = Gender)) + # x 軸為 Gender (類別), y 軸為 Income (數值), fill 根據 Gender 填充顏色
geom_boxplot(alpha = 0.8) + # geom_boxplot 繪製箱形圖
labs(title = "不同性別的收入分佈", x = "性別", y = "收入") +
theme_minimal() +
theme(legend.position = "none") # 隱藏圖例
# 3. 長條圖 (Bar Chart):查看類別型變數的頻率分佈
# 範例:查看產品偏好分佈
ggplot(customer_data_final, aes(x = ProductPreference, fill = ProductPreference)) + # x 軸為 ProductPreference, fill 根據 ProductPreference 填充顏色
geom_bar(stat = "count", alpha = 0.8) + # geom_bar 繪製長條圖,stat="count" 表示計算每個類別的數量
labs(title = "客戶產品偏好", x = "產品偏好", y = "客戶數量") +
theme_minimal() +
theme(axis.text.x = element_text(angle = 45, hjust = 1)) # 旋轉 x 軸標籤以避免重疊
# 4. 散點圖 (Scatter Plot):查看兩個數值型變數之間的關係
# 範例:查看 Age 與 Income 之間的關係,並按 Gender 區分顏色
ggplot(customer_data_final, aes(x = Age, y = Income, color = Gender)) + # x 軸為 Age, y 軸為 Income, color 根據 Gender 區分顏色
geom_point(alpha = 0.7, size = 3) + # geom_point 繪製散點圖,alpha 設定透明度,size 設定點的大小
labs(title = "年齡與收入的散點圖", x = "年齡", y = "收入") +
theme_minimal()
# 5. 密度圖 (Density Plot):查看數值型變數的分佈,特別適合比較不同組別的分佈
# 範例:比較不同性別的 Age 分佈
ggplot(customer_data_final, aes(x = Age, fill = Gender)) +
geom_density(alpha = 0.6) + # geom_density 繪製密度圖
labs(title = "不同性別的年齡密度分佈", x = "年齡", y = "密度") +
theme_minimal()
4.4 推論性統計簡介 (Inferential Statistics)
推論性統計 (Inferential Statistics) 旨在從樣本數據中推斷總體特徵,並對假設進行檢定。它是從描述性統計中獲得的樣本資訊推廣到更大總體的過程。這是一個更高級的主題,但了解其基本概念對於完整的統計分析至關重要。
- 假設檢定 (Hypothesis Testing):用於判斷樣本數據是否足以支持或拒絕關於總體的某個假設。通常涉及建立虛無假設 (Null Hypothesis, H0) 和對立假設 (Alternative Hypothesis, H1),然後計算 p 值來做出決策。
- t 檢定 (t-test):用於比較兩個樣本或一個樣本與已知總體平均值之間是否存在顯著差異。適用於數值型數據,且數據近似常態分佈或樣本量足夠大。
- 獨立樣本 t 檢定:比較兩個獨立組別的平均值 (例如,比較男性和女性客戶的平均收入是否有顯著差異)。
- 配對樣本 t 檢定:比較同一組別在不同條件下的平均值 (例如,比較某項干預前後的得分)。
- 單樣本 t 檢定:比較一個樣本的平均值與一個已知總體平均值。
- 卡方檢定 (Chi-squared test):用於檢定兩個類別變數之間是否存在關聯性。例如,檢定性別與產品偏好之間是否存在關聯。
- t 檢定 (t-test):用於比較兩個樣本或一個樣本與已知總體平均值之間是否存在顯著差異。適用於數值型數據,且數據近似常態分佈或樣本量足夠大。
- 相關分析 (Correlation Analysis):衡量兩個數值型變數之間線性關係的強度和方向。相關係數 (Pearson correlation coefficient) 介於 -1 和 1 之間,1 表示完全正相關,-1 表示完全負相關,0 表示無線性相關。
- 迴歸分析 (Regression Analysis):用於建立一個數學模型來描述一個或多個自變數 (independent variables) 如何影響一個因變數 (dependent variable)。最常見的是線性迴歸。
4.4.1 實作範例:推論性統計
# 範例:獨立樣本 t 檢定 (比較不同性別的收入差異)
# 假設我們想檢定男性和女性客戶的平均收入是否存在顯著差異
# 首先,確保 Gender 欄位是因子型,並且只有兩個級別 (在數據清理階段已完成)
# 執行獨立樣本 t 檢定
# 公式形式:因變數 ~ 自變數
t_test_result <- t.test(Income ~ Gender, data = customer_data_final)
print("獨立樣本 t 檢定結果 (收入 vs 性別):")
print(t_test_result)
# 結果解釋:
# p-value < 0.05 通常表示拒絕虛無假設 (H0: 男性與女性收入無顯著差異),接受對立假設 (H1: 男性與女性收入有顯著差異)。
# mean of x 和 mean of y 分別顯示兩個組別的平均值。
# confidence interval 顯示平均值差異的信賴區間。
# 範例:卡方檢定 (檢定性別與產品偏好之間的關聯)
# 創建列聯表 (Contingency Table)
contingency_table <- table(customer_data_final$Gender, customer_data_final$ProductPreference)
print("性別與產品偏好列聯表:")
print(contingency_table)
# 執行卡方檢定
chi_sq_test_result <- chisq.test(contingency_table)
print("卡方檢定結果 (性別 vs 產品偏好):")
print(chi_sq_test_result)
# 結果解釋:
# p-value < 0.05 通常表示拒絕虛無假設 (H0: 性別與產品偏好無關聯),接受對立假設 (H1: 性別與產品偏好有關聯)。
# X-squared 是卡方統計量,df 是自由度。
# 範例:相關分析 (檢定 Age 與 Income 之間的相關性)
# Pearson 相關係數適用於兩個數值型變數,且數據近似常態分佈。
correlation_result <- cor.test(customer_data_final$Age, customer_data_final$Income, method = "pearson")
print("年齡與收入相關性檢定結果:")
print(correlation_result)
# 結果解釋:
# correlation 顯示相關係數 (介於 -1 到 1)。
# p-value < 0.05 通常表示拒絕虛無假設 (H0: 相關係數為 0),接受對立假設 (H1: 相關係數不為 0)。
# confidence interval 顯示相關係數的信賴區間。
# 範例:簡單線性迴歸 (預測 Income 基於 Age)
# 建立線性迴歸模型
linear_model <- lm(Income ~ Age, data = customer_data_final)
print("線性迴歸模型摘要 (Income ~ Age):")
summary(linear_model)
# 結果解釋:
# Coefficients 部分顯示截距 (Intercept) 和 Age 變數的係數 (Estimate)。
# Age 的係數表示 Age 每增加一個單位,Income 平均增加多少。
# Pr(>|t|) 是對應係數的 p 值,用於判斷該係數是否顯著不為 0。
# Multiple R-squared 表示模型解釋了因變數變異的比例。
# 範例:獨立樣本 t 檢定 (比較不同性別的收入差異)
# 假設我們想檢定男性和女性客戶的平均收入是否存在顯著差異
# 首先,確保 Gender 欄位是因子型,並且只有兩個級別 (在數據清理階段已完成)
# 執行獨立樣本 t 檢定
# 公式形式:因變數 ~ 自變數
t_test_result <- t.test(Income ~ Gender, data = customer_data_final)
print("獨立樣本 t 檢定結果 (收入 vs 性別):")
print(t_test_result)
# 結果解釋:
# p-value < 0.05 通常表示拒絕虛無假設 (H0: 男性與女性收入無顯著差異),接受對立假設 (H1: 男性與女性收入有顯著差異)。
# mean of x 和 mean of y 分別顯示兩個組別的平均值。
# confidence interval 顯示平均值差異的信賴區間。
# 範例:卡方檢定 (檢定性別與產品偏好之間的關聯)
# 創建列聯表 (Contingency Table)
contingency_table <- table(customer_data_final$Gender, customer_data_final$ProductPreference)
print("性別與產品偏好列聯表:")
print(contingency_table)
# 執行卡方檢定
chi_sq_test_result <- chisq.test(contingency_table)
print("卡方檢定結果 (性別 vs 產品偏好):")
print(chi_sq_test_result)
# 結果解釋:
# p-value < 0.05 通常表示拒絕虛無假設 (H0: 性別與產品偏好無關聯),接受對立假設 (H1: 性別與產品偏好有關聯)。
# X-squared 是卡方統計量,df 是自由度。
# 範例:相關分析 (檢定 Age 與 Income 之間的相關性)
# Pearson 相關係數適用於兩個數值型變數,且數據近似常態分佈。
correlation_result <- cor.test(customer_data_final$Age, customer_data_final$Income, method = "pearson")
print("年齡與收入相關性檢定結果:")
print(correlation_result)
# 結果解釋:
# correlation 顯示相關係數 (介於 -1 到 1)。
# p-value < 0.05 通常表示拒絕虛無假設 (H0: 相關係數為 0),接受對立假設 (H1: 相關係數不為 0)。
# confidence interval 顯示相關係數的信賴區間。
# 範例:簡單線性迴歸 (預測 Income 基於 Age)
# 建立線性迴歸模型
linear_model <- lm(Income ~ Age, data = customer_data_final)
print("線性迴歸模型摘要 (Income ~ Age):")
summary(linear_model)
# 結果解釋:
# Coefficients 部分顯示截距 (Intercept) 和 Age 變數的係數 (Estimate)。
# Age 的係數表示 Age 每增加一個單位,Income 平均增加多少。
# Pr(>|t|) 是對應係數的 p 值,用於判斷該係數是否顯著不為 0。
# Multiple R-squared 表示模型解釋了因變數變異的比例。
4.5 常見錯誤與解決方案 (基礎統計分析)
在進行基礎統計分析時,初學者常會遇到一些概念性或操作性錯誤。了解這些錯誤並掌握解決方案,對於得出正確的分析結論至關重要。
- 未檢查數據分佈和假設:
- 錯誤:在進行推論性統計前,未檢查數據是否符合檢定所需的假設(例如常態性、方差齊性、獨立性)。如果數據不符合假設,檢定結果可能不可靠。
- 解決方案:在進行檢定前,使用視覺化工具(如直方圖、Q-Q 圖)和統計檢定(如 Shapiro-Wilk 檢定、Levene 檢定)來檢查數據分佈和假設。如果數據不符合假設,可以考慮數據轉換、使用非參數檢定或更穩健的統計方法。
- 誤解 p 值:
- 錯誤:p 值不代表效應大小,也不代表假設為真的機率。常見的誤解是 p 值小於 0.05 就意味著結果非常重要或效應很大。
- 解決方案:p 值僅表示在虛無假設為真的情況下,觀察到當前或更極端結果的機率。除了 p 值,還應關注效應大小 (Effect Size) 和信賴區間 (Confidence Interval),以全面評估結果的實際意義。
- 因果關係的誤判:
- 錯誤:相關不等於因果。即使兩個變數高度相關,也無法直接推斷因果關係。例如,冰淇淋銷量和溺水人數同時增加,並不意味著冰淇淋導致溺水,可能兩者都受夏季氣溫升高的影響。
- 解決方案:要建立因果關係,需要更嚴謹的研究設計(如隨機對照實驗)和更高級的統計方法。在觀察性研究中,只能說明變數之間存在關聯。
- 選擇錯誤的統計方法:
- 錯誤:根據數據類型、研究問題和假設條件選擇合適的統計方法至關重要。例如,對類別型數據使用 t 檢定,或對非線性關係使用線性迴歸。
- 解決方案:深入理解各種統計方法的適用條件和限制。在選擇方法時,考慮變數的測量尺度、數據分佈、研究設計和研究問題。必要時諮詢統計學家。
- 過度解讀結果:
- 錯誤:統計結果應結合專業知識和實際背景進行解釋,避免過度推論或將統計顯著性等同於實際重要性。
- 解決方案:將統計結果置於實際情境中進行解釋。例如,一個統計上顯著但效應很小的結果,在實際應用中可能沒有太大意義。同時,也要注意樣本的代表性,避免將樣本結果不當推廣到不相關的總體。
- 未考慮多重比較問題:
- 錯誤:當進行多個假設檢定時,錯誤地不調整 p 值,增加了犯第一型錯誤(拒絕真實的虛無假設)的機率。
- 解決方案:在進行多重比較時,應使用 Bonferroni 校正、Holm 校正或 FDR (False Discovery Rate) 等方法來調整 p 值,以控制整體錯誤率。
第五章:進階數據處理與分析技巧 (新增章節)
為了進一步提升文章的深度和實用性,本章將介紹一些進階的數據處理與分析技巧,包括數據合併、分組聚合以及時間序列數據的初步處理。
5.1 數據合併 (Data Merging)
在實際數據分析中,我們經常需要將來自不同來源的數據集進行合併。R 語言提供了多種函數來實現數據框的合併,其中最常用的是 merge() 函數和 dplyr 套件中的 join 系列函數。
5.1.1 merge() 函數
merge() 函數是 R 基礎包中用於合併數據框的通用函數,它支援多種合併方式,類似於資料庫中的 JOIN 操作。
# 創建兩個範例數據框
df_customers <- data.frame(
CustomerID = c(1, 2, 3, 4),
Name = c("Alice", "Bob", "Charlie", "David"),
City = c("Taipei", "Tainan", "Kaohsiung", "Taipei")
)
df_orders <- data.frame(
OrderID = c(101, 102, 103, 104, 105),
CustomerID = c(1, 3, 2, 1, 5), # 注意 CustomerID 5 不在 df_customers 中
Amount = c(150, 200, 120, 300, 80)
)
print("客戶數據:")
print(df_customers)
print("訂單數據:")
print(df_orders)
# 內連接 (Inner Join):只保留兩個數據框中共同的 CustomerID
merged_inner <- merge(df_customers, df_orders, by = "CustomerID")
print("內連接結果:")
print(merged_inner)
# 左連接 (Left Join):保留 df_customers 的所有行,並匹配 df_orders 的數據。df_orders 中沒有匹配的 CustomerID 將顯示 NA。
merged_left <- merge(df_customers, df_orders, by = "CustomerID", all.x = TRUE)
print("左連接結果:")
print(merged_left)
# 右連接 (Right Join):保留 df_orders 的所有行,並匹配 df_customers 的數據。df_customers 中沒有匹配的 CustomerID 將顯示 NA。
merged_right <- merge(df_customers, df_orders, by = "CustomerID", all.y = TRUE)
print("右連接結果:")
print(merged_right)
# 全連接 (Full Outer Join):保留兩個數據框的所有行,沒有匹配的數據將顯示 NA。
merged_full <- merge(df_customers, df_orders, by = "CustomerID", all.x = TRUE, all.y = TRUE)
print("全連接結果:")
print(merged_full)
# 創建兩個範例數據框
df_customers <- data.frame(
CustomerID = c(1, 2, 3, 4),
Name = c("Alice", "Bob", "Charlie", "David"),
City = c("Taipei", "Tainan", "Kaohsiung", "Taipei")
)
df_orders <- data.frame(
OrderID = c(101, 102, 103, 104, 105),
CustomerID = c(1, 3, 2, 1, 5), # 注意 CustomerID 5 不在 df_customers 中
Amount = c(150, 200, 120, 300, 80)
)
print("客戶數據:")
print(df_customers)
print("訂單數據:")
print(df_orders)
# 內連接 (Inner Join):只保留兩個數據框中共同的 CustomerID
merged_inner <- merge(df_customers, df_orders, by = "CustomerID")
print("內連接結果:")
print(merged_inner)
# 左連接 (Left Join):保留 df_customers 的所有行,並匹配 df_orders 的數據。df_orders 中沒有匹配的 CustomerID 將顯示 NA。
merged_left <- merge(df_customers, df_orders, by = "CustomerID", all.x = TRUE)
print("左連接結果:")
print(merged_left)
# 右連接 (Right Join):保留 df_orders 的所有行,並匹配 df_customers 的數據。df_customers 中沒有匹配的 CustomerID 將顯示 NA。
merged_right <- merge(df_customers, df_orders, by = "CustomerID", all.y = TRUE)
print("右連接結果:")
print(merged_right)
# 全連接 (Full Outer Join):保留兩個數據框的所有行,沒有匹配的數據將顯示 NA。
merged_full <- merge(df_customers, df_orders, by = "CustomerID", all.x = TRUE, all.y = TRUE)
print("全連接結果:")
print(merged_full)
5.1.2 dplyr 套件的 join 系列函數
dplyr 套件提供了更直觀且高效的 join 系列函數,包括 inner_join(), left_join(), right_join(), full_join(), semi_join(), anti_join()。
library(dplyr)
# 內連接
df_inner_join <- df_customers %>% inner_join(df_orders, by = "CustomerID")
print("dplyr 內連接結果:")
print(df_inner_join)
# 左連接
df_left_join <- df_customers %>% left_join(df_orders, by = "CustomerID")
print("dplyr 左連接結果:")
print(df_left_join)
# 右連接
df_right_join <- df_customers %>% right_join(df_orders, by = "CustomerID")
print("dplyr 右連接結果:")
print(df_right_join)
# 全連接
df_full_join <- df_customers %>% full_join(df_orders, by = "CustomerID")
print("dplyr 全連接結果:")
print(df_full_join)
# 半連接 (Semi Join):保留 df_customers 中在 df_orders 中有匹配的行,但不添加 df_orders 的列
df_semi_join <- df_customers %>% semi_join(df_orders, by = "CustomerID")
print("dplyr 半連接結果:")
print(df_semi_join)
# 反連接 (Anti Join):保留 df_customers 中在 df_orders 中沒有匹配的行
df_anti_join <- df_customers %>% anti_join(df_orders, by = "CustomerID")
print("dplyr 反連接結果:")
print(df_anti_join)
library(dplyr)
# 內連接
df_inner_join <- df_customers %>% inner_join(df_orders, by = "CustomerID")
print("dplyr 內連接結果:")
print(df_inner_join)
# 左連接
df_left_join <- df_customers %>% left_join(df_orders, by = "CustomerID")
print("dplyr 左連接結果:")
print(df_left_join)
# 右連接
df_right_join <- df_customers %>% right_join(df_orders, by = "CustomerID")
print("dplyr 右連接結果:")
print(df_right_join)
# 全連接
df_full_join <- df_customers %>% full_join(df_orders, by = "CustomerID")
print("dplyr 全連接結果:")
print(df_full_join)
# 半連接 (Semi Join):保留 df_customers 中在 df_orders 中有匹配的行,但不添加 df_orders 的列
df_semi_join <- df_customers %>% semi_join(df_orders, by = "CustomerID")
print("dplyr 半連接結果:")
print(df_semi_join)
# 反連接 (Anti Join):保留 df_customers 中在 df_orders 中沒有匹配的行
df_anti_join <- df_customers %>% anti_join(df_orders, by = "CustomerID")
print("dplyr 反連接結果:")
print(df_anti_join)
5.2 分組聚合 (Group By and Summarize)
分組聚合是數據分析中非常常見的操作,它允許我們根據一個或多個類別變數對數據進行分組,然後對每個組別計算摘要統計量。dplyr 套件的 group_by() 和 summarise() 函數是實現這一功能的強大組合。
# 使用 customer_data_final 數據集
print("原始數據:")
print(customer_data_final)
# 按 Gender 分組,計算平均收入和年齡
df_summary_gender <- customer_data_final %>%
group_by(Gender) %>%
summarise(
Avg_Income = mean(Income),
Avg_Age = mean(Age),
Count = n() # 計算每個組別的觀測值數量
)
print("按性別分組的摘要統計:")
print(df_summary_gender)
# 按 ProductPreference 分組,計算平均收入和客戶數量
df_summary_product <- customer_data_final %>%
group_by(ProductPreference) %>%
summarise(
Avg_Income = mean(Income),
Total_Customers = n()
)
print("按產品偏好分組的摘要統計:")
print(df_summary_product)
# 按 Gender 和 ProductPreference 雙重分組
df_summary_gender_product <- customer_data_final %>%
group_by(Gender, ProductPreference) %>%
summarise(
Avg_Income = mean(Income),
Count = n()
)
print("按性別和產品偏好分組的摘要統計:")
print(df_summary_gender_product)
# 使用 customer_data_final 數據集
print("原始數據:")
print(customer_data_final)
# 按 Gender 分組,計算平均收入和年齡
df_summary_gender <- customer_data_final %>%
group_by(Gender) %>%
summarise(
Avg_Income = mean(Income),
Avg_Age = mean(Age),
Count = n() # 計算每個組別的觀測值數量
)
print("按性別分組的摘要統計:")
print(df_summary_gender)
# 按 ProductPreference 分組,計算平均收入和客戶數量
df_summary_product <- customer_data_final %>%
group_by(ProductPreference) %>%
summarise(
Avg_Income = mean(Income),
Total_Customers = n()
)
print("按產品偏好分組的摘要統計:")
print(df_summary_product)
# 按 Gender 和 ProductPreference 雙重分組
df_summary_gender_product <- customer_data_final %>%
group_by(Gender, ProductPreference) %>%
summarise(
Avg_Income = mean(Income),
Count = n()
)
print("按性別和產品偏好分組的摘要統計:")
print(df_summary_gender_product)
5.3 時間序列數據的初步處理 (Basic Time Series Handling)
時間序列數據在金融、氣象、銷售預測等領域非常常見。R 語言對時間序列數據有很好的支持。這裡我們介紹一些初步的處理方法。
# 創建一個範例時間序列數據框
df_ts <- data.frame(
Date = seq(as.Date("2023-01-01"), by = "month", length.out = 12),
Sales = c(100, 120, 110, 130, 150, 140, 160, 170, 155, 180, 190, 200)
)
print("原始時間序列數據:")
print(df_ts)
# 提取時間資訊 (年、月、日)
library(lubridate) # lubridate 套件讓日期時間處理更簡便
# install.packages("lubridate") # 如果尚未安裝,請先執行此行
df_ts_enriched <- df_ts %>%
mutate(
Year = year(Date),
Month = month(Date),
Day = day(Date),
Weekday = wday(Date, label = TRUE) # 星期幾
)
print("提取時間資訊後的數據:")
print(df_ts_enriched)
# 時間序列可視化
ggplot(df_ts_enriched, aes(x = Date, y = Sales)) +
geom_line(color = "blue", size = 1) +
geom_point(color = "red", size = 2) +
labs(title = "月銷售額趨勢", x = "日期", y = "銷售額") +
theme_minimal()
# 計算移動平均 (Moving Average) - 簡單平滑化
# install.packages("TTR") # TTR 套件提供技術分析指標,包括移動平均
library(TTR)
df_ts_ma <- df_ts_enriched %>%
mutate(Sales_MA3 = SMA(Sales, n = 3)) # 計算 3 個月的簡單移動平均
print("計算移動平均後的數據:")
print(df_ts_ma)
# 可視化移動平均
ggplot(df_ts_ma, aes(x = Date, y = Sales)) +
geom_line(color = "blue", size = 1, alpha = 0.7) +
geom_line(aes(y = Sales_MA3), color = "red", size = 1.2) +
labs(title = "月銷售額與 3 個月移動平均", x = "日期", y = "銷售額") +
theme_minimal()
# 創建一個範例時間序列數據框
df_ts <- data.frame(
Date = seq(as.Date("2023-01-01"), by = "month", length.out = 12),
Sales = c(100, 120, 110, 130, 150, 140, 160, 170, 155, 180, 190, 200)
)
print("原始時間序列數據:")
print(df_ts)
# 提取時間資訊 (年、月、日)
library(lubridate) # lubridate 套件讓日期時間處理更簡便
# install.packages("lubridate") # 如果尚未安裝,請先執行此行
df_ts_enriched <- df_ts %>%
mutate(
Year = year(Date),
Month = month(Date),
Day = day(Date),
Weekday = wday(Date, label = TRUE) # 星期幾
)
print("提取時間資訊後的數據:")
print(df_ts_enriched)
# 時間序列可視化
ggplot(df_ts_enriched, aes(x = Date, y = Sales)) +
geom_line(color = "blue", size = 1) +
geom_point(color = "red", size = 2) +
labs(title = "月銷售額趨勢", x = "日期", y = "銷售額") +
theme_minimal()
# 計算移動平均 (Moving Average) - 簡單平滑化
# install.packages("TTR") # TTR 套件提供技術分析指標,包括移動平均
library(TTR)
df_ts_ma <- df_ts_enriched %>%
mutate(Sales_MA3 = SMA(Sales, n = 3)) # 計算 3 個月的簡單移動平均
print("計算移動平均後的數據:")
print(df_ts_ma)
# 可視化移動平均
ggplot(df_ts_ma, aes(x = Date, y = Sales)) +
geom_line(color = "blue", size = 1, alpha = 0.7) +
geom_line(aes(y = Sales_MA3), color = "red", size = 1.2) +
labs(title = "月銷售額與 3 個月移動平均", x = "日期", y = "銷售額") +
theme_minimal()
結論
本篇文章為您提供了 R 語言統計分析的入門指南,涵蓋了從環境建置、數據讀取、數據清理到基礎統計分析的關鍵步驟。我們學習了如何安裝 R 和 RStudio,掌握了讀取 CSV、Excel 等多種數據格式的方法,並深入探討了缺失值、異常值、重複值、數據類型轉換和數據重塑等數據清理技術。隨後,我們透過描述性統計、數據可視化和基礎推論性統計的實作範例,展示了如何從數據中提取有價值的資訊,並對常見錯誤進行了提醒。最後,我們還介紹了數據合併、分組聚合和時間序列數據的初步處理等進階技巧,旨在幫助讀者更全面地應用 R 語言進行數據分析。
R 語言作為一個強大的數據分析工具,其應用範圍遠不止於此。掌握了這些基礎知識,您將能夠更自信地處理真實世界的數據,並為更高級的統計建模和機器學習打下堅實的基礎。數據分析的旅程充滿挑戰,但也充滿樂趣。持續學習、不斷實踐是精通 R 語言和數據分析的關鍵。
學習建議:
- 多動手實作:理論知識固然重要,但只有透過實際操作才能真正掌握。嘗試使用不同的數據集,解決不同的數據問題。動手編寫程式碼,觀察結果,並從錯誤中學習。
- 善用 R 說明文件:R 的每個函數和套件都有詳細的說明文件,遇到問題時,使用
?函數名或help(函數名)查詢是最好的學習方式。這能幫助您理解函數的參數、用法和範例。 - 參與社群:R 語言擁有活躍的全球社群。在 Stack Overflow、RStudio Community、GitHub 等平台提問或瀏覽問題,可以快速獲得幫助並學習新知識。參與討論也能提升您的解決問題能力。
- 閱讀優秀的 R 語言書籍和部落格:例如 Hadley Wickham 的
R for Data Science[2] (學習tidyverse的最佳入門書)、Advanced R(深入理解 R 語言)、Statistical Rethinking(貝葉斯統計與 R 實作)。 - 實踐專案:嘗試將所學知識應用到實際專案中,例如分析公開數據集、參與數據科學競賽等。這能幫助您鞏固知識,並將理論與實踐結合。
希望這份指南能幫助您開啟 R 語言統計分析的大門,祝您在數據探索的旅程中一切順利!
FAQ (常見問題)
-
R 語言和 Python 哪個更適合統計分析?
- R 語言在統計學術界和數據可視化方面擁有深厚的積累和豐富的套件,特別適合統計建模和研究。許多最新的統計方法和學術論文的實作都會首先在 R 中出現。Python 則在機器學習、深度學習和通用程式設計方面更具優勢,其生態系統更廣泛,與軟體開發的結合更緊密。兩者各有側重,許多數據科學家會同時學習和使用這兩種語言,根據專案需求選擇最合適的工具。
-
我應該先學習 R 還是 RStudio?
- R 是底層的程式語言和計算環境,RStudio 是一個提供更友善介面的整合開發環境 (IDE)。建議同時安裝 R 和 RStudio,並主要在 RStudio 中進行學習和開發,因為它能極大地提升您的工作效率和體驗。RStudio 提供了程式碼編輯器、控制台、環境管理、繪圖預覽等一站式功能,讓您能更專注於數據分析本身。
-
如何處理大型數據集?R 語言會不會很慢?
- 對於非常大的數據集(例如數 GB 甚至數 TB),R 語言的基礎函數可能會比較慢,因為 R 傾向於將數據載入記憶體。但有許多優化的套件可以處理大數據,例如
data.table(以其極高的運行效率著稱) 和dplyr(特別是結合dtplyr或arrow套件,可以處理超出記憶體的數據)。此外,也可以考慮使用資料庫連接 (如DBI套件) 或雲端計算資源 (如 Sparklyr) 來處理分佈式數據。
- 對於非常大的數據集(例如數 GB 甚至數 TB),R 語言的基礎函數可能會比較慢,因為 R 傾向於將數據載入記憶體。但有許多優化的套件可以處理大數據,例如
-
什麼是
tidyverse?我需要學習它嗎?tidyverse是一組由 Hadley Wickham 開發的 R 套件集合,旨在提供一套統一的數據科學工作流程,包括數據導入 (readr)、數據清理 (tidyr)、數據轉換 (dplyr) 和數據可視化 (ggplot2) 等。它的設計理念是讓數據處理和分析的程式碼更具可讀性、一致性和效率。對於現代 R 語言數據分析,強烈建議學習tidyverse,它已經成為 R 社群的主流。
-
如果我的數據中有很多缺失值,我應該怎麼辦?
- 處理缺失值沒有一勞永逸的方法,需要根據具體情況判斷。首先要理解缺失值的原因和模式。如果缺失值佔比很小且隨機分佈,可以考慮刪除含有缺失值的行。如果缺失值較多,可以嘗試使用平均數、中位數、眾數或更複雜的預測模型(如
mice套件進行多重填補)進行填補。重要的是要評估不同處理方法對分析結果的影響,並在報告中說明處理方式。
- 處理缺失值沒有一勞永逸的方法,需要根據具體情況判斷。首先要理解缺失值的原因和模式。如果缺失值佔比很小且隨機分佈,可以考慮刪除含有缺失值的行。如果缺失值較多,可以嘗試使用平均數、中位數、眾數或更複雜的預測模型(如
-
如何選擇正確的統計檢定方法?
- 選擇統計檢定方法需要考慮多個因素,包括:
- 研究問題的類型:您是想比較組間差異、檢定變數間關聯性還是預測某個結果?
- 數據的類型:變數是數值型(連續、離散)還是類別型(名義、次序)?
- 數據的分佈:數據是否符合常態分佈?方差是否齊性?
- 樣本大小:樣本量的大小會影響某些檢定的適用性。
- 數據是否獨立:樣本是獨立的還是配對的?
- 建議查閱統計學教材、決策樹圖或諮詢統計專家。常見的檢定包括 t 檢定、ANOVA、卡方檢定、相關分析、迴歸分析等。
- 選擇統計檢定方法需要考慮多個因素,包括:
-
R 語言的學習資源有哪些推薦?
- 官方文件:R 內建的說明文件 (
?function_name) 是最權威的參考資料。 - 書籍:
R for Data Science[2] (學習tidyverse的最佳入門書)、Advanced R(深入理解 R 語言)、Statistical Rethinking(貝葉斯統計與 R 實作)。 - 線上課程:Coursera、edX、DataCamp、Udemy 等平台提供豐富的 R 語言相關課程,從入門到進階都有。
- 社群:Stack Overflow、RStudio Community、GitHub 是尋求幫助和學習新技巧的好地方。
- 部落格:許多數據科學家和統計學家會分享 R 語言的教學和實踐經驗,例如 RStudio 部落格、Towards Data Science 等。
- 官方文件:R 內建的說明文件 (
-
R 語言的 SEO 優化有哪些技巧?
- 關鍵字研究:使用相關的長尾關鍵字,例如「R 語言數據清理教學」、「R 統計圖表製作」、「R 數據分析實戰」、「R 語言入門指南」。將這些關鍵字自然地融入標題、副標題和正文中。
- 內容深度與廣度:提供全面且深入的內容,涵蓋讀者可能感興趣的所有方面,並解決他們可能遇到的問題。文章結構清晰,易於閱讀。
- 結構化數據:使用清晰的 H1/H2/H3 標題、列表、粗體強調和程式碼區塊,提高文章可讀性,同時也讓搜尋引擎更容易理解文章結構。
- 內部連結:連結到您網站內的其他相關文章或資源,增加網站的權重和用戶停留時間。例如,在提到
ggplot2時,可以連結到一篇專門介紹ggplot2的文章。 - 外部連結:引用權威來源(如學術論文、官方文件、知名數據科學部落格),增加文章的可信度和權威性。
- 圖片與圖表:使用描述性的圖片名稱和替代文字 (alt text),並確保圖片優化(檔案大小適中,加載速度快)。圖表應清晰易懂,並輔以文字說明。
- 網址優化:使用簡潔、包含關鍵字的網址 (slug),例如本篇文章的 slug 為
r-language-statistics-guide。 - 元描述 (Meta Description):撰寫吸引人的元描述,概括文章內容,鼓勵用戶點擊。
參考資料
[1] R 語言基本概論. (n.d.). In R 資料科學與統計. Retrieved from https://bookdown.org/jefflinmd38/r4biost/intro.html [2] Wickham, H., & Grolemund, G. (2017). R for Data Science: Import, Tidy, Transform, Visualize, and Model Data. O'Reilly Media. Retrieved from https://r4ds.had.co.nz/
