在R中基于多就业时段判断纳税年度就业状态(data.table/dplyr)
解决多就业时段下纳税年度就业状态标记问题
我明白你遇到的痛点了——直接关联两个表时,因为部分人员有多个就业时段,导致df1的行被重复生成,根本没法得到正确的状态标记。核心问题在于:我们不需要把每个就业时段和纳税年度都做配对,而是要判断这个纳税年度结束日期是否落在该人员的任意一个就业区间内。下面分别用你优先想要的data.table和dplyr给出解决方案:
先整理好数据
首先把你提供的代码修正并标准化,确保日期列是可用于判断的Date类型:
# 加载需要的包 library(data.table) library(dplyr) library(tibble) # 构建df1:人员ID与纳税年度结束日期 df1 <- tibble::tribble( ~ID, ~TAX_YEAR_END_DATE, "01", "2009-04-06", "01", "2010-04-06", "01", "2011-04-06", "02", "2010-04-06", "02", "2011-04-06", "02", "2012-04-06" ) %>% mutate(TAX_YEAR_END_DATE = as.Date(TAX_YEAR_END_DATE)) # 构建df2:人员ID与多段就业的起止日期 df2 <- tibble::tribble( ~ID, ~START_DATE, ~END_DATE, "01", "2007-09-11", "2010-04-06", "02", "2008-06-06", "2010-04-06", "02", "2011-09-09", "2014-04-06" ) %>% mutate(across(c(START_DATE, END_DATE), as.Date))
方案1:data.table(高效推荐)
data.table的非等连接和分组操作特别适合这类存在性判断,而且性能很好:
# 转换为data.table格式 setDT(df1) setDT(df2) # 给df1新增STATUS列 df1[, STATUS := { # 针对当前分组的ID和纳税日期,检查是否有对应的就业区间包含它 has_job <- df2[ID == .BY$ID, any(TAX_YEAR_END_DATE %between% .(START_DATE, END_DATE))] ifelse(has_job, "EMPLOYED", "NOT") }, by = .(ID, TAX_YEAR_END_DATE)] # 查看结果 print(df1)
为什么这个方法可行?
我们按ID和TAX_YEAR_END_DATE逐组处理,对每个日期,只需要在df2的同ID数据里判断是否存在任意一个区间包含该日期(用any()来确认存在性),不会生成多余的重复行,直接在原df1的行上标记状态。
方案2:dplyr
如果更熟悉tidyverse的语法,用rowwise()逐行判断或者分组判断都可以:
# 方法:逐行检查每个纳税日期是否在同ID的任意就业区间内 df1_result <- df1 %>% rowwise() %>% mutate( STATUS = ifelse( # 筛选同ID且区间包含当前纳税日期的行,只要存在就标记为EMPLOYED nrow(filter(df2, ID == !!ID, START_DATE <= TAX_YEAR_END_DATE, END_DATE >= TAX_YEAR_END_DATE)) > 0, "EMPLOYED", "NOT" ) ) %>% ungroup() # 查看结果 print(df1_result)
或者用分组的方式,避免逐行处理(大数据量下更高效):
df1_result <- df1 %>% group_by(ID, TAX_YEAR_END_DATE) %>% mutate( STATUS = ifelse( any(df2[df2$ID == cur_group()$ID, ] %>% pull(START_DATE) <= TAX_YEAR_END_DATE & df2[df2$ID == cur_group()$ID, ] %>% pull(END_DATE) >= TAX_YEAR_END_DATE), "EMPLOYED", "NOT" ) ) %>% ungroup()
最终结果
两种方法都会得到你预期的输出:
ID TAX_YEAR_END_DATE STATUS 1: 01 2009-04-06 EMPLOYED 2: 01 2010-04-06 EMPLOYED 3: 01 2011-04-06 NOT 4: 02 2010-04-06 EMPLOYED 5: 02 2011-04-06 NOT 6: 02 2012-04-06 EMPLOYED
内容的提问来源于stack exchange,提问作者rw2
相关产品推荐
相关产品推荐

