类不平衡数据(Class-imbalanced Data)指分类问题中不同类别的样本量差异显著,通常表现为少数类(正类)样本远少于多数类(负类)样本的现象。例如信用卡欺诈检测中,欺诈交易占比可能不足1%。这种不平衡会导致模型偏向多数类,降低对少数类的识别能力。
方法主要包括数据层面的处理方法和算法层面的调整。数据层面有欠采样(如随机欠采样、Tomek Links)、过采样(如随机过采样、SMOTE)和混合采样。算法层面包括代价敏感学习和集成方法。
1. 数据层面调整
(1)过采样(Oversampling):增加少数类样本
随机复制:简单复制少数类样本,易导致过拟合。
SMOTE(合成少数类过采样):通过插值生成新样本,避免单纯复制。
ADASYN:根据样本分布自适应生成数据,更关注分类边界。
(二)欠采样(Undersampling):减少多数类样本
随机删除:随机移除多数类样本,可能丢失重要信息。
Tomek Links:移除与少数类样本接近的多数类样本,优化分类边界。
(三)混合方法(Hybrid):结合过采样与欠采样(如ROSE包)。
2. 算法层面调整
代价敏感学习:为少数类分错赋予更高惩罚(如class_weight参数)。
集成方法:如EasyEnsemble(多数类分块+少数类训练多个分类器)。
单分类模型:将少数类视为异常点,使用One-Class SVM检测。
3. 评估指标优化
避免使用准确率(Accuracy),改用:
召回率(Recall)、精确率(Precision)、F1-Score、AUC-ROC曲线、G-Mean(几何均值)
我们今天使用R语言,对类不平衡数据SMOTE过采样、混合采样处理方法及可视化操作进行一下简单演示。
#环境整理
rm(list=ls()) #移除所有变量数据
install.packages("") #安装包
if (!require("")) install.packages("")
library() #加载包
plot() #快速绘制简单图
dev.off()
library(showtext)
# 添加中文字体,例如使用系统字体
font_add("SimSun", "simsun.ttc") # 假设字体文件存在
showtext_auto()
# 加载必要包
library(tidyverse)
library(themis) # 提供SMOTE等采样方法
library(caret) # 模型训练与评估
library(ROCR) # ROC曲线绘制
# 生成模拟数据集(10000样本,欺诈类占5%)
set.seed(42)
n <- 10000
n_fraud <- round(n * 0.05)
data <- tibble(
transaction_id = 1:n,
feature1 = rnorm(n, mean = 10),
feature2 = rnorm(n, sd = 2),
amount = runif(n, 10, 1000),
class = factor(c(rep("Normal", n - n_fraud), rep("Fraud", n_fraud)))
)
data
# 查看类别分布
table(data$class) # Normal: 9500, Fraud: 500
# 划分训练集与测试集(7:3)
train_idx <- createDataPartition(data$class, p = 0.7, list = FALSE)
train_data <- data[train_idx, ]
test_data <- data[-train_idx, ]
# 方法1:SMOTE过采样(使用themis包)
recipe_smote <- recipe(class ~ ., data = train_data) %>%
step_smote(class, over_ratio = 0.5) # 调整比例使少数类达50%
train_smote <- prep(recipe_smote) %>% bake(new_data = NULL)
train_smote
# 方法2:混合采样(ROSE包)
library(ROSE)
train_rose <- ovun.sample(
class ~ ., data = train_data,
method = "both", # 混合采样
p = 0.5, # 目标少数类比例
seed = 42
)$data
# 训练逻辑回归模型(对比不同采样效果)
ctrl <- trainControl(method = "cv", number = 5, classProbs = TRUE)
# 原始数据模型
model_raw <- train(
class ~ feature1 + feature2 + amount,
data = train_data,
method = "glm",
trControl = ctrl
)
# SMOTE数据模型
model_smote <- train(
class ~ .,
data = train_smote,
method = "glm",
trControl = ctrl
)
model_smote
# 评估模型性能
eval_model <- function(model, test_data) {
pred <- predict(model, test_data, type = "prob")[, "Fraud"]
roc <- prediction(pred, test_data$class)
auc <- performance(roc, "auc")@y.values[[1]]
pred_class <- ifelse(pred > 0.5, "Fraud", "Normal")
cm <- confusionMatrix(factor(pred_class), test_data$class, positive = "Fraud")
list(
AUC = auc,
Recall = cm$byClass["Recall"],
Precision = cm$byClass["Precision"],
F1 = cm$byClass["F1"]
)
}
eval_model
# 输出结果对比
results <- list(
Raw = eval_model(model_raw, test_data),
SMOTE = eval_model(model_smote, test_data)
)
print(results)
#一、数据分布可视化
#1. 类别比例饼图
library(ggplot2)
# 生成示例数据(替换为您的数据框和类别列名)
set.seed(42)
data <- data.frame(
class = factor(c(rep("Normal", 9500), rep("Fraud", 500)))
)
# 绘制饼图
ggplot(data, aes(x = "", fill = class)) +
geom_bar(width = 1, color = "white") +
coord_polar("y") +
scale_fill_brewer(palette = "Set2") +
labs(title = "类别分布比例", fill = "类别") +
theme_void()
#2. 特征分布箱线图(按类别分组)
# 生成带特征的示例数据(替换为您的数据)
data$feature <- c(rnorm(9500, mean = 10), rnorm(500, mean = 15))
# 绘制箱线图
ggplot(data, aes(x = class, y = feature, fill = class)) +
geom_boxplot(alpha = 0.7) +
scale_fill_manual(values = c("#1f77b4", "#ff7f0e")) +
labs(title = "欺诈 vs 正常交易的特征分布", x = "类别", y = "特征值") +
theme_minimal()
#二、采样效果可视化
#1. SMOTE 采样前后对比(二维散点图)
library(themis)
library(ggpubr)
# 生成示例数据
set.seed(42)
train_data <- data.frame(
feat1 = c(rnorm(90, 0), rnorm(10, 2)),
feat2 = c(rnorm(90, 0), rnorm(10, 3)),
class = factor(c(rep(0, 90), rep(1, 10)))
)
# SMOTE 过采样
smote_recipe <- recipe(class ~ ., data = train_data) %>%
step_smote(class, over_ratio = 0.6)
train_smote <- prep(smote_recipe) %>% bake(new_data = NULL)
# 对比原始数据与SMOTE后数据
p1 <- ggplot(train_data, aes(feat1, feat2, color = class)) +
geom_point(alpha = 0.6) +
labs(title = "原始数据分布")
p2 <- ggplot(train_smote, aes(feat1, feat2, color = class)) +
geom_point(alpha = 0.6) +
labs(title = "SMOTE后数据分布")
ggarrange(p1, p2, ncol = 2, common.legend = TRUE)
#2. 特征分布密度对比
ggplot() +
geom_density(data = train_data, aes(x = feat1, fill = "原始"), alpha = 0.4) +
geom_density(data = train_smote, aes(x = feat1, fill = "SMOTE"), alpha = 0.4) +
scale_fill_manual(values = c("原始" = "blue", "SMOTE" = "red")) +
labs(title = "SMOTE前后特征分布对比", x = "特征值", fill = "数据集") +
facet_wrap(~class) # 按类别分面显示
#3. 特征重要性排序图
# 获取特征重要性(以随机森林为例)
library(randomForest)
rf_model <- randomForest(class ~ ., data = train_smote, importance = TRUE)
imp_df <- data.frame(Feature = rownames(importance(rf_model)), Importance = importance(rf_model)[, "MeanDecreaseAccuracy"])
# 绘制条形图
ggplot(imp_df, aes(x = reorder(Feature, Importance), y = Importance)) +
geom_bar(stat = "identity", fill = "#2ca25f") +
coord_flip() +
labs(title = "特征重要性排序", x = "特征", y = "重要性得分")
!!!可加我粉丝群!!!
有临床流行病学数据分析如(t检验、方差分析、χ2检验、logistic回归)、(重复测量方差分析与配对T检验、ROC曲线)、(非参数检验、生存分析、样本含量估计)、(筛检试验:灵敏度、特异度、约登指数等计算)、(绘制柱状图、散点图、小提琴图、列线图等)、机器学习、深度学习、生存分析等需求的同仁们,加入【临床】粉丝群。
疾控,公卫岗位的同仁,可以加一下【公卫】粉丝群,分享生态学研究、空间分析、时间序列、监测数据分析、时空面板技巧等工作科研自动化内容。
有实验室数据分析需求的同仁们,可以加入【生信】粉丝群,交流NCBI(基因序列)、UniProt(蛋白质)、KEGG(通路)、GEO(公共数据集)等公共数据库、基因组学转录组学蛋白组学代谢组学表型组学等数据分析和可视化内容。
一起学习,共同进步!!!
对五运六气,阴阳五行,八卦周易,奇门遁甲对健康生死影响研究课题感兴趣的同仁,可以加入【易经研究院】粉丝群一起学习,共同进步!
医学统计数据分析分享交流SPSS、R语言、Python、ArcGis、Geoda、GraphPad、数据分析图表制作等心得。承接数据分析,论文返修,医学统计,机器学习,生存分析,空间分析,问卷分析业务。若有投稿和数据分析代做需求,可以直接联系我,谢谢!
往期推荐:样本含量估计(样本量计算与功效分析)
往期推荐:SPSS、R语言、Python等临床数据分析专题
往期推荐:科研图表绘制专题
往期推荐:重复测量数据分析专题
往期推荐:生信分析、基因测序数据、实验室数据专题
往期推荐:生存分析及机器学习
往期推荐:二分类因变量机器学习及相关评价可视化
技术分享|如何综合评价临床预测模型?手把手教你学会9种机器学习模型×5种模型评价曲线的综合评价方法(附Python批处理代码)
一文读懂十模型lasso、贝叶斯、KNN、Logistic、决策树、随机森林、SVM、神经网络、XGBoost、lightGBM
一文读懂十模型lasso、贝叶斯、KNN、Logistic、决策树、随机森林、SVM、神经网络、XGBoost、lightGBM
【Python机器学习】十分钟读懂Logistic、决策树、随机森林、SVM、神经网络、XGBoost、lightGBM七种模型
【R语言机器学习】十分钟读懂Logistic回归、决策树、随机森林、SVM、神经网络、XGBoost、lightGBM七种模型
往期推荐:时间序列分析
往期推荐:地统计分析-GIS、地图、相关、聚类、回归
往期推荐:科研自动化探究
往期推荐:趣味阅读

