R - 数据重塑
使用 Tidyverse 进行现代数据重塑
Section titled “使用 Tidyverse 进行现代数据重塑”数据重塑是改变数据结构以使其适合分析的过程。R 中的现代方法遵循 整洁数据 的原则,即每个变量拥有自己的列,每个观测值拥有自己的行,每个值拥有自己的单元格。
Tidyverse 是一个为数据科学设计的 R 包集合,它们共享这一底层理念。我们将使用其中的两个关键包:
dplyr:用于数据操作任务,例如组合数据集。tidyr:用于在长格式和宽格式之间重塑数据(数据透视)。
组合数据集:bind_rows() 和 bind_cols()
Section titled “组合数据集:bind_rows() 和 bind_cols()”基础 R 函数 rbind() 和 cbind() 可能不可预测,有时会将所有数据强制转换为不太有用的类型(如字符)。dplyr 函数 bind_rows() 和 bind_cols() 是更安全、更快、更灵活的替代方案。
library(dplyr)
# 最佳实践:直接创建数据框或 tibbleaddresses <- tibble( city = c("Tampa", "Seattle", "Hartford", "Denver"), state = c("FL", "WA", "CT", "CO"), zipcode = c(33602, 98104, 06161, 80294))
cat("### 第一个 Tibble\n")print(addresses)
# 创建另一个 tibblenew_addresses <- tibble( city = c("Lowry", "Charlotte"), state = c("CO", "NC"), # 注意:将 FL 改为 NC 以获得更好的示例 zipcode = c(80230, 28202))
cat("\n### 第二个 Tibble\n")print(new_addresses)
# 使用 bind_rows() 组合它们。它比 rbind() 更安全、更高效。all_addresses <- bind_rows(addresses, new_addresses)
cat("\n### 组合后的 Tibble\n")print(all_addresses)执行上述代码后,将产生以下结果:
### The First Tibble# 一个 tibble: 4 × 3 city state zipcode <chr> <chr> <dbl>1 Tampa FL 336022 Seattle WA 981043 Hartford CT 61614 Denver CO 80294
### The Second Tibble# 一个 tibble: 2 × 3 city state zipcode <chr> <chr> <dbl>1 Lowry CO 802302 Charlotte NC 28202
### The Combined Tibble# 一个 tibble: 6 × 3 city state zipcode <chr> <chr> <dbl>1 Tampa FL 336022 Seattle WA 981043 Hartford CT 61614 Denver CO 802945 Lowry CO 802306 Charlotte NC 28202连接(合并)数据框
Section titled “连接(合并)数据框”与基础的 merge() 函数不同,dplyr 提供了一系列更具表达性、速度更快的连接函数。它们允许您根据匹配的键列组合数据框。
inner_join():只保留两个表中键匹配的行。left_join():保留左表中的所有行,以及右表中匹配的行。full_join():保留两个表中的所有行。anti_join():保留左表中在右表中没有匹配项的行。
library(MASS)library(dplyr)
# Pima.te 和 Pima.tr 数据框在 MASS 包中可用。# 让我们看看基于 bp 和 bmi 的内连接是什么样子。
merged_pima <- inner_join(Pima.te, Pima.tr, by = c("bp", "bmi"))
# .x 和 .y 后缀会自动添加,以区分原始表中同名的非连接列。print(head(merged_pima))cat("\nTotal rows in merged data:", nrow(merged_pima))执行上述代码后,将产生以下结果:
npreg.x glu.x bp skin.x ped.x age.x type.x bmi npreg.y glu.y skin.y ped.y age.y type.y1 1 117 60 23 0.466 27 No 33.8 2 125 20 0.088 31 No2 2 75 64 24 0.370 33 No 29.7 2 100 23 0.368 21 No3 5 189 64 33 0.583 29 Yes 31.2 3 158 13 0.295 24 No4 4 117 64 27 0.230 24 No 33.2 1 96 27 0.289 21 No5 3 115 66 39 0.150 28 No 38.1 1 114 36 0.289 21 No6 2 100 68 25 0.324 26 No 38.5 7 129 49 0.439 43 Yes
Total rows in merged data: 17数据透视:从宽格式到长格式再返回
Section titled “数据透视:从宽格式到长格式再返回”旧版 reshape2 包中的 melt() 和 cast() 函数已被 tidyr 包中更强大、更直观的一对函数 pivot_longer() 和 pivot_wider() 所取代。
使用 pivot_longer() 从宽格式到长格式
Section titled “使用 pivot_longer() 从宽格式到长格式”pivot_longer() 通过将多个列收集到两个新列中,使“宽”数据集变得更长:一个新列用于存储原始列的名称(变量),另一个用于存储其值。
library(tidyr)library(MASS)
# 来自 MASS 包的原始“ships”数据集print(head(ships))
# 我们希望“加长”数据,将 period、service 和 incidents# 转换为键值对。标识变量是 type 和 year。ships_long <- ships |> pivot_longer( cols = -c(type, year), # 选择除 type 和 year 之外的所有列 names_to = "variable", # 用于存储原始列名的新列 values_to = "value" # 用于存储值的新列 )
print(head(ships_long))结果是一个整洁的长格式 tibble,非常适合绘图或汇总。
# 原始数据:# type year period service incidents# 1 A 60 60 127 0# 2 A 60 75 63 0# 3 A 65 60 1095 3# ...
# 透视后的数据:# 一个 tibble: 6 × 4# type year variable value# <fct> <fct> <chr> <int># 1 A 60 period 60# 2 A 60 service 127# 3 A 60 incidents 0# 4 A 60 period 75# 5 A 60 service 63# 6 A 60 incidents 0使用 pivot_wider() 从长格式到宽格式
Section titled “使用 pivot_wider() 从长格式到宽格式”旧版 cast() 函数结合了重塑和聚合。现代 tidyverse 方法将这些关注点分离,以提高清晰度和灵活性。首先,我们聚合数据,然后将其透视成宽格式。
# 让我们从原始教程中重新创建汇总表。# 步骤 1:按标识符和我们想要汇总的变量进行分组。# 步骤 2:汇总值(在此例中,求和)。# 步骤 3:将汇总后的数据透视成宽格式。
ships_summary_wide <- ships_long |> group_by(type, year, variable) |> summarise(total_value = sum(value), .groups = 'drop') |> pivot_wider( names_from = variable, # 用于获取新列名的列 values_from = total_value # 用于获取单元格值的列 )
print(head(ships_summary_wide))这种明确的多步骤过程比旧的单个 cast() 函数更容易阅读、调试和调整。
# 一个 tibble: 6 × 5# 分组:type, year [6]# type year incidents period service# <fct> <fct> <int> <int> <int># 1 A 60 0 135 190# 2 A 65 7 135 2190# 3 A 70 24 135 4865# 4 A 75 11 135 2244# 5 B 60 68 135 62058# 6 B 65 111 135 48979