从常见数据分析方法到可视化:一份完整的 R 语言实战代码手册

发布时间:2026/11/5 3:58:11
从常见数据分析方法到可视化:一份完整的 R 语言实战代码手册

从常见数据分析方法到可视化:一份完整的 R 语言实战代码手册

  • 一、数据准备与描述性统计
    • 可视化:直方图 + 密度曲线
  • 二、分组对比:箱线图与统计检验
    • 可视化:箱线图叠加抖动点
  • 三、相关分析与线性回归
    • 可视化:散点图 + 回归线 + 置信带
  • 四、二分类建模:Logistic 回归与 ROC 曲线
    • 预测与 ROC 曲线

在上一篇文章中,我们讨论了 R 语言数据整理的基本操作。这次我们把视角往前推一步:拿到一份数据后,如何根据分析目的选择合适的方法,并用 ggplot2 把结果讲清楚。

一、数据准备与描述性统计

任何分析的第一步都是了解数据的分布形态和集中趋势。我们使用 tidyverse 生态完成数据导入、缺失值概览和描述统计。

# 加载核心包library(tidyverse)library(readr)library(magrittr)library(dplyr)library(ggplot2)# 模拟一份健康调查数据(实际使用时替换为 read_csv("your_data.csv"))set.seed(42)df<-tibble(id=1:200,age=rnorm(200,mean=45,sd=15),bmi=rnorm(200,mean=24,sd=4),gender=sample(c("Male","Female"),200,replace=TRUE),education=sample(c("Low","Medium","High"),200,replace=TRUE),outcome=rbinom(200,1,plogis(-2+0.03*age+0.1*bmi))# 二分类结局)%>%mutate(age=ifelse(age<18,18,age))# 简单清理

描述统计不用一上来就 summary() 了事。对于连续变量,我们关心均值、标准差、中位数和四分位距;对于分类变量,频数分布才是重点。

# 连续变量:均值与标准差df%>%summarise(mean_age=mean(age,na.rm=TRUE),sd_age=sd(age,na.rm=TRUE),median_bmi=median(bmi,na.rm=TRUE),iqr_bmi=IQR(bmi,na.rm=TRUE))# 分类变量:频数与比例df%>%count(education)%>%mutate(prop=n/sum(n))

结果:

# A tibble: 1 x 4mean_age sd_age median_bmi iqr_bmi<dbl><dbl><dbl><dbl>144.914.023.95.36# A tibble: 3 x 3education n prop<chr><int><dbl>1High620.312Low640.323Medium740.37

可视化:直方图 + 密度曲线

直方图用于看分布形态,叠加密度曲线可以辅助判断偏态。binwidth 的选择比默认值更能反映数据结构。

ggplot(df,aes(x=age))+geom_histogram(aes(y=after_stat(density)),binwidth=5,fill="#2E86AB",color="white",alpha=0.8)+geom_density(linewidth=1,color="#A23B72")+labs(title="年龄分布",x="年龄",y="密度")+theme_minimal(base_size=14)


图上分布接近对称、无明显双峰或极端偏态,因此后续用均值 ± 标准差描述、用 t 检验/线性回归等基于正态假设的方法是合理的。如果这图明显右偏(比如收入分布),就应该改报中位数和四分位距,或考虑对数变换。

二、分组对比:箱线图与统计检验

我们以性别分组,比较 BMI 的分布差异。

# 分组描述统计df%>%group_by(gender)%>%summarise(mean_bmi=mean(bmi,na.rm=TRUE),sd_bmi=sd(bmi,na.rm=TRUE),n=n())# 独立样本 t 检验(若方差不齐可使用 var.equal = FALSE)t.test(bmi~gender,data=df)

结果:

# A tibble: 2 x 4gender mean_bmi sd_bmi n<chr><dbl><dbl><int>1Female23.73.67952Male24.33.88105Welch Two Sample t-test data:bmi by gender t=-1.1558,df=197.59,p-value=0.2492alternative hypothesis:true differenceinmeans between group Female and group Male is not equal to095percent confidence interval:-1.67178550.4362989sample estimates:meaningroup Female meaningroup Male23.7208224.33856

可视化:箱线图叠加抖动点

ggplot(df,aes(x=gender,y=bmi,fill=gender))+geom_boxplot(width=0.4,alpha=0.6,outlier.shape=NA)+geom_jitter(width=0.1,alpha=0.4,size=1.8)+labs(title="BMI 的性别差异",x="性别",y="BMI")+theme_bw(base_size=14)+theme(legend.position="none")


如果分组超过两组(比如教育水平三组),把 t.test 换成 aov() 做方差分析,箱线图的 x 映射到分类变量即可。若数据严重偏态或方差不齐,可改用 Kruskal-Wallis 检验(kruskal.test),箱线图依然适用,因为它展示的是中位数和四分位距,对分布假设不敏感。

三、相关分析与线性回归

当两个连续变量之间存在关联时,散点图加拟合线是最直接的呈现方式。相关系数给出方向与强度,回归模型给出可解释的系数。

# 相关系数(Pearson)cor.test(df$age,df$bmi,use="complete.obs")# 简单线性回归model_lm<-lm(bmi~age,data=df)summary(model_lm)

结果:

Call:lm(formula=bmi~age,data=df)Residuals:Min1Q Median3Q Max-10.5287-2.5595-0.30342.47889.7228Coefficients:Estimate Std. Error t value Pr(>|t|)(Intercept)25.061070.8999727.847<2e-16***age-0.022650.01916-1.1820.238---Signif. codes:0‘***’0.001‘**’0.01‘*’0.05‘.’0.1‘ ’1Residual standard error:3.785on198degrees of freedom Multiple R-squared:0.007011,Adjusted R-squared:0.001996F-statistic:1.398on1and198DF,p-value:0.2385

summary() 输出中重点看三处:Estimate(效应大小)、Pr(>|t|)(显著性)、Multiple R-squared(解释比例)。如果 R² 很低(比如 0.05),说明年龄只能解释 BMI 变异的 5%,即使 p 值显著,实际意义也有限。

可视化:散点图 + 回归线 + 置信带

ggplot(df,aes(x=age,y=bmi))+geom_point(alpha=0.5,color="#2E86AB",size=2)+geom_smooth(method="lm",se=TRUE,color="#A23B72",fill="grey70")+labs(title="年龄与 BMI 的线性关系",subtitle=paste("R² =",round(summary(model_lm)$r.squared,3)),x="年龄",y="BMI")+theme_minimal(base_size=14)

四、二分类建模:Logistic 回归与 ROC 曲线

当结局是“是/否”类型的二分类变量时,线性回归不再适用,应使用 Logistic 回归。

# 拟合 Logistic 回归model_glm<-glm(outcome~age+bmi+gender,data=df,family=binomial)summary(model_glm)# 将系数转换为优势比(OR)exp(coef(model_glm))

结果:

Call:glm(formula=outcome~age+bmi+gender,family=binomial,data=df)Deviance Residuals:Min1Q Median3Q Max-2.21930.42520.52890.62580.8543Coefficients:Estimate Std. Error z value Pr(>|z|)(Intercept)-1.428461.57470-0.9070.3643age0.020580.014791.3910.1641bmi0.094230.056931.6550.0979. genderMale0.075700.401230.1890.8504---Signif. codes:0‘***’0.001‘**’0.01‘*’0.05‘.’0.1‘ ’1(Dispersion parameterforbinomial family taken to be1)Null deviance:169.08on199degrees of freedom Residual deviance:164.67on196degrees of freedom AIC:172.67Number of Fisher Scoring iterations:4

exp(coef) 是 Logistic 回归中最实用的输出。比如 age 的 OR 为 1.03,表示年龄每增加一岁,结局发生的优势增加约 3%。注意 OR 不等于风险比。

预测与 ROC 曲线

library(pROC)# 预测概率df$pred_prob<-predict(model_glm,type="response")# 绘制 ROC 曲线并计算 AUCroc_obj<-roc(df$outcome,df$pred_prob)auc_value<-auc(roc_obj)plot(roc_obj,col="#2E86AB",lwd=2,main=paste("ROC 曲线 (AUC =",round(auc_value,3),")"))abline(a=0,b=1,lty=2,col="grey60")


AUC 在 0.5 到 1 之间,0.7 以上通常认为模型有一定区分能力。如果 AUC 接近 0.5,说明当前预测变量几乎没有区分力,需要重新审视特征选择

混淆矩阵用 caret 包可以快速得到准确率、敏感度、特异度:

library(caret)df$pred_class<-factor(ifelse(df$pred_prob>0.5,1,0),levels=c(0,1))confusionMatrix(df$pred_class,factor(df$outcome,levels=c(0,1)))
Confusion Matrix and Statistics Reference Prediction01000130170Accuracy:0.8595% CI:(0.7928,0.8965)No Information Rate:0.85P-Value[Acc>NIR]:0.5485Kappa:0Mcnemar's Test P-Value:1.192e-07Sensitivity:0.00Specificity:1.00Pos Pred Value:NaNNeg Pred Value:0.85Prevalence:0.15Detection Rate:0.00Detection Prevalence:0.00Balanced Accuracy:0.50'Positive'Class:0

准确率 85%,但 NIR 也是 0.85、Kappa = 0。这是一个经典的“无效模型”案例,提醒我们:面对倾斜的类分布,盲目相信 Accuracy 会导致严重的误判。