R语言中的宽长数据转换:tidyr包的使用指南

在数据分析中,数据的存储方式直接影响分析过程的效率和准确性。常见的数据存储形式有宽型数据(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,该数据框包含三个变量(xyz)和一个时间变量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()函数将xyz列转换为长型格式,新的数据框datapati_long中包含三个变量:timepatientsmeasurement

  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(),提供了强大的功能,可以简化数据格式转换,提高数据分析的效率和准确性。在实际工作中,合理利用这些工具,可以极大地优化数据处理流程,提升分析质量。

相关推荐
诚丞成20 分钟前
计算世界之安生:C++继承的文水和智慧(上)
开发语言·c++
Smile灬凉城66632 分钟前
反序列化为啥可以利用加号绕过php正则匹配
开发语言·php
lsx20240643 分钟前
SQL MID()
开发语言
Dream_Snowar1 小时前
速通Python 第四节——函数
开发语言·python·算法
西猫雷婶1 小时前
python学opencv|读取图像(十四)BGR图像和HSV图像通道拆分
开发语言·python·opencv
鸿蒙自习室1 小时前
鸿蒙UI开发——组件滤镜效果
开发语言·前端·javascript
言、雲1 小时前
从tryLock()源码来出发,解析Redisson的重试机制和看门狗机制
java·开发语言·数据库
汪洪墩1 小时前
【Mars3d】设置backgroundImage、map.scene.skyBox、backgroundImage来回切换
开发语言·javascript·python·ecmascript·webgl·cesium
云空2 小时前
《QT 5.14.1 搭建 opencv 环境全攻略》
开发语言·qt·opencv
Anna。。2 小时前
Java入门2-idea 第五章:IO流(java.io包中)
java·开发语言·intellij-idea