Skip to content

Commit e3aa026

Browse files
committed
Merge dev-current (1.1.0) into master branch
Merge branch 'dev-current' # Conflicts: # DESCRIPTION # NEWS.md # R/parse_args.R # R/sysdata.rda # README.Rmd # README.md
2 parents 63d20bd + 011bbc7 commit e3aa026

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+5124
-2177
lines changed

.Rbuildignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,11 @@
1010
^TODO.txt$
1111
^README.md$
1212
^README.Rmd$
13-
^tests$
13+
^tests*$
1414
^COPYING$
1515
^inst/WORDLIST$
1616
vignettes/*.R$
1717
vignettes/*.html$
1818
^doc$
1919
^misc$
20+
^tests$

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,5 @@ inst/doc
1313
inst/includes/win_includes
1414
inst/includes/linux_includes
1515
/Meta/
16+
/doc/
17+
/lua/

DESCRIPTION

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
Package: summarytools
22
Type: Package
33
Title: Tools to Quickly and Neatly Summarize Data
4-
Version: 1.0.2
4+
Version: 1.1.0
55
Authors@R: person("Dominic", "Comtois", email = "[email protected]",
66
role = c("aut", "cre"))
77
Author: Dominic Comtois [aut, cre]
@@ -38,12 +38,13 @@ Suggests:
3838
knitr,
3939
magrittr,
4040
rmarkdown,
41-
rstudioapi
41+
rstudioapi,
42+
backports
4243
Depends: R (>= 2.10)
4344
VignetteBuilder: knitr
4445
LazyData: true
4546
License: GPL-2
4647
URL: https://github.com/dcomtois/summarytools
4748
BugReports: https://github.com/dcomtois/summarytools/issues
4849
Encoding: UTF-8
49-
RoxygenNote: 7.1.2
50+
RoxygenNote: 7.3.2

NAMESPACE

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,15 @@
11
# Generated by roxygen2: do not edit by hand
22

3+
S3method(descr,default)
4+
S3method(descr,grouped_df)
35
S3method(print,list)
46
S3method(print,stby)
57
S3method(print,summarytools)
6-
S3method(stby,data.frame)
7-
S3method(stby,default)
8+
S3method(tb,by)
9+
S3method(tb,default)
10+
S3method(tb,list)
11+
S3method(tb,stby)
12+
S3method(tb,summarytools)
813
export("label<-")
914
export(cleartmp)
1015
export(ctable)
@@ -13,6 +18,7 @@ export(descr)
1318
export(dfSummary)
1419
export(freq)
1520
export(label)
21+
export(labls)
1622
export(st_css)
1723
export(st_options)
1824
export(stby)
@@ -24,6 +30,7 @@ export(view)
2430
export(what.is)
2531
import(htmltools)
2632
importFrom(base64enc,base64encode)
33+
importFrom(checkmate,anyNaN)
2734
importFrom(checkmate,check_file_exists)
2835
importFrom(checkmate,check_path_for_output)
2936
importFrom(checkmate,test_character)
@@ -35,15 +42,15 @@ importFrom(checkmate,test_number)
3542
importFrom(checkmate,test_path_for_output)
3643
importFrom(checkmate,test_string)
3744
importFrom(dplyr,"%>%")
38-
importFrom(dplyr,as_tibble)
3945
importFrom(dplyr,bind_cols)
4046
importFrom(dplyr,bind_rows)
4147
importFrom(dplyr,group_keys)
4248
importFrom(dplyr,group_vars)
49+
importFrom(dplyr,n)
4350
importFrom(dplyr,n_distinct)
4451
importFrom(dplyr,select)
4552
importFrom(dplyr,starts_with)
46-
importFrom(dplyr,summarize_all)
53+
importFrom(dplyr,summarise_all)
4754
importFrom(grDevices,dev.list)
4855
importFrom(grDevices,dev.off)
4956
importFrom(grDevices,nclass.Sturges)

NEWS.md

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,36 @@
1-
# summarytools 1.0.2 (2022-09-20)
2-
- This version contains many bug fixes and improvements
1+
# summarytools 1.1.0
2+
- Optimized metadata extraction
3+
- Improved support for dplyr::group_by()
4+
- `labls()` wrapper added for `label(x, all = TRUE)`
5+
- In `stby()`
6+
+ New parameter `useNA` adds a group for missing values in
7+
grouping variable(s); set to `FALSE` to avoid the message displayed
8+
when `NA`s are detected.
9+
- In `tb()`
10+
+ Fix for broken proportions in freq tables
11+
+ New parameters `fct.to.chr` and `recalculate` for freq tables
12+
- In `dfSummary()`:
13+
+ New parameter `class` allows switching off class reporting in *Variable*
14+
column.
15+
- In `freq()`:
16+
+ New parameter `na.val` allows specifying a value (factor level) that
17+
is to be considered `NA`. In turn, the value "(Missing)" is no longer
18+
considered missing by default; using `na.val = "(Missing)"`
19+
will yield the same results.
20+
+ Fix for weights not being applied correctly in by-group processing.
21+
- In `descr()`:
22+
+ "n" (total number of observations, also displayed in heading) added to
23+
available statistics.
24+
+ `stats` parameter more flexible: keywords (*all*, *fivenum*, and
25+
*common*) can be used in conjunction with statistics, to add or
26+
remove them. `stats= c("common", "n", "-pct.valid")` adds *N* to, and
27+
excludes *Pct. Valid* from, *common* statistics.
28+
+ Fix for *N* in header showing 1st group's size rather than global size.
29+
+ Fix for weights not being applied correctly in by-group processing.
30+
31+
# summarytools 1.0.2 (2022-07-10)
32+
- Github-only release
33+
- Various fixes and minor improvements
334

435
# summarytools 1.0.1 (2022-05-19)
536
- This version only includes minors fixes requested by CRAN.

R/args_validation.R

Lines changed: 45 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -4,24 +4,13 @@
44
#' @importFrom dplyr n_distinct
55
#' @importFrom stats na.omit
66
#' @keywords internal
7-
check_args <- function(mc, dotArgs) {
7+
check_args <- function(mc, dotArgs, caller) {
88

9-
caller <- sub(".+::","",as.character(sys.call(-1))[1])
109
pf <- parent.frame()
1110
errmsg <- character()
12-
caller_orig <- caller
1311

14-
if (caller == "FUN") {
12+
if (mc[[1]] == "FUN" || mc$x == "dd[x, , drop = FALSE]") {
1513
pf$flag_by <- TRUE
16-
# When stby() was called, deduce caller from formals
17-
if ("cumul" %in% names(pf))
18-
caller <- "freq"
19-
else if ("transpose" %in% names(pf))
20-
caller <- "descr"
21-
else if ("chisq" %in% names(pf))
22-
caller <- "ctable"
23-
else if ("graph.col" %in% names(pf))
24-
caller <- "dfSummary"
2514
} else {
2615
pf$flag_by <- FALSE
2716
}
@@ -114,6 +103,32 @@ check_args <- function(mc, dotArgs) {
114103
errmsg %+=% "'cumul' must be either TRUE or FALSE"
115104
}
116105

106+
if ("na.val" %in% names(mc) && !is.null(pf$na.val)) {
107+
varname <- pf$varname %||% "x"
108+
if (length(pf$na.val) > 1) {
109+
errmsg %+=% "'na.val' can only contain one value"
110+
}
111+
if (!is.factor(pf$x)) {
112+
if (isFALSE(st_options("freq.silent")))
113+
message("'na.val' only applies to factors & will be ignored for ",
114+
varname)
115+
}
116+
if (!isTRUE(test_character(pf$na.val, any.missing = FALSE))) {
117+
errmsg %+=% "'na.val' must be character"
118+
}
119+
if (is.factor(pf$x)) {
120+
if (!pf$na.val %in% levels(pf$x)) {
121+
if (isFALSE(st_options("freq.silent")))
122+
message(paste0("'", pf$na.val, "' is not a level of ",
123+
varname, " and will be ignored"))
124+
pf$na.val <- NULL
125+
} else if (anyNA(pf$x)) {
126+
errmsg %+=% paste(varname, "contains NA values; 'na.val' is only",
127+
"valid in the absence of actual NA values")
128+
}
129+
}
130+
}
131+
117132
if ("order" %in% names(mc)) {
118133
order <- switch(tolower(substr(sub("[+-]", "", pf$order), 1, 1)),
119134
d = "default",
@@ -187,7 +202,7 @@ check_args <- function(mc, dotArgs) {
187202
if (!identical(pf$weights, NA)) {
188203
if (is.null(pf$weights)) {
189204
errmsg %+=% "weights vector not found"
190-
} else if (caller_orig != "FUN" &&
205+
} else if (isFALSE(pf$flag_by) &&
191206
length(pf$weights) != nrow(as.data.frame(pf$x))) {
192207
errmsg %+=% "weights vector must have same length as 'x'"
193208
}
@@ -288,18 +303,16 @@ check_args <- function(mc, dotArgs) {
288303
}
289304
} else {
290305
# order has length > 1 -- all elements must correspond to column names
291-
if (length(ind <- which(!pf$order %in% colnames(pf$x.df))) > 0) {
306+
if (length(ind <- which(!pf$order %in% colnames(pf$xx))) > 0) {
292307
errmsg %+=% paste("Following ordering element(s) not recognized:",
293308
paste(pf$order[ind], sep = ", "),
294309
collapse = " ")
295310
}
296311
}
297312
}
298313

299-
if (!identical(pf$weights, NA)) {
300-
if (is.null(pf$weights)) {
301-
errmsg %+=% "weights vector not found"
302-
} else if (caller_orig != "FUN" && (length(pf$weights) != nrow(pf$x.df))) {
314+
if (!is.null(pf$weights)) {
315+
if (isFALSE(pf$flag_by) && (length(pf$weights) != nrow(pf$xx))) {
303316
errmsg %+=% "weights vector must have same length as 'x'"
304317
}
305318
}
@@ -449,14 +462,6 @@ check_args_print <- function(mc) {
449462
errmsg %+=% "'file' path is not valid - check that directory exists"
450463
}
451464

452-
# # Change method to browser when file name was (most likely) provided by user
453-
# if (grepl("\\.html$", pf$file, ignore.case = TRUE, perl = TRUE) &&
454-
# !grepl(pattern = tempdir(), x = pf$file, fixed = TRUE) &&
455-
# pf$method == "pander") {
456-
# pf$method <- "browser"
457-
# message("Switching method to 'browser'")
458-
# }
459-
#
460465
if (pf$method == "pander" && !is.na(pf$table.classes)) {
461466
errmsg %+=% "'table.classes' option does not apply to method 'pander'"
462467
}
@@ -613,22 +618,23 @@ check_args_st_options <- function(mc) {
613618
errmsg %+=% "'ctable.totals' must be either TRUE or FALSE"
614619
}
615620

616-
if ("descr_stats" %in% names(mc)) {
617-
valid_stats <- c("mean", "sd", "min", "q1", "med", "q3","max", "mad",
618-
"iqr", "cv", "skewness", "se.skewness", "kurtosis",
619-
"n.valid", "pct.valid")
621+
if ("descr.stats" %in% names(mc)) {
620622

621-
if (length(pf$descr_stats) == 1 &&
622-
!(pf$descr_stats %in% c("fivevnum", "common")) &&
623-
!(pf$descr_stats %in% valid_stats)) {
623+
# Check for invalid items
624+
stats <- tolower(pf$descr.stats)
625+
invalid_stats <- setdiff(
626+
stats, c(.st_env$descr.stats.valid$no_wgts,
627+
paste0("-", .st_env$descr.stats.valid$no_wgts),
628+
"all", "common", "fivenum"))
629+
630+
if (length(invalid_stats) > 0) {
624631
errmsg %+=%
625-
paste("'descr_stats' value", dQuote(pf$descr_stats), "not recognized;",
626-
"allowed values are: ",
627-
paste('"fivenum", "common", or a combination of :',
628-
paste0(dQuote(valid_stats), sep = ", ")))
632+
paste("descr.stats: values",
633+
paste(dQuote(invalid_stats), collapse = ", "),
634+
"not recognized; see ?descr")
629635
}
630636
}
631-
637+
632638
if ("descr.transpose" %in% names(mc) &&
633639
!isTRUE(test_logical(pf$descr.transpose, len = 1, any.missing = FALSE))) {
634640
errmsg %+=% "'descr.transpose' must be either TRUE or FALSE"

R/ctable.R

Lines changed: 28 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -126,11 +126,11 @@ ctable <- function(x,
126126
...) {
127127

128128
# Check for group_by()
129-
if (any(grepl("group_by(", deparse(sys.calls()[[1]]), fixed = TRUE))) {
130-
stop("ctable() doesn't support group_by(); use stby() instead")
129+
if (inherits(x, "grouped_df")) {
130+
stop("ctable() does not support group_by(); use stby() instead")
131131
}
132132

133-
# Support for by()
133+
# Adjustment for by() / syby()
134134
if (length(dim(x)) == 2) {
135135
x_tmp <- x[[1]]
136136
y <- x[[2]]
@@ -166,7 +166,7 @@ ctable <- function(x,
166166
}
167167
}
168168

169-
errmsg <- c(errmsg, check_args(match.call(), list(...)))
169+
errmsg <- c(errmsg, check_args(match.call(), list(...), "ctable"))
170170

171171
if (length(errmsg) > 0) {
172172
stop(paste(errmsg, collapse = "\n "))
@@ -192,9 +192,8 @@ ctable <- function(x,
192192
# Get x & y metadata from parsing function
193193
if (isTRUE(flag_by)) {
194194
parse_info_x <- try(
195-
parse_args(sys.calls(), sys.frames(), match.call(),
196-
var = c("x", "y"), silent = "dnn" %in% names(match.call()),
197-
var_label = FALSE, caller = "ctable"),
195+
parse_call(mc = match.call(), var = c("x", "y"), var_label = FALSE,
196+
caller = "ctable"),
198197
silent = TRUE)
199198

200199
if (inherits(parse_info_x, "try-error")) {
@@ -209,19 +208,17 @@ ctable <- function(x,
209208
}
210209
} else {
211210
parse_info_x <- try(
212-
parse_args(sys.calls(), sys.frames(), match.call(),
213-
var = "x", silent = "dnn" %in% names(match.call()),
214-
var_label = FALSE, caller = "ctable"),
211+
parse_call(mc = match.call(), var = "x", var_label = FALSE,
212+
caller = "ctable"),
215213
silent = TRUE)
216214

217215
if (inherits(parse_info_x, "try-error")) {
218216
parse_info_x <- list()
219217
}
220218

221219
parse_info_y <- try(
222-
parse_args(sys.calls(), sys.frames(), match.call(),
223-
var = "y", silent = "dnn" %in% names(match.call()),
224-
var_label = FALSE, caller = "ctable"),
220+
parse_call(mc = match.call(), var = "y", var_label = FALSE,
221+
caller = "ctable"),
225222
silent = TRUE)
226223

227224
if (inherits(parse_info_y, "try-error")) {
@@ -346,7 +343,7 @@ ctable <- function(x,
346343

347344
if (isTRUE(chisq)) {
348345
tmp.chisq <- chisq.test(freq_table_min)
349-
tmp.chisq <- c(Chi.squared = round(tmp.chisq$statistic[[1]], 4),
346+
tmp.chisq <- c(Chi.squared = round(tmp.chisq$statistic[[1]], 2),
350347
tmp.chisq$parameter,
351348
p.value = round(tmp.chisq$p.value, 4))
352349
attr(output, "chisq") <- tmp.chisq
@@ -436,8 +433,7 @@ ctable <- function(x,
436433

437434
# Prepare metadata to be stored as the data_info attribute
438435
data_info <-
439-
list(Data.frame = ifelse(exists("df_name", inherits = FALSE),
440-
df_name, NA),
436+
list(Data.frame = dfn,
441437
Data.frame.label = ifelse(exists("df_label", inherits = FALSE),
442438
df_label, NA),
443439
Row.variable = x_name,
@@ -459,6 +455,8 @@ ctable <- function(x,
459455
replacement = "",
460456
x = weights_string,
461457
fixed = TRUE))),
458+
by_var = if ("by_group" %in% names(parse_info_x))
459+
parse_info_x$by_var else NA,
462460
Group = ifelse("by_group" %in% names(parse_info_x),
463461
parse_info_x$by_group, NA),
464462
by_first = ifelse("by_group" %in% names(parse_info_x),
@@ -477,9 +475,20 @@ ctable <- function(x,
477475
headings = headings,
478476
display.labels = display.labels)
479477

480-
attr(output, "user_fmt") <- list(... = ...)
481-
482-
attr(output, "lang") <- st_options("lang")
478+
# Keep ... arguments that could be relevant for pander of format
479+
user_fmt <- list()
480+
dotArgs <- list(...)
481+
for (i in seq_along(dotArgs)) {
482+
if (class(dotArgs[[i]]) %in%
483+
c("character", "numeric", "integer", "logical") &&
484+
length(names(dotArgs[1])) == length(dotArgs[[i]]))
485+
user_fmt <- append(user_fmt, dotArgs[i])
486+
}
483487

488+
if (length(user_fmt) > 0)
489+
attr(output, "user_fmt") <- user_fmt
490+
491+
attr(output, "lang") <- st_options("lang")
492+
484493
return(output)
485494
}

0 commit comments

Comments
 (0)