From 91be317a462749ce81e3047625ce047efcad1a32 Mon Sep 17 00:00:00 2001 From: Clara Baudry Date: Thu, 6 Aug 2026 11:55:46 +0200 Subject: [PATCH 01/15] feat: summary added to tab_multi_manager() tab by tab --- R/summarize_secret.R | 608 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 608 insertions(+) create mode 100644 R/summarize_secret.R diff --git a/R/summarize_secret.R b/R/summarize_secret.R new file mode 100644 index 0000000..e89c26e --- /dev/null +++ b/R/summarize_secret.R @@ -0,0 +1,608 @@ +journal_add_break_line <- function(journal){ + sep_char_jour <- "-----------------------------------------" + cat(sep_char_jour, file = journal, fill = TRUE, append = TRUE) +} + +journal_add_line <- function(journal,...){ + cat(..., file = journal, fill = TRUE, append = TRUE) +} + +#' Manages the secondary secret of a list of tables +#' @inheritParams tab_rtauargus +#' @param list_tables named list of `data.frame` or `data.table` representing the tables to protect +#' @param list_explanatory_vars named list of character vectors of explanatory +#' variables of each table mentionned in list_tables. Names of the list are the same as of the list of tables. +#' @param alt_hrc named list for alternative hierarchies (useful for non nested-hierarchies) +#' @param alt_totcode named list for alternative codes +#' @param ip_start integer: Interval protection level to apply at first treatment of each table +#' @param ip_end integer: Interval protection level to apply at other treatments +#' @param num_iter_max integer: Maximum of treatments to do on each table (default to 10) +#' @param summarise_secret boolean: if TRUE adds a dataframe (secret_summary) to the list of protected +#' dataframes returned. In this dataframe a summary of the protection is provided. +#' @param ... other arguments of `tab_rtauargus2()` +#' +#' @return original list of tables. Secret Results of each iteration is added to each table. +#' For example, the result of first iteration is called 'is_secret_1' in each table. +#' It's a boolean variable, whether the cell has to be masked or not. +#' +#' @seealso `tab_rtauargus2` +#' +#' @examples +#' library(rtauargus) +#' library(dplyr) +#' data(turnover_act_size) +#' data(turnover_act_cj) +#' data(activity_corr_table) +#' +#' #0-Making hrc file of business sectors ---- +#' hrc_file_activity <- activity_corr_table %>% +#' write_hrc2(file_name = "hrc/activity") +#' +#' #1-Prepare data ---- +#' #Indicate whether each cell complies with the primary rules +#' #Boolean variable created is TRUE if the cell doesn't comply. +#' #Here the frequency rule is freq in (0;3) +#' #and the dominance rule is NK(1,85) +#' list_data_2_tabs <- list( +#' act_size = turnover_act_size, +#' act_cj = turnover_act_cj +#' ) %>% +#' purrr::map( +#' function(df){ +#' df %>% +#' mutate( +#' is_secret_freq = N_OBS > 0 & N_OBS < 3, +#' is_secret_dom = ifelse(MAX == 0, FALSE, MAX/TOT>0.85), +#' is_secret_prim = is_secret_freq | is_secret_dom +#' ) +#' } +#' ) +#' \dontrun{ +#' options( +#' rtauargus.tauargus_exe = +#' "Y:/Logiciels/TauArgus/TauArgus4.2.3/TauArgus.exe" +#' ) +#' res_1 <- tab_multi_manager( +#' list_tables = list_data_2_tabs, +#' list_explanatory_vars = list( +#' act_size = c("ACTIVITY", "SIZE"), +#' act_cj = c("ACTIVITY", "CJ") +#' ), +#' hrc = c(ACTIVITY = hrc_file_activity), +#' dir_name = "tauargus_files", +#' value = "TOT", +#' freq = "N_OBS", +#' secret_var = "is_secret_prim", +#' totcode = "Total" +#' ) +#' +#' +#' # With the reduction dimensions feature +#' +#' data("datatest1") +#' data("datatest2") +#' +#' datatest2b <- datatest2 %>% +#' filter(cj == "Total", treff == "Total", type_distrib == "Total") %>% +#' select(-cj, -treff, -type_distrib) +#' +#' str(datatest2b) +#' +#' res <- tab_multi_manager( +#' list_tables = list(d1 = datatest1, d2 = datatest2b), +#' list_explanatory_vars = list( +#' d1 = names(datatest1)[1:4], +#' d2 = names(datatest2b)[1:2] +#' ), +#' dir_name = "tauargus_files", +#' value = "pizzas_tot_abs", +#' freq = "nb_obs_rnd", +#' secret_var = "is_secret_prim", +#' totcode = "Total", +#' split_tab = TRUE +#' ) +#' +#' } +#' +#' @importFrom rlang .data +#' +#' @export + +tab_multi_manager_cb <- function( + list_tables, + list_explanatory_vars, + dir_name = NULL, + hrc = NULL, + alt_hrc = NULL, + totcode = getOption("rtauargus.totcode"), + alt_totcode = NULL, + value = "value", + freq = "freq", + secret_var = "is_secret_prim", + cost_var = NULL, + suppress = "MOD(1,5,1,0,0)", + ip_start = 10, + ip_end = 0, + num_iter_max = 10, + split_tab = FALSE, + nb_tab_option = "smart", + limit = 14700, + summarise_secret = FALSE, + ... +){ + start_time <- Sys.time() + dir_name <- if(is.null(dir_name)) getwd() else dir_name + dir.create(dir_name, recursive = TRUE, showWarnings = FALSE) + + + func_to_call <- "tab_rtauargus2" + .dots = list(...) + params <- param_function(eval(parse(text=func_to_call)), .dots) + params$dir_name = dir_name + params$cost_var = cost_var + params$value = value + params$freq = freq + params$suppress = suppress + params$suppress = suppress + params$split_tab = split_tab + params$nb_tab_option = nb_tab_option + params$limit = limit + + n_tbx = length(list_tables) # nombre de tableaux + + if(n_tbx == 0){ + stop("Your list of tables is empty !") + } + if(n_tbx == 1){ + stop("To protect a single table, please use the function `tab_rtauargus`.") + } + if(is.null(names(list_tables))){ + names(list_tables) <- paste0("tab", 1:n_tbx) + names(list_explanatory_vars) <- paste0("tab", 1:n_tbx) + } + noms_tbx <- names(list_tables) + all_expl_vars <- unique(unname(unlist(list_explanatory_vars))) + + if( (!is.null(hrc)) & is.list(hrc)) hrc <- unlist(hrc) + + if( (!is.null(hrc)) & (length(names(hrc)) == 0)){ + stop("hrc must have names corresponding to the adequate explanatory variables") + } + if(length(setdiff(names(hrc), all_expl_vars)) > 0){ + stop("some names in hrc argument are not mentionned in list_explanatory_vars") + } + if(!is.null(alt_hrc)){ + if((length(names(alt_hrc)) == 0)){ + stop("alt_hrc must have names corresponding to the adequate tables names") + } + if(length(setdiff(names(alt_hrc), noms_tbx)) > 0){ + stop("some names in alt_hrc argument are not mentionned in list_tables") + } + } + if(!is.null(alt_totcode)){ + if((length(names(alt_totcode)) == 0)){ + stop("alt_totcode must have names corresponding to the adequate tables names") + } + if(length(setdiff(names(alt_totcode), noms_tbx)) > 0){ + stop("some names in alt_totcode argument are not mentionned in list_tables") + } + } + + # list_totcode management + # first case : list_totcode is one length-character vector : + # all the expl variables in all the tables have the same value to refer to the total + if(is.character(totcode)){ + if(length(totcode) == 1){ + list_totcode <- purrr::map( + list_explanatory_vars, + function(nom_tab){ + stats::setNames( + rep(totcode, length(nom_tab)), + nom_tab + ) + } + ) + }else if(length(totcode) == length(all_expl_vars)){ + if(is.null(names(totcode))){ + stop("totcode of length > 1 must have names (explanatory_vars)") + }else{ + if(!all(sort(names(totcode)) == sort(all_expl_vars))){ + stop("Names of explanatory vars mentioned in totcode are not consistent with those used in list_explanatory_vars") + }else{ + list_totcode <- purrr::map( + list_explanatory_vars, + function(nom_vars){ + totcode[nom_vars] + } + ) + } + } + }else{ + stop("totcode has to be a character vector of length 1 or a named vector of length equal to the number of unique explanatory vars") + } + }else{ + stop("totcode has to be a character vector of length 1 or a named vector of length equal to the number of unique explanatory vars") + } + + purrr::walk( + names(alt_totcode), + function(tab){ + purrr::walk( + names(alt_totcode[[tab]]), + function(var) list_totcode[[tab]][[var]] <<- alt_totcode[[tab]][[var]] + ) + } + ) + + noms_vars_init <- c() + for (tab in list_tables){ + noms_vars_init <- c(noms_vars_init, names(tab)) + } + noms_vars_init <- noms_vars_init[!duplicated(noms_vars_init)] + + noms_col_T <- stats::setNames(paste0("T_", noms_tbx), noms_tbx) + + table_majeure <- purrr::imap( + .x = list_tables, + .f = function(tableau,nom_tab){ + + if(!is.null(cost_var)){ + cost_var_tab <- if(cost_var %in% names(tableau)) cost_var else NULL + }else{ + cost_var_tab <- NULL + } + secret_var_tab <- if(!is.null(params$secret_no_pl)) c(secret_var,params$secret_no_pl) else secret_var + + tableau <- as.data.frame(tableau)[, c(list_explanatory_vars[[nom_tab]], value, freq, cost_var_tab, secret_var_tab)] + + if(!is.null(params$secret_no_pl)){ + names(tableau)[names(tableau) == params$secret_no_pl] = "secret_no_pl" + } else { + tableau$secret_no_pl <- FALSE + } + + var_a_ajouter <- setdiff(all_expl_vars, names(tableau)) + for (nom_col in var_a_ajouter){ + tableau[[nom_col]] <- unname( + purrr::keep( + list_totcode, function(x) nom_col %in% names(x) + )[[1]][nom_col] + ) + } + + tableau[[noms_col_T[[nom_tab]]]] <- TRUE + + return(as.data.frame(tableau)) + } + ) + + # by_vars = setdiff(unique(unlist(purrr::map(table_majeure, names))), noms_col_T) + by_vars = purrr::reduce(purrr::map(table_majeure, names), intersect) + table_majeure <- purrr::reduce( + .x = table_majeure, + .f = merge, + by = by_vars, + all = TRUE + ) + + table_majeure$secret_no_pl_iter <- table_majeure$secret_no_pl + secret_no_pl_iter <- "secret_no_pl_iter" + + purrr::walk( + noms_col_T, + function(col_T){ + e_par <- rlang::env_parent() + e_par$table_majeure[[col_T]] <- ifelse( + is.na(e_par$table_majeure[[col_T]]), + FALSE, + e_par$table_majeure[[col_T]] + ) + } + ) + + # Uniformisation des libelles des variables explicatives + # res_unif <- uniformize_labels(table_majeure, all_expl_vars, hrc, list_totcode) + # table_majeure <- res_unif$data + # hrc_unif <- res_unif$hrc_unif + + list_hrc <- purrr::map( + list_explanatory_vars, + function(nom_vars){ + purrr::discard(hrc[nom_vars], is.na) %>% unlist() + } + ) + + list_hrc <- purrr::map(list_hrc, function(l) if(length(l) == 0) NULL else l) + + purrr::walk( + names(alt_hrc), + function(tab){ + purrr::walk( + names(alt_hrc[[tab]]), + function(var) list_hrc[[tab]][[var]] <<- alt_hrc[[tab]][[var]] + ) + } + ) + + # listes de travail + + has_primary_secret <- purrr::map_lgl( + list_tables, + function(tab){ + sum(tab[[secret_var]]) != 0 + } + ) + if(sum(has_primary_secret) == 0){ + message("None of the tables have any primary secret cells") + return(list_tables) + } + todolist <- noms_tbx[has_primary_secret][1] + remainlist <- noms_tbx[has_primary_secret][-1] + + num_iter_par_tab = stats::setNames(rep(0, length(list_tables)), noms_tbx) + num_iter_par_tab[!has_primary_secret] <- 1 + num_iter_all = 0 + + # common_cells_modified <- as.data.frame(matrix(ncol = length(all_expl_vars)+1)) + # names(common_cells_modified) <- c(all_expl_vars, "iteration") + + n_common_cells_modified <- 0 + + journal <- file.path(dir_name,"journal.txt") + if(file.exists(journal)) invisible(file.remove(journal)) + journal_add_line(journal, "Start time:", format(start_time, "%Y-%m-%d %H:%M:%S")) + journal_add_break_line(journal) + journal_add_line(journal, "Function called to protect the tables:", func_to_call) + journal_add_line(journal, "Interval Protection Level for primary secret cells:", ip_start) + journal_add_line(journal, "Interval Protection Level for other iterations:", ip_end) + journal_add_line(journal, "Nb of tables to treat: ", n_tbx) + journal_add_break_line(journal) + journal_add_line(journal, "Tables to treat:", noms_tbx) + journal_add_break_line(journal) + journal_add_line(journal, "All explanatory variables:", all_expl_vars) + journal_add_break_line(journal) + journal_add_line(journal, "Initialisation work completed") + journal_add_break_line(journal) + journal_add_break_line(journal) + + while(length(todolist) > 0 & all(num_iter_par_tab <= num_iter_max)){ + + num_iter_all <- num_iter_all + 1 + num_tableau <- todolist[1] + num_iter_par_tab[num_tableau] <- num_iter_par_tab[num_tableau] + 1 + cat("--- Current table to treat: ", num_tableau, "---\n") + + nom_col_identifiante <- paste0("T_", num_tableau) + tableau_a_traiter <- which(table_majeure[[nom_col_identifiante]]) + + if (num_iter_all == 1){ + var_secret_apriori <- secret_var + } else { + var_secret_apriori <- paste0("is_secret_", num_iter_all-1, collapse = "") + } + + vrai_tableau <- table_majeure[tableau_a_traiter,] + + ex_var <- list_explanatory_vars[[num_tableau]] + + vrai_tableau <- vrai_tableau[,c(ex_var, value, freq,var_secret_apriori,secret_no_pl_iter, cost_var)] + + + # Other settings of the function to make secret ---- + params$tabular = vrai_tableau + params$files_name = num_tableau + params$explanatory_vars = ex_var + params$totcode = list_totcode[[num_tableau]] + params$hrc = list_hrc[[num_tableau]] + params$secret_var = var_secret_apriori + params$secret_no_pl = secret_no_pl_iter + params$suppress = if( + substr(suppress,1,3) == "MOD" & num_iter_par_tab[num_tableau] != 1 + ){ + # if modular deactivation of singleton and multisingleton after the first iteration + paste0( + paste( + c(strsplit(suppress, split = ",")[[1]][1:2], rep("0",3)), collapse = "," + ), + ")" + ) + }else{ + suppress + } + params$ip = if(num_iter_par_tab[num_tableau] == 1) ip_start else ip_end + # params$safety_rules <- "MAN(0)" + + res <- do.call(func_to_call, params) + res$is_secret <- res$Status != "V" + + # Statistiques + prim_stat <- sum(res$Status == "B", na.rm = TRUE) + sec_stat <- sum(res$Status == "D", na.rm = TRUE) + valid_stat <- sum(res$Status == "V", na.rm = TRUE) + denom_stat <- nrow(res) + + res <- subset(res, select = setdiff(names(res), "Status")) + + var_secret <- paste0("is_secret_", num_iter_all) + table_majeure <- merge(table_majeure, res, all = TRUE) + table_majeure[[var_secret]] <- table_majeure$is_secret + table_majeure <- subset( + table_majeure, + select = setdiff(names(table_majeure), "is_secret") + ) + + + table_majeure[[var_secret]] <- ifelse( + is.na(table_majeure[[var_secret]]), + table_majeure[[var_secret_apriori]], + table_majeure[[var_secret]] + ) + + table_majeure$secret_no_pl_iter <- ifelse( + table_majeure[[secret_var]], + table_majeure$secret_no_pl, + table_majeure[[var_secret]] + ) #TODO A REVOIR PR CORRIGER LES PL + + lignes_modifs <- which(table_majeure[[var_secret_apriori]] != table_majeure[[var_secret]]) + + cur_tab <- paste0("T_", num_tableau) + other_tabs <- setdiff(noms_col_T, cur_tab) + cur_cells <- rowSums(table_majeure[, cur_tab, drop=FALSE]) + other_cells <- rowSums(table_majeure[, other_tabs, drop=FALSE]) + + common_cells_rows <- which(cur_cells == 1 & other_cells > 0) + common_cells <- table_majeure[common_cells_rows, , drop=FALSE] + + # update of common cells that have been modified + modified <- common_cells[common_cells[[var_secret_apriori]] != common_cells[[var_secret]],all_expl_vars, drop=FALSE] + # modified <- if(sum(is.na(modified))>0) modified[1,][-1,] else modified + if(nrow(modified) > 0){ + modified <- cbind(modified, iteration = num_iter_all) + common_cells_modified <- if(n_common_cells_modified == 0) modified else rbind(common_cells_modified, modified) + n_common_cells_modified <- n_common_cells_modified + nrow(modified) + } + + for(tab in noms_tbx){ + nom_col_identifiante <- paste0("T_", tab) + if( !(tab %in% todolist) + & (any(table_majeure[[nom_col_identifiante]][lignes_modifs])) + ){ + todolist <- append(todolist,tab) + remainlist <- remainlist[remainlist != tab] + } + } + + todolist <- todolist[-1] + if(length(todolist) == 0){ + if(length(remainlist) > 0){ + todolist <- remainlist[1] + remainlist <- remainlist[-1] + } + } + + journal_add_line(journal, num_iter_all, "-Treatment of table", num_tableau) + journal_add_break_line(journal) + journal_add_line(journal, "New cells status counts: ") + journal_add_line(journal, "- apriori (primary) secret:", prim_stat, "(", round(prim_stat/denom_stat*100,1), "%)") + journal_add_line(journal, "- secondary secret:", sec_stat , "(", round(sec_stat/denom_stat*100,1), "%)") + journal_add_line(journal, "- valid cells:", valid_stat, "(", round(valid_stat/denom_stat*100,1), "%)") + journal_add_break_line(journal) + journal_add_line(journal, "Nb of new common cells hit by the secret:", nrow(modified)) + journal_add_break_line(journal) + journal_add_break_line(journal) + + } + + # Reconstruire la liste des tableaux d'entrée + liste_tbx_res <- purrr::imap( + list_tables, + function(tab,nom){ + expl_vars <- list_explanatory_vars[[nom]] + tab_rows <- table_majeure[[paste0("T_", nom)]] + secret_vars <- names(table_majeure)[grep("^is_secret_[1-9]", names(table_majeure))] + secret_vars <- secret_vars[order(as.integer(gsub("is_secret_", "", secret_vars)))] + res <- merge( + tab, + table_majeure[tab_rows, c(expl_vars, secret_vars)], + all.x = TRUE, all.y = FALSE, by = expl_vars + ) + } + ) + last_secret <- paste0("is_secret_", num_iter_all) + + stats <- purrr::imap_dfr( + liste_tbx_res, + function(tab, name){ + tab$primary_secret <- tab[[secret_var]] + tab$total_secret <- tab[[last_secret]] + tab$secondary_secret <- tab$total_secret & !tab$primary_secret + tab$valid_cells <- !tab$total_secret + res <- data.frame( + tab_name = name, + primary_secret = sum(tab$primary_secret), + secondary_secret = sum(tab$secondary_secret), + total_secret = sum(tab$total_secret), + valid_cells = sum(tab$valid_cells) + ) + } + ) + + purrr::iwalk( + num_iter_par_tab, + function(num,tab){ + journal_add_line( + journal, + "End of iterating after", num, "iterations for", tab + ) + } + ) + journal_add_break_line(journal) + journal_add_line(journal, "Final Summary") + journal_add_break_line(journal) + journal_add_line(journal, "Secreted cells counts per table") + journal_add_break_line(journal) + purrr::walk( + noms_tbx, + function(tab){ + journal_add_line( + journal, + "---TAB ", tab, " ---" + ) + df <- t(stats[stats$tab_name == tab,-1,drop=FALSE]) + suppressWarnings(gdata::write.fwf(df, rownames = TRUE, colnames = FALSE, file = journal, append = TRUE)) + journal_add_break_line(journal) + } + ) + journal_add_break_line(journal) + journal_add_line(journal, "Common cells hit by the secret:") + if(n_common_cells_modified > 0){ + suppressWarnings(gdata::write.fwf(common_cells_modified, file = journal, append = TRUE)) + } + journal_add_break_line(journal) + journal_add_line(journal, "End time: ", format(Sys.time(), "%Y-%m-%d %H:%M:%S")) + journal_add_break_line(journal) + + if(summarise_secret){ + summary <- purrr::imap_dfr( + liste_tbx_res, + function(tab,name){ + inner_cells <- tab %>% + rename_with(~"is_secret_final", last_col()) |> + mutate(status = case_when( + is_secret_prim ~ "primary", + is_secret_final ~ "suppressed", + TRUE ~ "published" + )) |> + mutate(status = factor(status, levels = c("primary", "suppressed", "published"))) |> + group_by(status) %>% + dplyr::summarise( + nb_cells = n(), + value = sum(.data[[params$value]], na.rm = TRUE), + .groups = "drop" + ) %>% + mutate( + pourc_cells = round(nb_cells / sum(nb_cells) * 100, 2), + pourc_value = round(value / sum(value) * 100, 2), + table = name + ) + + total <- inner_cells %>% + dplyr::summarise( + status = "total", + nb_cells = sum(nb_cells), + value = sum(value), + pourc_cells = 100, + pourc_value = 100, + table = name + ) + + dplyr::bind_rows(inner_cells, total) %>% dplyr::relocate(table) + } + ) + liste_tbx_res_and_summary <- c(liste_tbx_res, list(secret_summary = summary)) + return(liste_tbx_res_and_summary) + } + + return(liste_tbx_res) +} From 4a4ed4597c91c0c395d921579085561a8c55de0a Mon Sep 17 00:00:00 2001 From: Clara Baudry Date: Thu, 6 Aug 2026 13:50:46 +0200 Subject: [PATCH 02/15] feat: one summary for all tables in one cluster (i.e. tab_multi_manager()) --- R/summarize_secret.R | 65 ++++++++++++++++++++------------------------ 1 file changed, 30 insertions(+), 35 deletions(-) diff --git a/R/summarize_secret.R b/R/summarize_secret.R index e89c26e..71a3b8e 100644 --- a/R/summarize_secret.R +++ b/R/summarize_secret.R @@ -564,42 +564,37 @@ tab_multi_manager_cb <- function( journal_add_break_line(journal) if(summarise_secret){ - summary <- purrr::imap_dfr( - liste_tbx_res, - function(tab,name){ - inner_cells <- tab %>% - rename_with(~"is_secret_final", last_col()) |> - mutate(status = case_when( - is_secret_prim ~ "primary", + combined_tab <- purrr::imap_dfr(liste_tbx_res, function(tab, name) { + tab |> + rename_with(~"is_secret_final", last_col()) |> + mutate( + status = case_when( + is_secret_prim ~ "primary", is_secret_final ~ "suppressed", - TRUE ~ "published" - )) |> - mutate(status = factor(status, levels = c("primary", "suppressed", "published"))) |> - group_by(status) %>% - dplyr::summarise( - nb_cells = n(), - value = sum(.data[[params$value]], na.rm = TRUE), - .groups = "drop" - ) %>% - mutate( - pourc_cells = round(nb_cells / sum(nb_cells) * 100, 2), - pourc_value = round(value / sum(value) * 100, 2), - table = name - ) - - total <- inner_cells %>% - dplyr::summarise( - status = "total", - nb_cells = sum(nb_cells), - value = sum(value), - pourc_cells = 100, - pourc_value = 100, - table = name - ) - - dplyr::bind_rows(inner_cells, total) %>% dplyr::relocate(table) - } - ) + TRUE ~ "published" + )) + }) + inner_cells <- combined_tab %>% + mutate(status = factor(status, levels = c("primary", "suppressed", "published"))) %>% + group_by(status) %>% + dplyr::summarise( + nb_cells = n(), + value = sum(.data[[params$value]], na.rm = TRUE), + .groups = "drop" + ) %>% + mutate( + pourc_cells = round(nb_cells / sum(nb_cells) * 100, 2), + pourc_value = round(value / sum(value) * 100, 2) + ) + total <- inner_cells %>% + dplyr::summarise( + status = "total", + nb_cells = sum(nb_cells), + value = sum(value), + pourc_cells = 100, + pourc_value = 100 + ) + summary <- dplyr::bind_rows(inner_cells, total) liste_tbx_res_and_summary <- c(liste_tbx_res, list(secret_summary = summary)) return(liste_tbx_res_and_summary) } From 1b7a184d1beef9522e0ef7fefad765dfd11cf926 Mon Sep 17 00:00:00 2001 From: Clara Baudry Date: Thu, 6 Aug 2026 14:15:01 +0200 Subject: [PATCH 03/15] feat: summary for tab_rtauargus for output_type == 4 --- R/summarize_secret.R | 373 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 372 insertions(+), 1 deletion(-) diff --git a/R/summarize_secret.R b/R/summarize_secret.R index 71a3b8e..05f0e3b 100644 --- a/R/summarize_secret.R +++ b/R/summarize_secret.R @@ -1,3 +1,374 @@ +#' Protect one table by suppressing cells with Tau-Argus +#' +#' The function prepares all the files needed by Tau-Argus and launches the +#' software with the good settings and gets back the result. +#' +#' @inheritParams tab_rda +#' @inheritParams tab_arb +#' @inheritParams run_arb +#' +#' @param files_name string used to name all the files needed to process. +#' All files will have the same name, only their extension will be different. +#' @param dir_name string indicated the path of the directory in which to save +#' all the files (.rda, .hst, .txt, .arb, .csv) generated by the function. +#' @param unif_labels boolean, if explanatory variables have to be standardized +#' @param split_tab `r lifecycle::badge("experimental")` boolean, +#' whether to reduce dimension to 3 while treating a table of dimension 4 or 5 +#' (default to `FALSE`) +#' @param limit `r lifecycle::badge("experimental")` numeric, used to choose +#' which variable to merge (if nb_tab_option = 'smart') +#' and split table with a number of row above this limit in order to avoid +#' tauargus failures +#' @param nb_tab_option `r lifecycle::badge("experimental")` strategy to follow +#' to choose variables automatically while splitting: +#' \itemize{ +#' \item{`"min"`: minimize the number of tables;} +#' \item{`"max"`: maximize the number of tables;} +#' \item{`"smart"`: minimize the number of tables under the constraint +#' of their row count.} +#' } +#' @param summarise_secret boolean: if TRUE adds a dataframe (secret_summary) to the list of protected +#' dataframes returned. In this dataframe a summary of the protection is provided. +#' @param ... any parameter of the tab_rda, tab_arb or run_arb functions, relevant +#' for the treatment of tabular. +#' +#' @return +#' If output_type equals to 4 and split_tab = FALSE, +#' then the original tabular is returned with a new +#' column called Status, indicating the status of the cell coming from Tau-Argus : +#' "A" for a primary secret due to frequency rule, "B" for a primary secret due +#' to dominance rule, "D" for secondary secret and "V" for no secret cell. +#' +#' If split_tab = TRUE, +#' then the original tabular is returned with some new columns which are boolean +#' variables indicating the status of a cell at each iteration of the protection +#' process as we get with `tab_multi_manager()` function. `TRUE` +#' denotes a cell that have to be suppressed. The last column is then the +#' final status of the suppression process of the original table. +#' +#' If `split_tab = FALSE` and `output_type` doesn't equal to `4`, +#' then the raw result from tau-argus is returned. +#' +#' @section Standardization of explanatory variables and hierarchies: +#' +#' The boolean argument `unif_labels` is useful to +#' prevent some common errors in using Tau-Argus. Indeed, Tau-Argus needs that, +#' within a same level of a hierarchy, the labels have the same number of +#' characters. When the argument is set to TRUE, `tab_rtauargus` +#' standardizes the explanatory variables to prevent this issue. +#' Hierarchical explanatory variables (explanatory variables associated to +#' a hrc file) are then modified in the tabular data and an another hrc file is +#' created to be relevant with the tabular. In the output, these modifications +#' are removed. +#' +#' @examples +#'\dontrun{ +#' library(dplyr) +#' data(turnover_act_size) +#' +#' # Prepare data with primary secret ---- +#' turnover_act_size <- turnover_act_size %>% +#' mutate( +#' is_secret_freq = N_OBS > 0 & N_OBS < 3, +#' is_secret_dom = ifelse(MAX == 0, FALSE, MAX/TOT>0.85), +#' is_secret_prim = is_secret_freq | is_secret_dom +#' ) +#' +#' # Make hrc file of business sectors ---- +#' data(activity_corr_table) +#' hrc_file_activity <- activity_corr_table %>% +#' write_hrc2(file_name = "hrc/activity") +#' +#' # Compute the secondary secret ---- +#' options( +#' rtauargus.tauargus_exe = +#' "Y:/Logiciels/TauArgus/TauArgus4.2.3/TauArgus.exe" +#' ) +#' +#' res <- tab_rtauargus( +#' tabular = turnover_act_size, +#' files_name = "turn_act_size", +#' dir_name = "tauargus_files", +#' explanatory_vars = c("ACTIVITY", "SIZE"), +#' hrc = c(ACTIVITY = hrc_file_activity), +#' totcode = c(ACTIVITY = "Total", SIZE = "Total"), +#' secret_var = "is_secret_prim", +#' value = "TOT", +#' freq = "N_OBS", +#' verbose = FALSE +#' ) +#' +#' # Reduce dims feature +#' +#' data(datatest1) +#' res_dim4 <- tab_rtauargus( +#' tabular = datatest1, +#' dir_name = "tauargus_files", +#' explanatory_vars = c("A10", "treff","type_distrib","cj"), +#' totcode = rep("Total", 4), +#' secret_var = "is_secret_prim", +#' value = "pizzas_tot_abs", +#' freq = "nb_obs_rnd", +#' split_tab = TRUE +#' ) +#' } +#' @export +tab_rtauargus_cb <- function( + tabular, + explanatory_vars, + files_name = NULL, + dir_name = NULL, + totcode = getOption("rtauargus.totcode"), + hrc = NULL, + secret_var = NULL, + secret_no_pl = NULL, + cost_var = NULL, + value = "value", + freq = "freq", + ip = 10, + maxscore = NULL, + suppress = "MOD(1,5,1,0,0)", + safety_rules = paste0("MAN(",ip,")"), + show_batch_console = FALSE, + output_type = 4, + output_options = "", + unif_labels = TRUE, + split_tab = FALSE, + nb_tab_option = "smart", + limit = 14700, + summarise_secret = FALSE, + ... +){ + + .dots <- list(...) + + ## 0. CONFLITS PARAMETRES ................. + + # tabular not a data.frame + if(!is.data.frame(tabular)){ + stop("tabular has to be a dataframe.") + } + if(any(!explanatory_vars %in% names(tabular))){ + stop("At least one of the explanatory vars is not a tabular's column name") + } + if(any(!c(value, freq) %in% names(tabular))){ + stop(paste0(value, " or ", freq, " is not a tabular's column name")) + } + if(!is.null(maxscore)){ + if(!maxscore %in% names(tabular)){ + stop(paste0(maxscore, " is not a tabular's column name")) + } + } + if(!is.null(cost_var)){ + if(!cost_var %in% names(tabular)){ + stop(paste0(cost_var, " is not a tabular's column name")) + } + } + if(!is.null(secret_var)){ + if(!secret_var %in% names(tabular)){ + stop(paste0(secret_var, " is not a tabular's column name")) + } + } + if(length(totcode) < length(explanatory_vars)){ + stop("totcode must have the same length as explanatory_vars") + } + if(length(names(totcode)) < length(explanatory_vars)){ + names(totcode) <- explanatory_vars + } + + if(is.null(files_name)) files_name <- "targus_file" + if(is.null(dir_name)) dir_name <- getwd() + + if (split_tab){ + # detect secret_var = NULL + # We want to split the table but the primary secret have not been posed + if ( !grepl("MAN", safety_rules) ){ + stop("While using split_tab = TRUE, you can't use tauargus to put primary secret") + } + if ( is.null(secret_var) ){ + stop("While using split_tab = TRUE, a secret_var has to be provided") + } + # split_tab strategy only work with dimension 4 or 5 tables + if ( ! length(explanatory_vars) %in% c(4,5) ){ + stop( + "You use split_tab = TRUE. However it only works with 4 or 5 dimensions + tables." + ) + } + } + + if (length(explanatory_vars) %in% c(4,5)){ + if (split_tab){ + + params_rt4 <- formals(fun = "tab_rtauargus4") + params_rt4 <- params_rt4[1:(length(params_rt4)-1)] + call <- sys.call(); call[[1]] <- as.name('list') + new_params <- eval.parent(call) + + for(param in intersect(names(params_rt4), names(new_params))){ + params_rt4[[param]] <- new_params[[param]] + } + + params_rt4$tabular <- tabular + params_rt4$totcode <- totcode + params_rt4$dir_name <- dir_name + params_rt4$files_name <- files_name + + return(do.call("tab_rtauargus4", params_rt4)) + + } else { + message("Warning : +It is highly recommended to use split_tab = TRUE when using rtauargus with 4 or 5 dimensions tables. +It allows to split the table in several tables with 3 dimensions. + +With split_tab = FALSE, tauargus treats the table in 4 or 5 dimensions. +In this case, the secondary secret may not being optimal according to tauargus itself +and the process may take longer.") + } + } + + + ## 1. TAB_RDA ..................... + tabular_original <- tabular + # uniformisation des chaines de caractères des variables catégorielles, hors total + # tabular ...................... + if(unif_labels){ + res_unif <- uniformize_labels(tabular, explanatory_vars, hrc, totcode) + tabular <- res_unif$data + if(!is.null(hrc)) hrc <- res_unif$hrc_unif + } + + # parametres + param_tab_rda <- param_function(tab_rda, .dots) + param_tab_rda$tabular <- tabular + param_tab_rda$tab_filename <- file.path(dir_name, paste0(files_name, ".tab")) + param_tab_rda$rda_filename <- file.path(dir_name, paste0(files_name, ".rda")) + param_tab_rda$hst_filename <- if(is.null(secret_var) & is.null(cost_var)) NULL else file.path(dir_name, paste0(files_name, ".hst")) + param_tab_rda$explanatory_vars <- explanatory_vars + param_tab_rda$hrc <- hrc + + param_tab_rda$totcode <- totcode + param_tab_rda$secret_var <- secret_var + param_tab_rda$secret_no_pl <- secret_no_pl + param_tab_rda$cost_var <- cost_var + param_tab_rda$value <- value + param_tab_rda$freq <- freq + param_tab_rda$ip <- ip + param_tab_rda$maxscore <- maxscore + + # appel (+ récuperation noms tab hst et rda) + input <- do.call(tab_rda, param_tab_rda) + + + ## 2. TAB_ARB ......................... + + # parametres + param_arb <- param_function(tab_arb, .dots) + param_arb$tab_filename <- input$tab_filename + param_arb$rda_filename <- input$rda_filename + param_arb$hst_filename <- input$hst_filename + param_arb$arb_filename <- file.path(dir_name, paste0(files_name, ".arb")) + param_arb$output_names <- file.path(dir_name, paste0(files_name, ".csv")) + #TODO : generaliser le choix de l'extension + param_arb$output_type <- output_type + param_arb$output_options <- output_options + param_arb$explanatory_vars <- explanatory_vars + param_arb$value <- value + param_arb$safety_rules <- safety_rules + param_arb$suppress <- suppress + + # appel (+ récupération nom batch) + batch <- do.call(tab_arb, param_arb) + + ## 3. RUN_ARB ........................... + + # parametres + param_run0 <- param_function(run_arb, .dots) + param_system <- param_function(system, .dots) + param_run <- c(param_run0, param_system) + param_run$arb_filename <- param_arb$arb_filename + param_run$logbook <- file.path(dir_name, paste0(files_name, ".txt")) + param_run$is_tabular <- TRUE + param_run$show_batch_console <- show_batch_console + + # appel + res <- do.call(run_arb, param_run) + + # RESULTAT ............................. + if(output_type == 4){ + + res_import <- utils::read.csv( + param_arb$output_names, + header = FALSE, + col.names = c(explanatory_vars, value, freq, "Status","Dom"), + colClasses = c(rep("character", length(explanatory_vars)), rep("numeric",2), "character", "numeric"), + stringsAsFactors = FALSE, + na.strings = "" + ) + if(unif_labels){ + res_import <- cbind.data.frame( + apply(res_import[,explanatory_vars,drop=FALSE], 2, rev_var_pour_tau_argus), + res_import[, !names(res_import) %in% explanatory_vars] + ) + } + mask <- merge(tabular_original, res_import[,c(explanatory_vars,"Status")], by = explanatory_vars, all = TRUE) + + utils::write.csv( + res_import, + file = param_arb$output_names, + row.names = FALSE + ) + + if(summarise_secret){ + inner_cells <- mask |> + mutate( + status = case_when( + is_secret_prim ~ "primary", + Status != "V" ~ "suppressed", + TRUE ~ "published" + )) |> + mutate(status = factor(status, levels = c("primary", "suppressed", "published"))) %>% + group_by(status) %>% + dplyr::summarise( + nb_cells = n(), + value = sum(.data[[param_tab_rda$value]], na.rm = TRUE), + .groups = "drop" + ) %>% + mutate( + pourc_cells = round(nb_cells / sum(nb_cells) * 100, 2), + pourc_value = round(value / sum(value) * 100, 2) + ) + total <- inner_cells %>% + dplyr::summarise( + status = "total", + nb_cells = sum(nb_cells), + value = sum(value), + pourc_cells = 100, + pourc_value = 100 + ) + summary <- dplyr::bind_rows(inner_cells, total) + list_mask_and_summary <- c(mask, list(secret_summary = summary)) + return(list_mask_and_summary) + } + + return(mask) + + }else{ + if(unif_labels){ + res <- cbind.data.frame( + apply(res[,explanatory_vars,drop=FALSE], 2, rev_var_pour_tau_argus), + res[, !names(res) %in% explanatory_vars] + ) + } + return(res) + } + +} + +################################################################################ +################################################################################ +################################################################################ + journal_add_break_line <- function(journal){ sep_char_jour <- "-----------------------------------------" cat(sep_char_jour, file = journal, fill = TRUE, append = TRUE) @@ -571,7 +942,7 @@ tab_multi_manager_cb <- function( status = case_when( is_secret_prim ~ "primary", is_secret_final ~ "suppressed", - TRUE ~ "published" + TRUE ~ "published" )) }) inner_cells <- combined_tab %>% From cfd46670a1d6c45f803ec058e44b0ad5096364b6 Mon Sep 17 00:00:00 2001 From: julienjamme Date: Thu, 13 Aug 2026 14:45:03 +0200 Subject: [PATCH 04/15] only output_options = 4 returns a result in R, for the others the result is only written --- R/tab_rtauargus.R | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/R/tab_rtauargus.R b/R/tab_rtauargus.R index 89ffaa4..4752ec8 100644 --- a/R/tab_rtauargus.R +++ b/R/tab_rtauargus.R @@ -289,7 +289,7 @@ and the process may take longer.") param_run$show_batch_console <- show_batch_console # appel - res <- do.call(run_arb, param_run) + invisible( do.call(run_arb, param_run) ) # RESULTAT ............................. if(output_type == 4){ @@ -318,14 +318,6 @@ and the process may take longer.") return(mask) - }else{ - if(unif_labels){ - res <- cbind.data.frame( - apply(res[,explanatory_vars,drop=FALSE], 2, rev_var_pour_tau_argus), - res[, !names(res) %in% explanatory_vars] - ) - } - return(res) } } From a93ae46a9c8136faefa2e4bcbf242820e14cbfc8 Mon Sep 17 00:00:00 2001 From: julienjamme Date: Thu, 13 Aug 2026 14:47:46 +0200 Subject: [PATCH 05/15] add a function to compute the suppressions in a table or a list of tables produced by rtauargus --- R/summarize_secret.R | 1904 ++++++++++++++++++++++-------------------- 1 file changed, 1013 insertions(+), 891 deletions(-) diff --git a/R/summarize_secret.R b/R/summarize_secret.R index 05f0e3b..b5ea67d 100644 --- a/R/summarize_secret.R +++ b/R/summarize_secret.R @@ -1,65 +1,12 @@ -#' Protect one table by suppressing cells with Tau-Argus -#' -#' The function prepares all the files needed by Tau-Argus and launches the -#' software with the good settings and gets back the result. -#' -#' @inheritParams tab_rda -#' @inheritParams tab_arb -#' @inheritParams run_arb -#' -#' @param files_name string used to name all the files needed to process. -#' All files will have the same name, only their extension will be different. -#' @param dir_name string indicated the path of the directory in which to save -#' all the files (.rda, .hst, .txt, .arb, .csv) generated by the function. -#' @param unif_labels boolean, if explanatory variables have to be standardized -#' @param split_tab `r lifecycle::badge("experimental")` boolean, -#' whether to reduce dimension to 3 while treating a table of dimension 4 or 5 -#' (default to `FALSE`) -#' @param limit `r lifecycle::badge("experimental")` numeric, used to choose -#' which variable to merge (if nb_tab_option = 'smart') -#' and split table with a number of row above this limit in order to avoid -#' tauargus failures -#' @param nb_tab_option `r lifecycle::badge("experimental")` strategy to follow -#' to choose variables automatically while splitting: -#' \itemize{ -#' \item{`"min"`: minimize the number of tables;} -#' \item{`"max"`: maximize the number of tables;} -#' \item{`"smart"`: minimize the number of tables under the constraint -#' of their row count.} -#' } -#' @param summarise_secret boolean: if TRUE adds a dataframe (secret_summary) to the list of protected -#' dataframes returned. In this dataframe a summary of the protection is provided. -#' @param ... any parameter of the tab_rda, tab_arb or run_arb functions, relevant -#' for the treatment of tabular. -#' -#' @return -#' If output_type equals to 4 and split_tab = FALSE, -#' then the original tabular is returned with a new -#' column called Status, indicating the status of the cell coming from Tau-Argus : -#' "A" for a primary secret due to frequency rule, "B" for a primary secret due -#' to dominance rule, "D" for secondary secret and "V" for no secret cell. -#' -#' If split_tab = TRUE, -#' then the original tabular is returned with some new columns which are boolean -#' variables indicating the status of a cell at each iteration of the protection -#' process as we get with `tab_multi_manager()` function. `TRUE` -#' denotes a cell that have to be suppressed. The last column is then the -#' final status of the suppression process of the original table. -#' -#' If `split_tab = FALSE` and `output_type` doesn't equal to `4`, -#' then the raw result from tau-argus is returned. -#' -#' @section Standardization of explanatory variables and hierarchies: -#' -#' The boolean argument `unif_labels` is useful to -#' prevent some common errors in using Tau-Argus. Indeed, Tau-Argus needs that, -#' within a same level of a hierarchy, the labels have the same number of -#' characters. When the argument is set to TRUE, `tab_rtauargus` -#' standardizes the explanatory variables to prevent this issue. -#' Hierarchical explanatory variables (explanatory variables associated to -#' a hrc file) are then modified in the tabular data and an another hrc file is -#' created to be relevant with the tabular. In the output, these modifications -#' are removed. +#' Provide the summary of the suppression pattern from a rtauargus result +#' +#' @param res_tau either the data.frame resulting from tab_rtauargus run or +#' the list of data.frame resulting from tab_multimanager run +#' @param var the quantitative variable name to use for values stats of suppression +#' (default to `NULL` that is the stats are only computed depending on the number of cells ) +#' @param secret_var the name of the variable indicating the primary suppressed cells +#' @returns +#' @export #' #' @examples #'\dontrun{ @@ -98,877 +45,1052 @@ #' verbose = FALSE #' ) #' -#' # Reduce dims feature -#' -#' data(datatest1) -#' res_dim4 <- tab_rtauargus( -#' tabular = datatest1, -#' dir_name = "tauargus_files", -#' explanatory_vars = c("A10", "treff","type_distrib","cj"), -#' totcode = rep("Total", 4), -#' secret_var = "is_secret_prim", -#' value = "pizzas_tot_abs", -#' freq = "nb_obs_rnd", -#' split_tab = TRUE -#' ) +#' summarize_secret(res, "TOT") +#' summarize_secret(res) #' } -#' @export -tab_rtauargus_cb <- function( - tabular, - explanatory_vars, - files_name = NULL, - dir_name = NULL, - totcode = getOption("rtauargus.totcode"), - hrc = NULL, - secret_var = NULL, - secret_no_pl = NULL, - cost_var = NULL, - value = "value", - freq = "freq", - ip = 10, - maxscore = NULL, - suppress = "MOD(1,5,1,0,0)", - safety_rules = paste0("MAN(",ip,")"), - show_batch_console = FALSE, - output_type = 4, - output_options = "", - unif_labels = TRUE, - split_tab = FALSE, - nb_tab_option = "smart", - limit = 14700, - summarise_secret = FALSE, - ... -){ +summarize_secret <- function(res_tau, var = NULL, secret_var = "is_secret_prim"){ - .dots <- list(...) + if( is.data.frame(res_tau) ) { - ## 0. CONFLITS PARAMETRES ................. - - # tabular not a data.frame - if(!is.data.frame(tabular)){ - stop("tabular has to be a dataframe.") - } - if(any(!explanatory_vars %in% names(tabular))){ - stop("At least one of the explanatory vars is not a tabular's column name") - } - if(any(!c(value, freq) %in% names(tabular))){ - stop(paste0(value, " or ", freq, " is not a tabular's column name")) - } - if(!is.null(maxscore)){ - if(!maxscore %in% names(tabular)){ - stop(paste0(maxscore, " is not a tabular's column name")) - } - } - if(!is.null(cost_var)){ - if(!cost_var %in% names(tabular)){ - stop(paste0(cost_var, " is not a tabular's column name")) - } - } - if(!is.null(secret_var)){ - if(!secret_var %in% names(tabular)){ - stop(paste0(secret_var, " is not a tabular's column name")) - } - } - if(length(totcode) < length(explanatory_vars)){ - stop("totcode must have the same length as explanatory_vars") - } - if(length(names(totcode)) < length(explanatory_vars)){ - names(totcode) <- explanatory_vars - } + if( !is.null(var) && ! var %in% names(res_tau) ){ - if(is.null(files_name)) files_name <- "targus_file" - if(is.null(dir_name)) dir_name <- getwd() + stop("The variable has to be present in the data.frame") - if (split_tab){ - # detect secret_var = NULL - # We want to split the table but the primary secret have not been posed - if ( !grepl("MAN", safety_rules) ){ - stop("While using split_tab = TRUE, you can't use tauargus to put primary secret") } - if ( is.null(secret_var) ){ - stop("While using split_tab = TRUE, a secret_var has to be provided") - } - # split_tab strategy only work with dimension 4 or 5 tables - if ( ! length(explanatory_vars) %in% c(4,5) ){ - stop( - "You use split_tab = TRUE. However it only works with 4 or 5 dimensions - tables." - ) - } - } - - if (length(explanatory_vars) %in% c(4,5)){ - if (split_tab){ - params_rt4 <- formals(fun = "tab_rtauargus4") - params_rt4 <- params_rt4[1:(length(params_rt4)-1)] - call <- sys.call(); call[[1]] <- as.name('list') - new_params <- eval.parent(call) + if( ! secret_var %in% names(res_tau) ){ - for(param in intersect(names(params_rt4), names(new_params))){ - params_rt4[[param]] <- new_params[[param]] - } + stop("The primary secret variable has to be present in the data.frame") - params_rt4$tabular <- tabular - params_rt4$totcode <- totcode - params_rt4$dir_name <- dir_name - params_rt4$files_name <- files_name - - return(do.call("tab_rtauargus4", params_rt4)) - - } else { - message("Warning : -It is highly recommended to use split_tab = TRUE when using rtauargus with 4 or 5 dimensions tables. -It allows to split the table in several tables with 3 dimensions. - -With split_tab = FALSE, tauargus treats the table in 4 or 5 dimensions. -In this case, the secondary secret may not being optimal according to tauargus itself -and the process may take longer.") } - } - - - ## 1. TAB_RDA ..................... - tabular_original <- tabular - # uniformisation des chaines de caractères des variables catégorielles, hors total - # tabular ...................... - if(unif_labels){ - res_unif <- uniformize_labels(tabular, explanatory_vars, hrc, totcode) - tabular <- res_unif$data - if(!is.null(hrc)) hrc <- res_unif$hrc_unif - } - - # parametres - param_tab_rda <- param_function(tab_rda, .dots) - param_tab_rda$tabular <- tabular - param_tab_rda$tab_filename <- file.path(dir_name, paste0(files_name, ".tab")) - param_tab_rda$rda_filename <- file.path(dir_name, paste0(files_name, ".rda")) - param_tab_rda$hst_filename <- if(is.null(secret_var) & is.null(cost_var)) NULL else file.path(dir_name, paste0(files_name, ".hst")) - param_tab_rda$explanatory_vars <- explanatory_vars - param_tab_rda$hrc <- hrc - - param_tab_rda$totcode <- totcode - param_tab_rda$secret_var <- secret_var - param_tab_rda$secret_no_pl <- secret_no_pl - param_tab_rda$cost_var <- cost_var - param_tab_rda$value <- value - param_tab_rda$freq <- freq - param_tab_rda$ip <- ip - param_tab_rda$maxscore <- maxscore - - # appel (+ récuperation noms tab hst et rda) - input <- do.call(tab_rda, param_tab_rda) + tab_mod <- res_tau %>% + {if( ! is.null(var) ) rename_with(., ~"VALUE", all_of(var)) else .} |> + rename_with(~"final_status_ta", last_col()) |> + rename_with(~"is_secret_prim", all_of(secret_var)) |> + mutate( + status = case_when( + is_secret_prim ~ "primary suppr.", + final_status_ta != "V" ~ "secondary suppr.", + TRUE ~ "published" + )) |> + mutate(status = factor( + status, + levels = c("primary suppr.", "secondary suppr.", "published", "total"), + ordered = TRUE) + ) - ## 2. TAB_ARB ......................... + stats <- tab_mod |> + group_by(status) %>% + {if( ! is.null(var) ) summarise(., nb_cells = n(), value = sum(VALUE)) else summarise(., nb_cells = n()) } |> + bind_rows( + tibble( + status = "total", + tab_mod %>% {if( ! is.null(var) ) summarise(., nb_cells = n(), value = sum(VALUE)) else summarise(., nb_cells = n()) } + ) + ) |> + mutate(pourc_cells = nb_cells/nb_cells[status == "total"]*100) %>% + {if( ! is.null(var) ) mutate(., pourc_value = value/value[status == "total"]*100 ) else . } - # parametres - param_arb <- param_function(tab_arb, .dots) - param_arb$tab_filename <- input$tab_filename - param_arb$rda_filename <- input$rda_filename - param_arb$hst_filename <- input$hst_filename - param_arb$arb_filename <- file.path(dir_name, paste0(files_name, ".arb")) - param_arb$output_names <- file.path(dir_name, paste0(files_name, ".csv")) - #TODO : generaliser le choix de l'extension - param_arb$output_type <- output_type - param_arb$output_options <- output_options - param_arb$explanatory_vars <- explanatory_vars - param_arb$value <- value - param_arb$safety_rules <- safety_rules - param_arb$suppress <- suppress + return(stats) - # appel (+ récupération nom batch) - batch <- do.call(tab_arb, param_arb) + }else if( any(! purrr::map(res_tau, is.data.frame) |> purrr::list_c()) ){ - ## 3. RUN_ARB ........................... + stop("res_tau has to be a data.frame or a list of data.frames") - # parametres - param_run0 <- param_function(run_arb, .dots) - param_system <- param_function(system, .dots) - param_run <- c(param_run0, param_system) - param_run$arb_filename <- param_arb$arb_filename - param_run$logbook <- file.path(dir_name, paste0(files_name, ".txt")) - param_run$is_tabular <- TRUE - param_run$show_batch_console <- show_batch_console + }else{ - # appel - res <- do.call(run_arb, param_run) + if( !( is.null(var) ) & any(! purrr::map(res_tau, \(t) var %in% names(t)) |> purrr::list_c()) ){ - # RESULTAT ............................. - if(output_type == 4){ + stop("The variable has to be present in each of the dataframes") - res_import <- utils::read.csv( - param_arb$output_names, - header = FALSE, - col.names = c(explanatory_vars, value, freq, "Status","Dom"), - colClasses = c(rep("character", length(explanatory_vars)), rep("numeric",2), "character", "numeric"), - stringsAsFactors = FALSE, - na.strings = "" - ) - if(unif_labels){ - res_import <- cbind.data.frame( - apply(res_import[,explanatory_vars,drop=FALSE], 2, rev_var_pour_tau_argus), - res_import[, !names(res_import) %in% explanatory_vars] - ) } - mask <- merge(tabular_original, res_import[,c(explanatory_vars,"Status")], by = explanatory_vars, all = TRUE) - - utils::write.csv( - res_import, - file = param_arb$output_names, - row.names = FALSE - ) - if(summarise_secret){ - inner_cells <- mask |> - mutate( - status = case_when( - is_secret_prim ~ "primary", - Status != "V" ~ "suppressed", - TRUE ~ "published" - )) |> - mutate(status = factor(status, levels = c("primary", "suppressed", "published"))) %>% - group_by(status) %>% - dplyr::summarise( - nb_cells = n(), - value = sum(.data[[param_tab_rda$value]], na.rm = TRUE), - .groups = "drop" - ) %>% - mutate( - pourc_cells = round(nb_cells / sum(nb_cells) * 100, 2), - pourc_value = round(value / sum(value) * 100, 2) - ) - total <- inner_cells %>% - dplyr::summarise( - status = "total", - nb_cells = sum(nb_cells), - value = sum(value), - pourc_cells = 100, - pourc_value = 100 - ) - summary <- dplyr::bind_rows(inner_cells, total) - list_mask_and_summary <- c(mask, list(secret_summary = summary)) - return(list_mask_and_summary) - } + if( any(! purrr::map(res_tau, \(t) secret_var %in% names(t)) |> purrr::list_c()) ){ - return(mask) + stop("The primary secret variable has to be present in the data.frame") - }else{ - if(unif_labels){ - res <- cbind.data.frame( - apply(res[,explanatory_vars,drop=FALSE], 2, rev_var_pour_tau_argus), - res[, !names(res) %in% explanatory_vars] - ) } - return(res) - } -} + return( purrr::map(res_tau, summarize_secret, var = var) ) -################################################################################ -################################################################################ -################################################################################ + } -journal_add_break_line <- function(journal){ - sep_char_jour <- "-----------------------------------------" - cat(sep_char_jour, file = journal, fill = TRUE, append = TRUE) } -journal_add_line <- function(journal,...){ - cat(..., file = journal, fill = TRUE, append = TRUE) -} -#' Manages the secondary secret of a list of tables -#' @inheritParams tab_rtauargus -#' @param list_tables named list of `data.frame` or `data.table` representing the tables to protect -#' @param list_explanatory_vars named list of character vectors of explanatory -#' variables of each table mentionned in list_tables. Names of the list are the same as of the list of tables. -#' @param alt_hrc named list for alternative hierarchies (useful for non nested-hierarchies) -#' @param alt_totcode named list for alternative codes -#' @param ip_start integer: Interval protection level to apply at first treatment of each table -#' @param ip_end integer: Interval protection level to apply at other treatments -#' @param num_iter_max integer: Maximum of treatments to do on each table (default to 10) -#' @param summarise_secret boolean: if TRUE adds a dataframe (secret_summary) to the list of protected -#' dataframes returned. In this dataframe a summary of the protection is provided. -#' @param ... other arguments of `tab_rtauargus2()` -#' -#' @return original list of tables. Secret Results of each iteration is added to each table. -#' For example, the result of first iteration is called 'is_secret_1' in each table. -#' It's a boolean variable, whether the cell has to be masked or not. -#' -#' @seealso `tab_rtauargus2` #' -#' @examples -#' library(rtauargus) -#' library(dplyr) -#' data(turnover_act_size) -#' data(turnover_act_cj) -#' data(activity_corr_table) +#' #' Protect one table by suppressing cells with Tau-Argus +#' #' +#' #' The function prepares all the files needed by Tau-Argus and launches the +#' #' software with the good settings and gets back the result. +#' #' +#' #' @inheritParams tab_rda +#' #' @inheritParams tab_arb +#' #' @inheritParams run_arb +#' #' +#' #' @param files_name string used to name all the files needed to process. +#' #' All files will have the same name, only their extension will be different. +#' #' @param dir_name string indicated the path of the directory in which to save +#' #' all the files (.rda, .hst, .txt, .arb, .csv) generated by the function. +#' #' @param unif_labels boolean, if explanatory variables have to be standardized +#' #' @param split_tab `r lifecycle::badge("experimental")` boolean, +#' #' whether to reduce dimension to 3 while treating a table of dimension 4 or 5 +#' #' (default to `FALSE`) +#' #' @param limit `r lifecycle::badge("experimental")` numeric, used to choose +#' #' which variable to merge (if nb_tab_option = 'smart') +#' #' and split table with a number of row above this limit in order to avoid +#' #' tauargus failures +#' #' @param nb_tab_option `r lifecycle::badge("experimental")` strategy to follow +#' #' to choose variables automatically while splitting: +#' #' \itemize{ +#' #' \item{`"min"`: minimize the number of tables;} +#' #' \item{`"max"`: maximize the number of tables;} +#' #' \item{`"smart"`: minimize the number of tables under the constraint +#' #' of their row count.} +#' #' } +#' #' @param summarise_secret boolean: if TRUE adds a dataframe (secret_summary) to the list of protected +#' #' dataframes returned. In this dataframe a summary of the protection is provided. +#' #' @param ... any parameter of the tab_rda, tab_arb or run_arb functions, relevant +#' #' for the treatment of tabular. +#' #' +#' #' @return +#' #' If output_type equals to 4 and split_tab = FALSE, +#' #' then the original tabular is returned with a new +#' #' column called Status, indicating the status of the cell coming from Tau-Argus : +#' #' "A" for a primary secret due to frequency rule, "B" for a primary secret due +#' #' to dominance rule, "D" for secondary secret and "V" for no secret cell. +#' #' +#' #' If split_tab = TRUE, +#' #' then the original tabular is returned with some new columns which are boolean +#' #' variables indicating the status of a cell at each iteration of the protection +#' #' process as we get with `tab_multi_manager()` function. `TRUE` +#' #' denotes a cell that have to be suppressed. The last column is then the +#' #' final status of the suppression process of the original table. +#' #' +#' #' If `split_tab = FALSE` and `output_type` doesn't equal to `4`, +#' #' then the raw result from tau-argus is returned. +#' #' +#' #' @section Standardization of explanatory variables and hierarchies: +#' #' +#' #' The boolean argument `unif_labels` is useful to +#' #' prevent some common errors in using Tau-Argus. Indeed, Tau-Argus needs that, +#' #' within a same level of a hierarchy, the labels have the same number of +#' #' characters. When the argument is set to TRUE, `tab_rtauargus` +#' #' standardizes the explanatory variables to prevent this issue. +#' #' Hierarchical explanatory variables (explanatory variables associated to +#' #' a hrc file) are then modified in the tabular data and an another hrc file is +#' #' created to be relevant with the tabular. In the output, these modifications +#' #' are removed. +#' #' +#' #' @examples +#' #'\dontrun{ +#' #' library(dplyr) +#' #' data(turnover_act_size) +#' #' +#' #' # Prepare data with primary secret ---- +#' #' turnover_act_size <- turnover_act_size %>% +#' #' mutate( +#' #' is_secret_freq = N_OBS > 0 & N_OBS < 3, +#' #' is_secret_dom = ifelse(MAX == 0, FALSE, MAX/TOT>0.85), +#' #' is_secret_prim = is_secret_freq | is_secret_dom +#' #' ) +#' #' +#' #' # Make hrc file of business sectors ---- +#' #' data(activity_corr_table) +#' #' hrc_file_activity <- activity_corr_table %>% +#' #' write_hrc2(file_name = "hrc/activity") +#' #' +#' #' # Compute the secondary secret ---- +#' #' options( +#' #' rtauargus.tauargus_exe = +#' #' "Y:/Logiciels/TauArgus/TauArgus4.2.3/TauArgus.exe" +#' #' ) +#' #' +#' #' res <- tab_rtauargus( +#' #' tabular = turnover_act_size, +#' #' files_name = "turn_act_size", +#' #' dir_name = "tauargus_files", +#' #' explanatory_vars = c("ACTIVITY", "SIZE"), +#' #' hrc = c(ACTIVITY = hrc_file_activity), +#' #' totcode = c(ACTIVITY = "Total", SIZE = "Total"), +#' #' secret_var = "is_secret_prim", +#' #' value = "TOT", +#' #' freq = "N_OBS", +#' #' verbose = FALSE +#' #' ) +#' #' +#' #' # Reduce dims feature +#' #' +#' #' data(datatest1) +#' #' res_dim4 <- tab_rtauargus( +#' #' tabular = datatest1, +#' #' dir_name = "tauargus_files", +#' #' explanatory_vars = c("A10", "treff","type_distrib","cj"), +#' #' totcode = rep("Total", 4), +#' #' secret_var = "is_secret_prim", +#' #' value = "pizzas_tot_abs", +#' #' freq = "nb_obs_rnd", +#' #' split_tab = TRUE +#' #' ) +#' #' } +#' #' @export +#' tab_rtauargus_cb <- function( +#' tabular, +#' explanatory_vars, +#' files_name = NULL, +#' dir_name = NULL, +#' totcode = getOption("rtauargus.totcode"), +#' hrc = NULL, +#' secret_var = NULL, +#' secret_no_pl = NULL, +#' cost_var = NULL, +#' value = "value", +#' freq = "freq", +#' ip = 10, +#' maxscore = NULL, +#' suppress = "MOD(1,5,1,0,0)", +#' safety_rules = paste0("MAN(",ip,")"), +#' show_batch_console = FALSE, +#' output_type = 4, +#' output_options = "", +#' unif_labels = TRUE, +#' split_tab = FALSE, +#' nb_tab_option = "smart", +#' limit = 14700, +#' summarise_secret = FALSE, +#' ... +#' ){ #' -#' #0-Making hrc file of business sectors ---- -#' hrc_file_activity <- activity_corr_table %>% -#' write_hrc2(file_name = "hrc/activity") +#' .dots <- list(...) #' -#' #1-Prepare data ---- -#' #Indicate whether each cell complies with the primary rules -#' #Boolean variable created is TRUE if the cell doesn't comply. -#' #Here the frequency rule is freq in (0;3) -#' #and the dominance rule is NK(1,85) -#' list_data_2_tabs <- list( -#' act_size = turnover_act_size, -#' act_cj = turnover_act_cj -#' ) %>% -#' purrr::map( -#' function(df){ -#' df %>% -#' mutate( -#' is_secret_freq = N_OBS > 0 & N_OBS < 3, -#' is_secret_dom = ifelse(MAX == 0, FALSE, MAX/TOT>0.85), -#' is_secret_prim = is_secret_freq | is_secret_dom +#' ## 0. CONFLITS PARAMETRES ................. +#' +#' # tabular not a data.frame +#' if(!is.data.frame(tabular)){ +#' stop("tabular has to be a dataframe.") +#' } +#' if(any(!explanatory_vars %in% names(tabular))){ +#' stop("At least one of the explanatory vars is not a tabular's column name") +#' } +#' if(any(!c(value, freq) %in% names(tabular))){ +#' stop(paste0(value, " or ", freq, " is not a tabular's column name")) +#' } +#' if(!is.null(maxscore)){ +#' if(!maxscore %in% names(tabular)){ +#' stop(paste0(maxscore, " is not a tabular's column name")) +#' } +#' } +#' if(!is.null(cost_var)){ +#' if(!cost_var %in% names(tabular)){ +#' stop(paste0(cost_var, " is not a tabular's column name")) +#' } +#' } +#' if(!is.null(secret_var)){ +#' if(!secret_var %in% names(tabular)){ +#' stop(paste0(secret_var, " is not a tabular's column name")) +#' } +#' } +#' if(length(totcode) < length(explanatory_vars)){ +#' stop("totcode must have the same length as explanatory_vars") +#' } +#' if(length(names(totcode)) < length(explanatory_vars)){ +#' names(totcode) <- explanatory_vars +#' } +#' +#' if(is.null(files_name)) files_name <- "targus_file" +#' if(is.null(dir_name)) dir_name <- getwd() +#' +#' if (split_tab){ +#' # detect secret_var = NULL +#' # We want to split the table but the primary secret have not been posed +#' if ( !grepl("MAN", safety_rules) ){ +#' stop("While using split_tab = TRUE, you can't use tauargus to put primary secret") +#' } +#' if ( is.null(secret_var) ){ +#' stop("While using split_tab = TRUE, a secret_var has to be provided") +#' } +#' # split_tab strategy only work with dimension 4 or 5 tables +#' if ( ! length(explanatory_vars) %in% c(4,5) ){ +#' stop( +#' "You use split_tab = TRUE. However it only works with 4 or 5 dimensions +#' tables." #' ) +#' } #' } -#' ) -#' \dontrun{ -#' options( -#' rtauargus.tauargus_exe = -#' "Y:/Logiciels/TauArgus/TauArgus4.2.3/TauArgus.exe" -#' ) -#' res_1 <- tab_multi_manager( -#' list_tables = list_data_2_tabs, -#' list_explanatory_vars = list( -#' act_size = c("ACTIVITY", "SIZE"), -#' act_cj = c("ACTIVITY", "CJ") -#' ), -#' hrc = c(ACTIVITY = hrc_file_activity), -#' dir_name = "tauargus_files", -#' value = "TOT", -#' freq = "N_OBS", -#' secret_var = "is_secret_prim", -#' totcode = "Total" -#' ) #' +#' if (length(explanatory_vars) %in% c(4,5)){ +#' if (split_tab){ #' -#' # With the reduction dimensions feature +#' params_rt4 <- formals(fun = "tab_rtauargus4") +#' params_rt4 <- params_rt4[1:(length(params_rt4)-1)] +#' call <- sys.call(); call[[1]] <- as.name('list') +#' new_params <- eval.parent(call) #' -#' data("datatest1") -#' data("datatest2") +#' for(param in intersect(names(params_rt4), names(new_params))){ +#' params_rt4[[param]] <- new_params[[param]] +#' } #' -#' datatest2b <- datatest2 %>% -#' filter(cj == "Total", treff == "Total", type_distrib == "Total") %>% -#' select(-cj, -treff, -type_distrib) +#' params_rt4$tabular <- tabular +#' params_rt4$totcode <- totcode +#' params_rt4$dir_name <- dir_name +#' params_rt4$files_name <- files_name #' -#' str(datatest2b) +#' return(do.call("tab_rtauargus4", params_rt4)) #' -#' res <- tab_multi_manager( -#' list_tables = list(d1 = datatest1, d2 = datatest2b), -#' list_explanatory_vars = list( -#' d1 = names(datatest1)[1:4], -#' d2 = names(datatest2b)[1:2] -#' ), -#' dir_name = "tauargus_files", -#' value = "pizzas_tot_abs", -#' freq = "nb_obs_rnd", -#' secret_var = "is_secret_prim", -#' totcode = "Total", -#' split_tab = TRUE -#' ) +#' } else { +#' message("Warning : +#' It is highly recommended to use split_tab = TRUE when using rtauargus with 4 or 5 dimensions tables. +#' It allows to split the table in several tables with 3 dimensions. +#' +#' With split_tab = FALSE, tauargus treats the table in 4 or 5 dimensions. +#' In this case, the secondary secret may not being optimal according to tauargus itself +#' and the process may take longer.") +#' } +#' } +#' +#' +#' ## 1. TAB_RDA ..................... +#' tabular_original <- tabular +#' # uniformisation des chaines de caractères des variables catégorielles, hors total +#' # tabular ...................... +#' if(unif_labels){ +#' res_unif <- uniformize_labels(tabular, explanatory_vars, hrc, totcode) +#' tabular <- res_unif$data +#' if(!is.null(hrc)) hrc <- res_unif$hrc_unif +#' } +#' +#' # parametres +#' param_tab_rda <- param_function(tab_rda, .dots) +#' param_tab_rda$tabular <- tabular +#' param_tab_rda$tab_filename <- file.path(dir_name, paste0(files_name, ".tab")) +#' param_tab_rda$rda_filename <- file.path(dir_name, paste0(files_name, ".rda")) +#' param_tab_rda$hst_filename <- if(is.null(secret_var) & is.null(cost_var)) NULL else file.path(dir_name, paste0(files_name, ".hst")) +#' param_tab_rda$explanatory_vars <- explanatory_vars +#' param_tab_rda$hrc <- hrc +#' +#' param_tab_rda$totcode <- totcode +#' param_tab_rda$secret_var <- secret_var +#' param_tab_rda$secret_no_pl <- secret_no_pl +#' param_tab_rda$cost_var <- cost_var +#' param_tab_rda$value <- value +#' param_tab_rda$freq <- freq +#' param_tab_rda$ip <- ip +#' param_tab_rda$maxscore <- maxscore +#' +#' # appel (+ récuperation noms tab hst et rda) +#' input <- do.call(tab_rda, param_tab_rda) +#' +#' +#' ## 2. TAB_ARB ......................... +#' +#' # parametres +#' param_arb <- param_function(tab_arb, .dots) +#' param_arb$tab_filename <- input$tab_filename +#' param_arb$rda_filename <- input$rda_filename +#' param_arb$hst_filename <- input$hst_filename +#' param_arb$arb_filename <- file.path(dir_name, paste0(files_name, ".arb")) +#' param_arb$output_names <- file.path(dir_name, paste0(files_name, ".csv")) +#' #TODO : generaliser le choix de l'extension +#' param_arb$output_type <- output_type +#' param_arb$output_options <- output_options +#' param_arb$explanatory_vars <- explanatory_vars +#' param_arb$value <- value +#' param_arb$safety_rules <- safety_rules +#' param_arb$suppress <- suppress +#' +#' # appel (+ récupération nom batch) +#' batch <- do.call(tab_arb, param_arb) +#' +#' ## 3. RUN_ARB ........................... +#' +#' # parametres +#' param_run0 <- param_function(run_arb, .dots) +#' param_system <- param_function(system, .dots) +#' param_run <- c(param_run0, param_system) +#' param_run$arb_filename <- param_arb$arb_filename +#' param_run$logbook <- file.path(dir_name, paste0(files_name, ".txt")) +#' param_run$is_tabular <- TRUE +#' param_run$show_batch_console <- show_batch_console +#' +#' # appel +#' res <- do.call(run_arb, param_run) +#' +#' # RESULTAT ............................. +#' if(output_type == 4){ +#' +#' res_import <- utils::read.csv( +#' param_arb$output_names, +#' header = FALSE, +#' col.names = c(explanatory_vars, value, freq, "Status","Dom"), +#' colClasses = c(rep("character", length(explanatory_vars)), rep("numeric",2), "character", "numeric"), +#' stringsAsFactors = FALSE, +#' na.strings = "" +#' ) +#' if(unif_labels){ +#' res_import <- cbind.data.frame( +#' apply(res_import[,explanatory_vars,drop=FALSE], 2, rev_var_pour_tau_argus), +#' res_import[, !names(res_import) %in% explanatory_vars] +#' ) +#' } +#' mask <- merge(tabular_original, res_import[,c(explanatory_vars,"Status")], by = explanatory_vars, all = TRUE) +#' +#' utils::write.csv( +#' res_import, +#' file = param_arb$output_names, +#' row.names = FALSE +#' ) +#' +#' if(summarise_secret){ +#' inner_cells <- mask |> +#' mutate( +#' status = case_when( +#' is_secret_prim ~ "primary", +#' Status != "V" ~ "suppressed", +#' TRUE ~ "published" +#' )) |> +#' mutate(status = factor(status, levels = c("primary", "suppressed", "published"))) %>% +#' group_by(status) %>% +#' dplyr::summarise( +#' nb_cells = n(), +#' value = sum(.data[[param_tab_rda$value]], na.rm = TRUE), +#' .groups = "drop" +#' ) %>% +#' mutate( +#' pourc_cells = round(nb_cells / sum(nb_cells) * 100, 2), +#' pourc_value = round(value / sum(value) * 100, 2) +#' ) +#' total <- inner_cells %>% +#' dplyr::summarise( +#' status = "total", +#' nb_cells = sum(nb_cells), +#' value = sum(value), +#' pourc_cells = 100, +#' pourc_value = 100 +#' ) +#' summary <- dplyr::bind_rows(inner_cells, total) +#' list_mask_and_summary <- c(mask, list(secret_summary = summary)) +#' return(list_mask_and_summary) +#' } +#' +#' return(mask) +#' +#' }else{ +#' if(unif_labels){ +#' res <- cbind.data.frame( +#' apply(res[,explanatory_vars,drop=FALSE], 2, rev_var_pour_tau_argus), +#' res[, !names(res) %in% explanatory_vars] +#' ) +#' } +#' return(res) +#' } #' #' } #' -#' @importFrom rlang .data +#' ################################################################################ +#' ################################################################################ +#' ################################################################################ #' -#' @export - -tab_multi_manager_cb <- function( - list_tables, - list_explanatory_vars, - dir_name = NULL, - hrc = NULL, - alt_hrc = NULL, - totcode = getOption("rtauargus.totcode"), - alt_totcode = NULL, - value = "value", - freq = "freq", - secret_var = "is_secret_prim", - cost_var = NULL, - suppress = "MOD(1,5,1,0,0)", - ip_start = 10, - ip_end = 0, - num_iter_max = 10, - split_tab = FALSE, - nb_tab_option = "smart", - limit = 14700, - summarise_secret = FALSE, - ... -){ - start_time <- Sys.time() - dir_name <- if(is.null(dir_name)) getwd() else dir_name - dir.create(dir_name, recursive = TRUE, showWarnings = FALSE) - - - func_to_call <- "tab_rtauargus2" - .dots = list(...) - params <- param_function(eval(parse(text=func_to_call)), .dots) - params$dir_name = dir_name - params$cost_var = cost_var - params$value = value - params$freq = freq - params$suppress = suppress - params$suppress = suppress - params$split_tab = split_tab - params$nb_tab_option = nb_tab_option - params$limit = limit - - n_tbx = length(list_tables) # nombre de tableaux - - if(n_tbx == 0){ - stop("Your list of tables is empty !") - } - if(n_tbx == 1){ - stop("To protect a single table, please use the function `tab_rtauargus`.") - } - if(is.null(names(list_tables))){ - names(list_tables) <- paste0("tab", 1:n_tbx) - names(list_explanatory_vars) <- paste0("tab", 1:n_tbx) - } - noms_tbx <- names(list_tables) - all_expl_vars <- unique(unname(unlist(list_explanatory_vars))) - - if( (!is.null(hrc)) & is.list(hrc)) hrc <- unlist(hrc) - - if( (!is.null(hrc)) & (length(names(hrc)) == 0)){ - stop("hrc must have names corresponding to the adequate explanatory variables") - } - if(length(setdiff(names(hrc), all_expl_vars)) > 0){ - stop("some names in hrc argument are not mentionned in list_explanatory_vars") - } - if(!is.null(alt_hrc)){ - if((length(names(alt_hrc)) == 0)){ - stop("alt_hrc must have names corresponding to the adequate tables names") - } - if(length(setdiff(names(alt_hrc), noms_tbx)) > 0){ - stop("some names in alt_hrc argument are not mentionned in list_tables") - } - } - if(!is.null(alt_totcode)){ - if((length(names(alt_totcode)) == 0)){ - stop("alt_totcode must have names corresponding to the adequate tables names") - } - if(length(setdiff(names(alt_totcode), noms_tbx)) > 0){ - stop("some names in alt_totcode argument are not mentionned in list_tables") - } - } - - # list_totcode management - # first case : list_totcode is one length-character vector : - # all the expl variables in all the tables have the same value to refer to the total - if(is.character(totcode)){ - if(length(totcode) == 1){ - list_totcode <- purrr::map( - list_explanatory_vars, - function(nom_tab){ - stats::setNames( - rep(totcode, length(nom_tab)), - nom_tab - ) - } - ) - }else if(length(totcode) == length(all_expl_vars)){ - if(is.null(names(totcode))){ - stop("totcode of length > 1 must have names (explanatory_vars)") - }else{ - if(!all(sort(names(totcode)) == sort(all_expl_vars))){ - stop("Names of explanatory vars mentioned in totcode are not consistent with those used in list_explanatory_vars") - }else{ - list_totcode <- purrr::map( - list_explanatory_vars, - function(nom_vars){ - totcode[nom_vars] - } - ) - } - } - }else{ - stop("totcode has to be a character vector of length 1 or a named vector of length equal to the number of unique explanatory vars") - } - }else{ - stop("totcode has to be a character vector of length 1 or a named vector of length equal to the number of unique explanatory vars") - } - - purrr::walk( - names(alt_totcode), - function(tab){ - purrr::walk( - names(alt_totcode[[tab]]), - function(var) list_totcode[[tab]][[var]] <<- alt_totcode[[tab]][[var]] - ) - } - ) - - noms_vars_init <- c() - for (tab in list_tables){ - noms_vars_init <- c(noms_vars_init, names(tab)) - } - noms_vars_init <- noms_vars_init[!duplicated(noms_vars_init)] - - noms_col_T <- stats::setNames(paste0("T_", noms_tbx), noms_tbx) - - table_majeure <- purrr::imap( - .x = list_tables, - .f = function(tableau,nom_tab){ - - if(!is.null(cost_var)){ - cost_var_tab <- if(cost_var %in% names(tableau)) cost_var else NULL - }else{ - cost_var_tab <- NULL - } - secret_var_tab <- if(!is.null(params$secret_no_pl)) c(secret_var,params$secret_no_pl) else secret_var - - tableau <- as.data.frame(tableau)[, c(list_explanatory_vars[[nom_tab]], value, freq, cost_var_tab, secret_var_tab)] - - if(!is.null(params$secret_no_pl)){ - names(tableau)[names(tableau) == params$secret_no_pl] = "secret_no_pl" - } else { - tableau$secret_no_pl <- FALSE - } - - var_a_ajouter <- setdiff(all_expl_vars, names(tableau)) - for (nom_col in var_a_ajouter){ - tableau[[nom_col]] <- unname( - purrr::keep( - list_totcode, function(x) nom_col %in% names(x) - )[[1]][nom_col] - ) - } - - tableau[[noms_col_T[[nom_tab]]]] <- TRUE - - return(as.data.frame(tableau)) - } - ) - - # by_vars = setdiff(unique(unlist(purrr::map(table_majeure, names))), noms_col_T) - by_vars = purrr::reduce(purrr::map(table_majeure, names), intersect) - table_majeure <- purrr::reduce( - .x = table_majeure, - .f = merge, - by = by_vars, - all = TRUE - ) - - table_majeure$secret_no_pl_iter <- table_majeure$secret_no_pl - secret_no_pl_iter <- "secret_no_pl_iter" - - purrr::walk( - noms_col_T, - function(col_T){ - e_par <- rlang::env_parent() - e_par$table_majeure[[col_T]] <- ifelse( - is.na(e_par$table_majeure[[col_T]]), - FALSE, - e_par$table_majeure[[col_T]] - ) - } - ) - - # Uniformisation des libelles des variables explicatives - # res_unif <- uniformize_labels(table_majeure, all_expl_vars, hrc, list_totcode) - # table_majeure <- res_unif$data - # hrc_unif <- res_unif$hrc_unif - - list_hrc <- purrr::map( - list_explanatory_vars, - function(nom_vars){ - purrr::discard(hrc[nom_vars], is.na) %>% unlist() - } - ) - - list_hrc <- purrr::map(list_hrc, function(l) if(length(l) == 0) NULL else l) - - purrr::walk( - names(alt_hrc), - function(tab){ - purrr::walk( - names(alt_hrc[[tab]]), - function(var) list_hrc[[tab]][[var]] <<- alt_hrc[[tab]][[var]] - ) - } - ) - - # listes de travail - - has_primary_secret <- purrr::map_lgl( - list_tables, - function(tab){ - sum(tab[[secret_var]]) != 0 - } - ) - if(sum(has_primary_secret) == 0){ - message("None of the tables have any primary secret cells") - return(list_tables) - } - todolist <- noms_tbx[has_primary_secret][1] - remainlist <- noms_tbx[has_primary_secret][-1] - - num_iter_par_tab = stats::setNames(rep(0, length(list_tables)), noms_tbx) - num_iter_par_tab[!has_primary_secret] <- 1 - num_iter_all = 0 - - # common_cells_modified <- as.data.frame(matrix(ncol = length(all_expl_vars)+1)) - # names(common_cells_modified) <- c(all_expl_vars, "iteration") - - n_common_cells_modified <- 0 - - journal <- file.path(dir_name,"journal.txt") - if(file.exists(journal)) invisible(file.remove(journal)) - journal_add_line(journal, "Start time:", format(start_time, "%Y-%m-%d %H:%M:%S")) - journal_add_break_line(journal) - journal_add_line(journal, "Function called to protect the tables:", func_to_call) - journal_add_line(journal, "Interval Protection Level for primary secret cells:", ip_start) - journal_add_line(journal, "Interval Protection Level for other iterations:", ip_end) - journal_add_line(journal, "Nb of tables to treat: ", n_tbx) - journal_add_break_line(journal) - journal_add_line(journal, "Tables to treat:", noms_tbx) - journal_add_break_line(journal) - journal_add_line(journal, "All explanatory variables:", all_expl_vars) - journal_add_break_line(journal) - journal_add_line(journal, "Initialisation work completed") - journal_add_break_line(journal) - journal_add_break_line(journal) - - while(length(todolist) > 0 & all(num_iter_par_tab <= num_iter_max)){ - - num_iter_all <- num_iter_all + 1 - num_tableau <- todolist[1] - num_iter_par_tab[num_tableau] <- num_iter_par_tab[num_tableau] + 1 - cat("--- Current table to treat: ", num_tableau, "---\n") - - nom_col_identifiante <- paste0("T_", num_tableau) - tableau_a_traiter <- which(table_majeure[[nom_col_identifiante]]) - - if (num_iter_all == 1){ - var_secret_apriori <- secret_var - } else { - var_secret_apriori <- paste0("is_secret_", num_iter_all-1, collapse = "") - } - - vrai_tableau <- table_majeure[tableau_a_traiter,] - - ex_var <- list_explanatory_vars[[num_tableau]] - - vrai_tableau <- vrai_tableau[,c(ex_var, value, freq,var_secret_apriori,secret_no_pl_iter, cost_var)] - - - # Other settings of the function to make secret ---- - params$tabular = vrai_tableau - params$files_name = num_tableau - params$explanatory_vars = ex_var - params$totcode = list_totcode[[num_tableau]] - params$hrc = list_hrc[[num_tableau]] - params$secret_var = var_secret_apriori - params$secret_no_pl = secret_no_pl_iter - params$suppress = if( - substr(suppress,1,3) == "MOD" & num_iter_par_tab[num_tableau] != 1 - ){ - # if modular deactivation of singleton and multisingleton after the first iteration - paste0( - paste( - c(strsplit(suppress, split = ",")[[1]][1:2], rep("0",3)), collapse = "," - ), - ")" - ) - }else{ - suppress - } - params$ip = if(num_iter_par_tab[num_tableau] == 1) ip_start else ip_end - # params$safety_rules <- "MAN(0)" - - res <- do.call(func_to_call, params) - res$is_secret <- res$Status != "V" - - # Statistiques - prim_stat <- sum(res$Status == "B", na.rm = TRUE) - sec_stat <- sum(res$Status == "D", na.rm = TRUE) - valid_stat <- sum(res$Status == "V", na.rm = TRUE) - denom_stat <- nrow(res) - - res <- subset(res, select = setdiff(names(res), "Status")) - - var_secret <- paste0("is_secret_", num_iter_all) - table_majeure <- merge(table_majeure, res, all = TRUE) - table_majeure[[var_secret]] <- table_majeure$is_secret - table_majeure <- subset( - table_majeure, - select = setdiff(names(table_majeure), "is_secret") - ) - - - table_majeure[[var_secret]] <- ifelse( - is.na(table_majeure[[var_secret]]), - table_majeure[[var_secret_apriori]], - table_majeure[[var_secret]] - ) - - table_majeure$secret_no_pl_iter <- ifelse( - table_majeure[[secret_var]], - table_majeure$secret_no_pl, - table_majeure[[var_secret]] - ) #TODO A REVOIR PR CORRIGER LES PL - - lignes_modifs <- which(table_majeure[[var_secret_apriori]] != table_majeure[[var_secret]]) - - cur_tab <- paste0("T_", num_tableau) - other_tabs <- setdiff(noms_col_T, cur_tab) - cur_cells <- rowSums(table_majeure[, cur_tab, drop=FALSE]) - other_cells <- rowSums(table_majeure[, other_tabs, drop=FALSE]) - - common_cells_rows <- which(cur_cells == 1 & other_cells > 0) - common_cells <- table_majeure[common_cells_rows, , drop=FALSE] - - # update of common cells that have been modified - modified <- common_cells[common_cells[[var_secret_apriori]] != common_cells[[var_secret]],all_expl_vars, drop=FALSE] - # modified <- if(sum(is.na(modified))>0) modified[1,][-1,] else modified - if(nrow(modified) > 0){ - modified <- cbind(modified, iteration = num_iter_all) - common_cells_modified <- if(n_common_cells_modified == 0) modified else rbind(common_cells_modified, modified) - n_common_cells_modified <- n_common_cells_modified + nrow(modified) - } - - for(tab in noms_tbx){ - nom_col_identifiante <- paste0("T_", tab) - if( !(tab %in% todolist) - & (any(table_majeure[[nom_col_identifiante]][lignes_modifs])) - ){ - todolist <- append(todolist,tab) - remainlist <- remainlist[remainlist != tab] - } - } - - todolist <- todolist[-1] - if(length(todolist) == 0){ - if(length(remainlist) > 0){ - todolist <- remainlist[1] - remainlist <- remainlist[-1] - } - } - - journal_add_line(journal, num_iter_all, "-Treatment of table", num_tableau) - journal_add_break_line(journal) - journal_add_line(journal, "New cells status counts: ") - journal_add_line(journal, "- apriori (primary) secret:", prim_stat, "(", round(prim_stat/denom_stat*100,1), "%)") - journal_add_line(journal, "- secondary secret:", sec_stat , "(", round(sec_stat/denom_stat*100,1), "%)") - journal_add_line(journal, "- valid cells:", valid_stat, "(", round(valid_stat/denom_stat*100,1), "%)") - journal_add_break_line(journal) - journal_add_line(journal, "Nb of new common cells hit by the secret:", nrow(modified)) - journal_add_break_line(journal) - journal_add_break_line(journal) - - } - - # Reconstruire la liste des tableaux d'entrée - liste_tbx_res <- purrr::imap( - list_tables, - function(tab,nom){ - expl_vars <- list_explanatory_vars[[nom]] - tab_rows <- table_majeure[[paste0("T_", nom)]] - secret_vars <- names(table_majeure)[grep("^is_secret_[1-9]", names(table_majeure))] - secret_vars <- secret_vars[order(as.integer(gsub("is_secret_", "", secret_vars)))] - res <- merge( - tab, - table_majeure[tab_rows, c(expl_vars, secret_vars)], - all.x = TRUE, all.y = FALSE, by = expl_vars - ) - } - ) - last_secret <- paste0("is_secret_", num_iter_all) - - stats <- purrr::imap_dfr( - liste_tbx_res, - function(tab, name){ - tab$primary_secret <- tab[[secret_var]] - tab$total_secret <- tab[[last_secret]] - tab$secondary_secret <- tab$total_secret & !tab$primary_secret - tab$valid_cells <- !tab$total_secret - res <- data.frame( - tab_name = name, - primary_secret = sum(tab$primary_secret), - secondary_secret = sum(tab$secondary_secret), - total_secret = sum(tab$total_secret), - valid_cells = sum(tab$valid_cells) - ) - } - ) - - purrr::iwalk( - num_iter_par_tab, - function(num,tab){ - journal_add_line( - journal, - "End of iterating after", num, "iterations for", tab - ) - } - ) - journal_add_break_line(journal) - journal_add_line(journal, "Final Summary") - journal_add_break_line(journal) - journal_add_line(journal, "Secreted cells counts per table") - journal_add_break_line(journal) - purrr::walk( - noms_tbx, - function(tab){ - journal_add_line( - journal, - "---TAB ", tab, " ---" - ) - df <- t(stats[stats$tab_name == tab,-1,drop=FALSE]) - suppressWarnings(gdata::write.fwf(df, rownames = TRUE, colnames = FALSE, file = journal, append = TRUE)) - journal_add_break_line(journal) - } - ) - journal_add_break_line(journal) - journal_add_line(journal, "Common cells hit by the secret:") - if(n_common_cells_modified > 0){ - suppressWarnings(gdata::write.fwf(common_cells_modified, file = journal, append = TRUE)) - } - journal_add_break_line(journal) - journal_add_line(journal, "End time: ", format(Sys.time(), "%Y-%m-%d %H:%M:%S")) - journal_add_break_line(journal) - - if(summarise_secret){ - combined_tab <- purrr::imap_dfr(liste_tbx_res, function(tab, name) { - tab |> - rename_with(~"is_secret_final", last_col()) |> - mutate( - status = case_when( - is_secret_prim ~ "primary", - is_secret_final ~ "suppressed", - TRUE ~ "published" - )) - }) - inner_cells <- combined_tab %>% - mutate(status = factor(status, levels = c("primary", "suppressed", "published"))) %>% - group_by(status) %>% - dplyr::summarise( - nb_cells = n(), - value = sum(.data[[params$value]], na.rm = TRUE), - .groups = "drop" - ) %>% - mutate( - pourc_cells = round(nb_cells / sum(nb_cells) * 100, 2), - pourc_value = round(value / sum(value) * 100, 2) - ) - total <- inner_cells %>% - dplyr::summarise( - status = "total", - nb_cells = sum(nb_cells), - value = sum(value), - pourc_cells = 100, - pourc_value = 100 - ) - summary <- dplyr::bind_rows(inner_cells, total) - liste_tbx_res_and_summary <- c(liste_tbx_res, list(secret_summary = summary)) - return(liste_tbx_res_and_summary) - } - - return(liste_tbx_res) -} +#' journal_add_break_line <- function(journal){ +#' sep_char_jour <- "-----------------------------------------" +#' cat(sep_char_jour, file = journal, fill = TRUE, append = TRUE) +#' } +#' +#' journal_add_line <- function(journal,...){ +#' cat(..., file = journal, fill = TRUE, append = TRUE) +#' } +#' +#' #' Manages the secondary secret of a list of tables +#' #' @inheritParams tab_rtauargus +#' #' @param list_tables named list of `data.frame` or `data.table` representing the tables to protect +#' #' @param list_explanatory_vars named list of character vectors of explanatory +#' #' variables of each table mentionned in list_tables. Names of the list are the same as of the list of tables. +#' #' @param alt_hrc named list for alternative hierarchies (useful for non nested-hierarchies) +#' #' @param alt_totcode named list for alternative codes +#' #' @param ip_start integer: Interval protection level to apply at first treatment of each table +#' #' @param ip_end integer: Interval protection level to apply at other treatments +#' #' @param num_iter_max integer: Maximum of treatments to do on each table (default to 10) +#' #' @param summarise_secret boolean: if TRUE adds a dataframe (secret_summary) to the list of protected +#' #' dataframes returned. In this dataframe a summary of the protection is provided. +#' #' @param ... other arguments of `tab_rtauargus2()` +#' #' +#' #' @return original list of tables. Secret Results of each iteration is added to each table. +#' #' For example, the result of first iteration is called 'is_secret_1' in each table. +#' #' It's a boolean variable, whether the cell has to be masked or not. +#' #' +#' #' @seealso `tab_rtauargus2` +#' #' +#' #' @examples +#' #' library(rtauargus) +#' #' library(dplyr) +#' #' data(turnover_act_size) +#' #' data(turnover_act_cj) +#' #' data(activity_corr_table) +#' #' +#' #' #0-Making hrc file of business sectors ---- +#' #' hrc_file_activity <- activity_corr_table %>% +#' #' write_hrc2(file_name = "hrc/activity") +#' #' +#' #' #1-Prepare data ---- +#' #' #Indicate whether each cell complies with the primary rules +#' #' #Boolean variable created is TRUE if the cell doesn't comply. +#' #' #Here the frequency rule is freq in (0;3) +#' #' #and the dominance rule is NK(1,85) +#' #' list_data_2_tabs <- list( +#' #' act_size = turnover_act_size, +#' #' act_cj = turnover_act_cj +#' #' ) %>% +#' #' purrr::map( +#' #' function(df){ +#' #' df %>% +#' #' mutate( +#' #' is_secret_freq = N_OBS > 0 & N_OBS < 3, +#' #' is_secret_dom = ifelse(MAX == 0, FALSE, MAX/TOT>0.85), +#' #' is_secret_prim = is_secret_freq | is_secret_dom +#' #' ) +#' #' } +#' #' ) +#' #' \dontrun{ +#' #' options( +#' #' rtauargus.tauargus_exe = +#' #' "Y:/Logiciels/TauArgus/TauArgus4.2.3/TauArgus.exe" +#' #' ) +#' #' res_1 <- tab_multi_manager( +#' #' list_tables = list_data_2_tabs, +#' #' list_explanatory_vars = list( +#' #' act_size = c("ACTIVITY", "SIZE"), +#' #' act_cj = c("ACTIVITY", "CJ") +#' #' ), +#' #' hrc = c(ACTIVITY = hrc_file_activity), +#' #' dir_name = "tauargus_files", +#' #' value = "TOT", +#' #' freq = "N_OBS", +#' #' secret_var = "is_secret_prim", +#' #' totcode = "Total" +#' #' ) +#' #' +#' #' +#' #' # With the reduction dimensions feature +#' #' +#' #' data("datatest1") +#' #' data("datatest2") +#' #' +#' #' datatest2b <- datatest2 %>% +#' #' filter(cj == "Total", treff == "Total", type_distrib == "Total") %>% +#' #' select(-cj, -treff, -type_distrib) +#' #' +#' #' str(datatest2b) +#' #' +#' #' res <- tab_multi_manager( +#' #' list_tables = list(d1 = datatest1, d2 = datatest2b), +#' #' list_explanatory_vars = list( +#' #' d1 = names(datatest1)[1:4], +#' #' d2 = names(datatest2b)[1:2] +#' #' ), +#' #' dir_name = "tauargus_files", +#' #' value = "pizzas_tot_abs", +#' #' freq = "nb_obs_rnd", +#' #' secret_var = "is_secret_prim", +#' #' totcode = "Total", +#' #' split_tab = TRUE +#' #' ) +#' #' +#' #' } +#' #' +#' #' @importFrom rlang .data +#' #' +#' #' @export +#' +#' tab_multi_manager_cb <- function( +#' list_tables, +#' list_explanatory_vars, +#' dir_name = NULL, +#' hrc = NULL, +#' alt_hrc = NULL, +#' totcode = getOption("rtauargus.totcode"), +#' alt_totcode = NULL, +#' value = "value", +#' freq = "freq", +#' secret_var = "is_secret_prim", +#' cost_var = NULL, +#' suppress = "MOD(1,5,1,0,0)", +#' ip_start = 10, +#' ip_end = 0, +#' num_iter_max = 10, +#' split_tab = FALSE, +#' nb_tab_option = "smart", +#' limit = 14700, +#' summarise_secret = FALSE, +#' ... +#' ){ +#' start_time <- Sys.time() +#' dir_name <- if(is.null(dir_name)) getwd() else dir_name +#' dir.create(dir_name, recursive = TRUE, showWarnings = FALSE) +#' +#' +#' func_to_call <- "tab_rtauargus2" +#' .dots = list(...) +#' params <- param_function(eval(parse(text=func_to_call)), .dots) +#' params$dir_name = dir_name +#' params$cost_var = cost_var +#' params$value = value +#' params$freq = freq +#' params$suppress = suppress +#' params$suppress = suppress +#' params$split_tab = split_tab +#' params$nb_tab_option = nb_tab_option +#' params$limit = limit +#' +#' n_tbx = length(list_tables) # nombre de tableaux +#' +#' if(n_tbx == 0){ +#' stop("Your list of tables is empty !") +#' } +#' if(n_tbx == 1){ +#' stop("To protect a single table, please use the function `tab_rtauargus`.") +#' } +#' if(is.null(names(list_tables))){ +#' names(list_tables) <- paste0("tab", 1:n_tbx) +#' names(list_explanatory_vars) <- paste0("tab", 1:n_tbx) +#' } +#' noms_tbx <- names(list_tables) +#' all_expl_vars <- unique(unname(unlist(list_explanatory_vars))) +#' +#' if( (!is.null(hrc)) & is.list(hrc)) hrc <- unlist(hrc) +#' +#' if( (!is.null(hrc)) & (length(names(hrc)) == 0)){ +#' stop("hrc must have names corresponding to the adequate explanatory variables") +#' } +#' if(length(setdiff(names(hrc), all_expl_vars)) > 0){ +#' stop("some names in hrc argument are not mentionned in list_explanatory_vars") +#' } +#' if(!is.null(alt_hrc)){ +#' if((length(names(alt_hrc)) == 0)){ +#' stop("alt_hrc must have names corresponding to the adequate tables names") +#' } +#' if(length(setdiff(names(alt_hrc), noms_tbx)) > 0){ +#' stop("some names in alt_hrc argument are not mentionned in list_tables") +#' } +#' } +#' if(!is.null(alt_totcode)){ +#' if((length(names(alt_totcode)) == 0)){ +#' stop("alt_totcode must have names corresponding to the adequate tables names") +#' } +#' if(length(setdiff(names(alt_totcode), noms_tbx)) > 0){ +#' stop("some names in alt_totcode argument are not mentionned in list_tables") +#' } +#' } +#' +#' # list_totcode management +#' # first case : list_totcode is one length-character vector : +#' # all the expl variables in all the tables have the same value to refer to the total +#' if(is.character(totcode)){ +#' if(length(totcode) == 1){ +#' list_totcode <- purrr::map( +#' list_explanatory_vars, +#' function(nom_tab){ +#' stats::setNames( +#' rep(totcode, length(nom_tab)), +#' nom_tab +#' ) +#' } +#' ) +#' }else if(length(totcode) == length(all_expl_vars)){ +#' if(is.null(names(totcode))){ +#' stop("totcode of length > 1 must have names (explanatory_vars)") +#' }else{ +#' if(!all(sort(names(totcode)) == sort(all_expl_vars))){ +#' stop("Names of explanatory vars mentioned in totcode are not consistent with those used in list_explanatory_vars") +#' }else{ +#' list_totcode <- purrr::map( +#' list_explanatory_vars, +#' function(nom_vars){ +#' totcode[nom_vars] +#' } +#' ) +#' } +#' } +#' }else{ +#' stop("totcode has to be a character vector of length 1 or a named vector of length equal to the number of unique explanatory vars") +#' } +#' }else{ +#' stop("totcode has to be a character vector of length 1 or a named vector of length equal to the number of unique explanatory vars") +#' } +#' +#' purrr::walk( +#' names(alt_totcode), +#' function(tab){ +#' purrr::walk( +#' names(alt_totcode[[tab]]), +#' function(var) list_totcode[[tab]][[var]] <<- alt_totcode[[tab]][[var]] +#' ) +#' } +#' ) +#' +#' noms_vars_init <- c() +#' for (tab in list_tables){ +#' noms_vars_init <- c(noms_vars_init, names(tab)) +#' } +#' noms_vars_init <- noms_vars_init[!duplicated(noms_vars_init)] +#' +#' noms_col_T <- stats::setNames(paste0("T_", noms_tbx), noms_tbx) +#' +#' table_majeure <- purrr::imap( +#' .x = list_tables, +#' .f = function(tableau,nom_tab){ +#' +#' if(!is.null(cost_var)){ +#' cost_var_tab <- if(cost_var %in% names(tableau)) cost_var else NULL +#' }else{ +#' cost_var_tab <- NULL +#' } +#' secret_var_tab <- if(!is.null(params$secret_no_pl)) c(secret_var,params$secret_no_pl) else secret_var +#' +#' tableau <- as.data.frame(tableau)[, c(list_explanatory_vars[[nom_tab]], value, freq, cost_var_tab, secret_var_tab)] +#' +#' if(!is.null(params$secret_no_pl)){ +#' names(tableau)[names(tableau) == params$secret_no_pl] = "secret_no_pl" +#' } else { +#' tableau$secret_no_pl <- FALSE +#' } +#' +#' var_a_ajouter <- setdiff(all_expl_vars, names(tableau)) +#' for (nom_col in var_a_ajouter){ +#' tableau[[nom_col]] <- unname( +#' purrr::keep( +#' list_totcode, function(x) nom_col %in% names(x) +#' )[[1]][nom_col] +#' ) +#' } +#' +#' tableau[[noms_col_T[[nom_tab]]]] <- TRUE +#' +#' return(as.data.frame(tableau)) +#' } +#' ) +#' +#' # by_vars = setdiff(unique(unlist(purrr::map(table_majeure, names))), noms_col_T) +#' by_vars = purrr::reduce(purrr::map(table_majeure, names), intersect) +#' table_majeure <- purrr::reduce( +#' .x = table_majeure, +#' .f = merge, +#' by = by_vars, +#' all = TRUE +#' ) +#' +#' table_majeure$secret_no_pl_iter <- table_majeure$secret_no_pl +#' secret_no_pl_iter <- "secret_no_pl_iter" +#' +#' purrr::walk( +#' noms_col_T, +#' function(col_T){ +#' e_par <- rlang::env_parent() +#' e_par$table_majeure[[col_T]] <- ifelse( +#' is.na(e_par$table_majeure[[col_T]]), +#' FALSE, +#' e_par$table_majeure[[col_T]] +#' ) +#' } +#' ) +#' +#' # Uniformisation des libelles des variables explicatives +#' # res_unif <- uniformize_labels(table_majeure, all_expl_vars, hrc, list_totcode) +#' # table_majeure <- res_unif$data +#' # hrc_unif <- res_unif$hrc_unif +#' +#' list_hrc <- purrr::map( +#' list_explanatory_vars, +#' function(nom_vars){ +#' purrr::discard(hrc[nom_vars], is.na) %>% unlist() +#' } +#' ) +#' +#' list_hrc <- purrr::map(list_hrc, function(l) if(length(l) == 0) NULL else l) +#' +#' purrr::walk( +#' names(alt_hrc), +#' function(tab){ +#' purrr::walk( +#' names(alt_hrc[[tab]]), +#' function(var) list_hrc[[tab]][[var]] <<- alt_hrc[[tab]][[var]] +#' ) +#' } +#' ) +#' +#' # listes de travail +#' +#' has_primary_secret <- purrr::map_lgl( +#' list_tables, +#' function(tab){ +#' sum(tab[[secret_var]]) != 0 +#' } +#' ) +#' if(sum(has_primary_secret) == 0){ +#' message("None of the tables have any primary secret cells") +#' return(list_tables) +#' } +#' todolist <- noms_tbx[has_primary_secret][1] +#' remainlist <- noms_tbx[has_primary_secret][-1] +#' +#' num_iter_par_tab = stats::setNames(rep(0, length(list_tables)), noms_tbx) +#' num_iter_par_tab[!has_primary_secret] <- 1 +#' num_iter_all = 0 +#' +#' # common_cells_modified <- as.data.frame(matrix(ncol = length(all_expl_vars)+1)) +#' # names(common_cells_modified) <- c(all_expl_vars, "iteration") +#' +#' n_common_cells_modified <- 0 +#' +#' journal <- file.path(dir_name,"journal.txt") +#' if(file.exists(journal)) invisible(file.remove(journal)) +#' journal_add_line(journal, "Start time:", format(start_time, "%Y-%m-%d %H:%M:%S")) +#' journal_add_break_line(journal) +#' journal_add_line(journal, "Function called to protect the tables:", func_to_call) +#' journal_add_line(journal, "Interval Protection Level for primary secret cells:", ip_start) +#' journal_add_line(journal, "Interval Protection Level for other iterations:", ip_end) +#' journal_add_line(journal, "Nb of tables to treat: ", n_tbx) +#' journal_add_break_line(journal) +#' journal_add_line(journal, "Tables to treat:", noms_tbx) +#' journal_add_break_line(journal) +#' journal_add_line(journal, "All explanatory variables:", all_expl_vars) +#' journal_add_break_line(journal) +#' journal_add_line(journal, "Initialisation work completed") +#' journal_add_break_line(journal) +#' journal_add_break_line(journal) +#' +#' while(length(todolist) > 0 & all(num_iter_par_tab <= num_iter_max)){ +#' +#' num_iter_all <- num_iter_all + 1 +#' num_tableau <- todolist[1] +#' num_iter_par_tab[num_tableau] <- num_iter_par_tab[num_tableau] + 1 +#' cat("--- Current table to treat: ", num_tableau, "---\n") +#' +#' nom_col_identifiante <- paste0("T_", num_tableau) +#' tableau_a_traiter <- which(table_majeure[[nom_col_identifiante]]) +#' +#' if (num_iter_all == 1){ +#' var_secret_apriori <- secret_var +#' } else { +#' var_secret_apriori <- paste0("is_secret_", num_iter_all-1, collapse = "") +#' } +#' +#' vrai_tableau <- table_majeure[tableau_a_traiter,] +#' +#' ex_var <- list_explanatory_vars[[num_tableau]] +#' +#' vrai_tableau <- vrai_tableau[,c(ex_var, value, freq,var_secret_apriori,secret_no_pl_iter, cost_var)] +#' +#' +#' # Other settings of the function to make secret ---- +#' params$tabular = vrai_tableau +#' params$files_name = num_tableau +#' params$explanatory_vars = ex_var +#' params$totcode = list_totcode[[num_tableau]] +#' params$hrc = list_hrc[[num_tableau]] +#' params$secret_var = var_secret_apriori +#' params$secret_no_pl = secret_no_pl_iter +#' params$suppress = if( +#' substr(suppress,1,3) == "MOD" & num_iter_par_tab[num_tableau] != 1 +#' ){ +#' # if modular deactivation of singleton and multisingleton after the first iteration +#' paste0( +#' paste( +#' c(strsplit(suppress, split = ",")[[1]][1:2], rep("0",3)), collapse = "," +#' ), +#' ")" +#' ) +#' }else{ +#' suppress +#' } +#' params$ip = if(num_iter_par_tab[num_tableau] == 1) ip_start else ip_end +#' # params$safety_rules <- "MAN(0)" +#' +#' res <- do.call(func_to_call, params) +#' res$is_secret <- res$Status != "V" +#' +#' # Statistiques +#' prim_stat <- sum(res$Status == "B", na.rm = TRUE) +#' sec_stat <- sum(res$Status == "D", na.rm = TRUE) +#' valid_stat <- sum(res$Status == "V", na.rm = TRUE) +#' denom_stat <- nrow(res) +#' +#' res <- subset(res, select = setdiff(names(res), "Status")) +#' +#' var_secret <- paste0("is_secret_", num_iter_all) +#' table_majeure <- merge(table_majeure, res, all = TRUE) +#' table_majeure[[var_secret]] <- table_majeure$is_secret +#' table_majeure <- subset( +#' table_majeure, +#' select = setdiff(names(table_majeure), "is_secret") +#' ) +#' +#' +#' table_majeure[[var_secret]] <- ifelse( +#' is.na(table_majeure[[var_secret]]), +#' table_majeure[[var_secret_apriori]], +#' table_majeure[[var_secret]] +#' ) +#' +#' table_majeure$secret_no_pl_iter <- ifelse( +#' table_majeure[[secret_var]], +#' table_majeure$secret_no_pl, +#' table_majeure[[var_secret]] +#' ) #TODO A REVOIR PR CORRIGER LES PL +#' +#' lignes_modifs <- which(table_majeure[[var_secret_apriori]] != table_majeure[[var_secret]]) +#' +#' cur_tab <- paste0("T_", num_tableau) +#' other_tabs <- setdiff(noms_col_T, cur_tab) +#' cur_cells <- rowSums(table_majeure[, cur_tab, drop=FALSE]) +#' other_cells <- rowSums(table_majeure[, other_tabs, drop=FALSE]) +#' +#' common_cells_rows <- which(cur_cells == 1 & other_cells > 0) +#' common_cells <- table_majeure[common_cells_rows, , drop=FALSE] +#' +#' # update of common cells that have been modified +#' modified <- common_cells[common_cells[[var_secret_apriori]] != common_cells[[var_secret]],all_expl_vars, drop=FALSE] +#' # modified <- if(sum(is.na(modified))>0) modified[1,][-1,] else modified +#' if(nrow(modified) > 0){ +#' modified <- cbind(modified, iteration = num_iter_all) +#' common_cells_modified <- if(n_common_cells_modified == 0) modified else rbind(common_cells_modified, modified) +#' n_common_cells_modified <- n_common_cells_modified + nrow(modified) +#' } +#' +#' for(tab in noms_tbx){ +#' nom_col_identifiante <- paste0("T_", tab) +#' if( !(tab %in% todolist) +#' & (any(table_majeure[[nom_col_identifiante]][lignes_modifs])) +#' ){ +#' todolist <- append(todolist,tab) +#' remainlist <- remainlist[remainlist != tab] +#' } +#' } +#' +#' todolist <- todolist[-1] +#' if(length(todolist) == 0){ +#' if(length(remainlist) > 0){ +#' todolist <- remainlist[1] +#' remainlist <- remainlist[-1] +#' } +#' } +#' +#' journal_add_line(journal, num_iter_all, "-Treatment of table", num_tableau) +#' journal_add_break_line(journal) +#' journal_add_line(journal, "New cells status counts: ") +#' journal_add_line(journal, "- apriori (primary) secret:", prim_stat, "(", round(prim_stat/denom_stat*100,1), "%)") +#' journal_add_line(journal, "- secondary secret:", sec_stat , "(", round(sec_stat/denom_stat*100,1), "%)") +#' journal_add_line(journal, "- valid cells:", valid_stat, "(", round(valid_stat/denom_stat*100,1), "%)") +#' journal_add_break_line(journal) +#' journal_add_line(journal, "Nb of new common cells hit by the secret:", nrow(modified)) +#' journal_add_break_line(journal) +#' journal_add_break_line(journal) +#' +#' } +#' +#' # Reconstruire la liste des tableaux d'entrée +#' liste_tbx_res <- purrr::imap( +#' list_tables, +#' function(tab,nom){ +#' expl_vars <- list_explanatory_vars[[nom]] +#' tab_rows <- table_majeure[[paste0("T_", nom)]] +#' secret_vars <- names(table_majeure)[grep("^is_secret_[1-9]", names(table_majeure))] +#' secret_vars <- secret_vars[order(as.integer(gsub("is_secret_", "", secret_vars)))] +#' res <- merge( +#' tab, +#' table_majeure[tab_rows, c(expl_vars, secret_vars)], +#' all.x = TRUE, all.y = FALSE, by = expl_vars +#' ) +#' } +#' ) +#' last_secret <- paste0("is_secret_", num_iter_all) +#' +#' stats <- purrr::imap_dfr( +#' liste_tbx_res, +#' function(tab, name){ +#' tab$primary_secret <- tab[[secret_var]] +#' tab$total_secret <- tab[[last_secret]] +#' tab$secondary_secret <- tab$total_secret & !tab$primary_secret +#' tab$valid_cells <- !tab$total_secret +#' res <- data.frame( +#' tab_name = name, +#' primary_secret = sum(tab$primary_secret), +#' secondary_secret = sum(tab$secondary_secret), +#' total_secret = sum(tab$total_secret), +#' valid_cells = sum(tab$valid_cells) +#' ) +#' } +#' ) +#' +#' purrr::iwalk( +#' num_iter_par_tab, +#' function(num,tab){ +#' journal_add_line( +#' journal, +#' "End of iterating after", num, "iterations for", tab +#' ) +#' } +#' ) +#' journal_add_break_line(journal) +#' journal_add_line(journal, "Final Summary") +#' journal_add_break_line(journal) +#' journal_add_line(journal, "Secreted cells counts per table") +#' journal_add_break_line(journal) +#' purrr::walk( +#' noms_tbx, +#' function(tab){ +#' journal_add_line( +#' journal, +#' "---TAB ", tab, " ---" +#' ) +#' df <- t(stats[stats$tab_name == tab,-1,drop=FALSE]) +#' suppressWarnings(gdata::write.fwf(df, rownames = TRUE, colnames = FALSE, file = journal, append = TRUE)) +#' journal_add_break_line(journal) +#' } +#' ) +#' journal_add_break_line(journal) +#' journal_add_line(journal, "Common cells hit by the secret:") +#' if(n_common_cells_modified > 0){ +#' suppressWarnings(gdata::write.fwf(common_cells_modified, file = journal, append = TRUE)) +#' } +#' journal_add_break_line(journal) +#' journal_add_line(journal, "End time: ", format(Sys.time(), "%Y-%m-%d %H:%M:%S")) +#' journal_add_break_line(journal) +#' +#' if(summarise_secret){ +#' combined_tab <- purrr::imap_dfr(liste_tbx_res, function(tab, name) { +#' tab |> +#' rename_with(~"is_secret_final", last_col()) |> +#' mutate( +#' status = case_when( +#' is_secret_prim ~ "primary", +#' is_secret_final ~ "suppressed", +#' TRUE ~ "published" +#' )) +#' }) +#' inner_cells <- combined_tab %>% +#' mutate(status = factor(status, levels = c("primary", "suppressed", "published"))) %>% +#' group_by(status) %>% +#' dplyr::summarise( +#' nb_cells = n(), +#' value = sum(.data[[params$value]], na.rm = TRUE), +#' .groups = "drop" +#' ) %>% +#' mutate( +#' pourc_cells = round(nb_cells / sum(nb_cells) * 100, 2), +#' pourc_value = round(value / sum(value) * 100, 2) +#' ) +#' total <- inner_cells %>% +#' dplyr::summarise( +#' status = "total", +#' nb_cells = sum(nb_cells), +#' value = sum(value), +#' pourc_cells = 100, +#' pourc_value = 100 +#' ) +#' summary <- dplyr::bind_rows(inner_cells, total) +#' liste_tbx_res_and_summary <- c(liste_tbx_res, list(secret_summary = summary)) +#' return(liste_tbx_res_and_summary) +#' } +#' +#' return(liste_tbx_res) +#' } From 85964458337a1005599a068aaaa4c4f999866a59 Mon Sep 17 00:00:00 2001 From: julienjamme Date: Thu, 13 Aug 2026 14:56:57 +0200 Subject: [PATCH 06/15] add option summary_secret in tab_rtauargus --- R/tab_rtauargus.R | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/R/tab_rtauargus.R b/R/tab_rtauargus.R index 4752ec8..f55e897 100644 --- a/R/tab_rtauargus.R +++ b/R/tab_rtauargus.R @@ -127,6 +127,7 @@ tab_rtauargus <- function( maxscore = NULL, suppress = "MOD(1,5,1,0,0)", safety_rules = paste0("MAN(",ip,")"), + summary_secret = FALSE, show_batch_console = FALSE, output_type = 4, output_options = "", @@ -316,8 +317,16 @@ and the process may take longer.") row.names = FALSE ) - return(mask) - + if(summary_secret){ + return( + list( + mask = mask, + stats = summarize_secret(mask, var = value, secret_var = secret_var) + ) + ) + }else{ + return(mask) + } } } From 4709324c71c73b3ac185b31ecbcd64e9f0db918a Mon Sep 17 00:00:00 2001 From: julienjamme Date: Thu, 13 Aug 2026 14:58:11 +0200 Subject: [PATCH 07/15] adjustment of tab_rtauargus2: no summary --- R/tab_rtauargus.R | 1 + 1 file changed, 1 insertion(+) diff --git a/R/tab_rtauargus.R b/R/tab_rtauargus.R index f55e897..31856f8 100644 --- a/R/tab_rtauargus.R +++ b/R/tab_rtauargus.R @@ -443,6 +443,7 @@ tab_rtauargus2 <- function( params$dir_name = if(params$split_tab) file.path(dir_name, files_name) else dir_name params$nb_tab_option = nb_tab_option params$limit = limit + params$summary_secret = FALSE params$show_batch_console = FALSE params$output_type = 4 params$output_options = "" From 2d528b05df8159fc1250141d7fcfbfa7ea73b630 Mon Sep 17 00:00:00 2001 From: julienjamme Date: Thu, 13 Aug 2026 15:36:08 +0200 Subject: [PATCH 08/15] add option summary_secret and factorization with the journal --- R/multitable.R | 44 ++++++++++++++++++++------------------------ 1 file changed, 20 insertions(+), 24 deletions(-) diff --git a/R/multitable.R b/R/multitable.R index 3e7e3fd..a945bd1 100644 --- a/R/multitable.R +++ b/R/multitable.R @@ -122,6 +122,7 @@ tab_multi_manager <- function( ip_start = 10, ip_end = 0, num_iter_max = 10, + summary_secret = FALSE, split_tab = FALSE, nb_tab_option = "smart", limit = 14700, @@ -506,24 +507,10 @@ tab_multi_manager <- function( ) } ) - last_secret <- paste0("is_secret_", num_iter_all) - stats <- purrr::imap_dfr( - liste_tbx_res, - function(tab, name){ - tab$primary_secret <- tab[[secret_var]] - tab$total_secret <- tab[[last_secret]] - tab$secondary_secret <- tab$total_secret & !tab$primary_secret - tab$valid_cells <- !tab$total_secret - res <- data.frame( - tab_name = name, - primary_secret = sum(tab$primary_secret), - secondary_secret = sum(tab$secondary_secret), - total_secret = sum(tab$total_secret), - valid_cells = sum(tab$valid_cells) - ) - } - ) + stats_out <- summarize_secret(liste_tbx_res, var = value, secret_var = secret_var) + + last_secret <- paste0("is_secret_", num_iter_all) purrr::iwalk( num_iter_par_tab, @@ -539,15 +526,15 @@ tab_multi_manager <- function( journal_add_break_line(journal) journal_add_line(journal, "Secreted cells counts per table") journal_add_break_line(journal) - purrr::walk( - noms_tbx, - function(tab){ + purrr::iwalk( + stats_out, + function(tab_stats, tab_name){ journal_add_line( journal, - "---TAB ", tab, " ---" + "---TAB ", tab_name, " ---" ) - df <- t(stats[stats$tab_name == tab,-1,drop=FALSE]) - suppressWarnings(gdata::write.fwf(df, rownames = TRUE, colnames = FALSE, file = journal, append = TRUE)) + # df <- t(stats[stats$tab_name == tab,-1,drop=FALSE]) + suppressWarnings(gdata::write.fwf(tab_stats, rownames = TRUE, colnames = TRUE, file = journal, append = TRUE)) journal_add_break_line(journal) } ) @@ -560,5 +547,14 @@ tab_multi_manager <- function( journal_add_line(journal, "End time: ", format(Sys.time(), "%Y-%m-%d %H:%M:%S")) journal_add_break_line(journal) - return(liste_tbx_res) + + if(summary_secret){ + + return( append(liste_tbx_res, list(stats = stats_out)) ) + + }else{ + + return(liste_tbx_res) + } + } From e0956df8a145508ce86bf060c647d55086ce0751 Mon Sep 17 00:00:00 2001 From: julienjamme Date: Thu, 13 Aug 2026 15:42:58 +0200 Subject: [PATCH 09/15] document summary secret argument --- R/tab_rtauargus.R | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/R/tab_rtauargus.R b/R/tab_rtauargus.R index 31856f8..c129f8b 100644 --- a/R/tab_rtauargus.R +++ b/R/tab_rtauargus.R @@ -11,6 +11,7 @@ #' All files will have the same name, only their extension will be different. #' @param dir_name string indicated the path of the directory in which to save #' all the files (.rda, .hst, .txt, .arb, .csv) generated by the function. +#' @param summary_secret If `TRUE`, a statistical summary of the suppression is provided along with the masked data. #' @param unif_labels boolean, if explanatory variables have to be standardized #' @param split_tab `r lifecycle::badge("experimental")` boolean, #' whether to reduce dimension to 3 while treating a table of dimension 4 or 5 @@ -31,12 +32,16 @@ #' for the treatment of tabular. #' #' @return -#' If output_type equals to 4 and split_tab = FALSE, +#' If output_type equals to 4 and summary_secret = FALSE and split_tab = FALSE, #' then the original tabular is returned with a new #' column called Status, indicating the status of the cell coming from Tau-Argus : #' "A" for a primary secret due to frequency rule, "B" for a primary secret due #' to dominance rule, "D" for secondary secret and "V" for no secret cell. #' +#' If summary_secret = TRUE, +#' then the function returns a list of two data.frames, +#' including the masked data (`mask`) and the stats data (`stats`). +#' #' If split_tab = TRUE, #' then the original tabular is returned with some new columns which are boolean #' variables indicating the status of a cell at each iteration of the protection @@ -45,7 +50,8 @@ #' final status of the suppression process of the original table. #' #' If `split_tab = FALSE` and `output_type` doesn't equal to `4`, -#' then the raw result from tau-argus is returned. +#' then the raw result from tau-argus is written as a csv file in `dir_name` +#' directory and `NULL` is returned to R. #' #' @section Standardization of explanatory variables and hierarchies: #' From 30026df648bb0b73a9bcc5c782f045575ae733b5 Mon Sep 17 00:00:00 2001 From: julienjamme Date: Thu, 13 Aug 2026 15:43:28 +0200 Subject: [PATCH 10/15] round the pourcentages --- R/summarize_secret.R | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/R/summarize_secret.R b/R/summarize_secret.R index b5ea67d..659c5f9 100644 --- a/R/summarize_secret.R +++ b/R/summarize_secret.R @@ -82,15 +82,15 @@ summarize_secret <- function(res_tau, var = NULL, secret_var = "is_secret_prim") stats <- tab_mod |> group_by(status) %>% - {if( ! is.null(var) ) summarise(., nb_cells = n(), value = sum(VALUE)) else summarise(., nb_cells = n()) } |> + {if( ! is.null(var) ) summarise(., nb_cells = n(), value = sum(VALUE) ) else summarise(., nb_cells = n()) } |> bind_rows( tibble( status = "total", tab_mod %>% {if( ! is.null(var) ) summarise(., nb_cells = n(), value = sum(VALUE)) else summarise(., nb_cells = n()) } ) ) |> - mutate(pourc_cells = nb_cells/nb_cells[status == "total"]*100) %>% - {if( ! is.null(var) ) mutate(., pourc_value = value/value[status == "total"]*100 ) else . } + mutate(pourc_cells = round( nb_cells/nb_cells[status == "total"]*100, 2 ) ) %>% + {if( ! is.null(var) ) mutate(., round( pourc_value = value/value[status == "total"]*100, 2 ) ) else . } return(stats) From 0d13132c70f9429b914ce9906ca0cad058edb290 Mon Sep 17 00:00:00 2001 From: julienjamme Date: Thu, 13 Aug 2026 15:45:40 +0200 Subject: [PATCH 11/15] add @return value --- R/summarize_secret.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/summarize_secret.R b/R/summarize_secret.R index 659c5f9..a7698d4 100644 --- a/R/summarize_secret.R +++ b/R/summarize_secret.R @@ -5,7 +5,7 @@ #' @param var the quantitative variable name to use for values stats of suppression #' (default to `NULL` that is the stats are only computed depending on the number of cells ) #' @param secret_var the name of the variable indicating the primary suppressed cells -#' @returns +#' @returns data.frame or list of data.frames #' @export #' #' @examples From a48f0064568835f8e252bdd41d45fa41cdc6effd Mon Sep 17 00:00:00 2001 From: julienjamme Date: Thu, 13 Aug 2026 16:11:12 +0200 Subject: [PATCH 12/15] document and check --- DESCRIPTION | 2 +- NAMESPACE | 92 ++++++++++++++++++++++++---------------- R/globals.R | 3 +- R/summarize_secret.R | 26 +++++++----- man/rtauargus-package.Rd | 1 + man/summarize_secret.Rd | 64 ++++++++++++++++++++++++++++ man/tab_multi_manager.Rd | 3 ++ man/tab_rtauargus.Rd | 12 +++++- 8 files changed, 152 insertions(+), 51 deletions(-) create mode 100644 man/summarize_secret.Rd diff --git a/DESCRIPTION b/DESCRIPTION index 77da1a1..b240bfd 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -67,7 +67,6 @@ Description: Protects tables by calling the Tau-Argus software from R. License: MIT + file LICENSE Encoding: UTF-8 LazyData: true -RoxygenNote: 7.3.3 VignetteBuilder: knitr URL: https://inseefrlab.github.io/rtauargus, https://github.com/inseefrlab/rtauargus, @@ -75,3 +74,4 @@ URL: https://inseefrlab.github.io/rtauargus, BugReports: https://github.com/inseefrlab/rtauargus/issues Roxygen: list(markdown = TRUE) StagedInstall: no +Config/roxygen2/version: 8.1.0 diff --git a/NAMESPACE b/NAMESPACE index 5fdf8a9..83d0eb6 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -28,6 +28,7 @@ export(run_arb) export(sp_format) export(split_dataframe) export(split_in_clusters) +export(summarize_secret) export(tab_arb) export(tab_multi_manager) export(tab_rda) @@ -41,45 +42,62 @@ export(write_hrc) export(write_hrc2) import(data.table, except = transpose) import(utils) -importFrom(dplyr,"%>%") -importFrom(dplyr,across) -importFrom(dplyr,all_of) -importFrom(dplyr,arrange) -importFrom(dplyr,bind_rows) -importFrom(dplyr,distinct) -importFrom(dplyr,everything) -importFrom(dplyr,filter) -importFrom(dplyr,group_by) -importFrom(dplyr,left_join) -importFrom(dplyr,mutate) -importFrom(dplyr,n_distinct) -importFrom(dplyr,pull) -importFrom(dplyr,rename) -importFrom(dplyr,row_number) -importFrom(dplyr,rowwise) -importFrom(dplyr,select) -importFrom(dplyr,summarise) -importFrom(dplyr,ungroup) -importFrom(dplyr,where) -importFrom(igraph,graph_from_data_frame) -importFrom(igraph,which_mutual) -importFrom(lifecycle,badge) -importFrom(lifecycle,deprecated) -importFrom(purrr,compact) -importFrom(purrr,discard) -importFrom(purrr,imap_dfr) -importFrom(purrr,map) -importFrom(purrr,map2) -importFrom(purrr,map_at) -importFrom(purrr,transpose) +importFrom(dplyr, + "%>%", + across, + all_of, + arrange, + bind_rows, + case_when, + distinct, + everything, + filter, + group_by, + last_col, + left_join, + mutate, + n, + n_distinct, + pull, + rename, + rename_with, + row_number, + rowwise, + select, + summarise, + tibble, + ungroup, + where +) +importFrom(igraph, + graph_from_data_frame, + which_mutual +) +importFrom(lifecycle, + badge, + deprecated +) +importFrom(purrr, + compact, + discard, + imap_dfr, + map, + map2, + map_at, + transpose +) importFrom(rlang,.data) -importFrom(sdcHierarchies,hier_convert) -importFrom(sdcHierarchies,hier_import) +importFrom(sdcHierarchies, + hier_convert, + hier_import +) importFrom(stats,setNames) importFrom(stringr,str_detect) -importFrom(tidyr,nest) -importFrom(tidyr,pivot_longer) -importFrom(tidyr,unnest) -importFrom(tidyr,unnest_wider) +importFrom(tidyr, + nest, + pivot_longer, + unnest, + unnest_wider +) importFrom(utils,combn) importFrom(zoo,na.locf) diff --git a/R/globals.R b/R/globals.R index 24bc305..35dd4a0 100644 --- a/R/globals.R +++ b/R/globals.R @@ -4,6 +4,7 @@ utils::globalVariables( "n_unique","column","unique_modalities","from.eg","to.eg","from","to","mutual_full", "Group","table_eg","spanning","hrc_spanning","spanning_old","tab_inclus", "starts_with","spanning_name","hrc_spanning_name","eq_indicator","rhs","total","term_number", - "eq_name","unit","var","n_total","total_alt","group", + "eq_name","unit","var","n_total","total_alt","group", "VALUE", "status", "nb_cells", + "value", ".") ) diff --git a/R/summarize_secret.R b/R/summarize_secret.R index a7698d4..12d4ed3 100644 --- a/R/summarize_secret.R +++ b/R/summarize_secret.R @@ -48,6 +48,12 @@ #' summarize_secret(res, "TOT") #' summarize_secret(res) #' } +#' @importFrom dplyr case_when +#' @importFrom dplyr last_col +#' @importFrom dplyr tibble +#' @importFrom dplyr rename_with +#' @importFrom dplyr n +#' @importFrom purrr list_c summarize_secret <- function(res_tau, var = NULL, secret_var = "is_secret_prim"){ if( is.data.frame(res_tau) ) { @@ -65,16 +71,16 @@ summarize_secret <- function(res_tau, var = NULL, secret_var = "is_secret_prim") } tab_mod <- res_tau %>% - {if( ! is.null(var) ) rename_with(., ~"VALUE", all_of(var)) else .} |> - rename_with(~"final_status_ta", last_col()) |> - rename_with(~"is_secret_prim", all_of(secret_var)) |> - mutate( + {if( ! is.null(var) ) dplyr::rename_with(., ~"VALUE", all_of(var)) else .} |> + dplyr::rename_with(~"final_status_ta", last_col()) |> + dplyr::rename_with(~"is_secret_prim", all_of(secret_var)) |> + dplyr::mutate( status = case_when( is_secret_prim ~ "primary suppr.", final_status_ta != "V" ~ "secondary suppr.", TRUE ~ "published" )) |> - mutate(status = factor( + dplyr::mutate(status = factor( status, levels = c("primary suppr.", "secondary suppr.", "published", "total"), ordered = TRUE) @@ -82,15 +88,15 @@ summarize_secret <- function(res_tau, var = NULL, secret_var = "is_secret_prim") stats <- tab_mod |> group_by(status) %>% - {if( ! is.null(var) ) summarise(., nb_cells = n(), value = sum(VALUE) ) else summarise(., nb_cells = n()) } |> - bind_rows( - tibble( + {if( ! is.null(var) ) dplyr::summarise(., nb_cells = n(), value = sum(VALUE) ) else dplyr::summarise(., nb_cells = n()) } |> + dplyr::bind_rows( + dplyr::tibble( status = "total", - tab_mod %>% {if( ! is.null(var) ) summarise(., nb_cells = n(), value = sum(VALUE)) else summarise(., nb_cells = n()) } + tab_mod %>% {if( ! is.null(var) ) dplyr::summarise(., nb_cells = n(), value = sum(VALUE)) else dplyr::summarise(., nb_cells = n()) } ) ) |> mutate(pourc_cells = round( nb_cells/nb_cells[status == "total"]*100, 2 ) ) %>% - {if( ! is.null(var) ) mutate(., round( pourc_value = value/value[status == "total"]*100, 2 ) ) else . } + {if( ! is.null(var) ) dplyr::mutate(., round( pourc_value = value/value[status == "total"]*100, 2 ) ) else . } return(stats) diff --git a/man/rtauargus-package.Rd b/man/rtauargus-package.Rd index 29e9789..5f1c96b 100644 --- a/man/rtauargus-package.Rd +++ b/man/rtauargus-package.Rd @@ -23,6 +23,7 @@ Useful links: Authors: \itemize{ + \item Julien Jamme \email{julien.jamme@insee.fr} \item Pierre-Yves Berrard \email{pierre-yves.berrard@insee.fr} \item Nathanaël Rastout \email{nathanael.rastout@insee.fr} \item Jeanne Pointet diff --git a/man/summarize_secret.Rd b/man/summarize_secret.Rd new file mode 100644 index 0000000..876f857 --- /dev/null +++ b/man/summarize_secret.Rd @@ -0,0 +1,64 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/summarize_secret.R +\name{summarize_secret} +\alias{summarize_secret} +\title{Provide the summary of the suppression pattern from a rtauargus result} +\usage{ +summarize_secret(res_tau, var = NULL, secret_var = "is_secret_prim") +} +\arguments{ +\item{res_tau}{either the data.frame resulting from tab_rtauargus run or +the list of data.frame resulting from tab_multimanager run} + +\item{var}{the quantitative variable name to use for values stats of suppression +(default to \code{NULL} that is the stats are only computed depending on the number of cells )} + +\item{secret_var}{the name of the variable indicating the primary suppressed cells} +} +\value{ +data.frame or list of data.frames +} +\description{ +Provide the summary of the suppression pattern from a rtauargus result +} +\examples{ +\dontrun{ +library(dplyr) +data(turnover_act_size) + +# Prepare data with primary secret ---- +turnover_act_size <- turnover_act_size \%>\% + mutate( + is_secret_freq = N_OBS > 0 & N_OBS < 3, + is_secret_dom = ifelse(MAX == 0, FALSE, MAX/TOT>0.85), + is_secret_prim = is_secret_freq | is_secret_dom + ) + +# Make hrc file of business sectors ---- +data(activity_corr_table) +hrc_file_activity <- activity_corr_table \%>\% + write_hrc2(file_name = "hrc/activity") + +# Compute the secondary secret ---- +options( + rtauargus.tauargus_exe = + "Y:/Logiciels/TauArgus/TauArgus4.2.3/TauArgus.exe" +) + +res <- tab_rtauargus( + tabular = turnover_act_size, + files_name = "turn_act_size", + dir_name = "tauargus_files", + explanatory_vars = c("ACTIVITY", "SIZE"), + hrc = c(ACTIVITY = hrc_file_activity), + totcode = c(ACTIVITY = "Total", SIZE = "Total"), + secret_var = "is_secret_prim", + value = "TOT", + freq = "N_OBS", + verbose = FALSE +) + +summarize_secret(res, "TOT") +summarize_secret(res) +} +} diff --git a/man/tab_multi_manager.Rd b/man/tab_multi_manager.Rd index 872e54c..6859740 100644 --- a/man/tab_multi_manager.Rd +++ b/man/tab_multi_manager.Rd @@ -20,6 +20,7 @@ tab_multi_manager( ip_start = 10, ip_end = 0, num_iter_max = 10, + summary_secret = FALSE, split_tab = FALSE, nb_tab_option = "smart", limit = 14700, @@ -89,6 +90,8 @@ parameters for it.\cr \item{num_iter_max}{integer: Maximum of treatments to do on each table (default to 10)} +\item{summary_secret}{If \code{TRUE}, a statistical summary of the suppression is provided along with the masked data.} + \item{split_tab}{\ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#experimental}{\figure{lifecycle-experimental.svg}{options: alt='[Experimental]'}}}{\strong{[Experimental]}} boolean, whether to reduce dimension to 3 while treating a table of dimension 4 or 5 (default to \code{FALSE})} diff --git a/man/tab_rtauargus.Rd b/man/tab_rtauargus.Rd index 7803b44..c365d5a 100644 --- a/man/tab_rtauargus.Rd +++ b/man/tab_rtauargus.Rd @@ -20,6 +20,7 @@ tab_rtauargus( maxscore = NULL, suppress = "MOD(1,5,1,0,0)", safety_rules = paste0("MAN(", ip, ")"), + summary_secret = FALSE, show_batch_console = FALSE, output_type = 4, output_options = "", @@ -114,6 +115,8 @@ for example.\cr Chaîne de caractères en syntaxe batch Tau-Argus. Si le secret primaire a été traité dans un fichier d'apriori : utiliser "MAN(10)")} +\item{summary_secret}{If \code{TRUE}, a statistical summary of the suppression is provided along with the masked data.} + \item{show_batch_console}{to display the batch progress in the console. \cr (pour afficher le déroulement du batch dans la @@ -156,12 +159,16 @@ tauargus failures} for the treatment of tabular.} } \value{ -If output_type equals to 4 and split_tab = FALSE, +If output_type equals to 4 and summary_secret = FALSE and split_tab = FALSE, then the original tabular is returned with a new column called Status, indicating the status of the cell coming from Tau-Argus : "A" for a primary secret due to frequency rule, "B" for a primary secret due to dominance rule, "D" for secondary secret and "V" for no secret cell. +If summary_secret = TRUE, +then the function returns a list of two data.frames, +including the masked data (\code{mask}) and the stats data (\code{stats}). + If split_tab = TRUE, then the original tabular is returned with some new columns which are boolean variables indicating the status of a cell at each iteration of the protection @@ -170,7 +177,8 @@ denotes a cell that have to be suppressed. The last column is then the final status of the suppression process of the original table. If \code{split_tab = FALSE} and \code{output_type} doesn't equal to \code{4}, -then the raw result from tau-argus is returned. +then the raw result from tau-argus is written as a csv file in \code{dir_name} +directory and \code{NULL} is returned to R. } \description{ The function prepares all the files needed by Tau-Argus and launches the From 7c0bb57b83cea79d7804e15b0f6e4c9642a07905 Mon Sep 17 00:00:00 2001 From: Julien Jamme Date: Thu, 13 Aug 2026 14:33:07 +0000 Subject: [PATCH 13/15] check package adjustment --- DESCRIPTION | 2 +- NAMESPACE | 97 +++++++++++++++++++++-------------------------- R/writehrc.R | 22 ++++++++--- man/write_hrc2.Rd | 22 ++++++++--- 4 files changed, 78 insertions(+), 65 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index b240bfd..7c43b9e 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -3,7 +3,7 @@ Type: Package Title: Using Tau-Argus from R Language: fr Version: 1.3.4 -Depends: R (>= 3.5.0) +Depends: R (>= 4.1.0) Imports: purrr (>= 0.2), dplyr (>= 0.7), diff --git a/NAMESPACE b/NAMESPACE index 83d0eb6..9b39347 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -42,62 +42,51 @@ export(write_hrc) export(write_hrc2) import(data.table, except = transpose) import(utils) -importFrom(dplyr, - "%>%", - across, - all_of, - arrange, - bind_rows, - case_when, - distinct, - everything, - filter, - group_by, - last_col, - left_join, - mutate, - n, - n_distinct, - pull, - rename, - rename_with, - row_number, - rowwise, - select, - summarise, - tibble, - ungroup, - where -) -importFrom(igraph, - graph_from_data_frame, - which_mutual -) -importFrom(lifecycle, - badge, - deprecated -) -importFrom(purrr, - compact, - discard, - imap_dfr, - map, - map2, - map_at, - transpose -) +importFrom(dplyr,"%>%") +importFrom(dplyr,across) +importFrom(dplyr,all_of) +importFrom(dplyr,arrange) +importFrom(dplyr,bind_rows) +importFrom(dplyr,case_when) +importFrom(dplyr,distinct) +importFrom(dplyr,everything) +importFrom(dplyr,filter) +importFrom(dplyr,group_by) +importFrom(dplyr,last_col) +importFrom(dplyr,left_join) +importFrom(dplyr,mutate) +importFrom(dplyr,n) +importFrom(dplyr,n_distinct) +importFrom(dplyr,pull) +importFrom(dplyr,rename) +importFrom(dplyr,rename_with) +importFrom(dplyr,row_number) +importFrom(dplyr,rowwise) +importFrom(dplyr,select) +importFrom(dplyr,summarise) +importFrom(dplyr,tibble) +importFrom(dplyr,ungroup) +importFrom(dplyr,where) +importFrom(igraph,graph_from_data_frame) +importFrom(igraph,which_mutual) +importFrom(lifecycle,badge) +importFrom(lifecycle,deprecated) +importFrom(purrr,compact) +importFrom(purrr,discard) +importFrom(purrr,imap_dfr) +importFrom(purrr,list_c) +importFrom(purrr,map) +importFrom(purrr,map2) +importFrom(purrr,map_at) +importFrom(purrr,transpose) importFrom(rlang,.data) -importFrom(sdcHierarchies, - hier_convert, - hier_import -) +importFrom(sdcHierarchies,hier_convert) +importFrom(sdcHierarchies,hier_import) importFrom(stats,setNames) importFrom(stringr,str_detect) -importFrom(tidyr, - nest, - pivot_longer, - unnest, - unnest_wider -) +importFrom(tidyr,nest) +importFrom(tidyr,pivot_longer) +importFrom(tidyr,unnest) +importFrom(tidyr,unnest_wider) importFrom(utils,combn) importFrom(zoo,na.locf) diff --git a/R/writehrc.R b/R/writehrc.R index 4c4b9da..3ade366 100644 --- a/R/writehrc.R +++ b/R/writehrc.R @@ -273,8 +273,11 @@ vect_aro <- Vectorize(arobase, vectorize.args = c("string", "number")) #' "telluric", "gasgiant", "bluestar", "whitedwarf", #' "reddwarf", "blackhole", "pulsar") #' ) +#' \dontrun{ #' path <- write_hrc2(astral) -#' \dontrun{read.table(path)} +#' read.table(path) +#' } +#' #' # Note that line order was changed ('other' comes before 'planet'), to no #' # consequence whatsoever for Tau-Argus. #' # Remarque : l'ordre des lignes a été modifié ('other' arrive avant 'planet'), @@ -288,16 +291,21 @@ vect_aro <- Vectorize(arobase, vectorize.args = c("string", "number")) #' "reddwarf", "blackhole", "pulsar"), #' type = c("planet", "planet", "star", "star", "star", "other", "other") #' ) +#' \dontrun{ #' path <- write_hrc2(astral_inv) -#' \dontrun{read.table(path)} +#' read.table(path) +#' } +#' #' # Because of the inverted order, everything is written backwards : planet is a #' # subtype of gasgiant, etc. #' # À cause de l'inversion des colonnes, tout est écrit à l'envers : planet est #' # devenu une sous-catégorie de gasgiant, par exemple. #' #' # Correction : +#' \dontrun{ #' path <- write_hrc2(astral_inv, rev = TRUE) -#' \dontrun{read.table(path)} +#' read.table(path) +#' } #' #' # 2.1 Sparse case #' # Cas creux @@ -310,8 +318,10 @@ vect_aro <- Vectorize(arobase, vectorize.args = c("string", "number")) #' # NAs in general are risky, but, in this case, the function works well. #' # Les valeurs manquantes causent un risque, mais, dans ce genre de cas, #' # la fonction a le comportement attendu. +#' \dontrun{ #' path <- write_hrc2(astral_sparse) -#' \dontrun{read.table(path)} +#' read.table(path) +#' } #' #' # 2.2 Non-uniform depth #' # Hiérarchie non-uniforme @@ -331,8 +341,10 @@ vect_aro <- Vectorize(arobase, vectorize.args = c("string", "number")) #' details = c("telluric", "gasgiant", "star", "blackhole", "pulsar") #' ) #' # The following code will work +#' \dontrun{ #' path <- write_hrc2(astral_nu_fill) -#' \dontrun{read.table(path)} +#' read.table(path) +#' } #' #' @importFrom zoo na.locf #' @export diff --git a/man/write_hrc2.Rd b/man/write_hrc2.Rd index b44ea90..644ebeb 100644 --- a/man/write_hrc2.Rd +++ b/man/write_hrc2.Rd @@ -253,8 +253,11 @@ astral <- data.frame( "telluric", "gasgiant", "bluestar", "whitedwarf", "reddwarf", "blackhole", "pulsar") ) +\dontrun{ path <- write_hrc2(astral) -\dontrun{read.table(path)} +read.table(path) +} + # Note that line order was changed ('other' comes before 'planet'), to no # consequence whatsoever for Tau-Argus. # Remarque : l'ordre des lignes a été modifié ('other' arrive avant 'planet'), @@ -268,16 +271,21 @@ astral_inv <- data.frame( "reddwarf", "blackhole", "pulsar"), type = c("planet", "planet", "star", "star", "star", "other", "other") ) +\dontrun{ path <- write_hrc2(astral_inv) -\dontrun{read.table(path)} +read.table(path) +} + # Because of the inverted order, everything is written backwards : planet is a # subtype of gasgiant, etc. # À cause de l'inversion des colonnes, tout est écrit à l'envers : planet est # devenu une sous-catégorie de gasgiant, par exemple. # Correction : +\dontrun{ path <- write_hrc2(astral_inv, rev = TRUE) -\dontrun{read.table(path)} +read.table(path) +} # 2.1 Sparse case # Cas creux @@ -290,8 +298,10 @@ astral_sparse <- data.frame( # NAs in general are risky, but, in this case, the function works well. # Les valeurs manquantes causent un risque, mais, dans ce genre de cas, # la fonction a le comportement attendu. +\dontrun{ path <- write_hrc2(astral_sparse) -\dontrun{read.table(path)} +read.table(path) +} # 2.2 Non-uniform depth # Hiérarchie non-uniforme @@ -311,7 +321,9 @@ astral_nu_fill <- data.frame( details = c("telluric", "gasgiant", "star", "blackhole", "pulsar") ) # The following code will work +\dontrun{ path <- write_hrc2(astral_nu_fill) -\dontrun{read.table(path)} +read.table(path) +} } From 1e7c63b264b69c49c759f51f693a60d7e1177422 Mon Sep 17 00:00:00 2001 From: julienjamme Date: Fri, 14 Aug 2026 12:37:05 +0200 Subject: [PATCH 14/15] clean file --- R/summarize_secret.R | 977 ------------------------------------------- 1 file changed, 977 deletions(-) diff --git a/R/summarize_secret.R b/R/summarize_secret.R index 12d4ed3..fa41681 100644 --- a/R/summarize_secret.R +++ b/R/summarize_secret.R @@ -123,980 +123,3 @@ summarize_secret <- function(res_tau, var = NULL, secret_var = "is_secret_prim") } } - - -#' -#' #' Protect one table by suppressing cells with Tau-Argus -#' #' -#' #' The function prepares all the files needed by Tau-Argus and launches the -#' #' software with the good settings and gets back the result. -#' #' -#' #' @inheritParams tab_rda -#' #' @inheritParams tab_arb -#' #' @inheritParams run_arb -#' #' -#' #' @param files_name string used to name all the files needed to process. -#' #' All files will have the same name, only their extension will be different. -#' #' @param dir_name string indicated the path of the directory in which to save -#' #' all the files (.rda, .hst, .txt, .arb, .csv) generated by the function. -#' #' @param unif_labels boolean, if explanatory variables have to be standardized -#' #' @param split_tab `r lifecycle::badge("experimental")` boolean, -#' #' whether to reduce dimension to 3 while treating a table of dimension 4 or 5 -#' #' (default to `FALSE`) -#' #' @param limit `r lifecycle::badge("experimental")` numeric, used to choose -#' #' which variable to merge (if nb_tab_option = 'smart') -#' #' and split table with a number of row above this limit in order to avoid -#' #' tauargus failures -#' #' @param nb_tab_option `r lifecycle::badge("experimental")` strategy to follow -#' #' to choose variables automatically while splitting: -#' #' \itemize{ -#' #' \item{`"min"`: minimize the number of tables;} -#' #' \item{`"max"`: maximize the number of tables;} -#' #' \item{`"smart"`: minimize the number of tables under the constraint -#' #' of their row count.} -#' #' } -#' #' @param summarise_secret boolean: if TRUE adds a dataframe (secret_summary) to the list of protected -#' #' dataframes returned. In this dataframe a summary of the protection is provided. -#' #' @param ... any parameter of the tab_rda, tab_arb or run_arb functions, relevant -#' #' for the treatment of tabular. -#' #' -#' #' @return -#' #' If output_type equals to 4 and split_tab = FALSE, -#' #' then the original tabular is returned with a new -#' #' column called Status, indicating the status of the cell coming from Tau-Argus : -#' #' "A" for a primary secret due to frequency rule, "B" for a primary secret due -#' #' to dominance rule, "D" for secondary secret and "V" for no secret cell. -#' #' -#' #' If split_tab = TRUE, -#' #' then the original tabular is returned with some new columns which are boolean -#' #' variables indicating the status of a cell at each iteration of the protection -#' #' process as we get with `tab_multi_manager()` function. `TRUE` -#' #' denotes a cell that have to be suppressed. The last column is then the -#' #' final status of the suppression process of the original table. -#' #' -#' #' If `split_tab = FALSE` and `output_type` doesn't equal to `4`, -#' #' then the raw result from tau-argus is returned. -#' #' -#' #' @section Standardization of explanatory variables and hierarchies: -#' #' -#' #' The boolean argument `unif_labels` is useful to -#' #' prevent some common errors in using Tau-Argus. Indeed, Tau-Argus needs that, -#' #' within a same level of a hierarchy, the labels have the same number of -#' #' characters. When the argument is set to TRUE, `tab_rtauargus` -#' #' standardizes the explanatory variables to prevent this issue. -#' #' Hierarchical explanatory variables (explanatory variables associated to -#' #' a hrc file) are then modified in the tabular data and an another hrc file is -#' #' created to be relevant with the tabular. In the output, these modifications -#' #' are removed. -#' #' -#' #' @examples -#' #'\dontrun{ -#' #' library(dplyr) -#' #' data(turnover_act_size) -#' #' -#' #' # Prepare data with primary secret ---- -#' #' turnover_act_size <- turnover_act_size %>% -#' #' mutate( -#' #' is_secret_freq = N_OBS > 0 & N_OBS < 3, -#' #' is_secret_dom = ifelse(MAX == 0, FALSE, MAX/TOT>0.85), -#' #' is_secret_prim = is_secret_freq | is_secret_dom -#' #' ) -#' #' -#' #' # Make hrc file of business sectors ---- -#' #' data(activity_corr_table) -#' #' hrc_file_activity <- activity_corr_table %>% -#' #' write_hrc2(file_name = "hrc/activity") -#' #' -#' #' # Compute the secondary secret ---- -#' #' options( -#' #' rtauargus.tauargus_exe = -#' #' "Y:/Logiciels/TauArgus/TauArgus4.2.3/TauArgus.exe" -#' #' ) -#' #' -#' #' res <- tab_rtauargus( -#' #' tabular = turnover_act_size, -#' #' files_name = "turn_act_size", -#' #' dir_name = "tauargus_files", -#' #' explanatory_vars = c("ACTIVITY", "SIZE"), -#' #' hrc = c(ACTIVITY = hrc_file_activity), -#' #' totcode = c(ACTIVITY = "Total", SIZE = "Total"), -#' #' secret_var = "is_secret_prim", -#' #' value = "TOT", -#' #' freq = "N_OBS", -#' #' verbose = FALSE -#' #' ) -#' #' -#' #' # Reduce dims feature -#' #' -#' #' data(datatest1) -#' #' res_dim4 <- tab_rtauargus( -#' #' tabular = datatest1, -#' #' dir_name = "tauargus_files", -#' #' explanatory_vars = c("A10", "treff","type_distrib","cj"), -#' #' totcode = rep("Total", 4), -#' #' secret_var = "is_secret_prim", -#' #' value = "pizzas_tot_abs", -#' #' freq = "nb_obs_rnd", -#' #' split_tab = TRUE -#' #' ) -#' #' } -#' #' @export -#' tab_rtauargus_cb <- function( -#' tabular, -#' explanatory_vars, -#' files_name = NULL, -#' dir_name = NULL, -#' totcode = getOption("rtauargus.totcode"), -#' hrc = NULL, -#' secret_var = NULL, -#' secret_no_pl = NULL, -#' cost_var = NULL, -#' value = "value", -#' freq = "freq", -#' ip = 10, -#' maxscore = NULL, -#' suppress = "MOD(1,5,1,0,0)", -#' safety_rules = paste0("MAN(",ip,")"), -#' show_batch_console = FALSE, -#' output_type = 4, -#' output_options = "", -#' unif_labels = TRUE, -#' split_tab = FALSE, -#' nb_tab_option = "smart", -#' limit = 14700, -#' summarise_secret = FALSE, -#' ... -#' ){ -#' -#' .dots <- list(...) -#' -#' ## 0. CONFLITS PARAMETRES ................. -#' -#' # tabular not a data.frame -#' if(!is.data.frame(tabular)){ -#' stop("tabular has to be a dataframe.") -#' } -#' if(any(!explanatory_vars %in% names(tabular))){ -#' stop("At least one of the explanatory vars is not a tabular's column name") -#' } -#' if(any(!c(value, freq) %in% names(tabular))){ -#' stop(paste0(value, " or ", freq, " is not a tabular's column name")) -#' } -#' if(!is.null(maxscore)){ -#' if(!maxscore %in% names(tabular)){ -#' stop(paste0(maxscore, " is not a tabular's column name")) -#' } -#' } -#' if(!is.null(cost_var)){ -#' if(!cost_var %in% names(tabular)){ -#' stop(paste0(cost_var, " is not a tabular's column name")) -#' } -#' } -#' if(!is.null(secret_var)){ -#' if(!secret_var %in% names(tabular)){ -#' stop(paste0(secret_var, " is not a tabular's column name")) -#' } -#' } -#' if(length(totcode) < length(explanatory_vars)){ -#' stop("totcode must have the same length as explanatory_vars") -#' } -#' if(length(names(totcode)) < length(explanatory_vars)){ -#' names(totcode) <- explanatory_vars -#' } -#' -#' if(is.null(files_name)) files_name <- "targus_file" -#' if(is.null(dir_name)) dir_name <- getwd() -#' -#' if (split_tab){ -#' # detect secret_var = NULL -#' # We want to split the table but the primary secret have not been posed -#' if ( !grepl("MAN", safety_rules) ){ -#' stop("While using split_tab = TRUE, you can't use tauargus to put primary secret") -#' } -#' if ( is.null(secret_var) ){ -#' stop("While using split_tab = TRUE, a secret_var has to be provided") -#' } -#' # split_tab strategy only work with dimension 4 or 5 tables -#' if ( ! length(explanatory_vars) %in% c(4,5) ){ -#' stop( -#' "You use split_tab = TRUE. However it only works with 4 or 5 dimensions -#' tables." -#' ) -#' } -#' } -#' -#' if (length(explanatory_vars) %in% c(4,5)){ -#' if (split_tab){ -#' -#' params_rt4 <- formals(fun = "tab_rtauargus4") -#' params_rt4 <- params_rt4[1:(length(params_rt4)-1)] -#' call <- sys.call(); call[[1]] <- as.name('list') -#' new_params <- eval.parent(call) -#' -#' for(param in intersect(names(params_rt4), names(new_params))){ -#' params_rt4[[param]] <- new_params[[param]] -#' } -#' -#' params_rt4$tabular <- tabular -#' params_rt4$totcode <- totcode -#' params_rt4$dir_name <- dir_name -#' params_rt4$files_name <- files_name -#' -#' return(do.call("tab_rtauargus4", params_rt4)) -#' -#' } else { -#' message("Warning : -#' It is highly recommended to use split_tab = TRUE when using rtauargus with 4 or 5 dimensions tables. -#' It allows to split the table in several tables with 3 dimensions. -#' -#' With split_tab = FALSE, tauargus treats the table in 4 or 5 dimensions. -#' In this case, the secondary secret may not being optimal according to tauargus itself -#' and the process may take longer.") -#' } -#' } -#' -#' -#' ## 1. TAB_RDA ..................... -#' tabular_original <- tabular -#' # uniformisation des chaines de caractères des variables catégorielles, hors total -#' # tabular ...................... -#' if(unif_labels){ -#' res_unif <- uniformize_labels(tabular, explanatory_vars, hrc, totcode) -#' tabular <- res_unif$data -#' if(!is.null(hrc)) hrc <- res_unif$hrc_unif -#' } -#' -#' # parametres -#' param_tab_rda <- param_function(tab_rda, .dots) -#' param_tab_rda$tabular <- tabular -#' param_tab_rda$tab_filename <- file.path(dir_name, paste0(files_name, ".tab")) -#' param_tab_rda$rda_filename <- file.path(dir_name, paste0(files_name, ".rda")) -#' param_tab_rda$hst_filename <- if(is.null(secret_var) & is.null(cost_var)) NULL else file.path(dir_name, paste0(files_name, ".hst")) -#' param_tab_rda$explanatory_vars <- explanatory_vars -#' param_tab_rda$hrc <- hrc -#' -#' param_tab_rda$totcode <- totcode -#' param_tab_rda$secret_var <- secret_var -#' param_tab_rda$secret_no_pl <- secret_no_pl -#' param_tab_rda$cost_var <- cost_var -#' param_tab_rda$value <- value -#' param_tab_rda$freq <- freq -#' param_tab_rda$ip <- ip -#' param_tab_rda$maxscore <- maxscore -#' -#' # appel (+ récuperation noms tab hst et rda) -#' input <- do.call(tab_rda, param_tab_rda) -#' -#' -#' ## 2. TAB_ARB ......................... -#' -#' # parametres -#' param_arb <- param_function(tab_arb, .dots) -#' param_arb$tab_filename <- input$tab_filename -#' param_arb$rda_filename <- input$rda_filename -#' param_arb$hst_filename <- input$hst_filename -#' param_arb$arb_filename <- file.path(dir_name, paste0(files_name, ".arb")) -#' param_arb$output_names <- file.path(dir_name, paste0(files_name, ".csv")) -#' #TODO : generaliser le choix de l'extension -#' param_arb$output_type <- output_type -#' param_arb$output_options <- output_options -#' param_arb$explanatory_vars <- explanatory_vars -#' param_arb$value <- value -#' param_arb$safety_rules <- safety_rules -#' param_arb$suppress <- suppress -#' -#' # appel (+ récupération nom batch) -#' batch <- do.call(tab_arb, param_arb) -#' -#' ## 3. RUN_ARB ........................... -#' -#' # parametres -#' param_run0 <- param_function(run_arb, .dots) -#' param_system <- param_function(system, .dots) -#' param_run <- c(param_run0, param_system) -#' param_run$arb_filename <- param_arb$arb_filename -#' param_run$logbook <- file.path(dir_name, paste0(files_name, ".txt")) -#' param_run$is_tabular <- TRUE -#' param_run$show_batch_console <- show_batch_console -#' -#' # appel -#' res <- do.call(run_arb, param_run) -#' -#' # RESULTAT ............................. -#' if(output_type == 4){ -#' -#' res_import <- utils::read.csv( -#' param_arb$output_names, -#' header = FALSE, -#' col.names = c(explanatory_vars, value, freq, "Status","Dom"), -#' colClasses = c(rep("character", length(explanatory_vars)), rep("numeric",2), "character", "numeric"), -#' stringsAsFactors = FALSE, -#' na.strings = "" -#' ) -#' if(unif_labels){ -#' res_import <- cbind.data.frame( -#' apply(res_import[,explanatory_vars,drop=FALSE], 2, rev_var_pour_tau_argus), -#' res_import[, !names(res_import) %in% explanatory_vars] -#' ) -#' } -#' mask <- merge(tabular_original, res_import[,c(explanatory_vars,"Status")], by = explanatory_vars, all = TRUE) -#' -#' utils::write.csv( -#' res_import, -#' file = param_arb$output_names, -#' row.names = FALSE -#' ) -#' -#' if(summarise_secret){ -#' inner_cells <- mask |> -#' mutate( -#' status = case_when( -#' is_secret_prim ~ "primary", -#' Status != "V" ~ "suppressed", -#' TRUE ~ "published" -#' )) |> -#' mutate(status = factor(status, levels = c("primary", "suppressed", "published"))) %>% -#' group_by(status) %>% -#' dplyr::summarise( -#' nb_cells = n(), -#' value = sum(.data[[param_tab_rda$value]], na.rm = TRUE), -#' .groups = "drop" -#' ) %>% -#' mutate( -#' pourc_cells = round(nb_cells / sum(nb_cells) * 100, 2), -#' pourc_value = round(value / sum(value) * 100, 2) -#' ) -#' total <- inner_cells %>% -#' dplyr::summarise( -#' status = "total", -#' nb_cells = sum(nb_cells), -#' value = sum(value), -#' pourc_cells = 100, -#' pourc_value = 100 -#' ) -#' summary <- dplyr::bind_rows(inner_cells, total) -#' list_mask_and_summary <- c(mask, list(secret_summary = summary)) -#' return(list_mask_and_summary) -#' } -#' -#' return(mask) -#' -#' }else{ -#' if(unif_labels){ -#' res <- cbind.data.frame( -#' apply(res[,explanatory_vars,drop=FALSE], 2, rev_var_pour_tau_argus), -#' res[, !names(res) %in% explanatory_vars] -#' ) -#' } -#' return(res) -#' } -#' -#' } -#' -#' ################################################################################ -#' ################################################################################ -#' ################################################################################ -#' -#' journal_add_break_line <- function(journal){ -#' sep_char_jour <- "-----------------------------------------" -#' cat(sep_char_jour, file = journal, fill = TRUE, append = TRUE) -#' } -#' -#' journal_add_line <- function(journal,...){ -#' cat(..., file = journal, fill = TRUE, append = TRUE) -#' } -#' -#' #' Manages the secondary secret of a list of tables -#' #' @inheritParams tab_rtauargus -#' #' @param list_tables named list of `data.frame` or `data.table` representing the tables to protect -#' #' @param list_explanatory_vars named list of character vectors of explanatory -#' #' variables of each table mentionned in list_tables. Names of the list are the same as of the list of tables. -#' #' @param alt_hrc named list for alternative hierarchies (useful for non nested-hierarchies) -#' #' @param alt_totcode named list for alternative codes -#' #' @param ip_start integer: Interval protection level to apply at first treatment of each table -#' #' @param ip_end integer: Interval protection level to apply at other treatments -#' #' @param num_iter_max integer: Maximum of treatments to do on each table (default to 10) -#' #' @param summarise_secret boolean: if TRUE adds a dataframe (secret_summary) to the list of protected -#' #' dataframes returned. In this dataframe a summary of the protection is provided. -#' #' @param ... other arguments of `tab_rtauargus2()` -#' #' -#' #' @return original list of tables. Secret Results of each iteration is added to each table. -#' #' For example, the result of first iteration is called 'is_secret_1' in each table. -#' #' It's a boolean variable, whether the cell has to be masked or not. -#' #' -#' #' @seealso `tab_rtauargus2` -#' #' -#' #' @examples -#' #' library(rtauargus) -#' #' library(dplyr) -#' #' data(turnover_act_size) -#' #' data(turnover_act_cj) -#' #' data(activity_corr_table) -#' #' -#' #' #0-Making hrc file of business sectors ---- -#' #' hrc_file_activity <- activity_corr_table %>% -#' #' write_hrc2(file_name = "hrc/activity") -#' #' -#' #' #1-Prepare data ---- -#' #' #Indicate whether each cell complies with the primary rules -#' #' #Boolean variable created is TRUE if the cell doesn't comply. -#' #' #Here the frequency rule is freq in (0;3) -#' #' #and the dominance rule is NK(1,85) -#' #' list_data_2_tabs <- list( -#' #' act_size = turnover_act_size, -#' #' act_cj = turnover_act_cj -#' #' ) %>% -#' #' purrr::map( -#' #' function(df){ -#' #' df %>% -#' #' mutate( -#' #' is_secret_freq = N_OBS > 0 & N_OBS < 3, -#' #' is_secret_dom = ifelse(MAX == 0, FALSE, MAX/TOT>0.85), -#' #' is_secret_prim = is_secret_freq | is_secret_dom -#' #' ) -#' #' } -#' #' ) -#' #' \dontrun{ -#' #' options( -#' #' rtauargus.tauargus_exe = -#' #' "Y:/Logiciels/TauArgus/TauArgus4.2.3/TauArgus.exe" -#' #' ) -#' #' res_1 <- tab_multi_manager( -#' #' list_tables = list_data_2_tabs, -#' #' list_explanatory_vars = list( -#' #' act_size = c("ACTIVITY", "SIZE"), -#' #' act_cj = c("ACTIVITY", "CJ") -#' #' ), -#' #' hrc = c(ACTIVITY = hrc_file_activity), -#' #' dir_name = "tauargus_files", -#' #' value = "TOT", -#' #' freq = "N_OBS", -#' #' secret_var = "is_secret_prim", -#' #' totcode = "Total" -#' #' ) -#' #' -#' #' -#' #' # With the reduction dimensions feature -#' #' -#' #' data("datatest1") -#' #' data("datatest2") -#' #' -#' #' datatest2b <- datatest2 %>% -#' #' filter(cj == "Total", treff == "Total", type_distrib == "Total") %>% -#' #' select(-cj, -treff, -type_distrib) -#' #' -#' #' str(datatest2b) -#' #' -#' #' res <- tab_multi_manager( -#' #' list_tables = list(d1 = datatest1, d2 = datatest2b), -#' #' list_explanatory_vars = list( -#' #' d1 = names(datatest1)[1:4], -#' #' d2 = names(datatest2b)[1:2] -#' #' ), -#' #' dir_name = "tauargus_files", -#' #' value = "pizzas_tot_abs", -#' #' freq = "nb_obs_rnd", -#' #' secret_var = "is_secret_prim", -#' #' totcode = "Total", -#' #' split_tab = TRUE -#' #' ) -#' #' -#' #' } -#' #' -#' #' @importFrom rlang .data -#' #' -#' #' @export -#' -#' tab_multi_manager_cb <- function( -#' list_tables, -#' list_explanatory_vars, -#' dir_name = NULL, -#' hrc = NULL, -#' alt_hrc = NULL, -#' totcode = getOption("rtauargus.totcode"), -#' alt_totcode = NULL, -#' value = "value", -#' freq = "freq", -#' secret_var = "is_secret_prim", -#' cost_var = NULL, -#' suppress = "MOD(1,5,1,0,0)", -#' ip_start = 10, -#' ip_end = 0, -#' num_iter_max = 10, -#' split_tab = FALSE, -#' nb_tab_option = "smart", -#' limit = 14700, -#' summarise_secret = FALSE, -#' ... -#' ){ -#' start_time <- Sys.time() -#' dir_name <- if(is.null(dir_name)) getwd() else dir_name -#' dir.create(dir_name, recursive = TRUE, showWarnings = FALSE) -#' -#' -#' func_to_call <- "tab_rtauargus2" -#' .dots = list(...) -#' params <- param_function(eval(parse(text=func_to_call)), .dots) -#' params$dir_name = dir_name -#' params$cost_var = cost_var -#' params$value = value -#' params$freq = freq -#' params$suppress = suppress -#' params$suppress = suppress -#' params$split_tab = split_tab -#' params$nb_tab_option = nb_tab_option -#' params$limit = limit -#' -#' n_tbx = length(list_tables) # nombre de tableaux -#' -#' if(n_tbx == 0){ -#' stop("Your list of tables is empty !") -#' } -#' if(n_tbx == 1){ -#' stop("To protect a single table, please use the function `tab_rtauargus`.") -#' } -#' if(is.null(names(list_tables))){ -#' names(list_tables) <- paste0("tab", 1:n_tbx) -#' names(list_explanatory_vars) <- paste0("tab", 1:n_tbx) -#' } -#' noms_tbx <- names(list_tables) -#' all_expl_vars <- unique(unname(unlist(list_explanatory_vars))) -#' -#' if( (!is.null(hrc)) & is.list(hrc)) hrc <- unlist(hrc) -#' -#' if( (!is.null(hrc)) & (length(names(hrc)) == 0)){ -#' stop("hrc must have names corresponding to the adequate explanatory variables") -#' } -#' if(length(setdiff(names(hrc), all_expl_vars)) > 0){ -#' stop("some names in hrc argument are not mentionned in list_explanatory_vars") -#' } -#' if(!is.null(alt_hrc)){ -#' if((length(names(alt_hrc)) == 0)){ -#' stop("alt_hrc must have names corresponding to the adequate tables names") -#' } -#' if(length(setdiff(names(alt_hrc), noms_tbx)) > 0){ -#' stop("some names in alt_hrc argument are not mentionned in list_tables") -#' } -#' } -#' if(!is.null(alt_totcode)){ -#' if((length(names(alt_totcode)) == 0)){ -#' stop("alt_totcode must have names corresponding to the adequate tables names") -#' } -#' if(length(setdiff(names(alt_totcode), noms_tbx)) > 0){ -#' stop("some names in alt_totcode argument are not mentionned in list_tables") -#' } -#' } -#' -#' # list_totcode management -#' # first case : list_totcode is one length-character vector : -#' # all the expl variables in all the tables have the same value to refer to the total -#' if(is.character(totcode)){ -#' if(length(totcode) == 1){ -#' list_totcode <- purrr::map( -#' list_explanatory_vars, -#' function(nom_tab){ -#' stats::setNames( -#' rep(totcode, length(nom_tab)), -#' nom_tab -#' ) -#' } -#' ) -#' }else if(length(totcode) == length(all_expl_vars)){ -#' if(is.null(names(totcode))){ -#' stop("totcode of length > 1 must have names (explanatory_vars)") -#' }else{ -#' if(!all(sort(names(totcode)) == sort(all_expl_vars))){ -#' stop("Names of explanatory vars mentioned in totcode are not consistent with those used in list_explanatory_vars") -#' }else{ -#' list_totcode <- purrr::map( -#' list_explanatory_vars, -#' function(nom_vars){ -#' totcode[nom_vars] -#' } -#' ) -#' } -#' } -#' }else{ -#' stop("totcode has to be a character vector of length 1 or a named vector of length equal to the number of unique explanatory vars") -#' } -#' }else{ -#' stop("totcode has to be a character vector of length 1 or a named vector of length equal to the number of unique explanatory vars") -#' } -#' -#' purrr::walk( -#' names(alt_totcode), -#' function(tab){ -#' purrr::walk( -#' names(alt_totcode[[tab]]), -#' function(var) list_totcode[[tab]][[var]] <<- alt_totcode[[tab]][[var]] -#' ) -#' } -#' ) -#' -#' noms_vars_init <- c() -#' for (tab in list_tables){ -#' noms_vars_init <- c(noms_vars_init, names(tab)) -#' } -#' noms_vars_init <- noms_vars_init[!duplicated(noms_vars_init)] -#' -#' noms_col_T <- stats::setNames(paste0("T_", noms_tbx), noms_tbx) -#' -#' table_majeure <- purrr::imap( -#' .x = list_tables, -#' .f = function(tableau,nom_tab){ -#' -#' if(!is.null(cost_var)){ -#' cost_var_tab <- if(cost_var %in% names(tableau)) cost_var else NULL -#' }else{ -#' cost_var_tab <- NULL -#' } -#' secret_var_tab <- if(!is.null(params$secret_no_pl)) c(secret_var,params$secret_no_pl) else secret_var -#' -#' tableau <- as.data.frame(tableau)[, c(list_explanatory_vars[[nom_tab]], value, freq, cost_var_tab, secret_var_tab)] -#' -#' if(!is.null(params$secret_no_pl)){ -#' names(tableau)[names(tableau) == params$secret_no_pl] = "secret_no_pl" -#' } else { -#' tableau$secret_no_pl <- FALSE -#' } -#' -#' var_a_ajouter <- setdiff(all_expl_vars, names(tableau)) -#' for (nom_col in var_a_ajouter){ -#' tableau[[nom_col]] <- unname( -#' purrr::keep( -#' list_totcode, function(x) nom_col %in% names(x) -#' )[[1]][nom_col] -#' ) -#' } -#' -#' tableau[[noms_col_T[[nom_tab]]]] <- TRUE -#' -#' return(as.data.frame(tableau)) -#' } -#' ) -#' -#' # by_vars = setdiff(unique(unlist(purrr::map(table_majeure, names))), noms_col_T) -#' by_vars = purrr::reduce(purrr::map(table_majeure, names), intersect) -#' table_majeure <- purrr::reduce( -#' .x = table_majeure, -#' .f = merge, -#' by = by_vars, -#' all = TRUE -#' ) -#' -#' table_majeure$secret_no_pl_iter <- table_majeure$secret_no_pl -#' secret_no_pl_iter <- "secret_no_pl_iter" -#' -#' purrr::walk( -#' noms_col_T, -#' function(col_T){ -#' e_par <- rlang::env_parent() -#' e_par$table_majeure[[col_T]] <- ifelse( -#' is.na(e_par$table_majeure[[col_T]]), -#' FALSE, -#' e_par$table_majeure[[col_T]] -#' ) -#' } -#' ) -#' -#' # Uniformisation des libelles des variables explicatives -#' # res_unif <- uniformize_labels(table_majeure, all_expl_vars, hrc, list_totcode) -#' # table_majeure <- res_unif$data -#' # hrc_unif <- res_unif$hrc_unif -#' -#' list_hrc <- purrr::map( -#' list_explanatory_vars, -#' function(nom_vars){ -#' purrr::discard(hrc[nom_vars], is.na) %>% unlist() -#' } -#' ) -#' -#' list_hrc <- purrr::map(list_hrc, function(l) if(length(l) == 0) NULL else l) -#' -#' purrr::walk( -#' names(alt_hrc), -#' function(tab){ -#' purrr::walk( -#' names(alt_hrc[[tab]]), -#' function(var) list_hrc[[tab]][[var]] <<- alt_hrc[[tab]][[var]] -#' ) -#' } -#' ) -#' -#' # listes de travail -#' -#' has_primary_secret <- purrr::map_lgl( -#' list_tables, -#' function(tab){ -#' sum(tab[[secret_var]]) != 0 -#' } -#' ) -#' if(sum(has_primary_secret) == 0){ -#' message("None of the tables have any primary secret cells") -#' return(list_tables) -#' } -#' todolist <- noms_tbx[has_primary_secret][1] -#' remainlist <- noms_tbx[has_primary_secret][-1] -#' -#' num_iter_par_tab = stats::setNames(rep(0, length(list_tables)), noms_tbx) -#' num_iter_par_tab[!has_primary_secret] <- 1 -#' num_iter_all = 0 -#' -#' # common_cells_modified <- as.data.frame(matrix(ncol = length(all_expl_vars)+1)) -#' # names(common_cells_modified) <- c(all_expl_vars, "iteration") -#' -#' n_common_cells_modified <- 0 -#' -#' journal <- file.path(dir_name,"journal.txt") -#' if(file.exists(journal)) invisible(file.remove(journal)) -#' journal_add_line(journal, "Start time:", format(start_time, "%Y-%m-%d %H:%M:%S")) -#' journal_add_break_line(journal) -#' journal_add_line(journal, "Function called to protect the tables:", func_to_call) -#' journal_add_line(journal, "Interval Protection Level for primary secret cells:", ip_start) -#' journal_add_line(journal, "Interval Protection Level for other iterations:", ip_end) -#' journal_add_line(journal, "Nb of tables to treat: ", n_tbx) -#' journal_add_break_line(journal) -#' journal_add_line(journal, "Tables to treat:", noms_tbx) -#' journal_add_break_line(journal) -#' journal_add_line(journal, "All explanatory variables:", all_expl_vars) -#' journal_add_break_line(journal) -#' journal_add_line(journal, "Initialisation work completed") -#' journal_add_break_line(journal) -#' journal_add_break_line(journal) -#' -#' while(length(todolist) > 0 & all(num_iter_par_tab <= num_iter_max)){ -#' -#' num_iter_all <- num_iter_all + 1 -#' num_tableau <- todolist[1] -#' num_iter_par_tab[num_tableau] <- num_iter_par_tab[num_tableau] + 1 -#' cat("--- Current table to treat: ", num_tableau, "---\n") -#' -#' nom_col_identifiante <- paste0("T_", num_tableau) -#' tableau_a_traiter <- which(table_majeure[[nom_col_identifiante]]) -#' -#' if (num_iter_all == 1){ -#' var_secret_apriori <- secret_var -#' } else { -#' var_secret_apriori <- paste0("is_secret_", num_iter_all-1, collapse = "") -#' } -#' -#' vrai_tableau <- table_majeure[tableau_a_traiter,] -#' -#' ex_var <- list_explanatory_vars[[num_tableau]] -#' -#' vrai_tableau <- vrai_tableau[,c(ex_var, value, freq,var_secret_apriori,secret_no_pl_iter, cost_var)] -#' -#' -#' # Other settings of the function to make secret ---- -#' params$tabular = vrai_tableau -#' params$files_name = num_tableau -#' params$explanatory_vars = ex_var -#' params$totcode = list_totcode[[num_tableau]] -#' params$hrc = list_hrc[[num_tableau]] -#' params$secret_var = var_secret_apriori -#' params$secret_no_pl = secret_no_pl_iter -#' params$suppress = if( -#' substr(suppress,1,3) == "MOD" & num_iter_par_tab[num_tableau] != 1 -#' ){ -#' # if modular deactivation of singleton and multisingleton after the first iteration -#' paste0( -#' paste( -#' c(strsplit(suppress, split = ",")[[1]][1:2], rep("0",3)), collapse = "," -#' ), -#' ")" -#' ) -#' }else{ -#' suppress -#' } -#' params$ip = if(num_iter_par_tab[num_tableau] == 1) ip_start else ip_end -#' # params$safety_rules <- "MAN(0)" -#' -#' res <- do.call(func_to_call, params) -#' res$is_secret <- res$Status != "V" -#' -#' # Statistiques -#' prim_stat <- sum(res$Status == "B", na.rm = TRUE) -#' sec_stat <- sum(res$Status == "D", na.rm = TRUE) -#' valid_stat <- sum(res$Status == "V", na.rm = TRUE) -#' denom_stat <- nrow(res) -#' -#' res <- subset(res, select = setdiff(names(res), "Status")) -#' -#' var_secret <- paste0("is_secret_", num_iter_all) -#' table_majeure <- merge(table_majeure, res, all = TRUE) -#' table_majeure[[var_secret]] <- table_majeure$is_secret -#' table_majeure <- subset( -#' table_majeure, -#' select = setdiff(names(table_majeure), "is_secret") -#' ) -#' -#' -#' table_majeure[[var_secret]] <- ifelse( -#' is.na(table_majeure[[var_secret]]), -#' table_majeure[[var_secret_apriori]], -#' table_majeure[[var_secret]] -#' ) -#' -#' table_majeure$secret_no_pl_iter <- ifelse( -#' table_majeure[[secret_var]], -#' table_majeure$secret_no_pl, -#' table_majeure[[var_secret]] -#' ) #TODO A REVOIR PR CORRIGER LES PL -#' -#' lignes_modifs <- which(table_majeure[[var_secret_apriori]] != table_majeure[[var_secret]]) -#' -#' cur_tab <- paste0("T_", num_tableau) -#' other_tabs <- setdiff(noms_col_T, cur_tab) -#' cur_cells <- rowSums(table_majeure[, cur_tab, drop=FALSE]) -#' other_cells <- rowSums(table_majeure[, other_tabs, drop=FALSE]) -#' -#' common_cells_rows <- which(cur_cells == 1 & other_cells > 0) -#' common_cells <- table_majeure[common_cells_rows, , drop=FALSE] -#' -#' # update of common cells that have been modified -#' modified <- common_cells[common_cells[[var_secret_apriori]] != common_cells[[var_secret]],all_expl_vars, drop=FALSE] -#' # modified <- if(sum(is.na(modified))>0) modified[1,][-1,] else modified -#' if(nrow(modified) > 0){ -#' modified <- cbind(modified, iteration = num_iter_all) -#' common_cells_modified <- if(n_common_cells_modified == 0) modified else rbind(common_cells_modified, modified) -#' n_common_cells_modified <- n_common_cells_modified + nrow(modified) -#' } -#' -#' for(tab in noms_tbx){ -#' nom_col_identifiante <- paste0("T_", tab) -#' if( !(tab %in% todolist) -#' & (any(table_majeure[[nom_col_identifiante]][lignes_modifs])) -#' ){ -#' todolist <- append(todolist,tab) -#' remainlist <- remainlist[remainlist != tab] -#' } -#' } -#' -#' todolist <- todolist[-1] -#' if(length(todolist) == 0){ -#' if(length(remainlist) > 0){ -#' todolist <- remainlist[1] -#' remainlist <- remainlist[-1] -#' } -#' } -#' -#' journal_add_line(journal, num_iter_all, "-Treatment of table", num_tableau) -#' journal_add_break_line(journal) -#' journal_add_line(journal, "New cells status counts: ") -#' journal_add_line(journal, "- apriori (primary) secret:", prim_stat, "(", round(prim_stat/denom_stat*100,1), "%)") -#' journal_add_line(journal, "- secondary secret:", sec_stat , "(", round(sec_stat/denom_stat*100,1), "%)") -#' journal_add_line(journal, "- valid cells:", valid_stat, "(", round(valid_stat/denom_stat*100,1), "%)") -#' journal_add_break_line(journal) -#' journal_add_line(journal, "Nb of new common cells hit by the secret:", nrow(modified)) -#' journal_add_break_line(journal) -#' journal_add_break_line(journal) -#' -#' } -#' -#' # Reconstruire la liste des tableaux d'entrée -#' liste_tbx_res <- purrr::imap( -#' list_tables, -#' function(tab,nom){ -#' expl_vars <- list_explanatory_vars[[nom]] -#' tab_rows <- table_majeure[[paste0("T_", nom)]] -#' secret_vars <- names(table_majeure)[grep("^is_secret_[1-9]", names(table_majeure))] -#' secret_vars <- secret_vars[order(as.integer(gsub("is_secret_", "", secret_vars)))] -#' res <- merge( -#' tab, -#' table_majeure[tab_rows, c(expl_vars, secret_vars)], -#' all.x = TRUE, all.y = FALSE, by = expl_vars -#' ) -#' } -#' ) -#' last_secret <- paste0("is_secret_", num_iter_all) -#' -#' stats <- purrr::imap_dfr( -#' liste_tbx_res, -#' function(tab, name){ -#' tab$primary_secret <- tab[[secret_var]] -#' tab$total_secret <- tab[[last_secret]] -#' tab$secondary_secret <- tab$total_secret & !tab$primary_secret -#' tab$valid_cells <- !tab$total_secret -#' res <- data.frame( -#' tab_name = name, -#' primary_secret = sum(tab$primary_secret), -#' secondary_secret = sum(tab$secondary_secret), -#' total_secret = sum(tab$total_secret), -#' valid_cells = sum(tab$valid_cells) -#' ) -#' } -#' ) -#' -#' purrr::iwalk( -#' num_iter_par_tab, -#' function(num,tab){ -#' journal_add_line( -#' journal, -#' "End of iterating after", num, "iterations for", tab -#' ) -#' } -#' ) -#' journal_add_break_line(journal) -#' journal_add_line(journal, "Final Summary") -#' journal_add_break_line(journal) -#' journal_add_line(journal, "Secreted cells counts per table") -#' journal_add_break_line(journal) -#' purrr::walk( -#' noms_tbx, -#' function(tab){ -#' journal_add_line( -#' journal, -#' "---TAB ", tab, " ---" -#' ) -#' df <- t(stats[stats$tab_name == tab,-1,drop=FALSE]) -#' suppressWarnings(gdata::write.fwf(df, rownames = TRUE, colnames = FALSE, file = journal, append = TRUE)) -#' journal_add_break_line(journal) -#' } -#' ) -#' journal_add_break_line(journal) -#' journal_add_line(journal, "Common cells hit by the secret:") -#' if(n_common_cells_modified > 0){ -#' suppressWarnings(gdata::write.fwf(common_cells_modified, file = journal, append = TRUE)) -#' } -#' journal_add_break_line(journal) -#' journal_add_line(journal, "End time: ", format(Sys.time(), "%Y-%m-%d %H:%M:%S")) -#' journal_add_break_line(journal) -#' -#' if(summarise_secret){ -#' combined_tab <- purrr::imap_dfr(liste_tbx_res, function(tab, name) { -#' tab |> -#' rename_with(~"is_secret_final", last_col()) |> -#' mutate( -#' status = case_when( -#' is_secret_prim ~ "primary", -#' is_secret_final ~ "suppressed", -#' TRUE ~ "published" -#' )) -#' }) -#' inner_cells <- combined_tab %>% -#' mutate(status = factor(status, levels = c("primary", "suppressed", "published"))) %>% -#' group_by(status) %>% -#' dplyr::summarise( -#' nb_cells = n(), -#' value = sum(.data[[params$value]], na.rm = TRUE), -#' .groups = "drop" -#' ) %>% -#' mutate( -#' pourc_cells = round(nb_cells / sum(nb_cells) * 100, 2), -#' pourc_value = round(value / sum(value) * 100, 2) -#' ) -#' total <- inner_cells %>% -#' dplyr::summarise( -#' status = "total", -#' nb_cells = sum(nb_cells), -#' value = sum(value), -#' pourc_cells = 100, -#' pourc_value = 100 -#' ) -#' summary <- dplyr::bind_rows(inner_cells, total) -#' liste_tbx_res_and_summary <- c(liste_tbx_res, list(secret_summary = summary)) -#' return(liste_tbx_res_and_summary) -#' } -#' -#' return(liste_tbx_res) -#' } From c6e9a3b5baf48e52950d54f2d4166d94a145276e Mon Sep 17 00:00:00 2001 From: Julien Jamme Date: Fri, 14 Aug 2026 12:47:43 +0200 Subject: [PATCH 15/15] Add summarize_secret to pkgdown configuration --- _pkgdown.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/_pkgdown.yml b/_pkgdown.yml index 5253665..95cd36f 100644 --- a/_pkgdown.yml +++ b/_pkgdown.yml @@ -24,6 +24,7 @@ reference: - tab_rtauargus2 - tab_rtauargus4 - tabulate_micro_data + - summarize_secret - title: Proceed to an automatic analysis desc: > Functions to help the links analysis between tables from a metadata file.