在数据分析中,数据的存储方式直接影响分析过程的效率和准确性。常见的数据存储形式有宽型数据(wide format)和长型数据(long format)。宽型数据适合人类查看和理解,而长型数据则更适合计算机处理和分析。为此,R语言提供了tidyr
包,用于在这两种数据格式之间进行转换。本指南将详细介绍tidyr
包中最常用的两个函数:gather()
和spread()
,并结合实际案例进行讲解。
一、什么是宽型数据和长型数据?
宽型数据(Wide Format Data)
宽格式数据集中,每一行代表一个独特的实体(如一个病人),每一列代表不同的变量或属性。所有的变量都以列的形式展开,数据在水平方向上延展。例如,一个包含病人血压、血糖和胆固醇水平的数据集可能如下所示:
病人ID | 血压 | 血糖 | 胆固醇 |
---|---|---|---|
001 | 120 | 90 | 200 |
002 | 130 | 85 | 180 |
003 | 125 | 88 | 210 |
长型数据 (Long Format Data)
在长格式数据集中,同一个实体可以在多行中出现,每一行包含一个特定的观察值或测量值。通常有两个主要的列,一个表示变量名称,另一个表示变量值。例如,上述数据集的长格式可能如下所示:
病人ID | 变量 | 值 |
---|---|---|
001 | 血压 | 120 |
001 | 血糖 | 90 |
001 | 胆固醇 | 200 |
002 | 血压 | 130 |
002 | 血糖 | 85 |
002 | 胆固醇 | 180 |
二、为什么临床原始数据多是长格式?
1、纵向研究
纵向研究是指在不同时间点对同一组个体进行重复测量的研究设计。长格式数据结构能够更清晰地记录每个时间点的测量值,使得数据管理和分析更加方便。例如,在一项关于糖尿病患者血糖水平的纵向研究中,数据可以这样存储:
ID | 时间点 | 血糖水平 |
---|---|---|
001 | T1 | 6 |
001 | T2 | 6.5 |
001 | T3 | 6.8 |
2、重复测量
在临床试验中,通常会对同一个体在不同条件下进行多次测量。长格式数据结构可以很好地记录这些重复测量,方便数据的处理和分析。例如,一项关于药物效果的研究可能会记录每个患者在不同剂量下的反应:
ID | 剂量 | 反应值 |
---|---|---|
001 | 低剂量 | 5 |
001 | 中剂量 | 7 |
001 | 高剂量 | 9 |
这种格式便于进行统计分析,如方差分析(ANOVA)和混合效应模型。
3、数据扩展性
长格式数据便于扩展新的变量或观察值,而无需重新设计数据结构。例如,在一项长期的临床随访研究中,研究人员可以随时添加新的随访时间点或新的测量指标,而不需要改变现有数据结构:
ID | 时间点 | 血压 | 血糖值 | 胆固醇 |
---|---|---|---|---|
001 | T1 | 120 | 6 | 2 |
001 | T2 | 125 | 5 | 1.8 |
4、数据整合和处理
长格式数据更便于与其他数据源进行整合和处理。例如,在进行多中心临床研究时,不同中心的数据可以方便地合并到一个长格式的数据集中:
三、安装和加载tidyr包
tidyr
是R语言中用于数据清理和重塑的重要工具,其主要目的是帮助用户创建"整洁数据"(tidy data)。在整洁数据中,每一个变量占用一列,每一条观察值占用一行,每个单元格包含一个单一的值。tidyr
提供了丰富的工具来改变数据的形状(即数据的pivoting)和层次结构(即嵌套和解嵌套)。
在开始之前,确保已经安装并加载了tidyr
包。可以使用以下命令进行安装和加载:
install.packages("tidyr")
library(tidyr)
1、创建示例数据
我们首先创建一个简单的宽型数据框datapati
,该数据框包含三个变量(x
、y
、z
)和一个时间变量time
:
library(tibble)
datapati <- tibble(time = as.Date('2025-01-01') + 1:3, x = c(1, 2, 3), y = c(8, 5, 6), z = c(3, 0, 3))
print(datapati)
结果是:
# A tibble: 3 × 4
time x y z
<date> <dbl> <dbl> <dbl>
1 2025-01-02 1 8 3
2 2025-01-03 2 5 0
3 2025-01-04 3 6 3
2、gather()函数
gather()
函数是tidyr
包中的一个重要工具,用于将宽型数据转换为长型数据。
gather(data, key = "key", value = "value", ..., na.rm = FALSE, convert = FALSE, factor_key = FALSE)
data:要转换的数据框。这个参数指定了需要进行宽转长操作的数据集。
key:新数据框中存放原宽数据中需转列为行的原变量名称。这一列将在转换后包含原数据框中的列名。
value:新数据框中存放原宽数据中需转换列的某行具体数值。这一列将在转换后包含原数据框中对应列的数值。
...:需要转换的列。如果不设置,默认是全部转换。通过指定这些列,可以控制哪些列需要进行宽转长转换。
示例代码:
datapati_long <- gather(data = datapati, key = "patients", value = "measurement", -time)
print(datapati_long)
在这个示例中,gather()
函数将x
、y
、z
列转换为长型格式,新的数据框datapati_long
中包含三个变量:time
、patients
和measurement
。
time patients measurement
<date> <chr> <dbl>
1 2025-01-02 x 1
2 2025-01-03 x 2
3 2025-01-04 x 3
4 2025-01-02 y 8
5 2025-01-03 y 5
6 2025-01-04 y 6
7 2025-01-02 z 3
8 2025-01-03 z 0
9 2025-01-04 z 3
3、spread()函数
spread()
函数用于将长型数据转换为宽型数据。
spread(data, key, value, fill = NA, convert = FALSE, drop = TRUE, sep = NULL)
我们先创建一个新的长型数据框datapati2
:
datapati2 <- data.frame(x = c("a", "b", "c"), y = c(3, 4, 5))
print(datapati2)
结果是:
x y
1 a 3
2 b 4
3 c 5
使用spread()
函数将其转换为宽型数据:
datapati_wide <- spread(datapati2, key = x, value = y)
print(datapati_wide)
结果是:
a b c
1 3 4 5
更复杂的示例
考虑一个更复杂的情境,我们有一个包含多个变量的数据框:
library(tibble)
data_complex <- tibble(
id = 1:3,
age = c(23, 45, 31),
income = c(50000, 60000, 55000),
height = c(175, 160, 180),
weight = c(70, 80, 75)
)
print(data_complex)
结果是:
# A tibble: 3 × 5
id age income height weight
<int> <dbl> <dbl> <dbl> <dbl>
1 1 23 50000 175 70
2 2 45 60000 160 80
3 3 31 55000 180 75
将其转换为长型数据:
data_complex_long <- gather(data_complex, key = "variable", value = "value", -id)
print(data_complex_long)
结果是:
# A tibble: 12 × 3
id variable value
<int> <chr> <dbl>
1 1 age 23
2 2 age 45
3 3 age 31
4 1 income 50000
5 2 income 60000
6 3 income 55000
7 1 height 175
8 2 height 160
9 3 height 180
10 1 weight 70
11 2 weight 80
12 3 weight 75
将长型数据转换回宽型数据:
data_complex_wide <- spread(data_complex_long, key = variable, value = value)
print(data_complex_wide)
结果是:
# A tibble: 3 × 5
id age height income weight
<int> <dbl> <dbl> <dbl> <dbl>
1 1 23 175 50000 70
2 2 45 160 60000 80
3 3 31 180 55000 75
4、tidyr中的其他函数
除了 gather()
和 spread()
等函数外,tidyr
包还提供了许多其他有用的函数来处理数据格式转换。
例如,pivot_longer()
函数能够将数据从宽型转换为长型,它其实是 gather()
的升级版,在功能和灵活性上有所增强。还有 pivot_wider()
函数,它负责将数据从长型转换为宽型,是 spread()
的升级版。此外,separate()
函数可以将一个列拆分为多个列,比如原本一个包含日期和时间的列,可以通过它拆分为日期列和时间列。而 unite()
函数的作用则相反,能够将多个列合并为一个列,比如把姓名的姓和名两个列合并为一个完整姓名列。这些函数为数据的整理和转换提供了丰富的工具和便捷的操作方式。
使用pivot_longer()
pivot_longer()
函数可以替代gather()
,提供了更多的灵活性和功能:
datapati_longer <- pivot_longer(datapati, cols = -time, names_to = "patients", values_to = "measurement")
print(datapati_longer)
结果是:
# A tibble: 9 × 3
time patients measurement
<date> <chr> <dbl>
1 2025-01-02 x 1
2 2025-01-02 y 8
3 2025-01-02 z 3
4 2025-01-03 x 2
5 2025-01-03 y 5
6 2025-01-03 z 0
7 2025-01-04 x 3
8 2025-01-04 y 6
9 2025-01-04 z 3
使用pivot_wider()
pivot_wider()
函数可以替代spread()
datapati_wider <- pivot_wider(datapati_longer, names_from = patients, values_from = measurement)
print(datapati_wider)
结果是:
# A tibble: 3 × 4
time x y z
<date> <dbl> <dbl> <dbl>
1 2025-01-02 1 8 3
2 2025-01-03 2 5 0
3 2025-01-04 3 6 3
5、实际案例分析
为了更好地理解tidyr
包的实际应用,我们来看看一个真实案例。假设我们有一个关于不同地区气温变化的数据集,我们需要将其从宽型数据转换为长型数据,以便进行进一步的分析和绘图。
创建数据集
temperature_data <- tibble(
region = c("North", "South", "East", "West"),
Jan = c(32, 45, 50, 40),
Feb = c(35, 48, 53, 42),
Mar = c(40, 50, 60, 50)
)
print(temperature_data)
结果是:
# A tibble: 4 × 4
region Jan Feb Mar
<chr> <dbl> <dbl> <dbl>
1 North 32 35 40
2 South 45 48 50
3 East 50 53 60
4 West 40 42 50
转换为长型数据
temperature_long <- pivot_longer(temperature_data, cols = -region, names_to = "month", values_to = "temperature")
print(temperature_long)
结果是:
region month temperature
<chr> <chr> <dbl>
1 North Jan 32
2 North Feb 35
3 North Mar 40
4 South Jan 45
5 South Feb 48
6 South Mar 50
7 East Jan 50
8 East Feb 53
9 East Mar 60
10 West Jan 40
11 West Feb 42
12 West Mar 50
6、可视化数据分析
下一步,我们可以利用长型数据进行可视化分析。比如,用基本的R绘图库(base
绘图)来绘制各地区每月的平均气温变化:
# 提取不同区域的数据
regions <- unique(temperature_long$region)
months <- unique(temperature_long$month)
colors <- c("red", "green", "blue", "purple")
# 创建空的绘图区域
plot(1, type = "n", xlab = "Month", ylab = "Temperature (°F)",
xlim = c(1, length(months)), ylim = range(temperature_long$temperature),
xaxt = "n", main = "Monthly Temperature Changes by Region")
# 设置x轴标签
axis(1, at = 1:length(months), labels = months)
# 绘制每个区域的线和点
for (i in 1:length(regions)) {
region_data <- subset(temperature_long, region == regions[i])
lines(1:length(months), region_data$temperature, type = "o", col = colors[i], pch = 16)
}
# 添加图例
legend("topright", legend = regions, col = colors, pch = 16, lty = 1)
结果是:
也可以利用ggplot2包,它是R语言中一个功能强大且灵活的数据可视化工具。由Hadley Wickham开发并基于"Grammar of Graphics"理念,ggplot2
包使用户能够创建复杂且美观的图形,广泛应用于数据分析和可视化领域。
library(ggplot2)
ggplot(temperature_long, aes(x = month, y = temperature, color = region, group = region)) +
geom_line() +
geom_point() +
labs(title = "Monthly Temperature Changes by Region",
x = "Month",
y = "Temperature (°F)") +
theme_minimal()
结果是:
通过本文的讲解,我们详细介绍了R语言中tidyr
包用于宽长数据转换的基本方法。理解和掌握这些方法对于数据科学家和分析师在数据预处理和分析过程中是至关重要的。gather()
和spread()
函数,及其升级版pivot_longer()
和pivot_wider()
,提供了强大的功能,可以简化数据格式转换,提高数据分析的效率和准确性。在实际工作中,合理利用这些工具,可以极大地优化数据处理流程,提升分析质量。