📚 今日目標
- 掌握R函數編寫和調試技巧
- 學習函數式編程概念
- 掌握向量化操作和循環優化
- 學習代碼性能分析和優化
- 掌握代碼重構和最佳實踐
🏗️ 第一部分:函數基礎
1.1 函數定義與結構
# 基本函數結構
# function_name <- function(arguments) {
# # 函數體
# return(value)
# }
# 簡單示例
add_numbers <- function(x, y) {
result <- x + y
return(result)
}
# 調用函數
add_numbers(5, 3)
add_numbers(10, 20)
# 默認參數
calculate_area <- function(radius, pi = 3.14159) {
area <- pi * radius^2
return(area)
}
calculate_area(5) # 使用默認pi值
calculate_area(5, 3.14) # 指定pi值
# 多參數函數
create_person <- function(name, age, city = "未知") {
person <- list(
name = name,
age = age,
city = city,
introduction = paste(name, ",", age, "歲,來自", city)
)
return(person)
}
person1 <- create_person("張三", 25, "北京")
person2 <- create_person("李四", 30)
1.2 參數檢查與驗證
# 參數類型檢查
safe_divide <- function(x, y) {
# 參數檢查
if (!is.numeric(x) || !is.numeric(y)) {
stop("兩個參數都必須是數值型")
}
if (y == 0) {
warning("除數不能為零,返回Inf")
return(Inf)
}
return(x / y)
}
# 測試
safe_divide(10, 2)
safe_divide(10, 0)
# safe_divide("10", 2) # 會拋出錯誤
# 參數驗證函數
validate_input <- function(data, type = "numeric", min_val = NULL, max_val = NULL) {
# 檢查類型
if (type == "numeric" && !is.numeric(data)) {
stop("輸入必須是數值型")
}
if (type == "character" && !is.character(data)) {
stop("輸入必須是字符型")
}
# 檢查範圍
if (!is.null(min_val) && any(data < min_val)) {
warning(paste("有些值小於最小值", min_val))
}
if (!is.null(max_val) && any(data > max_val)) {
warning(paste("有些值大於最大值", max_val))
}
return(TRUE)
}
# 使用驗證
validate_input(c(1, 2, 3), "numeric", min_val = 0)
validate_input(c("a", "b", "c"), "character")
1.3 返回值與輸出
# 返回多個值
calculate_stats <- function(x) {
if (!is.numeric(x)) {
stop("輸入必須是數值向量")
}
stats <- list(
mean = mean(x, na.rm = TRUE),
median = median(x, na.rm = TRUE),
sd = sd(x, na.rm = TRUE),
min = min(x, na.rm = TRUE),
max = max(x, na.rm = TRUE),
n = length(x),
n_missing = sum(is.na(x))
)
return(stats)
}
# 調用
data <- c(1, 2, 3, 4, 5, NA, 7, 8, 9)
result <- calculate_stats(data)
result$mean
result$sd
# 使用cat和print輸出信息
verbose_calculate <- function(x, verbose = TRUE) {
if (verbose) {
cat("開始計算統計量...\n")
cat("數據長度:", length(x), "\n")
}
mean_val <- mean(x, na.rm = TRUE)
if (verbose) {
cat("計算完成\n")
cat("平均值:", mean_val, "\n")
}
return(mean_val)
}
verbose_calculate(c(1, 2, 3))
verbose_calculate(c(1, 2, 3), verbose = FALSE)
# 返回不可見值
invisible_return <- function(x) {
result <- x * 2
invisible(result) # 返回但不打印
}
temp <- invisible_return(5)
print(temp) # 需要顯式打印才能看到
🔧 第二部分:函數式編程
2.1 apply函數族
# apply - 對矩陣或數組的行或列應用函數
mat <- matrix(1:12, nrow = 3, ncol = 4)
# 對每列求均值
apply(mat, 2, mean) # 2表示列
# 對每行求和
apply(mat, 1, sum) # 1表示行
# 自定義函數
apply(mat, 1, function(x) max(x) - min(x)) # 每行的極差
# lapply - 對列表應用函數,返回列表
my_list <- list(a = 1:5, b = 6:10, c = 11:15)
lapply(my_list, mean) # 每個元素的均值
lapply(my_list, length) # 每個元素的長度
lapply(my_list, summary) # 每個元素的摘要統計
# 使用自定義函數
lapply(my_list, function(x) x[x > mean(x)])
# sapply - 簡化輸出為向量或矩陣
sapply(my_list, mean) # 返回向量
sapply(my_list, range) # 返回矩陣
# vapply - 指定返回類型,更安全
vapply(my_list, mean, numeric(1)) # 返回數值向量
vapply(my_list, length, integer(1)) # 返回整數向量
# tapply - 按因子分組應用函數
data <- data.frame(
value = c(10, 15, 20, 25, 30, 35),
group = factor(c("A", "A", "B", "B", "C", "C"))
)
tapply(data$value, data$group, mean)
tapply(data$value, data$group, function(x) c(mean = mean(x), sd = sd(x)))
2.2 purrr包函數式編程
# 安裝並加載purrr包
install.packages("purrr")
library(purrr)
library(dplyr)
# map系列函數(purrr的apply)
numbers <- list(1:5, 6:10, 11:15)
# map - 返回列表
map(numbers, mean)
# map_dbl - 返回數值向量
map_dbl(numbers, mean)
# map_int - 返回整數向量
map_int(numbers, length)
# map_chr - 返回字符向量
map_chr(numbers, paste, collapse = ",")
# map_df - 返回數據框
map_df(numbers, ~ data.frame(
mean = mean(.x),
sum = sum(.x),
length = length(.x)
))
# 使用公式簡寫
map_dbl(numbers, ~ mean(.x) * 2)
# walk - 執行副作用操作(不返回值)
walk(numbers, ~ cat("長度:", length(.x), "\n"))
# 處理多個輸入
map2(1:3, 4:6, ~ .x + .y) # 向量加法
pmap(list(1:3, 4:6, 7:9), ~ sum(c(...))) # 多參數函數
# 安全函數(處理錯誤)
safe_mean <- safely(mean, otherwise = NA)
result <- list(1:5, "not numeric", 6:10) %>%
map(safe_mean)
# 提取結果和錯誤
map(result, "result") # 成功的結果
map(result, "error") # 錯誤信息
# possibly - 發生錯誤時返回默認值
possibly_mean <- possibly(mean, otherwise = NA)
map(list(1:5, "not numeric", 6:10), possibly_mean)
2.3 高階函數
# 函數作為參數
apply_function <- function(f, x) {
if (!is.function(f)) {
stop("第一個參數必須是函數")
}
return(f(x))
}
apply_function(mean, c(1, 2, 3, 4, 5))
apply_function(sd, c(1, 2, 3, 4, 5))
apply_function(\(x) sum(x^2), c(1, 2, 3)) # 匿名函數
# 函數工廠(返回函數的函數)
create_multiplier <- function(factor) {
function(x) {
x * factor
}
}
double <- create_multiplier(2)
triple <- create_multiplier(3)
double(10) # 20
triple(10) # 30
# 創建一系列轉換函數
create_scaler <- function(method = "standardize") {
if (method == "standardize") {
function(x) {
(x - mean(x)) / sd(x)
}
} else if (method == "normalize") {
function(x) {
(x - min(x)) / (max(x) - min(x))
}
} else if (method == "robust") {
function(x) {
(x - median(x)) / IQR(x)
}
} else {
stop("未知的標準化方法")
}
}
std_scale <- create_scaler("standardize")
norm_scale <- create_scaler("normalize")
std_scale(c(1, 2, 3, 4, 5))
norm_scale(c(1, 2, 3, 4, 5))
# 記憶化(緩存函數結果)
library(memoise)
# 創建記憶化函數
slow_function <- function(x) {
Sys.sleep(1) # 模擬耗時操作
return(x^2)
}
memoized_slow <- memoise(slow_function)
# 第一次調用會慢
system.time(memoized_slow(5)) # 約1秒
# 第二次調用會快(使用緩存)
system.time(memoized_slow(5)) # 幾乎0秒
⚡ 第三部分:性能優化
3.1 向量化操作
# 向量化 vs 循環
n <- 1000000
x <- runif(n)
# 循環方法(慢)
system.time({
result_loop <- numeric(n)
for (i in 1:n) {
result_loop[i] <- x[i]^2 + sin(x[i])
}
})
# 向量化方法(快)
system.time({
result_vec <- x^2 + sin(x)
})
# 驗證結果
all.equal(result_loop, result_vec)
# 更多向量化示例
# 1. 數學運算
y <- x * 2 + 3
z <- exp(x) * log(x + 1)
# 2. 邏輯運算
logical_vec <- x > 0.5
count_true <- sum(logical_vec) # 向量化計數
# 3. ifelse向量化
categorized <- ifelse(x < 0.33, "低",
ifelse(x < 0.66, "中", "高"))
# 4. 使用rowSums, colSums等
mat <- matrix(runif(10000), nrow = 100)
row_sums <- rowSums(mat)
col_means <- colMeans(mat)
# 避免不必要的向量化
# 有時簡單的循環可能更快
small_n <- 10
small_x <- 1:small_n
# 小數據時差異不大
system.time(for(i in 1:small_n) small_x[i]^2)
system.time(small_x^2)
3.2 循環優化
# 預分配內存
n <- 10000
# 不好的做法:動態擴展
system.time({
bad_result <- c()
for (i in 1:n) {
bad_result <- c(bad_result, i^2)
}
})
# 好的做法:預分配
system.time({
good_result <- numeric(n) # 預分配內存
for (i in 1:n) {
good_result[i] <- i^2
}
})
# 使用適當的循環結構
# 計算矩陣每行的統計量
mat <- matrix(rnorm(10000), nrow = 100)
# 方法1:使用apply(推薦)
system.time({
stats1 <- apply(mat, 1, function(row) {
c(mean = mean(row), sd = sd(row), median = median(row))
})
})
# 方法2:預分配+循環
system.time({
n_rows <- nrow(mat)
stats2 <- matrix(NA, nrow = n_rows, ncol = 3)
colnames(stats2) <- c("mean", "sd", "median")
for (i in 1:n_rows) {
row <- mat[i, ]
stats2[i, ] <- c(mean(row), sd(row), median(row))
}
})
# 方法3:向量化計算
system.time({
means <- rowMeans(mat)
sds <- apply(mat, 1, sd) # rowSds需要matrixStats包
medians <- apply(mat, 1, median)
stats3 <- cbind(mean = means, sd = sds, median = medians)
})
# 使用Rcpp進行C++級別的循環優化
# install.packages("Rcpp")
library(Rcpp)
# 定義C++函數
cppFunction('
NumericVector square_cpp(NumericVector x) {
int n = x.size();
NumericVector result(n);
for(int i = 0; i < n; ++i) {
result[i] = x[i] * x[i];
}
return result;
}
')
# 比較性能
x <- rnorm(1000000)
system.time(x^2) # 向量化R
system.time(square_cpp(x)) # C++函數
3.3 內存管理
# 監控內存使用
library(pryr)
# 查看對象大小
x <- rnorm(1000000)
object_size(x) # 約8MB
# 比較不同數據結構的內存使用
vec <- 1:1000000
list_ <- as.list(1:1000000)
object_size(vec) # 約4MB
object_size(list_) # 約32MB
# 內存清理
# 查看當前內存中的對象
ls()
# 刪除不需要的對象
rm(x, vec, list_)
gc() # 強制垃圾回收
# 使用memory.size和memory.limit(Windows)
if (.Platform$OS.type == "windows") {
memory.size() # 當前使用內存
memory.limit() # 內存限制
memory.limit(size = 8000) # 增加內存限制到8GB
}
# 大數據集處理技巧
# 1. 使用data.table代替data.frame
install.packages("data.table")
library(data.table)
# 創建大數據集
n <- 1000000
big_df <- data.frame(
id = 1:n,
value = rnorm(n),
category = sample(letters[1:10], n, replace = TRUE)
)
big_dt <- as.data.table(big_df)
# 比較內存使用
object_size(big_df)
object_size(big_dt)
# 2. 使用ff或bigmemory包處理超大文件
install.packages("bigmemory")
library(bigmemory)
# 創建大矩陣
big_mat <- big.matrix(nrow = 10000, ncol = 1000,
init = 0,
backingfile = "big_mat.bin",
descriptorfile = "big_mat.desc")
# 3. 分塊處理
process_in_chunks <- function(data, chunk_size = 10000, fun) {
n <- nrow(data)
chunks <- ceiling(n / chunk_size)
results <- list()
for (i in 1:chunks) {
start <- (i - 1) * chunk_size + 1
end <- min(i * chunk_size, n)
chunk <- data[start:end, ]
results[[i]] <- fun(chunk)
}
return(do.call(rbind, results))
}
# 示例:分塊計算均值
chunk_means <- process_in_chunks(big_df, chunk_size = 50000,
function(chunk) {
colMeans(chunk[, c("id", "value")])
})
📊 第四部分:代碼性能分析
4.1 性能分析工具
# 使用system.time測量執行時間
system.time({
# 需要測量的代碼
x <- rnorm(1000000)
y <- x^2 + sin(x)
})
# 使用microbenchmark進行基準測試
install.packages("microbenchmark")
library(microbenchmark)
# 比較不同方法的性能
compare_speed <- microbenchmark(
循環 = {
result <- numeric(1000)
for(i in 1:1000) {
result[i] <- i^2
}
},
向量化 = {
result <- (1:1000)^2
},
sapply = {
result <- sapply(1:1000, function(x) x^2)
},
times = 100 # 每個方法運行100次
)
print(compare_speed)
# 可視化比較結果
library(ggplot2)
autoplot(compare_speed)
# 使用profvis進行代碼剖析
install.packages("profvis")
library(profvis)
# 啓動性能分析
profvis({
# 分析的代碼
n <- 10000
mat <- matrix(rnorm(n * 100), nrow = n)
# 方法1:循環
result1 <- matrix(NA, nrow = n, ncol = 3)
for (i in 1:n) {
result1[i, ] <- c(mean(mat[i, ]), sd(mat[i, ]), median(mat[i, ]))
}
# 方法2:apply
result2 <- t(apply(mat, 1, function(row) {
c(mean(row), sd(row), median(row))
}))
})
# 使用bench包進行更詳細的基準測試
install.packages("bench")
library(bench)
bm <- bench::mark(
循環 = {
result <- numeric(1000)
for(i in 1:1000) {
result[i] <- sqrt(i)
}
},
向量化 = sqrt(1:1000),
迭代次數 = 1000
)
print(bm)
plot(bm)
4.2 代碼重構示例
# 原始代碼(效率較低)
calculate_statistics_original <- function(data) {
n <- nrow(data)
results <- data.frame(
mean = numeric(n),
sd = numeric(n),
min = numeric(n),
max = numeric(n)
)
for (i in 1:n) {
row <- data[i, ]
results$mean[i] <- mean(row, na.rm = TRUE)
results$sd[i] <- sd(row, na.rm = TRUE)
results$min[i] <- min(row, na.rm = TRUE)
results$max[i] <- max(row, na.rm = TRUE)
}
return(results)
}
# 重構後的代碼(使用向量化)
calculate_statistics_optimized <- function(data) {
# 使用apply進行向量化計算
means <- apply(data, 1, mean, na.rm = TRUE)
sds <- apply(data, 1, sd, na.rm = TRUE)
mins <- apply(data, 1, min, na.rm = TRUE)
maxs <- apply(data, 1, max, na.rm = TRUE)
results <- data.frame(
mean = means,
sd = sds,
min = mins,
max = maxs
)
return(results)
}
# 進一步優化(使用矩陣運算)
calculate_statistics_fast <- function(data) {
# 轉換為矩陣
mat <- as.matrix(data)
# 使用行操作
means <- rowMeans(mat, na.rm = TRUE)
# 使用matrixStats包獲取更快的行統計
if (requireNamespace("matrixStats", quietly = TRUE)) {
library(matrixStats)
sds <- rowSds(mat, na.rm = TRUE)
mins <- rowMins(mat, na.rm = TRUE)
maxs <- rowMaxs(mat, na.rm = TRUE)
} else {
# 回退到apply
sds <- apply(mat, 1, sd, na.rm = TRUE)
mins <- apply(mat, 1, min, na.rm = TRUE)
maxs <- apply(mat, 1, max, na.rm = TRUE)
}
data.frame(mean = means, sd = sds, min = mins, max = maxs)
}
# 性能比較
test_data <- matrix(rnorm(1000 * 100), nrow = 1000)
bench_results <- microbenchmark(
原始 = calculate_statistics_original(test_data),
優化 = calculate_statistics_optimized(test_data),
快速 = calculate_statistics_fast(test_data),
times = 10
)
print(bench_results)
🎯 第五部分:最佳實踐
5.1 代碼風格指南
# 1. 命名規範
# 變量名:小寫,用下劃線分隔
student_age <- 20
average_score <- 85.5
data_file_path <- "/path/to/data.csv"
# 函數名:動詞開頭,描述操作
calculate_mean <- function(x) mean(x)
prepare_data <- function(data) {
# 數據處理
}
validate_input <- function(input) {
# 輸入驗證
}
# 2. 註釋規範
# 文件頭註釋
# 文件名: data_analysis.R
# 作者: 你的名字
# 創建日期: 2024-01-15
# 描述: 數據分析主腳本
# 函數註釋
#' 計算數據的描述性統計量
#'
#' @param data 數值向量或矩陣
#' @param na.rm 是否移除NA值,默認為TRUE
#' @return 包含均值、標準差等統計量的列表
#' @examples
#' calculate_descriptive_stats(c(1, 2, 3, 4, 5))
calculate_descriptive_stats <- function(data, na.rm = TRUE) {
# 函數體
}
# 3. 代碼格式化
# 使用一致的空格和縮進
good_format <- function(x, y) {
result <- x + y
if (result > 100) {
return("很大")
} else {
return("不大")
}
}
# 4. 錯誤處理
robust_function <- function(input) {
# 參數驗證
if (!is.numeric(input)) {
stop("輸入必須是數值型")
}
# 嘗試執行
tryCatch({
result <- log(input)
return(result)
}, error = function(e) {
warning("計算對數時出錯: ", e$message)
return(NA)
})
}
# 5. 使用管道提高可讀性
library(dplyr)
# 不好的寫法
result <- summarize(
filter(
group_by(mtcars, cyl),
mpg > 20
),
avg_hp = mean(hp)
)
# 好的寫法(使用管道)
result <- mtcars %>%
group_by(cyl) %>%
filter(mpg > 20) %>%
summarize(avg_hp = mean(hp))
5.2 模塊化編程
# 創建工具函數模塊
# utils.R - 工具函數
#' 數據標準化函數
#'
#' @param x 數值向量
#' @param method 標準化方法:"zscore", "minmax", "robust"
#' @return 標準化後的向量
scale_data <- function(x, method = "zscore") {
if (method == "zscore") {
(x - mean(x, na.rm = TRUE)) / sd(x, na.rm = TRUE)
} else if (method == "minmax") {
(x - min(x, na.rm = TRUE)) / (max(x, na.rm = TRUE) - min(x, na.rm = TRUE))
} else if (method == "robust") {
(x - median(x, na.rm = TRUE)) / IQR(x, na.rm = TRUE)
} else {
stop("未知的標準化方法")
}
}
#' 異常值檢測
#'
#' @param x 數值向量
#' @param method 檢測方法:"iqr", "sd"
#' @param threshold 閾值
#' @return 邏輯向量,TRUE表示異常值
detect_outliers <- function(x, method = "iqr", threshold = 1.5) {
if (method == "iqr") {
q1 <- quantile(x, 0.25, na.rm = TRUE)
q3 <- quantile(x, 0.75, na.rm = TRUE)
iqr <- q3 - q1
x < (q1 - threshold * iqr) | x > (q3 + threshold * iqr)
} else if (method == "sd") {
mu <- mean(x, na.rm = TRUE)
sigma <- sd(x, na.rm = TRUE)
abs(x - mu) > threshold * sigma
} else {
stop("未知的異常值檢測方法")
}
}
# 創建分析模塊
# analysis.R - 分析函數
#' 執行完整數據分析流程
#'
#' @param data 輸入數據框
#' @param target_var 目標變量名
#' @param predictor_vars 預測變量名向量
#' @return 分析結果列表
run_analysis <- function(data, target_var, predictor_vars) {
# 數據清洗
clean_data <- clean_dataset(data)
# 描述性統計
desc_stats <- calculate_descriptive_stats(clean_data)
# 可視化
plots <- create_visualizations(clean_data, target_var, predictor_vars)
# 建模
model <- build_model(clean_data, target_var, predictor_vars)
# 返回結果
list(
data = clean_data,
descriptive_stats = desc_stats,
plots = plots,
model = model
)
}
# 主腳本
# main.R - 主程序
# 加載模塊
source("utils.R")
source("analysis.R")
# 加載數據
data <- read.csv("data.csv")
# 運行分析
results <- run_analysis(
data = data,
target_var = "price",
predictor_vars = c("size", "rooms", "location")
)
# 輸出結果
print(results$descriptive_stats)
print(results$model$summary)
5.3 測試與調試
# 單元測試
# 使用testthat包
install.packages("testthat")
library(testthat)
# 創建測試文件
# test_utils.R
test_that("scale_data函數正常工作", {
# 測試1: zscore標準化
x <- c(1, 2, 3, 4, 5)
scaled <- scale_data(x, "zscore")
expect_equal(mean(scaled), 0, tolerance = 1e-10)
expect_equal(sd(scaled), 1, tolerance = 1e-10)
# 測試2: minmax標準化
scaled_minmax <- scale_data(x, "minmax")
expect_equal(min(scaled_minmax), 0, tolerance = 1e-10)
expect_equal(max(scaled_minmax), 1, tolerance = 1e-10)
# 測試3: 錯誤輸入
expect_error(scale_data(x, "unknown_method"))
})
test_that("detect_outliers函數正常工作", {
x <- c(1, 2, 3, 4, 5, 100) # 100是異常值
outliers_iqr <- detect_outliers(x, "iqr")
outliers_sd <- detect_outliers(x, "sd", threshold = 3)
expect_true(outliers_iqr[6]) # 第6個元素是異常值
expect_false(all(outliers_iqr[1:5])) # 前5個不是異常值
expect_true(outliers_sd[6])
})
# 運行測試
test_dir("tests/")
# 調試技巧
debug_function <- function(x, y) {
# 添加調試點
browser() # 在此處進入調試模式
result <- x + y
# 條件調試
if (result > 100) {
browser() # 只有結果大於100時調試
}
return(result)
}
# 使用debug()函數
debug(calculate_mean)
calculate_mean(c(1, 2, 3)) # 進入調試模式
undebug(calculate_mean) # 退出調試模式
# 跟蹤執行
trace("calculate_mean", quote(cat("計算均值中...\n")))
calculate_mean(c(1, 2, 3))
untrace("calculate_mean")
# 使用recover進行錯誤調試
options(error = recover) # 發生錯誤時進入調試模式
# 日誌記錄
setup_logging <- function(log_file = "analysis.log") {
log_file <<- file(log_file, open = "a")
# 自定義消息函數
log_message <- function(level, message) {
timestamp <- format(Sys.time(), "%Y-%m-%d %H:%M:%S")
cat(sprintf("[%s] [%s] %s\n", timestamp, level, message),
file = log_file, append = TRUE)
}
list(
info = function(msg) log_message("INFO", msg),
warn = function(msg) log_message("WARN", msg),
error = function(msg) log_message("ERROR", msg)
)
}
# 使用日誌
logger <- setup_logging()
logger$info("分析開始")
logger$warn("發現缺失值")
logger$error("計算失敗")
close(log_file)
🏭 第六部分:實戰案例
案例1:優化數據分析流水線
# 初始版本(需要優化)
process_data_slow <- function(file_path) {
# 讀取數據
data <- read.csv(file_path)
# 數據清洗
cleaned_data <- data.frame()
for (i in 1:nrow(data)) {
row <- data[i, ]
# 處理缺失值
for (j in 1:ncol(row)) {
if (is.na(row[j])) {
row[j] <- mean(data[, j], na.rm = TRUE)
}
}
cleaned_data <- rbind(cleaned_data, row)
}
# 計算統計量
stats <- list()
for (col in names(cleaned_data)) {
if (is.numeric(cleaned_data[[col]])) {
col_data <- cleaned_data[[col]]
stats[[col]] <- list(
mean = mean(col_data),
sd = sd(col_data),
min = min(col_data),
max = max(col_data)
)
}
}
return(list(data = cleaned_data, stats = stats))
}
# 優化版本
process_data_fast <- function(file_path) {
# 使用data.table快速讀取
library(data.table)
# 1. 高效讀取
data <- fread(file_path)
# 2. 向量化處理缺失值
impute_missing <- function(x) {
if (is.numeric(x)) {
x[is.na(x)] <- mean(x, na.rm = TRUE)
} else {
# 對於分類變量,用眾數填補
if (any(is.na(x))) {
freq_table <- table(x)
mode_value <- names(freq_table)[which.max(freq_table)]
x[is.na(x)] <- mode_value
}
}
return(x)
}
cleaned_data <- lapply(data, impute_missing)
cleaned_data <- as.data.table(cleaned_data)
# 3. 使用矩陣運算計算統計量
numeric_cols <- sapply(cleaned_data, is.numeric)
numeric_data <- cleaned_data[, ..numeric_cols]
if (ncol(numeric_data) > 0) {
# 使用colMeans等向量化函數
stats <- list(
means = colMeans(as.matrix(numeric_data), na.rm = TRUE),
sds = apply(numeric_data, 2, sd, na.rm = TRUE),
mins = apply(numeric_data, 2, min, na.rm = TRUE),
maxs = apply(numeric_data, 2, max, na.rm = TRUE)
)
# 轉換為數據框格式
stats_df <- data.frame(
variable = names(numeric_data),
mean = stats$means,
sd = stats$sds,
min = stats$mins,
max = stats$maxs,
row.names = NULL
)
} else {
stats_df <- NULL
}
# 4. 使用並行處理加速(如果數據很大)
if (nrow(cleaned_data) > 100000) {
library(parallel)
n_cores <- detectCores() - 1
# 並行計算相關性
parallel_stats <- mclapply(
numeric_data,
function(x) {
list(
mean = mean(x, na.rm = TRUE),
sd = sd(x, na.rm = TRUE)
)
},
mc.cores = n_cores
)
}
return(list(
data = cleaned_data,
stats = stats_df,
n_rows = nrow(cleaned_data),
n_cols = ncol(cleaned_data),
processed_time = Sys.time()
))
}
# 性能比較
library(microbenchmark)
# 創建測試數據
create_test_data <- function(n_rows, n_cols) {
data <- matrix(rnorm(n_rows * n_cols), nrow = n_rows)
# 添加一些缺失值
data[sample(1:length(data), length(data) * 0.1)] <- NA
colnames(data) <- paste0("var", 1:n_cols)
write.csv(data, "test_data.csv", row.names = FALSE)
}
# 創建中等大小數據集
create_test_data(10000, 50)
# 比較性能(注意:慢版本可能需要很長時間)
bench_results <- microbenchmark(
快速版本 = process_data_fast("test_data.csv"),
times = 10
)
print(bench_results)
# 清理測試文件
file.remove("test_data.csv")
案例2:創建可重用的分析框架
# analysis_framework.R
# 一個可重用的數據分析框架
#' 數據分析框架主類
AnalysisFramework <- R6::R6Class(
"AnalysisFramework",
public = list(
# 字段
data = NULL,
config = NULL,
results = NULL,
logs = list(),
# 初始化方法
initialize = function(data = NULL, config = list()) {
self$data <- data
self$config <- private$default_config()
self$update_config(config)
private$log("INFO", "分析框架初始化完成")
},
# 更新配置
update_config = function(new_config) {
for (name in names(new_config)) {
self$config[[name]] <- new_config[[name]]
}
invisible(self)
},
# 加載數據
load_data = function(file_path, format = "auto") {
private$log("INFO", paste("加載數據:", file_path))
if (format == "csv" || (format == "auto" && grepl("\\.csv$", file_path))) {
self$data <- private$read_csv(file_path)
} else if (format == "excel" || (format == "auto" && grepl("\\.xlsx$", file_path))) {
self$data <- private$read_excel(file_path)
} else {
stop("不支持的文件格式")
}
private$log("INFO", paste("數據加載完成,維度:",
n