在R语言数据框中按产品分组转换日期为自起始日期起的天数
解决按产品维度计算起始日后天数的问题
我来帮你搞定这个日期计算的问题!你想要的是每个产品从自己的首次出现日期开始计算经过的天数,核心问题在于之前的分组操作里没有正确获取分组内的起始日期,下面给你详细说明正确的解法和错误原因:
正确解法(推荐)
用dplyr的分组功能结合first()函数,就能精准获取每个产品的起始日期,再计算日期差即可:
library(dplyr) # 计算每个产品自起始日起的天数 result_df <- df1 %>% group_by(Product) %>% # 用first(Dates)获取分组内的第一个日期,日期相减后转成数值得到天数 mutate(Days = as.numeric(Dates - first(Dates))) %>% ungroup() # 记得取消分组,避免后续操作受影响
运行后得到的结果和你期望的desired_df完全一致:
# 部分结果展示 # Dates Product Days # 1 2021-01-01 Banana 0 # 2 2021-01-02 Banana 1 # ... # 6 2021-01-06 Apple 0 # 7 2021-01-07 Apple 1 # ... # 11 2021-01-11 Orange 0 # 12 2021-01-12 Orange 1
为什么你之前的方法出错?
你尝试的这段代码:
df1 %>% group_by(Product) %>% mutate(Days = as.numeric(Dates - Dates[1]))
问题出在Dates[1]——在dplyr的分组环境中,Dates[1]并不会自动指向当前分组的第一个日期,而是仍然引用整个原始数据框的第一行日期(也就是2021-01-01)。所以Apple的日期减去这个值就得到了5、6...,Orange得到10、11...,自然不符合需求。
而first(Dates)是dplyr专门为分组操作设计的函数,能准确提取每个分组内的第一个元素,完美解决这个问题。
关于你补充的lubridate方法的说明
你提到的这段代码虽然能工作:
df1 %>% group_by(Product) %>% mutate(Days=lubridate::day(Dates)-first(lubridate::day(Dates)))
但它有局限性:如果产品的日期跨月(比如起始日是1月31日,后续日期是2月1日),用day()提取日期计算就会得到错误的结果(1-31=-30)。而直接用日期相减的方法,不管跨月、跨年都能正确计算天数差,适用性更强。
其他思路(可选)
如果不想用dplyr,也可以用基础R的ave()函数实现:
df1$Days <- ave(as.numeric(df1$Dates), df1$Product, FUN = function(x) x - x[1])
这个方法不需要加载额外包,同样能得到正确结果。
内容的提问来源于stack exchange,提问作者Godrim




