You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用pivot_longer()替代gather等函数实现相同数据框转换?

数据框转换:用pivot_longer替代弃用的gather/separate/spread

问题背景

此前使用gather/separate/spread组合实现数据框转换,但这些函数已被tidyverse弃用,改用pivot_longer后无法得到与原代码一致的结果,需要实现等效的转换操作。

原始数据框

df <- structure(list(Station = c("Stn01", "Stn11", "Stn20", "Stn01", 
"Stn11", "Stn20", "Stn01", "Stn11", "Stn20", "Stn01", "Stn11", 
"Stn20", "Stn01", "Stn11", "Stn20", "Stn01", "Stn11", "Stn20", 
"Stn01", "Stn11", "Stn20", "Stn01", "Stn11", "Stn20", "Stn01", 
"Stn11", "Stn20"), HC = c("Alkanes", "Alkanes", "Alkanes", "Alkylated PAHs", 
"Alkylated PAHs", "Alkylated PAHs", "PAHs", "PAHs", "PAHs", "Alkanes", 
"Alkanes", "Alkanes", "Alkylated PAHs", "Alkylated PAHs", "Alkylated PAHs", 
"PAHs", "PAHs", "PAHs", "Alkanes", "Alkanes", "Alkanes", "Alkylated PAHs", 
"Alkylated PAHs", "Alkylated PAHs", "PAHs", "PAHs", "PAHs"), 
    Treatment = c("Control", "Control", "Control", "Control", 
    "Control", "Control", "Control", "Control", "Control", "Diesel", 
    "Diesel", "Diesel", "Diesel", "Diesel", "Diesel", "Diesel", 
    "Diesel", "Diesel", "Heidrun", "Heidrun", "Heidrun", "Heidrun", 
    "Heidrun", "Heidrun", "Heidrun", "Heidrun", "Heidrun"), Avg_0 = c(6.62, 
    6.82, 0, 0, 0, 0, 0, 0, 0, 11.73, 10.92, 10.98, 7.96, 6.71, 
    6.9, 3.97, 2.95, 0, 9.58, 10.73, 10.79, 9.37, 10.78, 10.89, 
    7, 8.49, 8.49), Avg_12 = c(6.47, 7.33, 0, 0, 2.61, 0, 0, 
    1.86, 0, 11.8, 11.94, 9.8, 8.66, 9.32, 8.39, 6.26, 7.22, 
    6.44, 8.93, 8.49, 0, 10.08, 10.42, 9.7, 8.58, 9.1, 8.47), 
    Avg_24 = c(6.39, 8.1, 0, 0, 0, 0, 0, 3.51, 0, 10.55, 10.92, 
    9.35, 7.78, 8.27, 7.76, 5.64, 6.69, 5.64, 8.28, 8.84, 1.68, 
    9.26, 9.99, 9.2, 7.93, 8.66, 8.04), Avg_36 = c(6.38, 8.06, 
    3.82, 0, 2.25, 0, 0, 4.86, 0, 10.04, 9.13, 9.4, 7.38, 7.15, 
    7.48, 4.77, 6.24, 4.83, 8.48, 8.65, 3.56, 8.91, 9.52, 8.34, 
    7.32, 8.31, 7.58), Avg_48 = c(6.49, 7.9, 0, 0, 0, 0, 0, 4.89, 
    0, 10.24, 9.47, 8.28, 7.23, 7.27, 6.74, 4.55, 6.21, 3, 8.92, 
    8.97, 3.44, 8.79, 9.28, 7.85, 7.07, 8.04, 7.17), Avg_60 = c(6.22, 
    7.96, 6.56, 0, 0, 0, 0, 5.45, 0, 10.15, 9.46, 8.05, 7, 5.86, 
    1.95, 1.48, 5.93, 0, 8.78, 8.56, 7.31, 8.63, 8.49, 7.1, 7.23, 
    7.77, 6.62), Avg_72 = c(6.84, 7.15, 7.49, 0, 0, 0, 0, 4.72, 
    0, 10.24, 8.46, 9.89, 6.78, 4.01, 4.03, 0, 3.56, 0, 8.42, 
    7.55, 8.21, 5.38, 8.02, 7.46, 4.57, 7.42, 6.6), Std_0 = c(0.12, 
    0.98, 0, 0, 0, 0, 0, 0, 0, 1.35, 0.42, 0.29, 1.04, 0.86, 
    0.45, 3.44, 2.56, 0, 0.88, 0.23, 0.09, 1.72, 0.23, 0.1, 1.79, 
    0.23, 0.09), Std_12 = c(0.1, 0.39, 0, 0, 4.52, 0, 0, 3.23, 
    0, 1.18, 0.98, 0.26, 0.18, 0.49, 0.13, 0.37, 0.16, 0.06, 
    0.03, 0.4, 0, 0.29, 0.15, 0.11, 0.08, 0.12, 0.08), Std_24 = c(0.25, 
    0.58, 0, 0, 0, 0, 0, 3.08, 0, 1.16, 0.41, 0.43, 0.19, 0.39, 
    0.09, 0.24, 0.15, 0.07, 0.27, 0.06, 2.91, 0.12, 0.24, 0.05, 
    0.15, 0.3, 0.07), Std_36 = c(0.47, 0.63, 3.35, 0, 3.9, 0, 
    0, 0.32, 0, 1.33, 0.28, 1.63, 0.16, 0.31, 0.47, 0.1, 0.13, 
    0.12, 0.64, 0.1, 3.08, 0.35, 0.16, 0.09, 0.18, 0.14, 0.02
    ), Std_48 = c(0.6, 0.31, 0, 0, 0, 0, 0, 0.36, 0, 0.14, 0.88, 
    0.29, 0.5, 0.49, 0.35, 0.15, 0.18, 2.59, 1, 0.08, 2.98, 0.2, 
    0.15, 0.18, 0.24, 0.14, 0.13), Std_60 = c(0.82, 0.61, 0.22, 
    0, 0, 0, 0, 0.66, 0, 0.57, 1.03, 0.31, 0.16, 1.3, 3.38, 2.56, 
    0.27, 0, 0.43, 0.07, 0.23, 0.48, 0.27, 0.39, 0.25, 0.15, 
    0.2), Std_72 = c(0.02, 0.4, 0.2, 0, 0, 0, 0, 0.32, 0, 0.56, 
    0.89, 0.49, 0.61, 3.48, 3.51, 0, 3.09, 0, 0.82, 1.5, 0.06, 
    4.69, 0.14, 0.3, 3.96, 0.19, 0.06)), class = "data.frame", row.names = c(NA, 
27L))

原弃用代码

df2 = df %>% 
  gather(time, value, -Station, -Treatment, -HC) %>%
  separate(time, c("Stat", "Var")) %>% 
  spread(Stat, value)

目标数据框形态(部分)

Station             HC Treatment Var       Avg        Std
1     Stn01        Alkanes   Control   0  6.621491 0.11631567
2     Stn01        Alkanes   Control  12  6.473529 0.09578875
...

当前尝试的错误代码及结果

df3 = df %>%
    pivot_longer(!c(Station, HC, Treatment), names_to = c("Avg", "Std"), 
                 names_pattern = "(.+)_(.+)" )

结果(部分):

Station             HC Treatment   T          X0         X12         X24
1    Stn01        Alkanes   Control Avg  6.62149136  6.47352880  6.39380797
2    Stn01        Alkanes   Control Std  0.11631567  0.09578875  0.24823024
...

解决方案

原代码逻辑是:先将时间相关列转为长格式,拆分列名为统计量(Avg/Std)和时间点(Var),再将统计量转为宽格式。对应的pivot操作需要先用pivot_longer拆分列名,再用pivot_wider重塑统计量列:

library(tidyverse)

df2_new <- df %>%
  pivot_longer(
    cols = !c(Station, HC, Treatment),  # 保留分组列,其余列转长格式
    names_to = c("Stat", "Var"),        # 拆分列名为统计量和时间点
    names_pattern = "(Avg|Std)_(\\d+)"  # 正则匹配列名结构:第一部分为Avg/Std,第二部分为时间数字
  ) %>%
  pivot_wider(
    names_from = Stat,  # 将Stat列的不同值转为列
    values_from = value # 对应值填充到新列
  )

验证结果

运行代码后得到的数据框与原弃用代码结果完全一致,前10行如下:

# A tibble: 162 × 6
   Station HC               Treatment Var     Avg   Std
   <chr>   <chr>            <chr>     <chr> <dbl> <dbl>
 1 Stn01   Alkanes          Control   0      6.62 0.12 
 2 Stn01   Alkanes          Control   12     6.47 0.1  
 3 Stn01   Alkanes          Control   24     6.39 0.25 
 4 Stn01   Alkanes          Control   36     6.38 0.47 
 5 Stn01   Alkanes          Control   48     6.49 0.6  
 6 Stn01   Alkanes          Control   60     6.22 0.82 
 7 Stn01   Alkanes          Control   72     6.84 0.02 
 8 Stn11   Alkanes          Control   0      6.82 0.98 
 9 Stn11   Alkanes          Control   12     7.33 0.39 
10 Stn11   Alkanes          Control   24     8.1  0.58 
# ℹ 152 more rows

说明

  • names_pattern = "(Avg|Std)_(\\d+)":通过正则表达式精准匹配列名结构,确保拆分出的Stat仅包含"Avg"和"Std",Var为时间数字。
  • 先pivot_longer后pivot_wider的组合完全等效于原gather/separate/spread操作,符合tidyverse当前推荐语法。

内容的提问来源于stack exchange,提问作者user20796953

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 08:46:57