如何在R中为价格差值计算添加百分比并保留原始值
问题
现有R数据集disk:
disk=structure(list(competitor_id = c(2L, 2L, 2L, 2L, 2L, 2L, 8L, 8L, 8L, 8L, 8L, 8L), comp_article_id = c("7976DD6D-38BD-D902-F7A8-A015B37A7128", "7976DD6D-38BD-D902-F7A8-A015B37A7128", "7976DD6D-38BD-D902-F7A8-A015B37A7128", "5683D414-DED2-1B4C-FD03-200B56319104", "5683D414-DED2-1B4C-FD03-200B56319104", "5683D414-DED2-1B4C-FD03-200B56319104", "7976DD6D-38BD-D902-F7A8-A015B37A7128", "7976DD6D-38BD-D902-F7A8-A015B37A7128", "7976DD6D-38BD-D902-F7A8-A015B37A7128", "5683D414-DED2-1B4C-FD03-200B56319104", "5683D414-DED2-1B4C-FD03-200B56319104", "5683D414-DED2-1B4C-FD03-200B56319104"), MDM_Key = c(16L, 16L, 16L, 49L, 49L, 49L, 16L, 16L, 16L, 49L, 49L, 49L), subgroup_id = c("04.01.2001", "04.01.2001", "04.01.2001", "04.01.2001", "04.01.2001", "04.01.2001", "04.01.2001", "04.01.2001", "04.01.2001", "04.01.2001", "04.01.2001", "04.01.2001"), month_id = c(202211L, 202211L, 202212L, 202003L, 202004L, 202007L, 202211L, 202211L, 202212L, 202003L, 202004L, 202007L), comp_disc_price = c(1207.06, 1207.06, 1207.06, 720.58, 774.62, 774.62, 1207.06, 1207.06, 1207.06, 720.58, 774.62, 774.62 ), iek_disc_price = c(553.83, 553.83, 537.7, 665.83, 665.83, 665.83, 553.83, 553.83, 537.7, 665.83, 665.83, 665.83)), class = "data.frame", row.names = c(NA, -12L))
需求:
- 按
competitor_id和MDM_Key分组 - 对
comp_disc_price和iek_disc_price两个变量,计算月度(month_id)间的绝对差值(大月份值减小月份值) - 同时计算对应的百分比差值
- 保留原始变量
现有代码仅能计算绝对差值,无法得到百分比差值:
disk %>% arrange(competitor_id, month_id) %>% group_by(competitor_id,MDM_Key) %>% mutate(across(c(comp_disc_price, iek_disc_price), ~ c(diff(.), NA))) %>% ungroup()
期望输出包含原始变量、绝对差值列(如abs_comp_disc、abs_iek_disk)和百分比差值列(如perc_comp_disk、perc_iek_disk),示例如下:
competitor_id comp_article_id MDM_Key 1 2 7976DD6D-38BD-D902-F7A8-A015B37A7128 16 2 2 7976DD6D-38BD-D902-F7A8-A015B37A7128 16 3 2 7976DD6D-38BD-D902-F7A8-A015B37A7128 16 4 2 5683D414-DED2-1B4C-FD03-200B56319104 49 5 2 5683D414-DED2-1B4C-FD03-200B56319104 49 subgroup_id month_id comp_disc_price iek_disc_price abs_comp_disc 1 04.01.2001 202210 1207.06 553.83 92.94 2 04.01.2001 202211 1300.00 553.83 -50.00 3 04.01.2001 202212 1250.00 537.70 NA 4 04.01.2001 202003 720.58 665.83 54.04 5 04.01.2001 202004 774.62 665.83 NA abs_iek_disk perc_comp_disk perc_iek_disk 1 0.00 0.07699700 0.00000000 2 -16.13 -0.03846154 -0.02912446 3 NA NA NA 4 0.00 0.00000000 0.00000000 5 NA NA NA
解决方法
可以通过dplyr的mutate结合across,为每个价格变量分别生成绝对差值和百分比差值列,同时保证分组内按月份排序。
完整代码如下:
library(dplyr) disk_result <- disk %>% # 按分组+月份排序,确保差值计算的时间顺序正确 arrange(competitor_id, MDM_Key, month_id) %>% group_by(competitor_id, MDM_Key) %>% mutate( # 生成绝对差值列,格式为abs_原变量名 across(c(comp_disc_price, iek_disc_price), ~ c(diff(.), NA), .names = "abs_{.col}"), # 生成百分比差值列,格式为perc_原变量名,公式为(当前值-上月值)/上月值 across(c(comp_disc_price, iek_disc_price), ~ c(diff(.)/lag(.), NA), .names = "perc_{.col}") ) %>% # 重命名列名,匹配期望输出的格式 rename( abs_comp_disc = abs_comp_disc_price, abs_iek_disk = abs_iek_disc_price, perc_comp_disk = perc_comp_disc_price, perc_iek_disk = perc_iek_disc_price ) %>% ungroup() # 查看结果 print(disk_result)
代码说明
- 排序处理:先按
competitor_id、MDM_Key和month_id排序,确保每个分组内的行按时间顺序排列,这样diff()和lag()能正确计算月度间的差值。 - 绝对差值计算:用
diff()计算当前值与前一个值的差,最后一行补NA,通过.names参数自动生成带abs_前缀的列。 - 百分比差值计算:通过
diff(.)/lag(.)计算相对变化率,同样最后一行补NA,生成带perc_前缀的列。 - 列名调整:将自动生成的列名重命名为期望的格式。
如果数据中存在同一分组同一月份的重复行(如示例数据中competitor_id=2、MDM_Key=16的202211月份有两行),可在排序后添加distinct(competitor_id, MDM_Key, month_id, .keep_all = TRUE)做去重处理,避免重复行的差值计算出现冗余。
内容的提问来源于stack exchange,提问作者psysky
相关产品推荐
相关产品推荐

