# 1. 读取数据
# ------------------------------------------------------------ # 1. 设置文件路径 # ------------------------------------------------------------ file_path <- "G:/学生成绩/学生成绩NA.csv" output_dir <- "G:/学生成绩/数据清洗结果" if (!dir.exists(output_dir)) { dir.create(output_dir, recursive = TRUE) } # ------------------------------------------------------------ # 2. 检查文件是否存在 # ------------------------------------------------------------ if (file.exists(file_path)) { cat("文件存在,可以读取。\n") } else { stop("文件不存在,请检查路径是否正确。") } # ------------------------------------------------------------ # 3. 读取 CSV 文件 # ------------------------------------------------------------ student_score <- read.csv( file = file_path, header = TRUE, # TRUE 表示第一行是列名 fileEncoding = "GB18030", # 防止中文乱码 check.names = FALSE, # 不自动修改中文列名 na.strings = c("", "NA", "NaN", "N/A", "缺失", "NULL") )#2. 基本查看
cat("\n========== 数据前6行 head() ==========\n") print(head(student_score)) cat("\n========== 数据结构 str() ==========\n") str(student_score) cat("\n========== 描述性统计 summary() ==========\n") print(summary(student_score)) cat("\n========== 数据维度 ==========\n") cat("行数:", nrow(student_score), "\n") cat("列数:", ncol(student_score), "\n")# 3. 数据类型初步处理
# 一般情况下,“姓名”“学生ID”为字符型,其余成绩列为数值型。
# 如果成绩列因为缺失或特殊字符被读成字符型,这里尝试转换为数值型。
id_cols <- c("姓名", "学生ID") score_cols <- setdiff(names(student_score), id_cols) for (col in score_cols) { student_score[[col]] <- suppressWarnings(as.numeric(student_score[[col]])) } cat("\n========== 类型转换后的数据结构 ==========\n") str(student_score)# 4. 缺失值识别 is.na()
cat("\n========== 缺失值逻辑矩阵前6行 is.na() ==========\n") print(head(is.na(student_score)))# 5. 缺失值统计
missing_count <- colSums(is.na(student_score)) missing_rate <- round( missing_count / nrow(student_score), 4 ) missing_summary <- data.frame( 变量名 = names(student_score), 缺失值个数 = as.integer(missing_count), 缺失比例 = missing_rate, check.names = FALSE ) cat("\n========== 各变量缺失值统计 ==========\n") print(missing_summary) missing_output_file <- file.path(output_dir, "缺失值统计.csv") write.csv( missing_summary, file = missing_output_file, row.names = FALSE, fileEncoding = "GB18030" ) cat("\n缺失值统计结果已保存到:\n") cat(missing_output_file, "\n")# 6. 删除含缺失值的行
student_score_delete_na <- na.omit(student_score) cat("\n========== 删除缺失值后的数据维度 ==========\n") cat("原始数据行数:", nrow(student_score), "\n") cat("删除缺失值后行数:", nrow(student_score_delete_na), "\n") delete_na_file <- file.path(output_dir, "删除缺失值后的学生成绩.csv") write.csv( student_score_delete_na, file = delete_na_file, row.names = FALSE, fileEncoding = "GB18030" ) cat("\n删除缺失值后的数据已保存到:\n") cat(delete_na_file, "\n")# 7. 均值填充缺失值
student_score_mean_fill <- student_score numeric_cols <- names(student_score_mean_fill)[sapply(student_score_mean_fill, is.numeric)] for (col in numeric_cols) { mean_value <- mean(student_score_mean_fill[[col]], na.rm = TRUE) student_score_mean_fill[[col]][is.na(student_score_mean_fill[[col]])] <- mean_value } cat("\n========== 均值填充后的数据前6行 ==========\n") print(head(student_score_mean_fill)) mean_fill_file <- file.path(output_dir, "均值填充后的学生成绩.csv") write.csv( student_score_mean_fill, file = mean_fill_file, row.names = FALSE, fileEncoding = "GB18030" ) cat("\n均值填充后的数据已保存到:\n") cat(mean_fill_file, "\n")# 8. 中位数填充缺失值
student_score_median_fill <- student_score numeric_cols <- names(student_score_median_fill)[sapply(student_score_median_fill, is.numeric)] for (col in numeric_cols) { median_value <- median(student_score_median_fill[[col]], na.rm = TRUE) student_score_median_fill[[col]][is.na(student_score_median_fill[[col]])] <- median_value } cat("\n========== 中位数填充后的数据前6行 ==========\n") print(head(student_score_median_fill)) median_fill_file <- file.path(output_dir, "中位数填充后的学生成绩.csv") write.csv( student_score_median_fill, file = median_fill_file, row.names = FALSE, fileEncoding = "GB18030" ) cat("\n中位数填充后的数据已保存到:\n") cat(median_fill_file, "\n")# 9. 箱线图法识别异常值
# 箱线图法基于 IQR: # 下界 = Q1 - 1.5 * IQR # 上界 = Q3 + 1.5 * IQR # 小于下界或大于上界的值视为异常值。 outlier_result <- data.frame( 变量名 = character(), Q1 = numeric(), Q3 = numeric(), IQR = numeric(), 下界 = numeric(), 上界 = numeric(), 异常值个数 = integer(), 异常值 = character(), check.names = FALSE ) for (col in numeric_cols) { x <- student_score[[col]] x_no_na <- x[!is.na(x)] Q1 <- quantile(x_no_na, 0.25) Q3 <- quantile(x_no_na, 0.75) IQR_value <- IQR(x_no_na) lower_bound <- Q1 - 1.5 * IQR_value upper_bound <- Q3 + 1.5 * IQR_value outliers <- x_no_na[x_no_na < lower_bound | x_no_na > upper_bound] outlier_result <- rbind( outlier_result, data.frame( 变量名 = col, Q1 = as.numeric(Q1), Q3 = as.numeric(Q3), IQR = as.numeric(IQR_value), 下界 = as.numeric(lower_bound), 上界 = as.numeric(upper_bound), 异常值个数 = length(outliers), 异常值 = paste(outliers, collapse = "; "), check.names = FALSE ) ) } cat("\n========== 箱线图法异常值识别结果 ==========\n") print(outlier_result) outlier_file <- file.path(output_dir, "箱线图异常值识别结果.csv") write.csv( outlier_result, file = outlier_file, row.names = FALSE, fileEncoding = "GB18030" ) cat("\n异常值识别结果已保存到:\n") cat(outlier_file, "\n")# 10. 绘制箱线图
boxplot_dir <- file.path(output_dir, "箱线图") if (!dir.exists(boxplot_dir)) { dir.create(boxplot_dir, recursive = TRUE) } for (col in numeric_cols) { png_file <- file.path(boxplot_dir, paste0(col, "_箱线图.png")) png( filename = png_file, width = 800, height = 600 ) boxplot( student_score[[col]], main = paste0(col, " 箱线图"), ylab = col, col = "lightblue", border = "gray30" ) dev.off() } cat("\n所有数值变量的箱线图已保存到:\n") cat(boxplot_dir, "\n")# 11. 生成异常值明细表
outlier_detail <- data.frame() for (col in numeric_cols) { x <- student_score[[col]] x_no_na <- x[!is.na(x)] Q1 <- quantile(x_no_na, 0.25) Q3 <- quantile(x_no_na, 0.75) IQR_value <- IQR(x_no_na) lower_bound <- Q1 - 1.5 * IQR_value upper_bound <- Q3 + 1.5 * IQR_value outlier_index <- which(!is.na(x) & (x < lower_bound | x > upper_bound)) if (length(outlier_index) > 0) { temp <- data.frame( 行号 = outlier_index, 姓名 = student_score$姓名[outlier_index], 学生ID = student_score$学生ID[outlier_index], 变量名 = col, 异常值 = student_score[[col]][outlier_index], 下界 = as.numeric(lower_bound), 上界 = as.numeric(upper_bound), check.names = FALSE ) outlier_detail <- rbind(outlier_detail, temp) } } cat("\n========== 异常值明细 ==========\n") print(outlier_detail) outlier_detail_file <- file.path(output_dir, "异常值明细表.csv") write.csv( outlier_detail, file = outlier_detail_file, row.names = FALSE, fileEncoding = "GB18030" ) cat("\n异常值明细表已保存到:\n") cat(outlier_detail_file, "\n")# 12. 最终提示
cat("\n========== 数据清洗与异常值识别完成 ==========\n") cat("结果文件夹:\n") cat(output_dir, "\n")