R语言SDC包ProtectTable()不收敛问题求解
问题描述
我有一个包含4个多标签分类变量的数据集:两个变量的基数为4,一个为2,一个为3。为了支持单个变量内多个值为真的场景,我将这些变量独热编码为二进制矩阵,最终得到13个基数为2的变量。但此时使用sdc包的ProtectTable()函数时无法收敛。我曾尝试创建变量所有可能组合的联合变量,但披露控制未成功;未编码原始变量时函数运行正常。
重现代码
onehotencoded <- df_ref %>% mutate( FQuarter_1 = if_else(FQuarter == "Q1", 1, 0), FQuarter_2 = if_else(FQuarter == "Q2", 1, 0), FQuarter_3 = if_else(FQuarter == "Q3", 1, 0), FQuarter_4 = if_else(FQuarter == "Q4", 1, 0), FYear_24 = if_else(FYear == "2024", 1, 0), FYear_23 = if_else(FYear == "2023", 1, 0), FYear_22 = if_else(FYear == "2022", 1, 0), FYear_21 = if_else(FYear == "2021", 1, 0), Working_model_FT = if_else(Working_model == "Full-time", 1, 0), Working_model_PT = if_else(Working_model == "Part-time", 1, 0), Gender_Male = if_else(Gender == "men", 1, 0), Gender_Female = if_else(Gender == "women", 1, 0), Gender_Diverse = if_else(Gender == "else", 1, 0) ) %>% select(-FQuarter, -FYear, -Working_model, -Gender) df_ref <- onehotencoded # Negative Threshold ------------------------------------------------------ negative_threshold_function <- function(df_ref, threshold) { # convert inputs to character type df_ref <- df_ref %>% mutate_all(as.character) # Create dimList object dynamically for all dimensions in df_ref (except the first column org_level_hierachy) dimList <- list() for (col_name in colnames(df_ref)) { dim_hierarchy <- sdcHierarchies::hier_create(root = "Total", nodes = unique(df_ref[[col_name]])) dim_data <- matrix(nrow = nrow(dim_hierarchy), ncol = 2) dim_data[,1] <- sapply(dim_hierarchy$level, function(x) paste(replicate(x, "@"), collapse = "")) dim_data[,2] <- dim_hierarchy$leaf colnames(dim_data) <- c("levels", "codes") dim_data <- as.data.frame(dim_data) dimList[[col_name]] <- dim_data } # Protect table, with secondary suppression df_aggregated_protected <- ProtectTable(df_ref, dimList = dimList, infoAsFrame = T, #method = "HITAS", method = "SIMPLEHEURISTIC_OLD", maxN = threshold, maxVars = 2, #maxVars = ncol(df_ref) - 1, fastSolution = TRUE, save = FALSE, # save tables in between timeLimit = 0.1, #so to say a timeout, set very low, trying to fasten computation verbose = TRUE) #"Total" means filter is not selected. #small adjustments: sdcStatusLegend, Suppression column df_aggregated_protectedFinal <- df_aggregated_protected[["data"]] %>% mutate(sdcStatusLegend = case_when( sdcStatus == "u" ~ "cell is primary suppressed and needs to be protected", sdcStatus == "x" ~ "cell has been secondary suppressed", sdcStatus == "s" ~ "cell can be published", sdcStatus == "z" ~ "cell must not be suppressed" )) %>% mutate(suppressed = case_when( is.na(suppressed) ~ "yes", !is.na(suppressed) ~ NA_character_ )) %>% rename(group_size = freq, suppress = suppressed) %>% relocate(sdcStatus, .after = suppress) return(df_aggregated_protectedFinal) } runtime <- system.time({ final_table <- negative_threshold_function(df_ref, threshold = 5) })
解决方案建议
1. 调整ProtectTable()参数解决收敛问题
独热编码后维度数量剧增(13个)导致计算量爆炸,当前参数设置过于严苛是无法收敛的核心原因:
- 增大
timeLimit:将timeLimit从0.1调整为60甚至300,给算法足够时间完成计算。 - 关闭
fastSolution:设置fastSolution = FALSE,牺牲部分速度换取更高的收敛概率。 - 切换抑制算法:尝试
method = "HITAS",该算法在高维度场景下比SIMPLEHEURISTIC_OLD更稳定。 - 调整
maxVars:适当增大maxVars(比如设为3或4),允许算法考虑更多变量组合的抑制策略,避免陷入局部最优。
调整后的核心代码片段:
df_aggregated_protected <- ProtectTable(df_ref, dimList = dimList, infoAsFrame = T, method = "HITAS", maxN = threshold, maxVars = 3, fastSolution = FALSE, save = FALSE, timeLimit = 60, verbose = TRUE)
2. 避免独热编码,直接处理多标签变量
独热编码会大幅增加维度数,转而直接处理多标签变量的组合更高效:
- 生成标签组合变量:将每个原始多标签变量的取值转换为标签组合字符串(比如某观测同时属于Q1和Q2,
FQuarter取值为"Q1+Q2"),保留原始的4个变量维度,而非拆分为13个二进制变量。 - 构建合理层级:使用
sdcHierarchies::hier_create()为每个组合变量创建层级结构(比如将单个标签作为底层,组合标签作为上层),帮助ProtectTable()利用层级减少计算复杂度。
示例代码(以FQuarter为例生成组合):
library(tidyr) library(dplyr) # 假设原始df_ref中FQuarter是多标签(用逗号分隔) df_ref <- df_ref %>% mutate(FQuarter_comb = case_when( grepl("Q1", FQuarter) & grepl("Q2", FQuarter) ~ "Q1+Q2", grepl("Q1", FQuarter) & grepl("Q3", FQuarter) ~ "Q1+Q3", # 补充其他组合情况 TRUE ~ FQuarter )) %>% select(-FQuarter)
3. 手动构建sdcProblem对象
如果上述方法仍不生效,可以手动构建问题对象,精准控制计算范围:
- 使用
sdcTable::makeProblem()函数,先定义所有需要聚合的维度和频数阈值。 - 手动标记需要抑制的单元格,再调用
sdcTable::protectTable()处理,减少不必要的计算开销。
内容的提问来源于stack exchange,提问作者stringdetector
相关产品推荐
相关产品推荐

