Skip to content

Commit 60b5242

Browse files
authored
Merge pull request #258 from Crunch-io/cbs_tracking_fixes
Fixes for tracking report edge cases
2 parents 5151749 + e213f01 commit 60b5242

13 files changed

+324
-70
lines changed

DESCRIPTION

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@ Package: crunchtabs
22
Type: Package
33
Title: Custom Report Generation for Crunch Datasets
44
Description: In order to generate custom survey reports, this package provides
5-
functions for computing 'toplines' (one-way frequency summaries) and
6-
'banners' (cross-tabulations) of datasets in the Crunch
5+
functions for computing 'toplines' (one-way frequency summaries),
6+
'banners' (cross-tabulations) and codebooks of datasets in the Crunch
77
(<https://crunch.io/>) web service. Reports can be written in 'PDF' format
88
using 'LaTeX' or in Microsoft Excel '.xlsx' files.
9-
Version: 1.4.0
9+
Version: 1.4.1
1010
Authors@R: c(
1111
person("Persephone", "Tsebelis", role="aut"),
1212
person("Kamil", "Sedrowicz", role="aut"),

NEWS.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,17 @@
1+
## crunchtabs 1.4.1
2+
3+
- Multiple adjustments for tracking report edge cases
4+
- In a tracking report, if an alias appears only once we now specify that by appending the wave label to the question's note.
5+
- In a tracking report, we now have the option to show a variable only once even if it appears in multiple datasets.
6+
7+
Fixes:
8+
9+
- In some cases there are duplicated row names in different positions when we use cbindFill, we did not account for this possibility.
10+
- When creating a tracking report for a multiple response question, it's possible that one or more of the responses is not included in one or more of the waves, we have added a tryCatch to accomodate this possibility.
11+
- themeNew documentation was missing a closing } that silently broke the display of documentation via ?themeNew.
12+
- When converting MR variables we rename subVars using numbers. If a researcher included an alias with the same number they would collide. Now we assign random strings instead of sequential numbers to the subVars.
13+
14+
115
## crunchtabs 1.4.0
216

317
Features:

R/asToplineCategoricalArray.R

Lines changed: 62 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -28,36 +28,6 @@ as.ToplineCategoricalArray <- function(
2828
)
2929
}
3030

31-
categoryFill <- function(clist) {
32-
cbindFill <- function(x, y) {
33-
r <- merge(x, y, by = "row.names", all = TRUE, sort = FALSE)
34-
rownames(r) <- r$Row.names
35-
r$Row.names <- NULL
36-
r
37-
}
38-
39-
addPos <- function(x) {
40-
x[,1] <- 1:nrow(x)
41-
x
42-
}
43-
44-
r <- lapply(clist, addPos)
45-
r <- do.call(rbind, r)
46-
r <- data.frame(nm = names(r[,1]), pos = r[,1])
47-
r <- unique(r)
48-
rownames(r) <- r$nm
49-
r$nm <- NULL
50-
51-
m <- Reduce(function(x,y) suppressWarnings(cbindFill(x,y)), clist)
52-
m <- suppressWarnings(merge(m, r, by = "row.names", all = TRUE, sort = FALSE))
53-
m <- m[with(m, order(pos)),]
54-
55-
rownames(m) <- m$Row.names
56-
m$Row.names <- NULL
57-
m$pos <- NULL
58-
as.matrix(m)
59-
}
60-
6131
counts <- obj$crosstabs$Results$`___total___`$counts
6232
second_label <- attr(counts, "dimnames")[[1]]
6333

@@ -111,7 +81,14 @@ catArrayToCategoricals <- function(questions, question_alias, labels) {
11181
labels <- paste0("Wave ", seq_along(questions))
11282
}
11383

114-
nms <- paste0(question_alias, seq_along(statements))
84+
# Collisions can occur so we randomize names because people version aliases sometimes
85+
# votefactors with sub vars + votefactors2 :/
86+
# randNames <- function(n) {
87+
# a <- do.call(paste0, replicate(5, sample(LETTERS, n, TRUE), FALSE))
88+
# paste0(a, sprintf("%04d", sample(9999, n, TRUE)), sample(LETTERS, n, TRUE))
89+
# }
90+
91+
nms <- paste0(question_alias, "_", seq_along(statements))
11592

11693
# Create list of objects to fill in, one for each sub statement of the
11794
# multiple response group
@@ -137,11 +114,13 @@ catArrayToCategoricals <- function(questions, question_alias, labels) {
137114

138115
# Pull out our data
139116
for (i in seq_len(nrow(guide))) {
140-
guide$value[i] <- questions[[
141-
guide$label[i]
142-
]]$crosstabs$Results$`___total___`$proportions[
143-
guide$cat[i], guide$statement[i]
144-
]
117+
guide$value[i] <- tryCatch({
118+
questions[[
119+
guide$label[i]
120+
]]$crosstabs$Results$`___total___`$proportions[
121+
guide$cat[i], guide$statement[i]
122+
]
123+
}, error = function(e) NA_real_)
145124
}
146125

147126
# Pre allocate
@@ -175,3 +154,50 @@ catArrayToCategoricals <- function(questions, question_alias, labels) {
175154
names(l) <- nms
176155
return(l)
177156
}
157+
158+
#' Merge two data.frames by rownames
159+
#'
160+
#' This function is designed to cbind via rownames where
161+
#' the rownames may not match and then fix the result so that
162+
#' it can be further merged to another data.frame.
163+
#' @param x A data.frame
164+
#' @param y A data.frame
165+
cbindFill <- function(x, y) {
166+
r <- merge(x, y, by = "row.names", all = TRUE, sort = FALSE)
167+
rownames(r) <- r$Row.names
168+
r$Row.names <- NULL
169+
r
170+
}
171+
172+
#' Add position
173+
#'
174+
#' A small utility function to add position to a matrix
175+
#' @param x A matrix
176+
addPos <- function(x) {
177+
x[,1] <- 1:nrow(x)
178+
x
179+
}
180+
181+
#' Column Bind Unequal Matrices
182+
#'
183+
#' This function takes a list of matrices and binds them together into
184+
#' a single frame. Accounts for missing or unequal rows, by rowname.
185+
#'
186+
#' @param clist A list of matrices with rownames
187+
categoryFill <- function(clist) {
188+
r <- lapply(clist, addPos)
189+
r <- do.call(rbind, r)
190+
r <- data.frame(nm = names(r[,1]), pos = r[,1])
191+
r <- r[!duplicated(r$nm),]
192+
rownames(r) <- r$nm
193+
r$nm <- NULL
194+
195+
m <- Reduce(function(x,y) suppressWarnings(cbindFill(x,y)), clist)
196+
m <- suppressWarnings(merge(m, r, by = "row.names", all = TRUE, sort = FALSE))
197+
m <- m[with(m, order(pos)),]
198+
199+
rownames(m) <- m$Row.names
200+
m$Row.names <- NULL
201+
m$pos <- NULL
202+
as.matrix(m)
203+
}

R/theme.R

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
#' `themeNew` produces themes for `writeExcel` or `writeLatex`.
44
#'
55
#' @section Theme Arguments:
6-
# nolint start
76
#' \describe{
87
#' \item{digits}{A numeric. How many digits should the data be rounded to? (In Excel, this is excel styling.) Defaults to 0.}
98
#' \item{digits_numeric}{A numeric. How many digits should continuous variable data be rounded to? (In Latex, , this is Latex styling.) Defaults to 2.}
@@ -82,7 +81,6 @@
8281
#' \item{border_color}{In Excel, an optional color. The border color of the relevant cells.}
8382
#' \item{border_left}{In Excel, an optional logical. Should there be a border on the left of the relevant cells? }
8483
#' \item{border_right}{In Excel, an optional logical. Should there be a border on the right of the relevant cells? }
85-
#' \item{border_style}{In Excel, an optional character. The style of the border of the relevant
8684
#' \item{border_style}{In Excel, an optional character. The style of the border of the relevant cells. Valid options are: "dashDot", "dashDotDot", "dashed", "dotted", "double", "hair", "medium", "mediumDashDot", "mediumDashDotDot", "mediumDashed", "none", "slantDashDot", "thick", and "thin".}
8785
#' \item{border_top}{In Excel, an optional logical. Should there be a border on the top of the relevant cells? }
8886
#' \item{decoration}{An optional character vector. Text decorations to be applied to relevant cells. Valid options are: "bold", "italic", "strikeout", "underline", and "underline2".}
@@ -153,8 +151,6 @@ themeNew <- function(..., default_theme = themeDefaultExcel()) {
153151
}
154152
}
155153

156-
# nolint end
157-
158154
theme <- modifyList(default_theme, dots, keep.null = TRUE)
159155
theme <- theme[union(names(dots), names(default_theme))]
160156

R/trackingReports.R

Lines changed: 37 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@ trackingReport <- function(dataset_list, vars, labels = NULL, weight = NULL, sho
3131
rebuilt_results <- list()
3232
class(rebuilt_results) <- c("Toplines", "CrunchTabs")
3333
rebuilt_results$results <- lapply(vars, function(x) NULL)
34-
rebuilt_results$metadata <- tabs[[1]]$metadata
34+
has_meta <- which(!unlist(lapply(lapply(tabs, function(x) x$metadata), is.null)))[1]
35+
rebuilt_results$metadata <- tabs[[has_meta]]$metadata
3536
names(rebuilt_results$results) <- vars
3637
rebuilt_results$banner <- NULL
3738

@@ -59,26 +60,10 @@ trackingReport <- function(dataset_list, vars, labels = NULL, weight = NULL, sho
5960
}
6061

6162
rebuilt_results$results[[v]]$available_at <- results_available
63+
rebuilt_results <- trackingDeclareAvailability(
64+
rebuilt_results, results_available, var = v, labels
65+
)
6266

63-
64-
# For each alias, we set an attribute that identifies it's availability
65-
# across all the datasets: "all", "partial", and "single"
66-
# - "all" means it is available in every dataset
67-
# - "partial" means it is available in only some datasets
68-
# - "single" means it is available in exactly one dataset
69-
70-
# Because we use subsetting at the list level, "all" and "partial"
71-
# would follow a typical path that labeling was adjusted appropriately
72-
# for presentation in the resulting pdf "single" should act as a simple
73-
# passthrough where no additional formatting or manipulation takes place
74-
# on the result.
75-
76-
# The single case
77-
if (length(results_available) == 1) {
78-
rebuilt_results$results[[v]]$availability <- "single"
79-
} else {
80-
rebuilt_results$results[[v]]$availability <- "general"
81-
}
8267
}
8368

8469
# Now that we have an attribute that identifies availability we can use it as
@@ -132,7 +117,39 @@ trackingReport <- function(dataset_list, vars, labels = NULL, weight = NULL, sho
132117
rebuilt_results
133118
}
134119

120+
#' Specify question availability in a tracking report
135121
#'
122+
#' For each alias, we set an attribute that identifies it's availability
123+
#' across all the datasets: "general", and "single"
124+
#' - "general" means it is available in only some datasets
125+
#' - "single" means it is available in exactly one dataset
126+
#' Because we use subsetting at the list level, "general" and "single"
127+
#' would follow a typical path that labeling was adjusted appropriately
128+
#' for presentation in the resulting pdf "single" should act as a simple
129+
#' passthrough where no additional formatting or manipulation takes place
130+
#' on the result.
131+
#' @md
132+
#' @param rebuilt_results A list of result objects from crunch
133+
#' @param results_available A vector identifying in which list elements
134+
#' @param var The name of the alias that we are declaring its availability
135+
#' @param labels The wave labels
136+
trackingDeclareAvailability <- function(rebuilt_results, results_available, var, labels) {
137+
if (length(results_available) == 1) {
138+
rebuilt_results$results[[var]]$availability <- "single"
139+
if(rebuilt_results$results[[var]]$notes == "") {
140+
rebuilt_results$results[[var]]$notes <- paste0("Asked in ", labels[results_available])
141+
} else {
142+
rebuilt_results$results[[var]]$notes <- paste0(
143+
rebuilt_results$results[[var]]$notes,
144+
" (Asked in ", labels[results_available], ")")
145+
}
146+
} else {
147+
rebuilt_results$results[[var]]$availability <- "general"
148+
}
149+
return(rebuilt_results)
150+
}
151+
152+
136153
trackingReport_tabs <- function(datasets, vars, weight = NULL) {
137154
lapply(
138155
datasets,

man/addPos.Rd

Lines changed: 14 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

man/categoryFill.Rd

Lines changed: 15 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

man/cbindFill.Rd

Lines changed: 18 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

man/crunchtabs-package.Rd

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)