format(as.Date(date),"%V")获取年度首周周数错误问题排查
问题描述
我用以下R代码从数据集df中提取date列的年份和周数,按年和周分组计算value列的均值:
df %>% mutate(date=as.Date(date)) %>% mutate(across(-contains('date'), as.numeric)) %>% mutate(year = as.numeric(year(date)), week = as.numeric(format(date, "%V")) ) %>% group_by(year, week) %>% summarise_at(.vars = c("value"), .funs = mean)
但我的数据集date截止到2023-02-07,统计结果却出现了2023年第52周的数据。排查发现format(as.Date("2023-01-01"), "%V")返回"52",而week("2023-01-01")返回1,请问这一错误的原因是什么?
数据集df结构如下:
df <- structure(list(date = structure(c(19358, 19359, 19360, 19361, 19362, 19363, 19364, 19365, 19366, 19367, 19368, 19369, 19370, 19371, 19372, 19373, 19374, 19375, 19376, 19377, 19378, 19379, 19380, 19381, 19382, 19383, 19384, 19385, 19386, 19387, 19388, 19389, 19390, 19391, 19392, 19393, 19394, 19395), class = "Date"), value = c(69.38, 68.29, 96.16, 97.24, 100.07, 105.6, 78.79, 73.6, 106.6, 107.96, 108.45, 105.56, 110.11, 78.28, 75.15, 106.56, 103.98, 98.91, 86.23, 66.53, 28.6, 48.81, 47.22, 54.84, 70.54, 70.29, 83.39, 109.73, 114.11, 122.61, 126.53, 123.74, 126.44, 134.4, 102.95, NA, NA, NA), year = c(2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023), week = c(52, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 6, 6)), row.names = c(NA, -38L), class = c("tbl_df", "tbl", "data.frame"))
统计输出结果:
year week value <dbl> <dbl> <dbl> 1 2023 1 88.5 2 2023 2 98.9 3 2023 3 77.1 4 2023 4 78.6 5 2023 5 NA 6 2023 6 NA 7 2023 52 69.4
错误原因
核心问题是**%V格式符和week()函数采用了完全不同的周数计算规则**,且你代码中用的年份和周数规则不匹配:
format(date, "%V")遵循ISO 8601标准:每周从周一开始,一年的第一周必须包含当年的周四(即该周至少有4天在当年)。2023年1月1日是周日,这一天属于2022年的第52个ISO周(2022年12月26日-2023年1月1日,该周有5天在2022年),所以%V返回52。week()(lubridate包)采用的是“自然周”规则:将1月1日所在的周直接算作当年第1周,不管该周有几天属于当年,因此2023年1月1日被归为第1周。
而你用year(date)提取的是日期的公历年份(2023),但%V返回的是ISO周对应的周数,两者规则不统一,就出现了2023年统计出第52周的矛盾结果。
解决方案
要解决这个问题,必须统一年份和周数的计算规则,推荐两种可行方案:
方案一:统一使用ISO 8601标准
用lubridate包的isoyear()和isoweek()获取匹配的ISO年份和周数,确保两者规则一致:
library(lubridate) df %>% mutate(date = as.Date(date)) %>% mutate(across(-contains('date'), as.numeric)) %>% mutate(year = isoyear(date), week = isoweek(date) ) %>% group_by(year, week) %>% summarise(value = mean(value, na.rm = TRUE))
此时2023年1月1日会被归为2022年第52周,不会出现在2023年的统计结果中。
方案二:统一使用自然周规则
如果希望1月1日所在周算作当年第1周,用lubridate的week()配合year()即可:
library(lubridate) df %>% mutate(date = as.Date(date)) %>% mutate(across(-contains('date'), as.numeric)) %>% mutate(year = year(date), week = week(date) ) %>% group_by(year, week) %>% summarise(value = mean(value, na.rm = TRUE))
这种情况下2023年1月1日会被归为2023年第1周,统计结果里不会出现2023年第52周。
内容的提问来源于stack exchange,提问作者ah bon
相关产品推荐
相关产品推荐

