在R语言中计算相邻列差值并插入原数据集
在R语言中插入相邻时间列的差值列
先构造和你一致的示例数据集:
df <- data.frame( id = c(1,2,3), time.1 = c(2,4,2), time.2 = c(3,6,2.1), time.3 = c(4.5,7,2.8) )
方法一:基础R实现
不需要额外安装包,直接用原生代码就能搞定:
# 提取所有时间列(排除id列) time_cols <- df[, -1] # 计算后一列减前一列的差值 diff_cols <- time_cols[, -1] - time_cols[, -ncol(time_cols)] # 给差值列命名为diff.后序号.前序号 colnames(diff_cols) <- paste0("diff.", seq(2, ncol(time_cols)), ".", seq(1, ncol(time_cols)-1)) # 构造最终的列顺序:id → time.1 → diff.2.1 → time.2 → diff.3.2 → time.3 new_col_order <- c() for(i in 1:ncol(time_cols)){ new_col_order <- c(new_col_order, colnames(time_cols)[i]) if(i < ncol(time_cols)){ new_col_order <- c(new_col_order, colnames(diff_cols)[i]) } } # 合并并按顺序排列列 new_df <- cbind(df[, "id", drop=FALSE], time_cols, diff_cols)[, c("id", new_col_order)]
运行后new_df就是你要的结果:
> new_df id time.1 diff.2.1 time.2 diff.3.2 time.3 1 1 2.0 1.0 3.0 1.5 4.5 2 2 4.0 2.0 6.0 1.0 7.0 3 3 2.0 0.1 2.1 0.7 2.8
方法二:用tidyverse工具实现
如果习惯用tidyverse生态(dplyr+tidyr),代码更简洁,适合列数较多的场景:
library(tidyverse) new_df_tidy <- df %>% # 把时间列转成长格式 pivot_longer(cols = starts_with("time"), names_to = "time_col", values_to = "value") %>% # 提取时间列的序号数字 mutate(time_num = as.numeric(str_extract(time_col, "\\d+"))) %>% group_by(id) %>% # 计算当前值与前一行值的差 mutate(diff = value - lag(value)) %>% # 给差值列生成目标名称 mutate(diff_col = ifelse(!is.na(diff), paste0("diff.", time_num, ".", time_num-1), NA)) %>% # 转回宽格式,同时保留原时间列和差值列 pivot_wider( id_cols = id, names_from = c(time_col, diff_col), values_from = c(value, diff), names_sep = "" ) %>% # 清理列名里的无效后缀 rename_with(~str_remove(., "_NA")) %>% # 调整列顺序到目标格式 select(id, time.1, diff.2.1, time.2, diff.3.2, time.3)
补充说明
- 两种方法都能得到相同结果,基础R适合不想额外装包的场景,tidyverse更灵活,后续要加更多时间列时几乎不用改代码。
- 如果你需要把
0.1显示成.1,可以用format()函数处理列值,比如diff_cols[] <- lapply(diff_cols, function(x) sub("^0", "", format(x, nsmall=1))),不过保留0.1的格式通常更规范。
内容的提问来源于stack exchange,提问作者alex
相关产品推荐
相关产品推荐

