From 0f539bffacb4c10deb5e7013f4074d9ead3933ac Mon Sep 17 00:00:00 2001 From: aashna-a-patel Date: Wed, 13 Aug 2025 11:58:39 -0500 Subject: [PATCH 01/14] merge --- R/make_p_tables.R | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/R/make_p_tables.R b/R/make_p_tables.R index f21a692c..3c7314fa 100644 --- a/R/make_p_tables.R +++ b/R/make_p_tables.R @@ -128,17 +128,17 @@ make_p_tables <- function( code_footnotes <- glue::glue( "```{{r}} -pre_title_footnote <- \"Preceptor Table Title\" -pre_units_footnote <- \"Units and time information\" -pre_outcome_footnote <- \"Outcome or potential outcomes description\" -pre_treatment_footnote <- \"Treatment or intervention description\" -pre_covariates_footnote <- \"Covariates and their units\" - -pop_title_footnote <- \"Population Table Title\" -pop_units_footnote <- \"Units and time information\" -pop_outcome_footnote <- \"Outcome or potential outcomes description\" -pop_treatment_footnote <- \"Treatment or intervention description\" -pop_covariates_footnote <- \"Covariates and their units\" +pre_title_footnote <- \"...\" +pre_units_footnote <- \"...\" +pre_outcome_footnote <- \"...\" +pre_treatment_footnote <- \"...\" +pre_covariates_footnote <- \"...\" + +pop_title_footnote <- \"...\" +pop_units_footnote <- \"...\" +pop_outcome_footnote <- \"...\" +pop_treatment_footnote <- \"...\" +pop_covariates_footnote <- \"...\" p_tibble <- tibble::tribble( {p_col_headers}, From 1b036eca251d29d7e5463658876b4a74e56cf4e2 Mon Sep 17 00:00:00 2001 From: aashna-a-patel Date: Wed, 13 Aug 2025 12:26:35 -0500 Subject: [PATCH 02/14] small changes --- R/make_p_tables.R | 38 +++++++++++++------------------------- 1 file changed, 13 insertions(+), 25 deletions(-) diff --git a/R/make_p_tables.R b/R/make_p_tables.R index ddbecfb1..8a7734bf 100644 --- a/R/make_p_tables.R +++ b/R/make_p_tables.R @@ -96,28 +96,16 @@ make_p_tables <- function( p_col_headers <- paste(make_labels(all_cols), collapse = ", ") d_col_headers <- paste(make_labels(pop_cols), collapse = ", ") -# Don't need duplicate code. - p_rows <- paste( + rows <- paste( paste(rep('"..."', length(all_cols)), collapse = ", "), paste(rep('"..."', length(all_cols)), collapse = ", "), paste(rep('"..."', length(all_cols)), collapse = ", "), sep = ",\n " ) - d_rows <- paste( - paste(rep('"..."', length(pop_cols)), collapse = ", "), - paste(rep('"..."', length(pop_cols)), collapse = ", "), - paste(rep('"..."', length(pop_cols)), collapse = ", "), - sep = ",\n " - ) - # No need to rename these variables. - unit_spanner_cols <- unit_label - outcome_spanner_cols <- outcome_label - treatment_spanner_cols <- treatment_label - covariate_spanner_cols <- covariate_label - pop_unit_cols <- if (source_col) c("Source", unit_spanner_cols) else unit_spanner_cols + pop_unit_cols <- if (source_col) c("Source", unit_label) else unit_label widths <- c( nchar(unit_label[1]) + 2, @@ -146,12 +134,12 @@ pop_covariates_footnote <- \"...\" p_tibble <- tibble::tribble( {p_col_headers}, - {p_rows} + {rows} ) d_tibble <- tibble::tribble( {d_col_headers}, - {d_rows} + {rows} ) ```" ) @@ -164,13 +152,13 @@ p_tibble_full <- p_tibble |> gt::gt(p_tibble_full) |> gt::tab_header(title = \"Preceptor Table\") |> - gt::tab_spanner(label = \"Unit\", id = \"unit_span\", columns = c({glue_cols(unit_spanner_cols)})) |> - gt::tab_spanner(label = \"Potential Outcomes\", id = \"outcome_span\", columns = c({glue_cols(outcome_spanner_cols)})) |> - gt::tab_spanner(label = \"Treatment\", id = \"treatment_span\", columns = c({glue_cols(treatment_spanner_cols)})) |> - gt::tab_spanner(label = \"Covariates\", id = \"covariates_span\", columns = c({glue_cols(covariate_spanner_cols)})) |> + gt::tab_spanner(label = \"Unit\", id = \"unit_span\", columns = c({glue_cols(unit_label)})) |> + gt::tab_spanner(label = \"Potential Outcomes\", id = \"outcome_span\", columns = c({glue_cols(outcome_label)})) |> + gt::tab_spanner(label = \"Treatment\", id = \"treatment_span\", columns = c({glue_cols(treatment_label)})) |> + gt::tab_spanner(label = \"Covariates\", id = \"covariates_span\", columns = c({glue_cols(covariate_label)})) |> gt::cols_align(align = \"center\", columns = gt::everything()) |> gt::cols_align(align = \"left\", columns = c(`{unit_label[1]}`)) |> - gt::cols_width(columns = c({glue_cols(c(unit_spanner_cols, outcome_spanner_cols, treatment_spanner_cols, covariate_spanner_cols, \"More\"))}), + gt::cols_width(columns = c({glue_cols(c(unit_label, outcome_label, treatment_label, covariate_label, \"More\"))}), widths = gt::px(c({paste(widths, collapse = \", \")}))) |> gt::fmt_markdown(columns = gt::everything()) ```" @@ -185,12 +173,12 @@ d_tibble_full <- d_tibble |> gt::gt(d_tibble_full) |> gt::tab_header(title = \"Population Table\") |> gt::tab_spanner(label = \"Unit/Time\", id = \"unit_span\", columns = c({glue_cols(pop_unit_cols)})) |> - gt::tab_spanner(label = \"Potential Outcomes\", id = \"outcome_span\", columns = c({glue_cols(outcome_spanner_cols)})) |> - gt::tab_spanner(label = \"Treatment\", id = \"treatment_span\", columns = c({glue_cols(treatment_spanner_cols)})) |> - gt::tab_spanner(label = \"Covariates\", id = \"covariates_span\", columns = c({glue_cols(covariate_spanner_cols)})) |> + gt::tab_spanner(label = \"Potential Outcomes\", id = \"outcome_span\", columns = c({glue_cols(outcome_label)})) |> + gt::tab_spanner(label = \"Treatment\", id = \"treatment_span\", columns = c({glue_cols(treatment_label)})) |> + gt::tab_spanner(label = \"Covariates\", id = \"covariates_span\", columns = c({glue_cols(covariate_label)})) |> gt::cols_align(align = \"center\", columns = gt::everything()) |> gt::cols_align(align = \"left\", columns = c(`{unit_label[1]}`)) |> - gt::cols_width(columns = c({glue_cols(c(pop_unit_cols, outcome_spanner_cols, treatment_spanner_cols, covariate_spanner_cols, \"More\"))}), + gt::cols_width(columns = c({glue_cols(c(pop_unit_cols, outcome_label, treatment_label, covariate_label, \"More\"))}), widths = gt::px(c({paste(widths, collapse = \", \")}))) |> gt::fmt_markdown(columns = gt::everything()) ```" From f0e47985e06de702a142e2c0a8d04d338fe052d0 Mon Sep 17 00:00:00 2001 From: aashna-a-patel Date: Wed, 13 Aug 2025 15:11:28 -0500 Subject: [PATCH 03/14] LOTS of changes --- R/make_p_tables.R | 236 +++++++++++++++++++++++++++++++++++--------- R/p_table_helpers.R | 90 ++++++++++------- 2 files changed, 246 insertions(+), 80 deletions(-) diff --git a/R/make_p_tables.R b/R/make_p_tables.R index 8a7734bf..886d3559 100644 --- a/R/make_p_tables.R +++ b/R/make_p_tables.R @@ -88,32 +88,24 @@ make_p_tables <- function( stop("`type` must be either 'causal' or 'predictive'.") } -# DK: Add Source at end. Not now. - + # Both p_tibble and d_tibble use the same columns (no Source column yet) all_cols <- c(unit_label, outcome_label, treatment_label, covariate_label) - pop_cols <- if (source_col) c("Source", all_cols) else all_cols - - p_col_headers <- paste(make_labels(all_cols), collapse = ", ") - d_col_headers <- paste(make_labels(pop_cols), collapse = ", ") - - - rows <- paste( - paste(rep('"..."', length(all_cols)), collapse = ", "), - paste(rep('"..."', length(all_cols)), collapse = ", "), - paste(rep('"..."', length(all_cols)), collapse = ", "), - sep = ",\n " - ) - - + + # Source column only added during population table rendering pop_unit_cols <- if (source_col) c("Source", unit_label) else unit_label + # Generate tribble code using helper function + p_tribble_code <- write_input_tribble(all_cols) + d_tribble_code <- write_input_tribble(all_cols) + widths <- c( - nchar(unit_label[1]) + 2, - nchar(unit_label[2]) + 2, - rep(nchar(outcome_label[1]) + 2, length(outcome_label)), - nchar(treatment_label) + 2, - nchar(covariate_label) + 2, - 5 + if (source_col) 80 else NULL, # Source column width + max(nchar(unit_label[1]) * 8, 100), # Minimum 100px for first unit column + max(nchar(unit_label[2]) * 8, 120), # Minimum 120px for second unit column + rep(max(max(nchar(outcome_label)) * 8, 120), length(outcome_label)), # Minimum 120px per outcome + max(nchar(treatment_label) * 8, 120), # Minimum 120px for treatment + max(nchar(covariate_label) * 8, 120), # Minimum 120px for covariate + 60 # More column ) glue_cols <- function(cols) paste0("`", cols, "`", collapse = ", ") @@ -132,43 +124,72 @@ pop_outcome_footnote <- \"...\" pop_treatment_footnote <- \"...\" pop_covariates_footnote <- \"...\" -p_tibble <- tibble::tribble( - {p_col_headers}, - {rows} -) +p_tibble <- {p_tribble_code} -d_tibble <- tibble::tribble( - {d_col_headers}, - {rows} -) +d_tibble <- {d_tribble_code} ```" ) code_p_table <- glue::glue( "```{{r}} -p_tibble_full <- p_tibble |> - dplyr::add_row(!!!as.list(rep(NA, ncol(p_tibble)))) |> - dplyr::mutate(More = c(rep(NA, nrow(.) - 1), \"...\")) +p_tibble_full <- expand_input_tibble(list(p_tibble), \"preceptor\") gt::gt(p_tibble_full) |> gt::tab_header(title = \"Preceptor Table\") |> - gt::tab_spanner(label = \"Unit\", id = \"unit_span\", columns = c({glue_cols(unit_label)})) |> + gt::tab_spanner(label = \"Unit/Time\", id = \"unit_span\", columns = c({glue_cols(unit_label)})) |> gt::tab_spanner(label = \"Potential Outcomes\", id = \"outcome_span\", columns = c({glue_cols(outcome_label)})) |> gt::tab_spanner(label = \"Treatment\", id = \"treatment_span\", columns = c({glue_cols(treatment_label)})) |> gt::tab_spanner(label = \"Covariates\", id = \"covariates_span\", columns = c({glue_cols(covariate_label)})) |> gt::cols_align(align = \"center\", columns = gt::everything()) |> gt::cols_align(align = \"left\", columns = c(`{unit_label[1]}`)) |> - gt::cols_width(columns = c({glue_cols(c(unit_label, outcome_label, treatment_label, covariate_label, \"More\"))}), - widths = gt::px(c({paste(widths, collapse = \", \")}))) |> + gt::cols_width({ + all_cols_with_more <- c(unit_label, outcome_label, treatment_label, covariate_label, \"More\") + width_assignments <- paste0('\"', all_cols_with_more, '\" ~ gt::px(', widths[!is.null(widths)], ')', collapse = \", \") + width_assignments + }) |> + gt::tab_style( + style = gt::cell_text(size = gt::px(14)), + locations = gt::cells_body() + ) |> + gt::tab_style( + style = list( + gt::cell_text(size = gt::px(14), weight = \"bold\"), + gt::cell_borders(sides = \"bottom\", weight = gt::px(2)) + ), + locations = gt::cells_column_labels() + ) |> + gt::tab_options( + table.font.size = gt::px(14), + data_row.padding = gt::px(12), + column_labels.padding = gt::px(12), + row_group.padding = gt::px(12), + table.width = gt::pct(100), + table.margin.left = gt::px(0), + table.margin.right = gt::px(0) + ) |> gt::fmt_markdown(columns = gt::everything()) ```" ) - code_pop_table <- glue::glue( - "```{{r}} -d_tibble_full <- d_tibble |> - dplyr::add_row(!!!as.list(rep(NA, ncol(d_tibble)))) |> - dplyr::mutate(More = c(rep(NA, nrow(.) - 1), \"...\")) + # Population table code - different based on source_col + if (source_col) { + # Add Source column to d_tibble before processing + code_pop_table <- glue::glue( + "```{{r}} +d_tibble_with_source <- d_tibble |> + dplyr::mutate(Source = c(\"Data\", \"Data\", \"Preceptor\"), .before = 1) + +data_tibble <- d_tibble_with_source |> + dplyr::filter(Source == \"Data\") |> + dplyr::select(-Source) |> + dplyr::mutate(Source = \"Data\", .before = 1) + +preceptor_tibble <- d_tibble_with_source |> + dplyr::filter(Source == \"Preceptor\") |> + dplyr::select(-Source) |> + dplyr::mutate(Source = \"Preceptor\", .before = 1) + +d_tibble_full <- expand_input_tibble(list(data_tibble, preceptor_tibble), \"population\", source = TRUE) gt::gt(d_tibble_full) |> gt::tab_header(title = \"Population Table\") |> @@ -178,11 +199,60 @@ gt::gt(d_tibble_full) |> gt::tab_spanner(label = \"Covariates\", id = \"covariates_span\", columns = c({glue_cols(covariate_label)})) |> gt::cols_align(align = \"center\", columns = gt::everything()) |> gt::cols_align(align = \"left\", columns = c(`{unit_label[1]}`)) |> - gt::cols_width(columns = c({glue_cols(c(pop_unit_cols, outcome_label, treatment_label, covariate_label, \"More\"))}), - widths = gt::px(c({paste(widths, collapse = \", \")}))) |> + gt::cols_width({ + all_cols_with_more <- c(pop_unit_cols, outcome_label, treatment_label, covariate_label, \"More\") + width_assignments <- paste0('\"', all_cols_with_more, '\" ~ gt::px(', widths[!is.null(widths)], ')', collapse = \", \") + width_assignments + }) |> + gt::tab_style( + style = gt::cell_text(size = gt::px(14)), + locations = gt::cells_body() + ) |> + gt::tab_style( + style = list( + gt::cell_text(size = gt::px(14), weight = \"bold\"), + gt::cell_borders(sides = \"bottom\", weight = gt::px(2)) + ), + locations = gt::cells_column_labels() + ) |> + gt::tab_options( + table.font.size = gt::px(14), + data_row.padding = gt::px(12), + column_labels.padding = gt::px(12), + row_group.padding = gt::px(12), + table.width = gt::pct(100), + table.margin.left = gt::px(0), + table.margin.right = gt::px(0) + ) |> gt::fmt_markdown(columns = gt::everything()) ```" - ) + ) + } else { + # Without Source column + code_pop_table <- glue::glue( + "```{{r}} +data_tibble <- d_tibble[1:2, ] +preceptor_tibble <- d_tibble[3, , drop = FALSE] + +d_tibble_full <- expand_input_tibble(list(data_tibble, preceptor_tibble), \"population\", source = FALSE) + +gt::gt(d_tibble_full) |> + gt::tab_header(title = \"Population Table\") |> + gt::tab_spanner(label = \"Unit/Time\", id = \"unit_span\", columns = c({glue_cols(pop_unit_cols)})) |> + gt::tab_spanner(label = \"Potential Outcomes\", id = \"outcome_span\", columns = c({glue_cols(outcome_label)})) |> + gt::tab_spanner(label = \"Treatment\", id = \"treatment_span\", columns = c({glue_cols(treatment_label)})) |> + gt::tab_spanner(label = \"Covariates\", id = \"covariates_span\", columns = c({glue_cols(covariate_label)})) |> + gt::cols_align(align = \"center\", columns = gt::everything()) |> + gt::cols_align(align = \"left\", columns = c(`{unit_label[1]}`)) |> + gt::cols_width({ + all_cols_with_more <- c(pop_unit_cols, outcome_label, treatment_label, covariate_label, \"More\") + width_assignments <- paste0('\"', all_cols_with_more, '\" ~ gt::px(', widths[!is.null(widths)], ')', collapse = \", \") + width_assignments + }) |> + gt::fmt_markdown(columns = gt::everything()) +```" + ) + } full_code <- paste( code_footnotes, @@ -199,7 +269,83 @@ gt::gt(d_tibble_full) |> invisible(NULL) } +write_input_tribble <- function(names) { + n <- length(names) + rows <- replicate(3, paste(rep('"..."', n), collapse = ", "), simplify = FALSE) + header <- paste0("~`", names, "`", collapse = ", ") + + tribble_text <- paste0( + "tibble::tribble(\n", + " ", header, ",\n ", + paste(rows, collapse = ",\n "), + "\n)" + ) + return(tribble_text) +} + +expand_input_tibble <- function(x, type, source = FALSE) { + stopifnot(type %in% c("preceptor", "population")) + + if (type == "preceptor") { + if (length(x) != 1) stop("For 'preceptor', x must be a list of length 1.") + tib <- x[[1]] + + if (nrow(tib) >= 3) { + new_row <- tib[1, , drop = FALSE] + new_row[,] <- "..." + + tib_expanded <- dplyr::bind_rows( + tib[1:(nrow(tib)-1), ], + new_row, + tib[nrow(tib), ] + ) + } else { + tib_expanded <- tib + } + + tib_expanded$More <- "..." + + return(tib_expanded) + + } else if (type == "population") { + if (length(x) != 2) stop("For 'population', x must be a list of length 2.") + + expand_one <- function(tib) { + if (nrow(tib) >= 3) { + new_row <- tib[1, , drop = FALSE] + new_row[,] <- "..." + + expanded <- dplyr::bind_rows( + tib[1:(nrow(tib)-1), ], + new_row, + tib[nrow(tib), ] + ) + } else { + expanded <- tib + } + return(expanded) + } + + tib1 <- expand_one(x[[1]]) + tib2 <- expand_one(x[[2]]) + + empty_row <- tib1[1, , drop = FALSE] + empty_row[,] <- NA_character_ + + combined <- dplyr::bind_rows( + empty_row, + tib1, + empty_row, + tib2, + empty_row + ) + + combined$More <- "..." + + return(combined) + } +} + make_labels <- function(x) { paste0("~`", x, "`") } - diff --git a/R/p_table_helpers.R b/R/p_table_helpers.R index b2c619d3..fd2d9623 100644 --- a/R/p_table_helpers.R +++ b/R/p_table_helpers.R @@ -43,26 +43,29 @@ write_input_tribble <- function(names) { #' @return A single tibble with added missing rows and 'More' column, suitable for piping to gt. #' #' @details -#' For "preceptor": adds missing rows to ensure at least 3 rows, adds a "More" column -#' with NA except last row contains "...". +#' For "preceptor": adds a third row between the last 2 rows filled with "...", +#' adds a "More" column with "..." in all positions. #' #' For "population": expands each tibble similarly, then combines them with -#' empty rows before, between, and after, then adds "More" column. +#' empty rows before, between, and after, then adds "More" column with "...". #' #' @examples #' # Preceptor example #' pre_tib <- tibble::tribble(~Unit, ~Year, ~Outcome, #' "A", "2020", "5", -#' "B", "2021", "6") +#' "B", "2021", "6", +#' "C", "2022", "7") #' expand_input_tibble(list(pre_tib), "preceptor") #' #' # Population example #' pop1 <- tibble::tribble(~Source, ~Unit, ~Year, #' "S1", "A", "2020", -#' "S2", "B", "2021") +#' "S2", "B", "2021", +#' "S3", "C", "2022") #' pop2 <- tibble::tribble(~Source, ~Unit, ~Year, -#' "S1", "C", "2022", -#' "S2", "D", "2023") +#' "S1", "D", "2023", +#' "S2", "E", "2024", +#' "S3", "F", "2025") #' expand_input_tibble(list(pop1, pop2), "population", source = TRUE) #' #' @export @@ -73,52 +76,69 @@ expand_input_tibble <- function(x, type, source = FALSE) { if (length(x) != 1) stop("For 'preceptor', x must be a list of length 1.") tib <- x[[1]] - # Add missing rows to reach at least 3 rows - n_missing <- max(0, 3 - nrow(tib)) - if (n_missing > 0) { - missing_rows <- tib[rep(nrow(tib), n_missing), , drop = FALSE] - missing_rows[,] <- NA_character_ - tib <- dplyr::bind_rows(tib, missing_rows) + # For a 3-row tibble, insert a new row between row 2 and row 3 (the last row) + # This new row should be filled with "..." + if (nrow(tib) >= 3) { + # Create a new row filled with "..." + new_row <- tib[1, , drop = FALSE] # Use first row as template + new_row[,] <- "..." # Fill all columns with "..." + + # Insert the new row between the second-to-last and last row + # For a 3-row table, this means between row 2 and row 3 + tib_expanded <- dplyr::bind_rows( + tib[1:(nrow(tib)-1), ], # All rows except the last + new_row, # New row with "..." + tib[nrow(tib), ] # The last row + ) + } else { + # If somehow less than 3 rows, just keep as is + tib_expanded <- tib } - # Add 'More' column: NA except last row is "..." - tib$More <- rep(NA_character_, nrow(tib)) - tib$More[nrow(tib)] <- "..." + # Add 'More' column filled with "..." + tib_expanded$More <- "..." - return(tib) + return(tib_expanded) } else if (type == "population") { if (length(x) != 2) stop("For 'population', x must be a list of length 2.") + # Function to expand a single tibble (data or preceptor) expand_one <- function(tib) { - n_missing <- max(0, 3 - nrow(tib)) - if (n_missing > 0) { - missing_rows <- tib[rep(nrow(tib), n_missing), , drop = FALSE] - missing_rows[,] <- NA_character_ - tib <- dplyr::bind_rows(tib, missing_rows) + # Insert a row between second-to-last and last row filled with "..." + if (nrow(tib) >= 3) { + new_row <- tib[1, , drop = FALSE] + new_row[,] <- "..." + + expanded <- dplyr::bind_rows( + tib[1:(nrow(tib)-1), ], + new_row, + tib[nrow(tib), ] + ) + } else { + expanded <- tib } - tib + return(expanded) } - tib1 <- expand_one(x[[1]]) - tib2 <- expand_one(x[[2]]) + tib1 <- expand_one(x[[1]]) # Data tibble + tib2 <- expand_one(x[[2]]) # Preceptor tibble - # Create empty row with same columns filled NA + # Create empty row with same columns filled with NA empty_row <- tib1[1, , drop = FALSE] empty_row[,] <- NA_character_ - # Combine with empty rows before, between, and after + # Combine with empty rows: blank, data, blank, preceptor, blank combined <- dplyr::bind_rows( - empty_row, - tib1, - empty_row, - tib2, - empty_row + empty_row, # blank row + tib1, # data rows (expanded) + empty_row, # blank row + tib2, # preceptor rows (expanded) + empty_row # blank row ) - # Add 'More' column with NA except last row is "..." - combined$More <- rep(NA_character_, nrow(combined)) - combined$More[nrow(combined)] <- "..." + # Add 'More' column filled with "..." + combined$More <- "..." return(combined) } From 2411b7c0e4e95589705aec34861724afaab9dce2 Mon Sep 17 00:00:00 2001 From: aashna-a-patel Date: Wed, 13 Aug 2025 15:23:44 -0500 Subject: [PATCH 04/14] ALMOST THERE --- R/make_p_tables.R | 98 ++++++++++++++++++++--------------------------- 1 file changed, 42 insertions(+), 56 deletions(-) diff --git a/R/make_p_tables.R b/R/make_p_tables.R index 886d3559..61eb8b52 100644 --- a/R/make_p_tables.R +++ b/R/make_p_tables.R @@ -171,22 +171,17 @@ gt::gt(p_tibble_full) |> ```" ) - # Population table code - different based on source_col + # Population table code - using p_tibble_full for preceptor rows if (source_col) { - # Add Source column to d_tibble before processing code_pop_table <- glue::glue( "```{{r}} -d_tibble_with_source <- d_tibble |> - dplyr::mutate(Source = c(\"Data\", \"Data\", \"Preceptor\"), .before = 1) - -data_tibble <- d_tibble_with_source |> - dplyr::filter(Source == \"Data\") |> - dplyr::select(-Source) |> +# Create data tibble from d_tibble (first 2 rows) with Source column +data_tibble <- d_tibble[1:2, , drop = FALSE] |> dplyr::mutate(Source = \"Data\", .before = 1) -preceptor_tibble <- d_tibble_with_source |> - dplyr::filter(Source == \"Preceptor\") |> - dplyr::select(-Source) |> +# Create preceptor tibble from p_tibble_full (remove More column, add Source) +preceptor_tibble <- p_tibble_full |> + dplyr::select(-More) |> dplyr::mutate(Source = \"Preceptor\", .before = 1) d_tibble_full <- expand_input_tibble(list(data_tibble, preceptor_tibble), \"population\", source = TRUE) @@ -228,11 +223,14 @@ gt::gt(d_tibble_full) |> ```" ) } else { - # Without Source column code_pop_table <- glue::glue( "```{{r}} -data_tibble <- d_tibble[1:2, ] -preceptor_tibble <- d_tibble[3, , drop = FALSE] +# Create data tibble from d_tibble (first 2 rows) +data_tibble <- d_tibble[1:2, , drop = FALSE] + +# Create preceptor tibble from p_tibble_full (remove More column) +preceptor_tibble <- p_tibble_full |> + dplyr::select(-More) d_tibble_full <- expand_input_tibble(list(data_tibble, preceptor_tibble), \"population\", source = FALSE) @@ -269,20 +267,7 @@ gt::gt(d_tibble_full) |> invisible(NULL) } -write_input_tribble <- function(names) { - n <- length(names) - rows <- replicate(3, paste(rep('"..."', n), collapse = ", "), simplify = FALSE) - header <- paste0("~`", names, "`", collapse = ", ") - - tribble_text <- paste0( - "tibble::tribble(\n", - " ", header, ",\n ", - paste(rows, collapse = ",\n "), - "\n)" - ) - return(tribble_text) -} - +# Fixed expand_input_tibble function expand_input_tibble <- function(x, type, source = FALSE) { stopifnot(type %in% c("preceptor", "population")) @@ -310,42 +295,43 @@ expand_input_tibble <- function(x, type, source = FALSE) { } else if (type == "population") { if (length(x) != 2) stop("For 'population', x must be a list of length 2.") - expand_one <- function(tib) { - if (nrow(tib) >= 3) { - new_row <- tib[1, , drop = FALSE] - new_row[,] <- "..." - - expanded <- dplyr::bind_rows( - tib[1:(nrow(tib)-1), ], - new_row, - tib[nrow(tib), ] - ) - } else { - expanded <- tib - } - return(expanded) - } + data_tibble <- x[[1]] # Should be 2 rows from d_tibble + preceptor_tibble <- x[[2]] # Should be 4 rows from p_tibble_full (without More column) + + # Create empty row template + empty_row <- data_tibble[1, , drop = FALSE] + empty_row[,] <- "..." + + # Build the 11-row structure: + # Row 1: blank (all "...") + # Rows 2-3: data rows (2 rows) + # Row 4: blank ("..." in all columns) + # Rows 5-8: preceptor rows (4 rows, already has "..." in 3rd position) + # Row 9: blank (all "...") - tib1 <- expand_one(x[[1]]) - tib2 <- expand_one(x[[2]]) + # For data section: 2 data rows + 1 blank + 1 more blank = 4 rows total + data_section <- dplyr::bind_rows( + data_tibble[1, ], # First data row + data_tibble[2, ], # Second data row + empty_row, # Blank row + empty_row # Another blank to match preceptor structure + ) - empty_row <- tib1[1, , drop = FALSE] - empty_row[,] <- NA_character_ + # Preceptor section is already 4 rows from p_tibble_full + preceptor_section <- preceptor_tibble + # Combine: blank + data(4) + blank + preceptor(4) + blank = 11 rows combined <- dplyr::bind_rows( - empty_row, - tib1, - empty_row, - tib2, - empty_row + empty_row, # Row 1: blank + data_section, # Rows 2-5: data section (2 data + 2 blank) + empty_row, # Row 6: blank + preceptor_section, # Rows 7-10: preceptor section (4 rows) + empty_row # Row 11: blank ) + # Add More column combined$More <- "..." return(combined) } } - -make_labels <- function(x) { - paste0("~`", x, "`") -} From 37e7fcf436afff1483db6e1864a372850b25fd7a Mon Sep 17 00:00:00 2001 From: aashna-a-patel Date: Wed, 13 Aug 2025 15:36:42 -0500 Subject: [PATCH 05/14] still 1 thing --- R/make_p_tables.R | 69 +---------------------------------------- R/p_table_helpers.R | 75 ++++++++++++++++++++------------------------- 2 files changed, 35 insertions(+), 109 deletions(-) diff --git a/R/make_p_tables.R b/R/make_p_tables.R index 61eb8b52..3c06edfa 100644 --- a/R/make_p_tables.R +++ b/R/make_p_tables.R @@ -267,71 +267,4 @@ gt::gt(d_tibble_full) |> invisible(NULL) } -# Fixed expand_input_tibble function -expand_input_tibble <- function(x, type, source = FALSE) { - stopifnot(type %in% c("preceptor", "population")) - - if (type == "preceptor") { - if (length(x) != 1) stop("For 'preceptor', x must be a list of length 1.") - tib <- x[[1]] - - if (nrow(tib) >= 3) { - new_row <- tib[1, , drop = FALSE] - new_row[,] <- "..." - - tib_expanded <- dplyr::bind_rows( - tib[1:(nrow(tib)-1), ], - new_row, - tib[nrow(tib), ] - ) - } else { - tib_expanded <- tib - } - - tib_expanded$More <- "..." - - return(tib_expanded) - - } else if (type == "population") { - if (length(x) != 2) stop("For 'population', x must be a list of length 2.") - - data_tibble <- x[[1]] # Should be 2 rows from d_tibble - preceptor_tibble <- x[[2]] # Should be 4 rows from p_tibble_full (without More column) - - # Create empty row template - empty_row <- data_tibble[1, , drop = FALSE] - empty_row[,] <- "..." - - # Build the 11-row structure: - # Row 1: blank (all "...") - # Rows 2-3: data rows (2 rows) - # Row 4: blank ("..." in all columns) - # Rows 5-8: preceptor rows (4 rows, already has "..." in 3rd position) - # Row 9: blank (all "...") - - # For data section: 2 data rows + 1 blank + 1 more blank = 4 rows total - data_section <- dplyr::bind_rows( - data_tibble[1, ], # First data row - data_tibble[2, ], # Second data row - empty_row, # Blank row - empty_row # Another blank to match preceptor structure - ) - - # Preceptor section is already 4 rows from p_tibble_full - preceptor_section <- preceptor_tibble - - # Combine: blank + data(4) + blank + preceptor(4) + blank = 11 rows - combined <- dplyr::bind_rows( - empty_row, # Row 1: blank - data_section, # Rows 2-5: data section (2 data + 2 blank) - empty_row, # Row 6: blank - preceptor_section, # Rows 7-10: preceptor section (4 rows) - empty_row # Row 11: blank - ) - - # Add More column - combined$More <- "..." - - return(combined) - } -} + diff --git a/R/p_table_helpers.R b/R/p_table_helpers.R index fd2d9623..5a842175 100644 --- a/R/p_table_helpers.R +++ b/R/p_table_helpers.R @@ -76,26 +76,19 @@ expand_input_tibble <- function(x, type, source = FALSE) { if (length(x) != 1) stop("For 'preceptor', x must be a list of length 1.") tib <- x[[1]] - # For a 3-row tibble, insert a new row between row 2 and row 3 (the last row) - # This new row should be filled with "..." if (nrow(tib) >= 3) { - # Create a new row filled with "..." - new_row <- tib[1, , drop = FALSE] # Use first row as template - new_row[,] <- "..." # Fill all columns with "..." + new_row <- tib[1, , drop = FALSE] + new_row[,] <- "..." - # Insert the new row between the second-to-last and last row - # For a 3-row table, this means between row 2 and row 3 tib_expanded <- dplyr::bind_rows( - tib[1:(nrow(tib)-1), ], # All rows except the last - new_row, # New row with "..." - tib[nrow(tib), ] # The last row + tib[1:(nrow(tib)-1), ], + new_row, + tib[nrow(tib), ] ) } else { - # If somehow less than 3 rows, just keep as is tib_expanded <- tib } - # Add 'More' column filled with "..." tib_expanded$More <- "..." return(tib_expanded) @@ -103,41 +96,41 @@ expand_input_tibble <- function(x, type, source = FALSE) { } else if (type == "population") { if (length(x) != 2) stop("For 'population', x must be a list of length 2.") - # Function to expand a single tibble (data or preceptor) - expand_one <- function(tib) { - # Insert a row between second-to-last and last row filled with "..." - if (nrow(tib) >= 3) { - new_row <- tib[1, , drop = FALSE] - new_row[,] <- "..." - - expanded <- dplyr::bind_rows( - tib[1:(nrow(tib)-1), ], - new_row, - tib[nrow(tib), ] - ) - } else { - expanded <- tib - } - return(expanded) - } + data_tibble <- x[[1]] # Should be 2 rows from d_tibble + preceptor_tibble <- x[[2]] # Should be 4 rows from p_tibble_full (without More column) + + # Create empty row template + empty_row <- data_tibble[1, , drop = FALSE] + empty_row[,] <- "..." - tib1 <- expand_one(x[[1]]) # Data tibble - tib2 <- expand_one(x[[2]]) # Preceptor tibble + # Build the 11-row structure: + # Row 1: blank (all "...") + # Rows 2-3: data rows (2 rows) + # Row 4: blank ("..." in all columns) + # Rows 5-8: preceptor rows (4 rows, already has "..." in 3rd position) + # Row 9: blank (all "...") + + # For data section: 2 data rows + 1 blank + 1 more blank = 4 rows total + data_section <- dplyr::bind_rows( + data_tibble[1, ], # First data row + data_tibble[2, ], # Second data row + empty_row, # Blank row + empty_row # Another blank to match preceptor structure + ) - # Create empty row with same columns filled with NA - empty_row <- tib1[1, , drop = FALSE] - empty_row[,] <- NA_character_ + # Preceptor section is already 4 rows from p_tibble_full + preceptor_section <- preceptor_tibble - # Combine with empty rows: blank, data, blank, preceptor, blank + # Combine: blank + data(4) + blank + preceptor(4) + blank = 11 rows combined <- dplyr::bind_rows( - empty_row, # blank row - tib1, # data rows (expanded) - empty_row, # blank row - tib2, # preceptor rows (expanded) - empty_row # blank row + empty_row, # Row 1: blank + data_section, # Rows 2-5: data section (2 data + 2 blank) + empty_row, # Row 6: blank + preceptor_section, # Rows 7-10: preceptor section (4 rows) + empty_row # Row 11: blank ) - # Add 'More' column filled with "..." + # Add More column combined$More <- "..." return(combined) From 39c3238753a0dd3d610a0084649e903d0a2d2b2d Mon Sep 17 00:00:00 2001 From: aashna-a-patel Date: Wed, 13 Aug 2025 15:46:23 -0500 Subject: [PATCH 06/14] final stretch --- R/make_p_tables.R | 89 +++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 75 insertions(+), 14 deletions(-) diff --git a/R/make_p_tables.R b/R/make_p_tables.R index 3c06edfa..392d2026 100644 --- a/R/make_p_tables.R +++ b/R/make_p_tables.R @@ -1,5 +1,5 @@ #' Insert Preceptor and Population Table Templates in Quarto -#' +#' #' Inserts a Quarto-ready template consisting of multiple code chunks for creating #' **Preceptor Tables** and **Population Tables**. These tables support both causal #' and predictive workflows. @@ -12,7 +12,7 @@ #' sized roughly proportional to label length #' - The Preceptor and Population tables include a final "More" column and #' a last empty row added during rendering for easier editing -#' +#' #' @name make_p_tables #' @title Insert Preceptor and Population Table Templates #' @@ -69,6 +69,7 @@ #' ) #' } + make_p_tables <- function( type, unit_label, @@ -88,16 +89,19 @@ make_p_tables <- function( stop("`type` must be either 'causal' or 'predictive'.") } + # Both p_tibble and d_tibble use the same columns (no Source column yet) all_cols <- c(unit_label, outcome_label, treatment_label, covariate_label) - + # Source column only added during population table rendering pop_unit_cols <- if (source_col) c("Source", unit_label) else unit_label + # Generate tribble code using helper function p_tribble_code <- write_input_tribble(all_cols) d_tribble_code <- write_input_tribble(all_cols) + widths <- c( if (source_col) 80 else NULL, # Source column width max(nchar(unit_label[1]) * 8, 100), # Minimum 100px for first unit column @@ -108,8 +112,10 @@ make_p_tables <- function( 60 # More column ) + glue_cols <- function(cols) paste0("`", cols, "`", collapse = ", ") + code_footnotes <- glue::glue( "```{{r}} pre_title_footnote <- \"...\" @@ -118,22 +124,27 @@ pre_outcome_footnote <- \"...\" pre_treatment_footnote <- \"...\" pre_covariates_footnote <- \"...\" + pop_title_footnote <- \"...\" pop_units_footnote <- \"...\" pop_outcome_footnote <- \"...\" pop_treatment_footnote <- \"...\" pop_covariates_footnote <- \"...\" + p_tibble <- {p_tribble_code} + d_tibble <- {d_tribble_code} ```" ) + code_p_table <- glue::glue( "```{{r}} p_tibble_full <- expand_input_tibble(list(p_tibble), \"preceptor\") + gt::gt(p_tibble_full) |> gt::tab_header(title = \"Preceptor Table\") |> gt::tab_spanner(label = \"Unit/Time\", id = \"unit_span\", columns = c({glue_cols(unit_label)})) |> @@ -171,22 +182,47 @@ gt::gt(p_tibble_full) |> ```" ) - # Population table code - using p_tibble_full for preceptor rows + + # Population table code - fixed to show all 4 data rows and proper structure if (source_col) { code_pop_table <- glue::glue( "```{{r}} -# Create data tibble from d_tibble (first 2 rows) with Source column -data_tibble <- d_tibble[1:2, , drop = FALSE] |> +# Create full data tibble with 4 rows (3 content, 1 blank in 3rd position) +data_tibble <- dplyr::bind_rows( + d_tibble[1:2, , drop = FALSE], # First 2 data rows + d_tibble[1, , drop = FALSE] |> dplyr::mutate(dplyr::across(dplyr::everything(), ~ \"...\")), # Blank row + d_tibble[3, , drop = FALSE] # Last data row +) |> dplyr::mutate(Source = \"Data\", .before = 1) + # Create preceptor tibble from p_tibble_full (remove More column, add Source) preceptor_tibble <- p_tibble_full |> dplyr::select(-More) |> dplyr::mutate(Source = \"Preceptor\", .before = 1) -d_tibble_full <- expand_input_tibble(list(data_tibble, preceptor_tibble), \"population\", source = TRUE) -gt::gt(d_tibble_full) |> +# Create the 11-row population table structure manually +# Row structure: blank, 4 data (3rd blank), blank, 4 preceptor (3rd blank), blank + +# Create empty row template +empty_row <- data_tibble[1, , drop = FALSE] +empty_row[,] <- \"...\" + +# Build the 11-row structure +population_tibble <- dplyr::bind_rows( + empty_row, # Row 1: blank + data_tibble, # Rows 2-5: 4 data rows (3rd is blank) + empty_row, # Row 6: blank + preceptor_tibble, # Rows 7-10: 4 preceptor rows (3rd is blank) + empty_row # Row 11: blank +) + +# Add More column +population_tibble$More <- \"...\" + + +gt::gt(population_tibble) |> gt::tab_header(title = \"Population Table\") |> gt::tab_spanner(label = \"Unit/Time\", id = \"unit_span\", columns = c({glue_cols(pop_unit_cols)})) |> gt::tab_spanner(label = \"Potential Outcomes\", id = \"outcome_span\", columns = c({glue_cols(outcome_label)})) |> @@ -225,16 +261,40 @@ gt::gt(d_tibble_full) |> } else { code_pop_table <- glue::glue( "```{{r}} -# Create data tibble from d_tibble (first 2 rows) -data_tibble <- d_tibble[1:2, , drop = FALSE] +# Create full data tibble with 4 rows (3 content, 1 blank in 3rd position) +data_tibble <- dplyr::bind_rows( + d_tibble[1:2, , drop = FALSE], # First 2 data rows + d_tibble[1, , drop = FALSE] |> dplyr::mutate(dplyr::across(dplyr::everything(), ~ \"...\")), # Blank row + d_tibble[3, , drop = FALSE] # Last data row +) + # Create preceptor tibble from p_tibble_full (remove More column) preceptor_tibble <- p_tibble_full |> dplyr::select(-More) -d_tibble_full <- expand_input_tibble(list(data_tibble, preceptor_tibble), \"population\", source = FALSE) -gt::gt(d_tibble_full) |> +# Create the 11-row population table structure manually +# Row structure: blank, 4 data (3rd blank), blank, 4 preceptor (3rd blank), blank + +# Create empty row template +empty_row <- data_tibble[1, , drop = FALSE] +empty_row[,] <- \"...\" + +# Build the 11-row structure +population_tibble <- dplyr::bind_rows( + empty_row, # Row 1: blank + data_tibble, # Rows 2-5: 4 data rows (3rd is blank) + empty_row, # Row 6: blank + preceptor_tibble, # Rows 7-10: 4 preceptor rows (3rd is blank) + empty_row # Row 11: blank +) + +# Add More column +population_tibble$More <- \"...\" + + +gt::gt(population_tibble) |> gt::tab_header(title = \"Population Table\") |> gt::tab_spanner(label = \"Unit/Time\", id = \"unit_span\", columns = c({glue_cols(pop_unit_cols)})) |> gt::tab_spanner(label = \"Potential Outcomes\", id = \"outcome_span\", columns = c({glue_cols(outcome_label)})) |> @@ -252,6 +312,7 @@ gt::gt(d_tibble_full) |> ) } + full_code <- paste( code_footnotes, code_p_table, @@ -259,12 +320,12 @@ gt::gt(d_tibble_full) |> sep = "\n\n" ) + rstudioapi::insertText( location = rstudioapi::getActiveDocumentContext()$selection[[1]]$range, text = full_code ) + invisible(NULL) } - - From a277637b084512aaae5fe5217a5e1432976260d8 Mon Sep 17 00:00:00 2001 From: aashna-a-patel Date: Wed, 13 Aug 2025 15:52:35 -0500 Subject: [PATCH 07/14] trying --- R/make_p_tables.R | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/R/make_p_tables.R b/R/make_p_tables.R index 392d2026..67f43b93 100644 --- a/R/make_p_tables.R +++ b/R/make_p_tables.R @@ -150,7 +150,7 @@ gt::gt(p_tibble_full) |> gt::tab_spanner(label = \"Unit/Time\", id = \"unit_span\", columns = c({glue_cols(unit_label)})) |> gt::tab_spanner(label = \"Potential Outcomes\", id = \"outcome_span\", columns = c({glue_cols(outcome_label)})) |> gt::tab_spanner(label = \"Treatment\", id = \"treatment_span\", columns = c({glue_cols(treatment_label)})) |> - gt::tab_spanner(label = \"Covariates\", id = \"covariates_span\", columns = c({glue_cols(covariate_label)})) |> + gt::tab_spanner(label = \"Covariates\", id = \"covariates_span\", columns = c({glue_cols(covariate_label)}, \"More\")) |> gt::cols_align(align = \"center\", columns = gt::everything()) |> gt::cols_align(align = \"left\", columns = c(`{unit_label[1]}`)) |> gt::cols_width({ @@ -227,7 +227,7 @@ gt::gt(population_tibble) |> gt::tab_spanner(label = \"Unit/Time\", id = \"unit_span\", columns = c({glue_cols(pop_unit_cols)})) |> gt::tab_spanner(label = \"Potential Outcomes\", id = \"outcome_span\", columns = c({glue_cols(outcome_label)})) |> gt::tab_spanner(label = \"Treatment\", id = \"treatment_span\", columns = c({glue_cols(treatment_label)})) |> - gt::tab_spanner(label = \"Covariates\", id = \"covariates_span\", columns = c({glue_cols(covariate_label)})) |> + gt::tab_spanner(label = \"Covariates\", id = \"covariates_span\", columns = c({glue_cols(covariate_label)}, \"More\")) |> gt::cols_align(align = \"center\", columns = gt::everything()) |> gt::cols_align(align = \"left\", columns = c(`{unit_label[1]}`)) |> gt::cols_width({ From f5e881353fe5dd96f1ba49aeeaa9b6ff478cf36f Mon Sep 17 00:00:00 2001 From: aashna-a-patel Date: Wed, 13 Aug 2025 15:54:51 -0500 Subject: [PATCH 08/14] I DID IT I THINK --- R/p_table_helpers.R | 38 ++++++++++++++++++++++++++++++-------- 1 file changed, 30 insertions(+), 8 deletions(-) diff --git a/R/p_table_helpers.R b/R/p_table_helpers.R index 5a842175..869cbc61 100644 --- a/R/p_table_helpers.R +++ b/R/p_table_helpers.R @@ -2,12 +2,12 @@ #' #' Generates a character string representing an R tibble::tribble() #' with the specified column names and 3 rows filled with placeholder -#' text `"..."`. Useful for quickly creating editable table templates. +#' text `"..."`. Column values are aligned under their headers for easy editing. #' #' @param names Character vector of column names. #' #' @return Character string containing the R code for an input tribble with placeholders `"..."`, -#' formatted for manual editing. +#' formatted for manual editing with aligned columns. #' #' @examples #' write_input_tribble(c("Unit", "Year", "Outcome", "Treatment")) @@ -15,16 +15,38 @@ #' @export write_input_tribble <- function(names) { n <- length(names) - # Create 3 rows of placeholders - rows <- replicate(3, paste(rep('"..."', n), collapse = ", "), simplify = FALSE) - # Prepare header row with backtick quoted column names - header <- paste0("~`", names, "`", collapse = ", ") - # Construct tribble text with line breaks for readability + # Calculate column widths based on header names (with backticks and ~) + header_widths <- nchar(paste0("~`", names, "`")) + # Ensure minimum width of 5 for "..." placeholder + col_widths <- pmax(header_widths, 5) + + # Create properly spaced header row + headers_spaced <- character(n) + for (i in 1:n) { + header_text <- paste0("~`", names[i], "`") + padding <- col_widths[i] - nchar(header_text) + headers_spaced[i] <- paste0(header_text, paste(rep(" ", padding), collapse = "")) + } + header <- paste(headers_spaced, collapse = ", ") + + # Create properly spaced data rows + placeholder_rows <- character(3) + for (row in 1:3) { + row_values <- character(n) + for (i in 1:n) { + value_text <- '"..."' + padding <- col_widths[i] - nchar(value_text) + row_values[i] <- paste0(value_text, paste(rep(" ", padding), collapse = "")) + } + placeholder_rows[row] <- paste(row_values, collapse = ", ") + } + + # Construct tribble text with aligned columns tribble_text <- paste0( "tibble::tribble(\n", " ", header, ",\n ", - paste(rows, collapse = ",\n "), + paste(placeholder_rows, collapse = ",\n "), "\n)" ) return(tribble_text) From 28c1fbf41dc3a50ed4957ef68f0b8b4f5de8ba90 Mon Sep 17 00:00:00 2001 From: aashna-a-patel Date: Wed, 13 Aug 2025 16:18:47 -0500 Subject: [PATCH 09/14] vignette stuff --- R/make_p_tables.R | 16 +- TODO.txt | 2 +- vignettes/tables.qmd | 364 ++++++++++++++++++++++++++----------------- 3 files changed, 231 insertions(+), 151 deletions(-) diff --git a/R/make_p_tables.R b/R/make_p_tables.R index 67f43b93..e95a4714 100644 --- a/R/make_p_tables.R +++ b/R/make_p_tables.R @@ -5,9 +5,9 @@ #' and predictive workflows. #' #' The output includes: -#' - Editable footnotes for documentation #' - Empty `tibble`s for the Preceptor Table and Population Table (the latter includes #' the Preceptor rows) +#' - Editable footnotes for documentation #' - `gt` code chunks to render each table with labeled spanners and columns #' sized roughly proportional to label length #' - The Preceptor and Population tables include a final "More" column and @@ -118,6 +118,14 @@ make_p_tables <- function( code_footnotes <- glue::glue( "```{{r}} +# Edit the following tibbles and footnotes, look at the vignette for more details + +p_tibble <- {p_tribble_code} + + +d_tibble <- {d_tribble_code} + + pre_title_footnote <- \"...\" pre_units_footnote <- \"...\" pre_outcome_footnote <- \"...\" @@ -130,12 +138,6 @@ pop_units_footnote <- \"...\" pop_outcome_footnote <- \"...\" pop_treatment_footnote <- \"...\" pop_covariates_footnote <- \"...\" - - -p_tibble <- {p_tribble_code} - - -d_tibble <- {d_tribble_code} ```" ) diff --git a/TODO.txt b/TODO.txt index 1d7dfb7b..0c756ba1 100644 --- a/TODO.txt +++ b/TODO.txt @@ -1,4 +1,4 @@ -Change DESCRIPTION to use the current released version of primer.data, so it stops updating each time. +Change DESCRIPTION to use the current released version of primer.data, so it stops updating each time. ```{r} pre_title_footnote <- "Preceptor Table Title" diff --git a/vignettes/tables.qmd b/vignettes/tables.qmd index 42762b7a..130d2320 100644 --- a/vignettes/tables.qmd +++ b/vignettes/tables.qmd @@ -13,7 +13,7 @@ vignette: > ## Overview -This vignette introduces the `make_p_tables()` function in the `primer.tutorials` package, which inserts a five-chunk Quarto-ready template into your open document for creating **Preceptor Tables** and **Population Tables**. +This vignette introduces the `make_p_tables()` function in the `primer.tutorials` package, which inserts a three-chunk Quarto-ready template into your open document for creating **Preceptor Tables** and **Population Tables**. These tables are designed to support both **causal** and **predictive** modeling workflows by clearly labeling variables with spanners and encouraging detailed documentation via footnotes. @@ -25,30 +25,31 @@ This format draws inspiration from the [Cardinal Virtues](https://ppbds.github.i ### Preceptor Table -A **Preceptor Table** contains hypothetical or expected outcomes for units (such as students or senators). It often includes unknowns (denoted by `"?"`) where real data is not yet available, and reflects researcher or instructor expectations. +A **Preceptor Table** contains hypothetical or expected outcomes for units (such as students or senators). It often includes unknowns (denoted by `"..."`) where real data is not yet available, and reflects researcher or instructor expectations. The table automatically includes a blank third row and a "More" column for additional covariates. ### Population Table -A **Population Table** contains a merged view of observed data (from the population) alongside preceptor-defined expectations. It includes an additional column `Source` that distinguishes between actual data (`"Data"`) and expectations (`"Preceptor Table"`). +A **Population Table** contains a merged view of observed data (from the population) alongside preceptor-defined expectations. It includes an additional column `Source` that distinguishes between actual data (`"Data"`) and expectations (`"Preceptor"`). The table follows an 11-row structure with proper spacing between data and preceptor sections. ## Key Features The output of `make_p_tables()` includes: -* An empty `tibble` for the **Preceptor Table** -* An empty `tibble` for the **Population Table** (which includes rows from the Preceptor Table) +* Editable footnotes for documentation +* An empty `tibble` for the **Preceptor Table** (`p_tibble`) +* An empty `tibble` for data input (`d_tibble`) * `gt` code to render both tables with grouped column headers ("spanners") -* Editable footnotes for each section of the table -* Cleanup code to remove temporary objects +* Automatic addition of missing rows and "More" column during rendering +* Column alignment in the tribble code for easier editing ## Spanner Structure Each table includes spanners for: -* **Unit** (or **Unit/Time** in the Population Table) -* **Outcome** (for predictive models) or **Potential Outcomes** (for causal models) +* **Unit/Time** (for the unit columns) +* **Potential Outcomes** (for causal models) or **Outcome** (for predictive models) * **Treatment** (included only in causal models) -* **Covariates** (typically 3 columns, customizable) +* **Covariates** (includes the covariate column and the "More" column) > **Note:** All table entries must be surrounded by **double quotes**, even for numeric values (e.g., `"42"`). @@ -66,75 +67,68 @@ library(primer.tutorials) ## Running `make_p_tables()` -Preceptor and Population Tables are inserted together. The Population Table includes a `"Source"` column as its first column, which takes values `"Data"` or `"Preceptor Table"` depending on origin. This structure encourages comparison between expected and observed values. +Preceptor and Population Tables are inserted together. The Population Table includes a `"Source"` column as its first column (controlled by the `source_col` argument), which takes values `"Data"` or `"Preceptor"` depending on origin. This structure encourages comparison between expected and observed values. -Behind the scenes, these tables are generated using `tibble::tribble()` for easier manual editing by row. This helps authors align values vertically and encourages clear visual structure in the Quarto document +Behind the scenes, these tables are generated using `tibble::tribble()` for easier manual editing by row. The tribble code is formatted with aligned columns to help authors maintain visual structure while editing. -When you run `primer.tutorials::make_p_tables()` without any argument values, the following error appears in the console. - -```` -> make_p_tables() -Error: -! Failed to evaluate glue component {covariate_1_label} -Caused by error: -! argument "covariate_1_label" is missing, with no default -Show Traceback -> -```` - -This is because the function requires labels to create a template of the tables. For the best results, it is encouraged to fill in each argument when calling the function. +When you run `primer.tutorials::make_p_tables()` without any argument values, you'll get an error because the function requires specific labels to create the template. ## Understanding the Function Arguments The `make_p_tables()` function takes a set of user-defined labels and options that control how the **Preceptor** and **Population** tables are built and displayed. Each argument serves a clear conceptual role and is used to populate column headers, spanner labels, and the default content in `tibble::tribble()` calls. Here is a detailed breakdown: -* We use the term "label" rather than "vars" to indicate that these are the labels in the table rather than the variable names from the data. As such, they are often human-readable phrases with spaces, like "Math Score if in Small Class". Will long labels be wrapped automagically? We probably want that. Of course, these descriptions can not be too long and so must be shortened in some way. In a pinch, the variable names may be used, but this is not preferred. +* We use the term "label" rather than "vars" to indicate that these are the labels in the table rather than the variable names from the data. As such, they are often human-readable phrases with spaces, like "Math Score if in Small Class". These descriptions should be concise but meaningful. -### `is_causal` *(Logical)* +### `type` *(Character)* -* Set to `TRUE` to generate a causal table structure, which includes: - - * Two columns for **Potential Outcomes** (under treatment and control). +* Set to `"causal"` to generate a causal table structure, which includes: + * Multiple columns for **Potential Outcomes** (specified in `outcome_label`). * A **Treatment** column representing an intervention or assigned condition. -* Set to `FALSE` for a predictive model: - - * Includes a single **Outcome** column. - * Omits the Treatment column entirely. -* This flag determines not only what variables appear in the tables, but also how they are **spanned** and **labeled** in the rendered `gt` tables. +* Set to `"predictive"` for a predictive model: + * Includes outcome columns as specified in `outcome_label`. + * Treatment column is still included but represents the predictor variable. +* This determines not only what variables appear in the tables, but also how they are **spanned** and **labeled** in the rendered `gt` tables. --- -### `unit_label` *(Character)* +### `unit_label` *(Character vector of length 2)* -* Human-readable name for the **unit of analysis** — e.g., `"Senator"`, `"Student"`, or `"School"`. -* This will appear both as a column name in the tibble and as part of the `"Unit"` or `"Unit/Time"` spanner in the `gt` table. -* The label should be capitalized and concise. +* Human-readable names for the **unit of analysis** — e.g., `c("Senator", "Session Year")` or `c("Student", "Grade Level")`. +* These will appear as the first two columns and are grouped under the `"Unit/Time"` spanner. +* The labels should be capitalized and concise. --- -### `outcome_label` *(Character)* +### `outcome_label` *(Character vector)* -* Describes the **key outcome** being predicted or causally modeled. -* In predictive mode, this creates one column: `Outcome`. -* In causal mode, this generates two: `{outcome_label} 1` and `{outcome_label} 2`, reflecting potential outcomes under treatment and control, respectively. -* Should be an interpretable phrase like `"Test Score"` or `"Re-election Status"`. +* Describes the **key outcomes** being predicted or causally modeled. +* For causal models, typically includes multiple potential outcomes like `c("Support Bill", "Oppose Bill")`. +* For predictive models, might be a single outcome like `c("Test Score")`. +* Should be interpretable phrases that clearly describe the outcomes. --- ### `treatment_label` *(Character)* -* Only required if `is_causal = TRUE`. -* Appears as the label of the treatment column, such as `"Phone Call"` or `"Received Tutoring"`. +* Label for the treatment/predictor column, such as `"Phone Call"` or `"Tutoring Program"`. * Used to title the corresponding `gt::tab_spanner()`. +* Required for both causal and predictive models. + +--- + +### `covariate_label` *(Character)* + +* Label for the main covariate column relevant to the analysis. +* This is grouped under the `"Covariates"` spanner along with the "More" column. +* Should be a simple phrase like `"Age"` or `"School Type"`. --- -### `covariate_1_label` and `covariate_2_label`*(Characters)* +### `source_col` *(Logical, default TRUE)* -* Labels for two covariates relevant to the analysis. -* These are grouped under a `"Covariates"` spanner. -* These labels will be used directly as column headers (e.g., `"Sex"`, `"Age"`, `"School Type"`). -* All should be simple phrases, and together they describe the pre-treatment information on which comparisons or predictions may be based. +* Controls whether the Population Table includes a `"Source"` column. +* When `TRUE`, adds a column distinguishing between `"Data"` and `"Preceptor"` rows. +* When `FALSE`, the Population Table omits the source column. Each of these labels should be understood as **descriptive display names**, not as variable names from an existing dataset. The goal is clarity and interpretability for readers of the resulting Quarto document. @@ -142,7 +136,7 @@ Each of these labels should be understood as **descriptive display names**, not ## Understanding the Footnotes -Footnotes in these tables are not decorative — they are an essential part of **documenting analytical intent**. When you use `make_p_tables()`, it generates editable placeholders for ten footnotes, five for each table. These can be filled in or deleted (by setting to `NULL`), and they appear in the rendered table using `gt::tab_footnote()`. +Footnotes in these tables are not decorative — they are an essential part of **documenting analytical intent**. When you use `make_p_tables()`, it generates editable placeholders for ten footnotes, five for each table. These can be filled in or deleted, and they appear in the rendered table using `gt::tab_footnote()`. Below is a guide to what each footnote is for: @@ -152,13 +146,13 @@ Below is a guide to what each footnote is for: * **`pre_title_footnote`**: Explains the background or motivation for the expectations shown. Useful for describing who the preceptors are, when the expectations were made, or why these units were selected. -* **`pre_units_footnote`**: Clarifies the definition of a “unit” in this context — e.g., “Each row represents a senator during the 2022 election.” Also helpful to include time span or location info if applicable. +* **`pre_units_footnote`**: Clarifies the definition of a "unit" in this context — e.g., "Each row represents a senator during the 2022 election." Also helpful to include time span or location info if applicable. -* **`pre_outcome_footnote`**: Documents why these potential outcomes are meaningful. May include a note on how they were estimated or what they signify (e.g., "Re-election status under treatment vs. control"). +* **`pre_outcome_footnote`**: Documents why these potential outcomes are meaningful. May include a note on how they were estimated or what they signify. * **`pre_treatment_footnote`**: Defines what the treatment actually entails and how it is operationalized. For example, a phone call campaign, assignment to tutoring, or access to a program. -* **`pre_covariates_footnote`**: Explains why the selected covariates were chosen and their role in forming expectations. Should also clarify whether these differ between tables. +* **`pre_covariates_footnote`**: Explains why the selected covariates were chosen and their role in forming expectations. Should also clarify the "More" column purpose. --- @@ -166,13 +160,13 @@ Below is a guide to what each footnote is for: * **`pop_title_footnote`**: Describes the purpose of the population table — usually to compare expected vs. observed outcomes, merged with preceptor rows. -* **`pop_units_footnote`**: Defines what each unit represents in the population — e.g., “Each row represents an observed student or a preceptor-generated scenario.” +* **`pop_units_footnote`**: Defines what each unit represents in the population — e.g., "Each row represents an observed student or a preceptor-generated scenario." -* **`pop_outcome_footnote`**: Documents the source of outcome data. For example: “Outcomes observed from the 2022 voter file” or “Final grades from school records.” +* **`pop_outcome_footnote`**: Documents the source of outcome data. For example: "Outcomes observed from the 2022 voter file" or "Final grades from school records." * **`pop_treatment_footnote`**: Explains how actual treatment status was observed or inferred. May differ from the assumptions made in the Preceptor Table. -* **`pop_covariates_footnote`**: Describes where the covariate data comes from in the population table and whether it’s measured identically to the preceptor rows. +* **`pop_covariates_footnote`**: Describes where the covariate data comes from in the population table and whether it's measured identically to the preceptor rows. --- @@ -180,116 +174,201 @@ These footnotes ensure that **both the data structure and the logic of your mode --- +## Examples -### Examples When you run: ```r make_p_tables( - is_causal = TRUE, - unit_label = "Senator", - outcome_label = "Potential Outcomes", - treatment_label = "Phone Call", - covariate_1_label = "Sex", - covariate_2_label = "Age" + type = "causal", + unit_label = c("Senator", "Session Year"), + outcome_label = c("Support Bill", "Oppose Bill"), + treatment_label = "Lobbying Contact", + covariate_label = "Senator Age" ) ``` -The following chunks are inserted (after manually filling in the rest): +The following chunks are inserted: -### 1. Installation + Footnotes +### 1. Footnotes and Data Setup ```{r} -pre_title_footnote <- "Expected outcomes for 2022 Senate elections based on expert judgment and historical data." -pre_units_footnote <- "Each row represents a single U.S. senator during the 2022 election cycle." -pre_outcome_footnote <- "Potential outcomes reflect election results with and without treatment (phone calls)." -pre_treatment_footnote <- "Treatment indicates whether targeted phone call campaigns were conducted." -pre_covariates_footnote <- "Covariates include demographic factors (Sex, Age) and incumbency status." - -pop_title_footnote <- "Observed 2022 Senate election data merged with preceptor expectations." -pop_units_footnote <- "Each row represents an observed senator or a preceptor-defined expected case." -pop_outcome_footnote <- "Observed election results recorded after the 2022 election." -pop_treatment_footnote <- "Observed phone call campaign status from actual campaign data." -pop_covariates_footnote <- "Covariates drawn from voter file demographics and campaign records." -``` - -### 2. Preceptor Table +pre_title_footnote <- "..." +pre_units_footnote <- "..." +pre_outcome_footnote <- "..." +pre_treatment_footnote <- "..." +pre_covariates_footnote <- "..." + +pop_title_footnote <- "..." +pop_units_footnote <- "..." +pop_outcome_footnote <- "..." +pop_treatment_footnote <- "..." +pop_covariates_footnote <- "..." -```{r} -# Use "?" for unknowns in Preceptor Table rows p_tibble <- tibble::tribble( - ~`Senator`, ~`Time/Year`, ~`Potential Outcomes 1`, ~`Potential Outcomes 2`, ~`Phone Call`, ~`Sex`, ~`Age`, ~`Other`, - "John Smith", "2025", "?", "Not Re-elected", "Yes", "Male", "58", "...", - "Jane Doe", "2025", "?", "Not Re-elected", "No", "Female", "49", "...", - "...", "...", "...", "...", "...", "...", "...", "...", - "Chris Lee", "2025", "Re-elected", "?", "No", "Male", "55", "...", - "...", "...", "...", "...", "...", "...", "...", "...", + ~`Senator` , ~`Session Year` , ~`Support Bill` , ~`Oppose Bill` , ~`Lobbying Contact` , ~`Senator Age` , + "..." , "..." , "..." , "..." , "..." , "..." , + "..." , "..." , "..." , "..." , "..." , "..." , + "..." , "..." , "..." , "..." , "..." , "..." ) -``` - -### 3. Population Table (Includes Preceptor Rows) -```{r} d_tibble <- tibble::tribble( - ~`Source`, ~`Senator`, ~`Time/Year`, ~`Potential Outcomes 1`, ~`Potential Outcomes 2`, ~`Phone Call`, ~`Sex`, ~`Age`, ~`Other`, - "...", "...", "...", "...", "...", "...", "...", "...", "...", - "Data", "Mary Jackson", "2021", "Re-elected", "---", "Yes", "Female", "51", "...", - "Data", "Alex Johnson", "2022", "---", "Not Re-elected", "Yes", "Male", "46", "...", - "...", "...", "...", "...", "...", "...", "...", "...", "...", - "Data", "Bobby Norris", "2022", "Re-elected", "---", "No", "Male", "63", "...", - "...", "...", "...", "...", "...", "...", "...", "...", "...", - # Add Preceptor rows - "Preceptor Table", "John Smith", "2025", "?", "Not Re-elected", "Yes", "Male", "58", "...", - "Preceptor Table", "Jane Doe", "2025", "?", "Not Re-elected", "No", "Female", "49", "...", - "...", "...", "...", "...", "...", "...", "...", "...", "...", - "Preceptor Table", "Chris Lee", "2025", "Re-elected", "?", "No", "Male", "55", "...", - "...", "...", "...", "...", "...", "...", "...", "...", "..." + ~`Senator` , ~`Session Year` , ~`Support Bill` , ~`Oppose Bill` , ~`Lobbying Contact` , ~`Senator Age` , + "..." , "..." , "..." , "..." , "..." , "..." , + "..." , "..." , "..." , "..." , "..." , "..." , + "..." , "..." , "..." , "..." , "..." , "..." ) ``` -### 4. Rendering Tables +### 2. Preceptor Table ```{r} -# Preceptor Table -gt::gt(data = p_tibble) |> +p_tibble_full <- expand_input_tibble(list(p_tibble), "preceptor") + +gt::gt(p_tibble_full) |> gt::tab_header(title = "Preceptor Table") |> - gt::tab_spanner(label = "Unit/Time", id = "unit_span", columns = c(`Senator`, `Year`)) |> - gt::tab_spanner(label = "Potential Outcomes", id = "outcome_span", columns = c(`Potential Outcomes 1`, `Potential Outcomes 2`)) |> - gt::tab_spanner(label = "Treatment", id = "treatment_span", columns = c(`Phone Call`)) |> - gt::tab_spanner(label = "Covariates", id = "covariates_span", columns = c(`Sex`, `Age`)) |> - gt::cols_align("center", columns = gt::everything()) |> - gt::cols_align("left", columns = `Senator`) |> - gt::fmt_markdown(columns = gt::everything()) |> - gt::tab_footnote(pre_title_footnote, locations = gt::cells_title("title")) |> - gt::tab_footnote(pre_units_footnote, locations = gt::cells_column_spanners("unit_span")) |> - gt::tab_footnote(pre_outcome_footnote, locations = gt::cells_column_spanners("outcome_span")) |> - gt::tab_footnote(pre_treatment_footnote, locations = gt::cells_column_spanners("treatment_span")) |> - gt::tab_footnote(pre_covariates_footnote, locations = gt::cells_column_spanners("covariates_span")) + gt::tab_spanner(label = "Unit/Time", id = "unit_span", columns = c(`Senator`, `Session Year`)) |> + gt::tab_spanner(label = "Potential Outcomes", id = "outcome_span", columns = c(`Support Bill`, `Oppose Bill`)) |> + gt::tab_spanner(label = "Treatment", id = "treatment_span", columns = c(`Lobbying Contact`)) |> + gt::tab_spanner(label = "Covariates", id = "covariates_span", columns = c(`Senator Age`, "More")) |> + gt::cols_align(align = "center", columns = gt::everything()) |> + gt::cols_align(align = "left", columns = c(`Senator`)) |> + gt::cols_width( + "Senator" ~ gt::px(100), + "Session Year" ~ gt::px(120), + "Support Bill" ~ gt::px(120), + "Oppose Bill" ~ gt::px(120), + "Lobbying Contact" ~ gt::px(120), + "Senator Age" ~ gt::px(120), + "More" ~ gt::px(60) + ) |> + gt::tab_style( + style = gt::cell_text(size = gt::px(14)), + locations = gt::cells_body() + ) |> + gt::tab_style( + style = list( + gt::cell_text(size = gt::px(14), weight = "bold"), + gt::cell_borders(sides = "bottom", weight = gt::px(2)) + ), + locations = gt::cells_column_labels() + ) |> + gt::tab_options( + table.font.size = gt::px(14), + data_row.padding = gt::px(12), + column_labels.padding = gt::px(12), + row_group.padding = gt::px(12), + table.width = gt::pct(100), + table.margin.left = gt::px(0), + table.margin.right = gt::px(0) + ) |> + gt::fmt_markdown(columns = gt::everything()) ``` +### 3. Population Table + ```{r} -# Population Table -gt::gt(data = d_tibble) |> +# Create full data tibble with 4 rows (3 content, 1 blank in 3rd position) +data_tibble <- dplyr::bind_rows( + d_tibble[1:2, , drop = FALSE], # First 2 data rows + d_tibble[1, , drop = FALSE] |> dplyr::mutate(dplyr::across(dplyr::everything(), ~ "...")), # Blank row + d_tibble[3, , drop = FALSE] # Last data row +) |> + dplyr::mutate(Source = "Data", .before = 1) + +# Create preceptor tibble from p_tibble_full (remove More column, add Source) +preceptor_tibble <- p_tibble_full |> + dplyr::select(-More) |> + dplyr::mutate(Source = "Preceptor", .before = 1) + +# Create the 11-row population table structure manually +# Row structure: blank, 4 data (3rd blank), blank, 4 preceptor (3rd blank), blank + +# Create empty row template +empty_row <- data_tibble[1, , drop = FALSE] +empty_row[,] <- "..." + +# Build the 11-row structure +population_tibble <- dplyr::bind_rows( + empty_row, # Row 1: blank + data_tibble, # Rows 2-5: 4 data rows (3rd is blank) + empty_row, # Row 6: blank + preceptor_tibble, # Rows 7-10: 4 preceptor rows (3rd is blank) + empty_row # Row 11: blank +) + +# Add More column +population_tibble$More <- "..." + +gt::gt(population_tibble) |> gt::tab_header(title = "Population Table") |> - gt::tab_spanner(label = "Unit/Time", id = "unit_span", columns = c(`Senator`, `Time/Year`)) |> - gt::tab_spanner(label = "Potential Outcomes", id = "outcome_span", columns = c(`Potential Outcomes 1`, `Potential Outcomes 2`)) |> - gt::tab_spanner(label = "Phone Call", id = "treatment_span", columns = c(`Phone Call`)) |> - gt::tab_spanner(label = "Covariates", id = "covariates_span", columns = c(`Sex`, `Age`)) |> - gt::cols_align("center", columns = gt::everything()) |> - gt::cols_align("left", columns = `Senator`) |> - gt::fmt_markdown(columns = gt::everything()) |> - gt::tab_footnote(pop_title_footnote, locations = gt::cells_title("title")) |> - gt::tab_footnote(pop_units_footnote, locations = gt::cells_column_spanners("unit_span")) |> - gt::tab_footnote(pop_outcome_footnote, locations = gt::cells_column_spanners("outcome_span")) |> - gt::tab_footnote(pop_treatment_footnote, locations = gt::cells_column_spanners("treatment_span")) |> - gt::tab_footnote(pop_covariates_footnote, locations = gt::cells_column_spanners("covariates_span")) + gt::tab_spanner(label = "Unit/Time", id = "unit_span", columns = c(`Source`, `Senator`, `Session Year`)) |> + gt::tab_spanner(label = "Potential Outcomes", id = "outcome_span", columns = c(`Support Bill`, `Oppose Bill`)) |> + gt::tab_spanner(label = "Treatment", id = "treatment_span", columns = c(`Lobbying Contact`)) |> + gt::tab_spanner(label = "Covariates", id = "covariates_span", columns = c(`Senator Age`, "More")) |> + gt::cols_align(align = "center", columns = gt::everything()) |> + gt::cols_align(align = "left", columns = c(`Senator`)) |> + gt::cols_width( + "Source" ~ gt::px(80), + "Senator" ~ gt::px(100), + "Session Year" ~ gt::px(120), + "Support Bill" ~ gt::px(120), + "Oppose Bill" ~ gt::px(120), + "Lobbying Contact" ~ gt::px(120), + "Senator Age" ~ gt::px(120), + "More" ~ gt::px(60) + ) |> + gt::tab_style( + style = gt::cell_text(size = gt::px(14)), + locations = gt::cells_body() + ) |> + gt::tab_style( + style = list( + gt::cell_text(size = gt::px(14), weight = "bold"), + gt::cell_borders(sides = "bottom", weight = gt::px(2)) + ), + locations = gt::cells_column_labels() + ) |> + gt::tab_options( + table.font.size = gt::px(14), + data_row.padding = gt::px(12), + column_labels.padding = gt::px(12), + row_group.padding = gt::px(12), + table.width = gt::pct(100), + table.margin.left = gt::px(0), + table.margin.right = gt::px(0) + ) |> + gt::fmt_markdown(columns = gt::everything()) ``` -### 5. Cleanup +After filling in the tibbles and footnotes with actual data, you would see properly formatted tables with: -```{r} -rm(p_tibble, d_tibble) -``` +- **Preceptor Table**: 4 rows (3 content + 1 blank) with a "More" column +- **Population Table**: 11 rows with proper separation between data and preceptor sections +- **Column alignment**: Easy-to-edit tribble format with aligned columns +- **Source labeling**: Clear distinction between "Data" and "Preceptor" rows + +--- + +## Table Structure Details + +### Preceptor Table +- Uses `p_tibble` as input (3 rows of placeholders) +- Automatically adds a blank third row and "More" column via `expand_input_tibble()` +- Results in 4 total rows for the final table + +### Population Table +- Uses `d_tibble` for data input (3 rows of placeholders) +- Creates 4 data rows (3 content + 1 blank in 3rd position) +- Uses the expanded preceptor table (4 rows) +- Combines into 11-row structure: blank + 4 data + blank + 4 preceptor + blank +- All rows are properly labeled in the Source column + +### Column Alignment +The tribble code is formatted with aligned columns to make editing easier: +- Headers and values are padded to align vertically +- Minimum column width accommodates the `"..."` placeholder +- Makes it easy to see which column you're editing --- @@ -298,14 +377,13 @@ rm(p_tibble, d_tibble) The `make_p_tables()` function simplifies the creation of interpretable, spanner-labeled tables for modeling workflows. It promotes clarity, transparency, and rigor by encouraging authors to: * Replace placeholders with meaningful values -* Use `"?"` or `"---"` for unknowns in pre/post data +* Use proper formatting with double quotes around all entries * Fill in footnotes with useful context -* Ensure every table entry is surrounded by double quotes +* Take advantage of the aligned column structure for easy editing +* Understand the automatic row and column additions during rendering This workflow supports better modeling documentation and instructional design. For more on how and why to use these tables, see: * [The Cardinal Virtues](https://ppbds.github.io/primer.tutorials/articles/cardinal_virtues.html#preceptor-table) article from *primer.tutorials* - - From 4d1cc978785935a5af86759bd4d4b0506b6883c8 Mon Sep 17 00:00:00 2001 From: aashna-a-patel Date: Wed, 13 Aug 2025 16:24:59 -0500 Subject: [PATCH 10/14] small details --- R/make_p_tables.R | 38 ++++++++++++-------------------------- 1 file changed, 12 insertions(+), 26 deletions(-) diff --git a/R/make_p_tables.R b/R/make_p_tables.R index e95a4714..50201b40 100644 --- a/R/make_p_tables.R +++ b/R/make_p_tables.R @@ -144,6 +144,8 @@ pop_covariates_footnote <- \"...\" code_p_table <- glue::glue( "```{{r}} +# This code chunk will generate the Preceptor Table + p_tibble_full <- expand_input_tibble(list(p_tibble), \"preceptor\") @@ -189,29 +191,23 @@ gt::gt(p_tibble_full) |> if (source_col) { code_pop_table <- glue::glue( "```{{r}} -# Create full data tibble with 4 rows (3 content, 1 blank in 3rd position) +# This code chunk will generate the Population Table + data_tibble <- dplyr::bind_rows( - d_tibble[1:2, , drop = FALSE], # First 2 data rows - d_tibble[1, , drop = FALSE] |> dplyr::mutate(dplyr::across(dplyr::everything(), ~ \"...\")), # Blank row - d_tibble[3, , drop = FALSE] # Last data row + d_tibble[1:2, , drop = FALSE], + d_tibble[1, , drop = FALSE] |> dplyr::mutate(dplyr::across(dplyr::everything(), ~ \"...\")), + d_tibble[3, , drop = FALSE] ) |> dplyr::mutate(Source = \"Data\", .before = 1) -# Create preceptor tibble from p_tibble_full (remove More column, add Source) preceptor_tibble <- p_tibble_full |> dplyr::select(-More) |> dplyr::mutate(Source = \"Preceptor\", .before = 1) - -# Create the 11-row population table structure manually -# Row structure: blank, 4 data (3rd blank), blank, 4 preceptor (3rd blank), blank - -# Create empty row template empty_row <- data_tibble[1, , drop = FALSE] empty_row[,] <- \"...\" -# Build the 11-row structure population_tibble <- dplyr::bind_rows( empty_row, # Row 1: blank data_tibble, # Rows 2-5: 4 data rows (3rd is blank) @@ -220,10 +216,8 @@ population_tibble <- dplyr::bind_rows( empty_row # Row 11: blank ) -# Add More column population_tibble$More <- \"...\" - gt::gt(population_tibble) |> gt::tab_header(title = \"Population Table\") |> gt::tab_spanner(label = \"Unit/Time\", id = \"unit_span\", columns = c({glue_cols(pop_unit_cols)})) |> @@ -263,27 +257,21 @@ gt::gt(population_tibble) |> } else { code_pop_table <- glue::glue( "```{{r}} -# Create full data tibble with 4 rows (3 content, 1 blank in 3rd position) +# This code chunk will generate the Population Table + data_tibble <- dplyr::bind_rows( - d_tibble[1:2, , drop = FALSE], # First 2 data rows - d_tibble[1, , drop = FALSE] |> dplyr::mutate(dplyr::across(dplyr::everything(), ~ \"...\")), # Blank row - d_tibble[3, , drop = FALSE] # Last data row + d_tibble[1:2, , drop = FALSE], + d_tibble[1, , drop = FALSE] |> dplyr::mutate(dplyr::across(dplyr::everything(), ~ \"...\")), + d_tibble[3, , drop = FALSE] ) - -# Create preceptor tibble from p_tibble_full (remove More column) preceptor_tibble <- p_tibble_full |> dplyr::select(-More) -# Create the 11-row population table structure manually -# Row structure: blank, 4 data (3rd blank), blank, 4 preceptor (3rd blank), blank - -# Create empty row template empty_row <- data_tibble[1, , drop = FALSE] empty_row[,] <- \"...\" -# Build the 11-row structure population_tibble <- dplyr::bind_rows( empty_row, # Row 1: blank data_tibble, # Rows 2-5: 4 data rows (3rd is blank) @@ -292,10 +280,8 @@ population_tibble <- dplyr::bind_rows( empty_row # Row 11: blank ) -# Add More column population_tibble$More <- \"...\" - gt::gt(population_tibble) |> gt::tab_header(title = \"Population Table\") |> gt::tab_spanner(label = \"Unit/Time\", id = \"unit_span\", columns = c({glue_cols(pop_unit_cols)})) |> From 4a21833e858f0228aa9ff4943144419db24842b6 Mon Sep 17 00:00:00 2001 From: aashna-a-patel Date: Wed, 13 Aug 2025 16:33:02 -0500 Subject: [PATCH 11/14] fixed footnote issue --- R/make_p_tables.R | 39 +++++++++++++++++++-------------------- vignettes/tables.qmd | 42 +++++++++++++++++++++++++++++++++++++----- 2 files changed, 56 insertions(+), 25 deletions(-) diff --git a/R/make_p_tables.R b/R/make_p_tables.R index 50201b40..9c3577ad 100644 --- a/R/make_p_tables.R +++ b/R/make_p_tables.R @@ -89,19 +89,16 @@ make_p_tables <- function( stop("`type` must be either 'causal' or 'predictive'.") } - # Both p_tibble and d_tibble use the same columns (no Source column yet) all_cols <- c(unit_label, outcome_label, treatment_label, covariate_label) # Source column only added during population table rendering pop_unit_cols <- if (source_col) c("Source", unit_label) else unit_label - # Generate tribble code using helper function p_tribble_code <- write_input_tribble(all_cols) d_tribble_code <- write_input_tribble(all_cols) - widths <- c( if (source_col) 80 else NULL, # Source column width max(nchar(unit_label[1]) * 8, 100), # Minimum 100px for first unit column @@ -112,27 +109,22 @@ make_p_tables <- function( 60 # More column ) - glue_cols <- function(cols) paste0("`", cols, "`", collapse = ", ") - code_footnotes <- glue::glue( "```{{r}} # Edit the following tibbles and footnotes, look at the vignette for more details p_tibble <- {p_tribble_code} - d_tibble <- {d_tribble_code} - pre_title_footnote <- \"...\" pre_units_footnote <- \"...\" pre_outcome_footnote <- \"...\" pre_treatment_footnote <- \"...\" pre_covariates_footnote <- \"...\" - pop_title_footnote <- \"...\" pop_units_footnote <- \"...\" pop_outcome_footnote <- \"...\" @@ -141,14 +133,12 @@ pop_covariates_footnote <- \"...\" ```" ) - code_p_table <- glue::glue( "```{{r}} # This code chunk will generate the Preceptor Table p_tibble_full <- expand_input_tibble(list(p_tibble), \"preceptor\") - gt::gt(p_tibble_full) |> gt::tab_header(title = \"Preceptor Table\") |> gt::tab_spanner(label = \"Unit/Time\", id = \"unit_span\", columns = c({glue_cols(unit_label)})) |> @@ -182,11 +172,15 @@ gt::gt(p_tibble_full) |> table.margin.left = gt::px(0), table.margin.right = gt::px(0) ) |> - gt::fmt_markdown(columns = gt::everything()) + gt::fmt_markdown(columns = gt::everything()) |> + gt::tab_footnote(footnote = pre_title_footnote, locations = gt::cells_title()) |> + gt::tab_footnote(footnote = pre_units_footnote, locations = gt::cells_column_spanners(spanners = \"unit_span\")) |> + gt::tab_footnote(footnote = pre_outcome_footnote, locations = gt::cells_column_spanners(spanners = \"outcome_span\")) |> + gt::tab_footnote(footnote = pre_treatment_footnote, locations = gt::cells_column_spanners(spanners = \"treatment_span\")) |> + gt::tab_footnote(footnote = pre_covariates_footnote, locations = gt::cells_column_spanners(spanners = \"covariates_span\")) ```" ) - # Population table code - fixed to show all 4 data rows and proper structure if (source_col) { code_pop_table <- glue::glue( @@ -200,7 +194,6 @@ data_tibble <- dplyr::bind_rows( ) |> dplyr::mutate(Source = \"Data\", .before = 1) - preceptor_tibble <- p_tibble_full |> dplyr::select(-More) |> dplyr::mutate(Source = \"Preceptor\", .before = 1) @@ -251,7 +244,12 @@ gt::gt(population_tibble) |> table.margin.left = gt::px(0), table.margin.right = gt::px(0) ) |> - gt::fmt_markdown(columns = gt::everything()) + gt::fmt_markdown(columns = gt::everything()) |> + gt::tab_footnote(footnote = pop_title_footnote, locations = gt::cells_title()) |> + gt::tab_footnote(footnote = pop_units_footnote, locations = gt::cells_column_spanners(spanners = \"unit_span\")) |> + gt::tab_footnote(footnote = pop_outcome_footnote, locations = gt::cells_column_spanners(spanners = \"outcome_span\")) |> + gt::tab_footnote(footnote = pop_treatment_footnote, locations = gt::cells_column_spanners(spanners = \"treatment_span\")) |> + gt::tab_footnote(footnote = pop_covariates_footnote, locations = gt::cells_column_spanners(spanners = \"covariates_span\")) ```" ) } else { @@ -268,7 +266,6 @@ data_tibble <- dplyr::bind_rows( preceptor_tibble <- p_tibble_full |> dplyr::select(-More) - empty_row <- data_tibble[1, , drop = FALSE] empty_row[,] <- \"...\" @@ -287,7 +284,7 @@ gt::gt(population_tibble) |> gt::tab_spanner(label = \"Unit/Time\", id = \"unit_span\", columns = c({glue_cols(pop_unit_cols)})) |> gt::tab_spanner(label = \"Potential Outcomes\", id = \"outcome_span\", columns = c({glue_cols(outcome_label)})) |> gt::tab_spanner(label = \"Treatment\", id = \"treatment_span\", columns = c({glue_cols(treatment_label)})) |> - gt::tab_spanner(label = \"Covariates\", id = \"covariates_span\", columns = c({glue_cols(covariate_label)})) |> + gt::tab_spanner(label = \"Covariates\", id = \"covariates_span\", columns = c({glue_cols(covariate_label)}, \"More\")) |> gt::cols_align(align = \"center\", columns = gt::everything()) |> gt::cols_align(align = \"left\", columns = c(`{unit_label[1]}`)) |> gt::cols_width({ @@ -295,12 +292,16 @@ gt::gt(population_tibble) |> width_assignments <- paste0('\"', all_cols_with_more, '\" ~ gt::px(', widths[!is.null(widths)], ')', collapse = \", \") width_assignments }) |> - gt::fmt_markdown(columns = gt::everything()) + gt::fmt_markdown(columns = gt::everything()) |> + gt::tab_footnote(footnote = pop_title_footnote, locations = gt::cells_title()) |> + gt::tab_footnote(footnote = pop_units_footnote, locations = gt::cells_column_spanners(spanners = \"unit_span\")) |> + gt::tab_footnote(footnote = pop_outcome_footnote, locations = gt::cells_column_spanners(spanners = \"outcome_span\")) |> + gt::tab_footnote(footnote = pop_treatment_footnote, locations = gt::cells_column_spanners(spanners = \"treatment_span\")) |> + gt::tab_footnote(footnote = pop_covariates_footnote, locations = gt::cells_column_spanners(spanners = \"covariates_span\")) ```" ) } - full_code <- paste( code_footnotes, code_p_table, @@ -308,12 +309,10 @@ gt::gt(population_tibble) |> sep = "\n\n" ) - rstudioapi::insertText( location = rstudioapi::getActiveDocumentContext()$selection[[1]]$range, text = full_code ) - invisible(NULL) } diff --git a/vignettes/tables.qmd b/vignettes/tables.qmd index 130d2320..54edc776 100644 --- a/vignettes/tables.qmd +++ b/vignettes/tables.qmd @@ -1,3 +1,32 @@ +## The "More" Column + +One of the key features of both the Preceptor and Population tables is the automatically-added **"More" column**. This column serves several important purposes: + +### Purpose and Functionality + +* **Placeholder for expansion**: The "More" column acknowledges that your initial analysis might not include all relevant covariates. As you develop your model, you might discover that additional variables like "Previous Voting History", "Campaign Spending", or "District Demographics" are important. + +* **Visual reminder**: By including this column in your initial table structure, you're reminded to think critically about what other variables might matter for your analysis. + +* **Easy modification**: Rather than having to restructure your entire table when you want to add a new covariate, you can simply replace the "More" column header and add additional "More" columns as needed. + +### How It Works + +* The "More" column is automatically added during table rendering by the `expand_input_tibble()` function +* It's not part of your initial `p_tibble` or `d_tibble` - those contain only the variables you explicitly specify +* The column is filled with `"..."` placeholders in all rows +* It's grouped under the "Covariates" spanner along with your main covariate column + +### Practical Usage + +When you're ready to expand your analysis: + +1. **Replace the header**: Change `"More"` to your new variable name (e.g., `"Previous Experience"`) +2. **Add content**: Replace the `"..."` placeholders with actual values +3. **Add another "More"**: If you need space for yet another variable, you can manually add another "More" column + +This design encourages iterative analysis development while maintaining a clean, organized table structure from the start. + --- title: "Preceptor and Population Tables" author: "David Kane and Aashna Patel" @@ -25,7 +54,7 @@ This format draws inspiration from the [Cardinal Virtues](https://ppbds.github.i ### Preceptor Table -A **Preceptor Table** contains hypothetical or expected outcomes for units (such as students or senators). It often includes unknowns (denoted by `"..."`) where real data is not yet available, and reflects researcher or instructor expectations. The table automatically includes a blank third row and a "More" column for additional covariates. +A **Preceptor Table** contains hypothetical or expected outcomes for units (such as students or senators). It often includes unknowns (denoted by `"..."`) where real data is not yet available, and reflects researcher or instructor expectations. The table automatically includes a blank third row and a "More" column for additional covariates that you might want to include in your analysis. ### Population Table @@ -49,7 +78,7 @@ Each table includes spanners for: * **Unit/Time** (for the unit columns) * **Potential Outcomes** (for causal models) or **Outcome** (for predictive models) * **Treatment** (included only in causal models) -* **Covariates** (includes the covariate column and the "More" column) +* **Covariates** (includes the main covariate column and the "More" column for additional variables you might identify during analysis) > **Note:** All table entries must be surrounded by **double quotes**, even for numeric values (e.g., `"42"`). @@ -119,8 +148,9 @@ The `make_p_tables()` function takes a set of user-defined labels and options th ### `covariate_label` *(Character)* * Label for the main covariate column relevant to the analysis. -* This is grouped under the `"Covariates"` spanner along with the "More" column. +* This is grouped under the `"Covariates"` spanner along with the automatically-added "More" column. * Should be a simple phrase like `"Age"` or `"School Type"`. +* The "More" column is added during rendering and provides space for additional covariates that you might discover are important during your analysis. --- @@ -152,7 +182,7 @@ Below is a guide to what each footnote is for: * **`pre_treatment_footnote`**: Defines what the treatment actually entails and how it is operationalized. For example, a phone call campaign, assignment to tutoring, or access to a program. -* **`pre_covariates_footnote`**: Explains why the selected covariates were chosen and their role in forming expectations. Should also clarify the "More" column purpose. +* **`pre_covariates_footnote`**: Explains why the selected covariates were chosen and their role in forming expectations. Should also clarify the purpose of the "More" column - typically used to indicate that additional covariates could be relevant to the analysis but are not yet specified. --- @@ -166,7 +196,7 @@ Below is a guide to what each footnote is for: * **`pop_treatment_footnote`**: Explains how actual treatment status was observed or inferred. May differ from the assumptions made in the Preceptor Table. -* **`pop_covariates_footnote`**: Describes where the covariate data comes from in the population table and whether it's measured identically to the preceptor rows. +* **`pop_covariates_footnote`**: Describes where the covariate data comes from in the population table and whether it's measured identically to the preceptor rows. Should also explain what additional variables might be included in the "More" column. --- @@ -355,12 +385,14 @@ After filling in the tibbles and footnotes with actual data, you would see prope ### Preceptor Table - Uses `p_tibble` as input (3 rows of placeholders) - Automatically adds a blank third row and "More" column via `expand_input_tibble()` +- The "More" column provides space for additional covariates you might identify later - Results in 4 total rows for the final table ### Population Table - Uses `d_tibble` for data input (3 rows of placeholders) - Creates 4 data rows (3 content + 1 blank in 3rd position) - Uses the expanded preceptor table (4 rows) +- The "More" column is carried over from the preceptor table and added to the population structure - Combines into 11-row structure: blank + 4 data + blank + 4 preceptor + blank - All rows are properly labeled in the Source column From a01fe41ff66f8bc6822424459ccde18554d5db8b Mon Sep 17 00:00:00 2001 From: aashna-a-patel Date: Wed, 13 Aug 2025 16:54:29 -0500 Subject: [PATCH 12/14] TODO stuff --- R/make_p_tables.R | 8 +++-- TODO.txt | 72 +++----------------------------------- man/expand_input_tibble.Rd | 17 +++++---- man/make_p_tables.Rd | 9 ++--- man/write_input_tribble.Rd | 4 +-- 5 files changed, 26 insertions(+), 84 deletions(-) diff --git a/R/make_p_tables.R b/R/make_p_tables.R index 9c3577ad..455aaa23 100644 --- a/R/make_p_tables.R +++ b/R/make_p_tables.R @@ -50,16 +50,17 @@ #' #' @examples #' \dontrun{ -#' # Insert causal tables for a study of senators' voting behavior over years +#' # Insert causal tables for a study of senators' voting behavior +#' # Outcomes reflect support conditional on the treatment #' make_p_tables( #' type = "causal", #' unit_label = c("Senator", "Session Year"), -#' outcome_label = c("Support Bill", "Oppose Bill"), +#' outcome_label = c("Support if Contact", "Support if No Contact"), #' treatment_label = "Lobbying Contact", #' covariate_label = "Senator Age" #' ) #' -#' # Insert predictive tables for a clinical trial measuring patient recovery over time +#' # Insert predictive tables for a clinical trial measuring patient recovery #' make_p_tables( #' type = "predictive", #' unit_label = c("Patient ID", "Visit Number"), @@ -70,6 +71,7 @@ #' } + make_p_tables <- function( type, unit_label, diff --git a/TODO.txt b/TODO.txt index 0c756ba1..46fe6501 100644 --- a/TODO.txt +++ b/TODO.txt @@ -1,78 +1,14 @@ Change DESCRIPTION to use the current released version of primer.data, so it stops updating each time. -```{r} -pre_title_footnote <- "Preceptor Table Title" -pre_units_footnote <- "Units and time information" -pre_outcome_footnote <- "Outcome or potential outcomes description" -pre_treatment_footnote <- "Treatment or intervention description" -pre_covariates_footnote <- "Covariates and their units" - -pop_title_footnote <- "Population Table Title" -pop_units_footnote <- "Units and time information" -pop_outcome_footnote <- "Outcome or potential outcomes description" -pop_treatment_footnote <- "Treatment or intervention description" -pop_covariates_footnote <- "Covariates and their units" - -p_tibble <- tibble::tribble( - ~`Senator`, ~`Session Year`, ~`Support Bill`, ~`Oppose Bill`, ~`Lobbying Contact`, ~`Senator Age`, - "...", "...", "...", "...", "...", "...", - "...", "...", "...", "...", "...", "...", - "...", "...", "...", "...", "...", "..." -) - -d_tibble <- tibble::tribble( - ~`Source`, ~`Senator`, ~`Session Year`, ~`Support Bill`, ~`Oppose Bill`, ~`Lobbying Contact`, ~`Senator Age`, - "...", "...", "...", "...", "...", "...", "...", - "...", "...", "...", "...", "...", "...", "...", - "...", "...", "...", "...", "...", "...", "..." -) -``` - -```{r} -p_tibble_full <- p_tibble |> - dplyr::add_row(!!!as.list(rep(NA, ncol(p_tibble)))) |> - dplyr::mutate(More = c(rep(NA, nrow(.) - 1), "...")) - -gt::gt(p_tibble_full) |> - gt::tab_header(title = "Preceptor Table") |> - gt::tab_spanner(label = "Unit", id = "unit_span", columns = c(`Senator`, `Session Year`)) |> - gt::tab_spanner(label = "Potential Outcomes", id = "outcome_span", columns = c(`Support Bill`, `Oppose Bill`)) |> - gt::tab_spanner(label = "Treatment", id = "treatment_span", columns = c(`Lobbying Contact`)) |> - gt::tab_spanner(label = "Covariates", id = "covariates_span", columns = c(`Senator Age`)) |> - gt::cols_align(align = "center", columns = gt::everything()) |> - gt::cols_align(align = "left", columns = c(`Senator`)) |> - gt::cols_width(columns = c(`Senator`, `Session Year`, `Support Bill`, `Oppose Bill`, `Lobbying Contact`, `Senator Age`, `More`), - widths = gt::px(c(9, 14, 14, 14, 18, 13, 5))) |> - gt::fmt_markdown(columns = gt::everything()) -``` - -```{r} -d_tibble_full <- d_tibble |> - dplyr::add_row(!!!as.list(rep(NA, ncol(d_tibble)))) |> - dplyr::mutate(More = c(rep(NA, nrow(.) - 1), "...")) - -gt::gt(d_tibble_full) |> - gt::tab_header(title = "Population Table") |> - gt::tab_spanner(label = "Unit/Time", id = "unit_span", columns = c(`Source`, `Senator`, `Session Year`)) |> - gt::tab_spanner(label = "Potential Outcomes", id = "outcome_span", columns = c(`Support Bill`, `Oppose Bill`)) |> - gt::tab_spanner(label = "Treatment", id = "treatment_span", columns = c(`Lobbying Contact`)) |> - gt::tab_spanner(label = "Covariates", id = "covariates_span", columns = c(`Senator Age`)) |> - gt::cols_align(align = "center", columns = gt::everything()) |> - gt::cols_align(align = "left", columns = c(`Senator`)) |> - gt::cols_width(columns = c(`Source`, `Senator`, `Session Year`, `Support Bill`, `Oppose Bill`, `Lobbying Contact`, `Senator Age`, `More`), - widths = gt::px(c(9, 14, 14, 14, 18, 13, 5))) |> - gt::fmt_markdown(columns = gt::everything()) -``` - ### make_p_table() -* "pre_title_footnote <- "Preceptor Table Title"" should be "pre_title_footnote <- "" +* "pre_title_footnote <- "Preceptor Table Title"" should be "pre_title_footnote <- "" AP: done -* center the ... in p/d_tibble creation +* center the ... in p/d_tibble creation AP: did something like that -* Example case does not work. +* Example case does not work. AP: fixed -* Example outcome labels need **if** and a reference to the treatment variable. Support in Contract and Support if No Contract. Include first sentence in vignette. +* Example outcome labels need **if** and a reference to the treatment variable. Support in Contract and Support if No Contract. Include first sentence in vignette. AP: fixed, need to include in vignette * d_tibble should not include Source. You add that at the end, just before the gt code. diff --git a/man/expand_input_tibble.Rd b/man/expand_input_tibble.Rd index 09eaea33..789743f6 100644 --- a/man/expand_input_tibble.Rd +++ b/man/expand_input_tibble.Rd @@ -22,26 +22,29 @@ Processes one or two input tibbles to add missing rows (to at least 3 rows), a final "More" column with "..." in the last row, and combines them if type is 'population'. } \details{ -For "preceptor": adds missing rows to ensure at least 3 rows, adds a "More" column -with NA except last row contains "...". +For "preceptor": adds a third row between the last 2 rows filled with "...", +adds a "More" column with "..." in all positions. For "population": expands each tibble similarly, then combines them with -empty rows before, between, and after, then adds "More" column. +empty rows before, between, and after, then adds "More" column with "...". } \examples{ # Preceptor example pre_tib <- tibble::tribble(~Unit, ~Year, ~Outcome, "A", "2020", "5", - "B", "2021", "6") + "B", "2021", "6", + "C", "2022", "7") expand_input_tibble(list(pre_tib), "preceptor") # Population example pop1 <- tibble::tribble(~Source, ~Unit, ~Year, "S1", "A", "2020", - "S2", "B", "2021") + "S2", "B", "2021", + "S3", "C", "2022") pop2 <- tibble::tribble(~Source, ~Unit, ~Year, - "S1", "C", "2022", - "S2", "D", "2023") + "S1", "D", "2023", + "S2", "E", "2024", + "S3", "F", "2025") expand_input_tibble(list(pop1, pop2), "population", source = TRUE) } diff --git a/man/make_p_tables.Rd b/man/make_p_tables.Rd index e1f99d88..1bd3a0b2 100644 --- a/man/make_p_tables.Rd +++ b/man/make_p_tables.Rd @@ -40,9 +40,9 @@ and predictive workflows. The output includes: \itemize{ -\item Editable footnotes for documentation \item Empty \code{tibble}s for the Preceptor Table and Population Table (the latter includes the Preceptor rows) +\item Editable footnotes for documentation \item \code{gt} code chunks to render each table with labeled spanners and columns sized roughly proportional to label length \item The Preceptor and Population tables include a final "More" column and @@ -65,16 +65,17 @@ helping maintain readable, centered columns. } \examples{ \dontrun{ -# Insert causal tables for a study of senators' voting behavior over years +# Insert causal tables for a study of senators' voting behavior +# Outcomes reflect support conditional on the treatment make_p_tables( type = "causal", unit_label = c("Senator", "Session Year"), - outcome_label = c("Support Bill", "Oppose Bill"), + outcome_label = c("Support if Contact", "Support if No Contact"), treatment_label = "Lobbying Contact", covariate_label = "Senator Age" ) -# Insert predictive tables for a clinical trial measuring patient recovery over time +# Insert predictive tables for a clinical trial measuring patient recovery make_p_tables( type = "predictive", unit_label = c("Patient ID", "Visit Number"), diff --git a/man/write_input_tribble.Rd b/man/write_input_tribble.Rd index 798fb518..42862748 100644 --- a/man/write_input_tribble.Rd +++ b/man/write_input_tribble.Rd @@ -11,12 +11,12 @@ write_input_tribble(names) } \value{ Character string containing the R code for an input tribble with placeholders \code{"..."}, -formatted for manual editing. +formatted for manual editing with aligned columns. } \description{ Generates a character string representing an R tibble::tribble() with the specified column names and 3 rows filled with placeholder -text \code{"..."}. Useful for quickly creating editable table templates. +text \code{"..."}. Column values are aligned under their headers for easy editing. } \examples{ write_input_tribble(c("Unit", "Year", "Outcome", "Treatment")) From 5a3d9b17befe71d79eb7a3ffcd0cb4f6d942933b Mon Sep 17 00:00:00 2001 From: aashna-a-patel Date: Wed, 13 Aug 2025 16:56:33 -0500 Subject: [PATCH 13/14] more TODO --- TODO.txt | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/TODO.txt b/TODO.txt index 46fe6501..7dadb12c 100644 --- a/TODO.txt +++ b/TODO.txt @@ -1,39 +1,39 @@ -Change DESCRIPTION to use the current released version of primer.data, so it stops updating each time. +Change DESCRIPTION to use the current released version of primer.data, so it stops updating each time. AP: how do i do this? ### make_p_table() * "pre_title_footnote <- "Preceptor Table Title"" should be "pre_title_footnote <- "" AP: done -* center the ... in p/d_tibble creation AP: did something like that +* center the ... in p/d_tibble creation AP: done, left align * Example case does not work. AP: fixed * Example outcome labels need **if** and a reference to the treatment variable. Support in Contract and Support if No Contract. Include first sentence in vignette. AP: fixed, need to include in vignette -* d_tibble should not include Source. You add that at the end, just before the gt code. +* d_tibble should not include Source. You add that at the end, just before the gt code. AP: DONE -* expand_input_tibble is not working. Ought to fail if not at least three rows. +* expand_input_tibble is not working. Ought to fail if not at least three rows. AP: fixed function -* Tell the AI to reorganize the code. Make sure that, at the top, it calculates the sizes of various things (and their names) before it needs to use those things. AP: had it calculate length and number of characters - +* Tell the AI to reorganize the code. Make sure that, at the top, it calculates the sizes of various things (and their names) before it needs to use those things. AP: had it calculate length and number of characters AP: all this is done +AP: all separate function stuff is done * Make this a couple of separate functions. All included on this one page. All with roxygen stuff which documents them. + Key function is write_input_tribble(names) which takes a character vector of names and then writes out the input tribble text, centering as appropriate. + Another function exapnd_input_tibble(x, type, source = FALSE) which takes a list of tibble x, a type which is preceptor or population and source which is TRUE or FALSE. If type is preceptor, then source must be false. If type is preceptor, then length(x) == 1. If type is population, then length(x) == 2. It first, takes any tibble in x and adds a more column and a missing third row. It preceptor, then done. If population, then combine the two tibbles and adding missing rows at start, between and end. Returns a single. - Feed the result from expand_input_tibble() directly to the gt pipeline for making the table. + Feed the result from expand_input_tibble() directly to the gt pipeline for making the table. AP: done -* Not this: "# Leave the third row and last column as-is to signal more rows exist" User only gets three rows. You add the third row of ... magically in your own code. +* Not this: "# Leave the third row and last column as-is to signal more rows exist" User only gets three rows. You add the third row of ... magically in your own code. AP: magically done * Use the number of characters in each label to space out the "..." entries so that they are beneath (centered) (even if only roughly) the relevant label. There is no Source column AP: still working on * You can change the `More` column header to ... in the gt() code using a command like col_label(). (Might not be exact name.) -* The Population Table code chunk does NOT get to assume that the Preceptor Table code was run. It begins with "raw" p_tibble and d_tibble, objects which only contain the data enterted by the user. That means that there will be some replication in the code in the two chunks: they both add a missing third row to p_tibble and a More column, for example. But that is OK. +* The Population Table code chunk does NOT get to assume that the Preceptor Table code was run. It begins with "raw" p_tibble and d_tibble, objects which only contain the data enterted by the user. That means that there will be some replication in the code in the two chunks: they both add a missing third row to p_tibble and a More column, for example. But that is OK. * Moreover, that suggest that we might want a transform_raw_tibble(x, which.rows = c(1, 3), more = TRUE) function which takes a tibble (either raw p_tibble or d_tibble) and then adds missing rows, which you specify as an argument. This function goes in its own .R file and is documented and exported. From 86ecd470ba210b967144bc5c84d1a9df926ee674 Mon Sep 17 00:00:00 2001 From: aashna-a-patel Date: Wed, 13 Aug 2025 17:53:52 -0500 Subject: [PATCH 14/14] added libraries --- DESCRIPTION | 2 ++ 1 file changed, 2 insertions(+) diff --git a/DESCRIPTION b/DESCRIPTION index 9d473684..4f3643c9 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -19,6 +19,8 @@ RoxygenNote: 7.3.2 Imports: primer.data, tutorial.helpers, + dplyr, + glue, katex, gt, tibble