
R语言纵向数据分析实战从宽数据到长数据的3步转换与4种可视化纵向数据在医学研究、心理学实验和社会学调查中无处不在。想象一下你手头有一份记录了500名患者在不同时间点的抑郁评分数据或者是一组学生在连续三个学期的考试成绩。这类数据最迷人的地方在于它们能揭示个体随时间变化的轨迹——就像给每个研究对象拍摄了一段成长纪录片。但现实往往骨感。当你兴奋地打开数据集看到的可能是这样的结构患者ID性别基线评分3个月评分6个月评分001男231815002女282219这种宽格式数据就像把多部电影胶片横向拼接在一起虽然节省空间却让时间维度的分析变得棘手。本文将带你用R语言的tidyr和ggplot2工具包完成从数据整理到可视化的完整工作流。1. 数据转换三步骤重塑时间维度1.1 理解长宽数据差异宽格式数据的特点是每个时间点的测量值占据单独一列时间信息隐藏在列名中如基线评分、3个月评分适合人类阅读但不利于分析长格式数据的优势在于每个时间点的测量值独占一行显式的时间变量列适合统计建模和可视化1.2 核心转换代码使用tidyr::pivot_longer()完成转换library(tidyverse) # 示例牙科生长数据集 dental_wide - data.frame( id 1:10, sex rep(c(男, 女), each5), y8 rnorm(10, 25, 2), y10 rnorm(10, 28, 2), y12 rnorm(10, 30, 2) ) dental_long - dental_wide %% pivot_longer( cols starts_with(y), # 选择需要转换的列 names_to age, # 新列名存储原列名 values_to distance # 新列名存储原数值 ) %% mutate( age parse_number(age), # 从y8提取数字8 age_factor factor(paste(age, 岁)) # 创建因子型年龄 )转换后的数据结构idsexagedistanceage_factor1男824.58 岁1男1027.810 岁1.3 处理常见问题问题1列名模式不一致当时间点列名没有统一前缀时# 不规则列名处理示例 df_long - df_wide %% pivot_longer( cols c(基线评分, 三月随访, 六月随访), names_to 时间点, values_to 评分 )问题2需要保留元数据添加变量标签保持数据可读性library(labelled) dental_long - dental_long %% set_variable_labels( age 测量时年龄(岁), distance 牙齿生长距离(mm) )2. 探索性分析四类可视化揭示时间模式2.1 意大利面条图(Spaghetti Plot)展示个体轨迹的经典方法ggplot(dental_long, aes(x age, y distance, group id)) geom_line(alpha 0.5) labs(x 年龄(岁), y 生长距离(mm)) theme_minimal()当样本量较大时50建议添加趋势线ggplot(dental_long, aes(x age, y distance)) geom_line(aes(group id), alpha 0.2) geom_smooth(method lm, se FALSE, color red, size 1.5) facet_wrap(~ sex) # 按性别分面2.2 分组箱线图比较不同时间点的分布差异ggplot(dental_long, aes(x age_factor, y distance, fill sex)) geom_boxplot() geom_jitter(width 0.1, alpha 0.3) # 添加数据点 scale_fill_manual(values c(#E69F00, #56B4E9)) labs(x , y 牙齿生长距离(mm), fill 性别)关键参数说明width: 控制箱线图宽度alpha: 调整透明度避免重叠position_dodge: 分组并列显示2.3 相关性矩阵检验不同时间点测量值的关联强度library(GGally) # 使用宽格式数据 ggpairs(dental_wide, columns 3:5, # 选择需要分析的列 mapping aes(color sex), lower list(continuous wrap(smooth, alpha 0.3)))输出解读对角线变量分布直方图下三角散点图与平滑曲线上三角相关系数及显著性2.4 时间轨迹图展示群体平均变化趋势dental_long %% group_by(sex, age) %% summarise( mean mean(distance), se sd(distance)/sqrt(n()) ) %% ggplot(aes(x age, y mean, color sex)) geom_line() geom_point(size 3) geom_errorbar(aes(ymin mean - 1.96*se, ymax mean 1.96*se), width 0.2) scale_x_continuous(breaks unique(dental_long$age)) labs(x 年龄(岁), y 平均生长距离(mm), color 性别)3. 进阶技巧提升分析深度的实用方法3.1 处理缺失数据纵向数据常见缺失模式及处理缺失类型R代码示例适用场景列表删除na.omit()缺失较少时线性插补approx()连续变量多重插补mice::mice()复杂缺失# 线性插补示例 dental_complete - dental_long %% group_by(id) %% mutate( distance approx(age, distance, xout age)$y ) %% ungroup()3.2 添加参考线增强可视化解释性ggplot(dental_long, aes(x age, y distance)) geom_hline(yintercept 25, linetype dashed, color gray) geom_vline(xintercept 10, linetype dotted, color blue) # ...其他图层3.3 交互式可视化用plotly创建动态图表library(plotly) p - ggplot(dental_long, aes(x age, y distance, color sex)) geom_line(aes(group id), alpha 0.3) geom_smooth(se FALSE) ggplotly(p)4. 案例实战牙科生长数据分析全流程4.1 数据准备加载内置数据集并转换data(Orthodont, package nlme) ortho_long - Orthodont %% mutate( Subject as.character(Subject), age age - 8 # 以8岁为基准 )4.2 可视化组合创建多面板图形library(patchwork) p1 - ggplot(ortho_long, aes(x age, y distance, group Subject)) geom_line(alpha 0.5) p2 - ggplot(ortho_long, aes(x age, y distance, fill Sex)) geom_boxplot() p3 - ggplot(ortho_long, aes(x distance, fill Sex)) geom_density(alpha 0.5) facet_wrap(~ age) (p1 p2) / p3 # 使用patchwork组合图形4.3 统计摘要生成专业统计表格ortho_long %% group_by(Sex, age) %% summarise( Mean mean(distance), SD sd(distance), N n(), .groups drop ) %% knitr::kable(digits 2)输出示例SexageMeanSDN男022.881.5216男123.811.6016在RMarkdown环境中添加kableExtra::kable_styling()可以让表格更美观。