• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Displayr / flipTables / 1062

pending completion
1062

push

travis-ci-com

web-flow
RS-12285 Improve warning message (#21)

* Improve warning message

* Fix typo

11 of 11 new or added lines in 1 file covered. (100.0%)

900 of 951 relevant lines covered (94.64%)

41.51 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

91.94
/R/tidytools.R
1
# Function to extract rows/columns from array
2
# It handles both 2d matrices and 3d arrays
3
# Will always try to copy attributes
4
#' @importFrom flipU IsQTable
5
extractArray <- function(x, row.index = 1:nrow(x), col.index = 1:ncol(x), keep.all.stats = TRUE)
6
{
7
    if (IsQTable(x) && is.null(attr(x, "statistic")) && length(dim(x)) == 2 && keep.all.stats)
75✔
8
        res <- x[row.index, , drop = FALSE]
2✔
9
    else if (isTableWithStats(x) && keep.all.stats)
73✔
10
        res <- x[row.index, col.index, , drop = FALSE]
19✔
11
    else if (isTableWithStats(x) && !keep.all.stats)
54✔
12
    {
13
        warning("Only the first statistic '", dimnames(x)[[3]][1], "' used.")
4✔
14
        res <- x[row.index, col.index, 1, drop = FALSE]
4✔
15
        attr(res, "statistic") <- dimnames(x)[[3]][1]
4✔
16
    }
17
    else
18
        res <- x[row.index, col.index, drop = FALSE]
50✔
19
    # Subscripting QTables (verbs:::`[.QTable`) already updates attributes
20
    if (!inherits(res, "qTable"))
75✔
21
        res <- CopyAttributes(res, x)
75✔
22
    return(res)
75✔
23
}
24

25
# Converts vector or 1-d array into matrix
26
# so that the other functions for tidying tables can be used
27
# note that no checking is done
28
convertToMatrix <- function(x)
29
{
30
    res <- as.matrix(x)
7✔
31
    return(CopyAttributes(res, x))
7✔
32
}
33

34
# Converts 1-d Q Table + statistics in to 3d array
35
#' @importFrom flipU IsQTable
36
convertTo3dQTable <- function(x)
37
{
38
    if (!IsQTable(x))           # ignore if not Q Table
17✔
39
       return(x)
2✔
40
    if (isTableWithStats(x))
15✔
41
        return(x)               # no further conversion for 2d table
10✔
42

43
    dims <- dim(x)
5✔
44
    n.dim <- length(dims)
5✔
45
    dim.names <- dimnames(x)
5✔
46
    stat <- attr(x, "statistic")
5✔
47
    has.only.one.stat <- !is.null(stat)
5✔
48

49
    if (!has.only.one.stat && n.dim > 1)
5✔
50
    {
51
        res <- array(x, c(dims[1], 1, dims[2]))
5✔
52
        dimnames(res) <- list(dim.names[[1]], NULL, dim.names[[2]])
5✔
53
        return(CopyAttributes(res, x))
5✔
54
    }
55
    return(x)
×
56
}
57

58

59
#' Reverse rows of a table
60
#' @description Reverse order of rows in matrix, dataframe or array
61
#' @param x Input table which may be a matrix, dataframe or array.
62
#'    Vectors or 1-d arrays will be converted into a matrix with 1 column
63
#' @export
64
ReverseRows <- function(x)
65
{
66
    if (length(dim(x)) < 2)
1✔
67
        x <- convertToMatrix(x)
1✔
68
    n <- nrow(x)
1✔
69
    extractArray(x, row.index = n:1)
1✔
70
}
71

72
#' Reverse columns of a table
73
#' @description Reverse order of columns in matrix, dataframe or array
74
#' @param x Input table which may be a matrix, dataframe or array.
75
#'    Vectors or 1-d arrays will be ignored
76
#' @export
77
ReverseColumns <- function(x)
78
{
79
    if (length(dim(x)) < 2)
1✔
80
        x <- return(x)
×
81
    n <- ncol(x)
1✔
82
    extractArray(x, col.index = n:1)
1✔
83
}
84

85
#' Select rows from a table
86
#' @description Function to select row from a table, either by rownames
87
#'   or as range from the top or bottom.
88
#' @param x Matrix or dataframe from which rows will be extracted
89
#' @param select A string containing a comma seperated list of the
90
#'  names or indices of the rows to be selected. If an empty list is
91
#'  supplied, then the whole of \code{x} will be returned.
92
#' @param first.k If a number greater than zero is supplied,
93
#'   The first (or up to) \code{first.k} rows from \code{x[select,]} is returned.
94
#' @param last.k If a number greater than zero is supplied,
95
#'   The last (or up to) \code{last.k} rows from \code{x[select,]} is returned.
96
#' @importFrom flipTransformations TextAsVector
97
#' @importFrom flipU CopyAttributes
98
#' @export
99
SelectRows <- function (x, select = NULL, first.k = NA, last.k = NA)
100
{
101
    if (!any(c(nzchar(select), as.integer(first.k), as.integer(last.k)), na.rm = TRUE))
18✔
102
        return(x)
1✔
103
    if (length(dim(x)) < 2)
17✔
104
        x <- convertToMatrix(x)
2✔
105

106
    ind <- indexSelected(x, "row", select, first.k, last.k)
17✔
107
    extractArray(x, row.index = ind)
14✔
108
}
109

110
#' Select columns from a table
111
#' @description Function to select column from a table, either by colnames
112
#'   or as range from the top or bottom.
113
#' @param x Matrix or dataframe from which columns will be extracted
114
#' @param select A string containing a comma seperated list of the
115
#'  names or indices of the columns to be selected. If an empty list is
116
#'  supplied, then the whole of \code{x} will be selected.
117
#' @param first.k If a number greater than zero is supplied,
118
#'   The first (or up to) \code{first.k} columns of \code{x[,select]} will be returned.
119
#' @param last.k If a number greater than zero is supplied,
120
#'   The last (or up to) \code{last.k} columns of \code{x[,select]} will be returned.
121
#' @export
122
SelectColumns <- function (x, select = NULL, first.k = NA, last.k = NA)
123
{
124
    if (!any(c(nzchar(select), as.integer(first.k), as.integer(last.k)), na.rm = TRUE))
4✔
125
        return(x)
×
126
    if (length(dim(x)) < 2)
4✔
127
        return(x)
×
128

129
    ind <- indexSelected(x, "column", select, first.k, last.k)
4✔
130
    extractArray(x, col.index = ind)
4✔
131
}
132

133

134
#' Select entries from a table
135
#' @description Function to select entries from a table using
136
#' either the row/column name or indices
137
#' @param x Matrix or dataframe from which values will be extracted.
138
#' @param row A string containing a comma seperated list of the
139
#' name or indices of the rows to be selected
140
#' @param column A string containing a comma seperated list of the
141
#' name or indices of the column to be selected, If \code{x} is a vector
142
#' or 1-dimensional array, \code{column} will be ignored.
143
#' @param return.single.value Logical; If this true, the function will
144
#' always return a single numeric value. If multiple cells are selected,
145
#' the entries are summed. If no entries are selected a
146
#' value of zero is returned.for
147
#' @param use.statistic.attribute Logical; indicates whether the statistic
148
#' attribute is used to represent a percentage. For legacy reasons it is off by
149
#' default (i.e. 5\% is returned as 0.05)
150
#' @importFrom verbs Sum
151
#' @export
152
SelectEntry <- function (x, row, column = NULL, return.single.value = FALSE,
153
                         use.statistic.attribute = FALSE)
154
{
155
    indRow <- indexSelected(x, "row", as.character(row))
21✔
156

157
    stat <- attr(x, "statistic")
20✔
158
    qst <- attr(x, "questions")
20✔
159
    oqst <- attr(x, "original.questions")
20✔
160
    is.qtable <- !is.null(oqst) || !is.null(qst)
20✔
161
    dnm <- dimnames(x)
20✔
162
    is.pct <- (!is.null(stat) && is.qtable && grepl("%)?$", stat)) ||
20✔
163
              (length(dnm) > 2 && grepl("%)?$", dnm[[3]][1]))
20✔
164

165
    if (length(dim(x)) < 2)
20✔
166
    {
167
        col.requested <- length(column) > 0 && any(nzchar(column))
3✔
168
        if (col.requested && all(as.numeric(column) != 1L, na.rm = TRUE))
3✔
169
            warning("Column", ngettext(length(column), paste0(" ", column, collapse = ""),
3✔
170
                                       paste("s", paste0(column, collapse = ", "))),
3✔
171
                    " ignored for a 1-dimensional table")
3✔
172
        res <- x[indRow]
3✔
173

174
    } else
175
    {
176
        indCol <- indexSelected(x, "column", as.character(column))
17✔
177
        if (!any(nzchar(column), na.rm = TRUE))
16✔
178
        {
179
            warning("First column was returned as no column was specified")
3✔
180
            indCol <- 1
3✔
181
        }
182
        res <- extractArray(x, row.index = indRow, col.index = indCol, keep.all.stats = FALSE)
16✔
183
        if (!is.null(attr(res, "statistic")) && grepl("%)?$", attr(res, "statistic")))
16✔
184
            is.pct <- TRUE
16✔
185
    }
186
    if (return.single.value && is.numeric(res))
19✔
187
        res <- Sum(res)
6✔
188
    res <- unlist(res)
19✔
189
    if (is.pct)
19✔
190
    {
191
        if (use.statistic.attribute)
16✔
192
            attr(res, "statistic") <- "%"
7✔
193
        else
194
        {
195
            res <- res/100
9✔
196
            attr(res, "statistic") <- NULL # set to null to indicate already converted to decimal
9✔
197
            attr(res, "format") <- "%"
9✔
198
        }
199
    }
200
    return(res)
19✔
201
}
202

203
#' @importFrom verbs First Last
204
indexSelected <- function(x, dim = "row", select = NULL, first.k = NA, last.k = NA)
205
{
206
    if (length(dim(x)) < 2)
59✔
207
        x <- convertToMatrix(x)
4✔
208
    if (!checkIsTable(x))
59✔
209
        return(x)
×
210

211
    sel.ind <- NULL
59✔
212
    dim.names <- if (dim == "column") colnames(x, do.NULL = FALSE, prefix = "")
59✔
213
                 else                 rownames(x, do.NULL = FALSE, prefix = "")
59✔
214
    max.dim <- if (dim == "column") ncol(x)
59✔
215
               else                 nrow(x)
59✔
216
    min.dim <- max(0, first.k, last.k, na.rm = TRUE)
59✔
217
    if (max.dim < min.dim)
59✔
218
        warning("Input table has less than ", min.dim, " ", dim, "s.")
×
219

220
    if (any(nzchar(select), na.rm = TRUE))
59✔
221
        sel.ind <- getMatchIndex(select, dim.names, dim = dim)
53✔
222
    else
223
        sel.ind <- 1:max.dim
6✔
224

225
    # Do the union of the first and last k row/columns
226
    # e.g. if user wants to select first and last rows to create summary table
227
    if (any(as.integer(first.k), na.rm = TRUE) && any(as.integer(last.k), na.rm = TRUE))
54✔
228
        return(unique(c(First(sel.ind, first.k), Last(sel.ind, last.k))))
1✔
229

230
    # Otherwise use the intersection to restrict number of values
231
    # e.g. DS-2552
232
    if (any(as.integer(first.k), na.rm = TRUE))
53✔
233
        sel.ind <- First(sel.ind, first.k)
2✔
234
    if (any(as.integer(last.k), na.rm = TRUE))
53✔
235
        sel.ind <- Last(sel.ind, last.k)
2✔
236
    return(sel.ind)
53✔
237
}
238

239
indexSortedByValues <- function(x,
240
                         values,
241
                         decreasing = FALSE,
242
                         exclude = "NET, SUM, Total",
243
                         dim = "row",
244
                         sort.by = "")
245
{
246
    max.dim <- if (dim == "column") ncol(x)
12✔
247
               else                 nrow(x)
12✔
248
    dim.names <- if (dim == "column") colnames(x, do.NULL = FALSE, prefix = "")
12✔
249
                 else                 rownames(x, do.NULL = FALSE, prefix = "")
12✔
250

251
    ind.excl <- getMatchIndex(exclude, dim.names, dim, warn = FALSE)
12✔
252
    ind.incl <- setdiff(1:max.dim, ind.excl)
12✔
253
    val.incl <- values[ind.incl]
12✔
254
    if (is.character(val.incl) &&
12✔
255
        all(!is.na(suppressWarnings(as.numeric(val.incl[!is.na(val.incl)])))))
12✔
256
        val.incl <- as.numeric(val.incl)
2✔
257
    if (is.list(val.incl))
12✔
258
        val.incl <- unlist(val.incl)
3✔
259

260
    # Give warnings if appropriate
261
    is.na <- is.na(val.incl)
12✔
262
    is.dup <- duplicated(val.incl) & !is.na
12✔
263
    if (any(is.dup) || any(is.na)) {
12✔
264
        warn.details <- ""
3✔
265
        if (sum(is.na) == 1)
3✔
266
            warn.details <- "1 NA"
1✔
267
        else if (sum(is.na) > 1)
2✔
268
            warn.details <- sprintf("%d NAs", sum(is.na))
1✔
269
        if (nzchar(warn.details) && any(is.dup))
3✔
270
            warn.details <- paste0(warn.details, " and ")
×
271
        if (sum(is.dup) == 1)
3✔
272
            warn.details <- "1 duplicate"
×
273
        else if (sum(is.dup) > 1)
3✔
274
            warn.details <- sprintf("%d duplicates", sum(is.dup))
1✔
275
        warning(sprintf("Table has been sorted on %s containing %d values with %s. Note that the order of these values is not guaranteed.",
3✔
276
            sort.by, length(val.incl), warn.details))
3✔
277
    }
278

279
    tmp.ord <- order(val.incl, decreasing = decreasing)
12✔
280
    ord.ind <- ind.incl[tmp.ord]
12✔
281
    return(c(ord.ind, ind.excl))
12✔
282
}
283

284
#' Automatically order the rows by correspondence analysis of the table
285
#' @description Rows of the table are ordered according to the row-coordinates
286
#'    given by correspondence analysis of the table (1st dimension)
287
#' @param x Input matrix
288
#' @importFrom ca ca
289
#' @export
290
AutoOrderRows <- function(x)
291
{
292
    if (!checkIsTable(x))
2✔
293
        return(x)
×
294
    tmp.dat <- if (isTableWithStats(x)) x[,,1] else x
2✔
295
    tmp.dat <- matrix(as.numeric(unlist(tmp.dat)), NROW(x), NCOL(x))
2✔
296
    tmp <- ca(tmp.dat)
2✔
297
    ord <- order(tmp$rowcoord[,1])
2✔
298
    extractArray(x, row.index = ord)
2✔
299
}
300

301
#' Automatically order the columns by correspondence analysis of the table
302
#' @description Columns of the table are ordered according to the column-coordinates
303
#'    given by correspondence analysis of the table (1st dimension)
304
#' @param x Input matrix
305
#' @importFrom ca ca
306
#' @export
307
AutoOrderColumns <- function(x)
308
{
309
    if (!checkIsTable(x))
3✔
310
        return(x)
×
311
    tmp.dat <- if (isTableWithStats(x)) x[,,1] else x
3✔
312
    tmp.dat <- matrix(as.numeric(unlist(tmp.dat)), NROW(x), NCOL(x))
3✔
313
    tmp <- ca(tmp.dat)
3✔
314
    ord <- order(tmp$colcoord[,1])
3✔
315
    extractArray(x, col.index = ord)
3✔
316
}
317

318
#' Sort rows of a table
319
#' @description Sorts the rows of the table based on the values in the specified column
320
#' @details This function differs from the QScript in a number of ways.
321
#' 1) Sorting does not respect spans.
322
#' 2) The default column in based on 'Column n' in 'Statistics - Below'. This is
323
#'    not available to R. Instead we pick the column with the largest sum. If there
324
#'    is a tie between columns, we will pick the rightmost column
325
#' 3) There is no parameter for 'Never exclude from sort'. This is because we require
326
#'    that entries in 'Exclude from sort' must match the rowname exactly, instead of
327
#'    a partial match used in the QScript
328
#' @param x Input matrix or dataframe which is being sorted. Values must be numeric.
329
#'  Vectors and 1-d arrays will be converted to a matrix.
330
#' @param decreasing Order to sort values.
331
#' @param column The column to sort by. If none is specified and the 'Column n'
332
#'  statistic is present in the table, it will use the column with the largest
333
#'  value of 'Column n'. Otherwise it will pick the right-most column.
334
#' @param exclude A string containing a comma-separated list of rows
335
#'    (either by name or index) which should not be sorted. These rows
336
#'    will remain at the bottom of the output table
337
#' @export
338
SortRows <- function(x,
339
                 decreasing = FALSE,
340
                 column = NULL, # integer, otherwise largest column
341
                 exclude = "NET, SUM, Total")
342
{
343
    if (length(dim(x)) < 2)
7✔
344
        x <- convertToMatrix(x)
×
345
    if (!checkIsTable(x))
7✔
346
        return(x)
×
347

348
    # Finding the column to sort on
349
    col.ind <- NULL
7✔
350
    if (!is.null(column))
7✔
351
    {
352
        col.ind <- matchNameOrIndex(column[1], colnames(x, do.NULL = FALSE, prefix = ""))
3✔
353
        if (length(col.ind) == 0)
3✔
354
            stop("Column '", column, "' was not found in the table.")
×
355
        if (length(column) > 1)
3✔
356
            warning("Only column '", column[1], "' was used to sort the table.")
×
357

358
    }
359
    if (length(col.ind) != 1 || !is.finite(col.ind))
7✔
360
        col.ind <- ncol(x)
4✔
361
    ind <- indexSortedByValues(x,
7✔
362
                 values = if (isTableWithStats(x)) x[,col.ind,1] else x[,col.ind],
7✔
363
                 decreasing, exclude, "row", paste("column", col.ind))
7✔
364
    extractArray(x, row.index = ind)
7✔
365
}
366

367
#' Sort columns of a table
368
#' @description Sorts the columns of the table based on the values in the specified column
369
#' @param x Input matrix or dataframe which is being sorted. Values must be numeric.
370
#'      Vectors and 1-d arrays will be ignored.
371
#' @param decreasing Order to sort values.
372
#' @param row The row to sort by. If none is specified and the 'Row n'
373
#'  statistic is present in the table, it will use the row with the largest
374
#'  value of 'Row n'. Otherwise it will pick the bottom row.
375
#' @param exclude A string containing a comma-separated list of columns
376
#'    (either by name or index) which should not be sorted. These columns
377
#'    will remain at the end of the output table
378
#' @export
379
SortColumns <- function(x,
380
                 decreasing = FALSE,
381
                 row = NULL, # integer, otherwise largest column
382
                 exclude = "NET, SUM, Total")
383
{
384
    if (length(dim(x)) < 2)
5✔
385
        return(x)
×
386
    if (!checkIsTable(x))
5✔
387
        return(x)
×
388

389
    # Finding the row to sort on
390
    row.ind <- NULL
5✔
391
    if (!is.null(row))
5✔
392
    {
393
        row.ind <- matchNameOrIndex(row[1],
4✔
394
            rownames(x, do.NULL = FALSE, prefix = ""))
4✔
395
        if (length(row.ind) == 0)
4✔
396
            stop("Row '", row, "' was not found in the table.")
×
397
        if (length(row) > 1)
4✔
398
            warning("Only row '", row[1], "' was used to sort the table.")
×
399

400
    }
401
    if (length(row.ind) != 1 || !is.finite(row.ind))
5✔
402
        row.ind <- nrow(x)
2✔
403

404
    ind <- indexSortedByValues(x,
5✔
405
                 values = if (isTableWithStats(x)) x[row.ind,,1] else x[row.ind,],
5✔
406
                 decreasing, exclude, "column", paste("row", row.ind))
5✔
407
    extractArray(x, col.index = ind)
5✔
408
}
409

410
# This is a wrapper for matchNameOrIndex
411
# It will break up the pattern from a commma-separated list to a vector
412
# It will also check for unmatched entries and give warnings
413
# warn = FALSE is used by indexSortedValues when no error/warning is required
414
getMatchIndex <- function(pattern, x, dim = "row", warn = TRUE)
415
{
416
    is.control <- attr(pattern, "is.control")
67✔
417
    pattern <- as.character(pattern)
67✔
418
    attr(pattern, "is.control") <- is.control
67✔
419
    sel.vec <- if (length(pattern) > 1) pattern else TextAsVector(pattern)
67✔
420
    sel.ind <- matchNameOrIndex(sel.vec, x, strip.zeros = FALSE)
67✔
421
    sel.na <- which(is.na(sel.ind))
66✔
422
    warning.msg <- ""
66✔
423
    if (length(sel.na) > 0)
66✔
424
        warning.msg <- paste0("Table does not contain ", dim, if (length(sel.na) > 1) "s" else "",  " '",
19✔
425
            paste(sel.vec[sel.na], collapse = "','"), "'.")
19✔
426

427
    # Check for consecutive matches - these are probably incorrectly split up patterns
428
    runs <- rle(sel.ind)
66✔
429
    r.ind <- which(runs$length > 1)
66✔
430
    if (any(runs$length))
66✔
431
        for (rri in r.ind)
66✔
432
        {
433
            r.pos <- (sum(runs$lengths[1:rri]) - runs$lengths[rri]) + (1:runs$lengths[rri])
4✔
434
            merged.patt <- paste(sel.vec[r.pos], collapse = ",")
4✔
435
            if (any(grepl(gsub(" ", "", merged.patt), gsub(" ", "", x))))
4✔
436
                sel.ind[r.pos[-1]] <- NA
1✔
437
        }
438

439
    sel.ind[sel.ind == 0] <- NA
66✔
440
    sel.ind <- sel.ind[which(!is.na(sel.ind))]
66✔
441
    if (warn && nchar(warning.msg) > 0 && length(sel.ind) == 0)
66✔
442
        stop(warning.msg)
4✔
443
    else if (warn && nchar(warning.msg))
62✔
444
        warning(warning.msg)
5✔
445
    return(sel.ind)
62✔
446
}
447

448
#' @param p.list Vector of patterns to match
449
#' @param x Vector of names
450
#' @param strip.zeros If false, then zero-values in the return vector indicate that
451
#'    some entries in \code{p.list} ambiguously match multiple names. This information
452
#'    is useful for writing warning messages (e.g. in \code{getMatchIndex}.
453
#'    If true, the zeros are converted to NAs.
454
#' @return list of indices mapping p.list to x.
455
#'      Unmatched entries in p.list are set to NA
456
#'                Ambiguous patterns are preferentially treated as indices
457
#' @importFrom flipU TrimWhitespace
458
#' @importFrom stringi stri_reverse
459
#' @noRd
460
matchNameOrIndex <- function(p.list, x, strip.zeros = TRUE)
461
{
462
    # Looking for string-to-string match
463
    p.list <- TrimWhitespace(p.list)
74✔
464
    x <- TrimWhitespace(x)
74✔
465
    ind.as.name <- match(p.list, x)
74✔
466
    exact.matches <- unique(ind.as.name[!is.na(ind.as.name)])
74✔
467

468
    # Check for partial matches in unmatched rownames
469
    .partialmatches <- function(p, x, exact.ind)
74✔
470
    {
471
        m <- charmatch(p, x)
148✔
472
        m[which(m %in% exact.ind)] <- NA
148✔
473
        return(m)
148✔
474
    }
475
    retry <- which(!is.finite(ind.as.name))
74✔
476
    ind.as.name[retry] <- .partialmatches(p.list[retry], x, exact.matches)
74✔
477
    retry <- which(!is.finite(ind.as.name))
74✔
478
    ind.as.name[retry] <- .partialmatches(stri_reverse(p.list[retry]),
74✔
479
            stri_reverse(x), exact.matches)
74✔
480

481
    # Give warnings if pattern can be used as both an index (numeric) or a name
482
    ind <- suppressWarnings(as.numeric(p.list))
74✔
483
    ind[ind < 1 | ind > length(x)] <- NA
74✔
484
    ambig.pos <- which(!is.na(ind) & !is.na(ind.as.name) & ind != ind.as.name)
74✔
485
    for (ii in ambig.pos)
74✔
486
    {
487
        # Check for an exact match
488
        if (p.list[ii] %in% x)
5✔
489
            warning("'", p.list[ii], "' treated as an index. ",
1✔
490
             "To select entry with name '", p.list[ii], "' use index ", ind.as.name[ii], "\n")
1✔
491
    }
492

493
        # Patterns are treated as indices where possible
494
        pos.as.name <- is.na(ind)
74✔
495
        ind[pos.as.name] <- ind.as.name[pos.as.name]
74✔
496

497
    # Give warnings from duplicate matches in charmatch if relevant
498
    dup.match <- which(ind.as.name == 0 & pos.as.name & nchar(p.list) > 0)
74✔
499
    if (length(dup.match) > 0)
74✔
500
    {
501
        warning.msg <- ""
2✔
502
        for (ii in dup.match)
2✔
503
        {
504
            tmp.match <- grep(p.list[ii], x, value = TRUE, fixed = TRUE)
2✔
505
            warning.msg <- paste0(warning.msg, "'", p.list[ii], "' matched multiple values ambiguously: '",
2✔
506
                paste(tmp.match, collapse = "', '"), "'.\n")
2✔
507
        }
508
        if (any(is.finite(ind) & ind > 0))
2✔
509
            warning(warning.msg)
1✔
510
        else
511
            stop(warning.msg)
1✔
512
    }
513
    if (strip.zeros)
73✔
514
        ind[ind == 0] <- NA
7✔
515
    return(ind)
73✔
516
}
517

518
isTableWithStats <- function(x)
519
{
520
    if (inherits(x, "array") && length(dim(x)) == 3)
252✔
521
        return(TRUE)
77✔
522
    return(FALSE)
175✔
523
}
524

525
checkIsTable <- function(x)
526
{
527
    if (isTableWithStats(x))
76✔
528
        return(TRUE)
19✔
529

530
    res <- length(dim(x)) == 2
57✔
531
    if (!res)
57✔
532
        warning("Input data should be a 2-dimensional matrix or dataframe.")
×
533
    return(res)
57✔
534
}
535

536

537
#' Throws error if table has small sample size
538
#' @description Throws an error if any of the 'Base n' values in the table are too small
539
#' @param x Input table, which must contain the 'Base n' statistic.
540
#' @param min.size Minimum sample size required.
541
#' @export
542
HideOutputsWithSmallSampleSizes <- function(x, min.size = 30)
543
{
544
    x <- convertTo3dQTable(x)
5✔
545
    if (!isTableWithStats(x) || !any(c("Base n", "Sample Size") %in% dimnames(x)[[3]]))
5✔
546
        stop("Table does not have 'Sample Size' or 'Base n'")
1✔
547

548
    d.ind <- which(dimnames(x)[[3]] %in% c("Base n", "Sample Size"))
4✔
549
    if (any(as.numeric(x[,,d.ind[1]]) < min.size, na.rm = TRUE))
4✔
550
        stop("Output not shown because it is based on less than ", min.size, " observations.")
2✔
551
    else
552
        return(x)
2✔
553
}
554

555
#' Removes rows with small sample sizes
556
#' @description Using the 'Column n' (preferred) or 'Base n' statistic
557
#'   this funcion will remove any rows from \code{x}, where all
558
#'   are smaller than \code{min.size}. If any rows/columns are removed
559
#'   then warnings will be given.
560
#' @param x Input table, which must contain the 'Column n' or 'Base n' statistic.
561
#' @param min.size Minimum sample size required.
562
#' @export
563
HideRowsWithSmallSampleSizes <- function(x, min.size = 30)
564
{
565
    x <- convertTo3dQTable(x)
1✔
566
    size.names <- c("Column Sample Size", "Column n", "Sample Size", "Base n")
1✔
567
    if (!isTableWithStats(x) || !any(size.names %in% dimnames(x)[[3]]))
1✔
568
        stop("Table must have at least one of the following statistics: '",
×
569
             paste(size.names, collapse = "', '", sep=""), "'.")
×
570

571
    j <- 1
1✔
572
    d.ind <- NULL
1✔
573
    while (length(d.ind) == 0)
1✔
574
    {
575
        d.ind <- which(dimnames(x)[[3]] == size.names[j])
2✔
576
        j <- j + 1
2✔
577
    }
578

579
    # Search rows
580
    row.rm <- c()
1✔
581
    for (i in 1:nrow(x))
1✔
582
    {
583
        if (all(as.numeric(x[i, ,d.ind]) < min.size))
10✔
584
            row.rm <- c(row.rm, i)
10✔
585
    }
586
    if (all(dim(x) > 0) && length(row.rm) > 0)
1✔
587
    {
588
        warning("Rows ", paste(row.rm, collapse=","), " have sample size less than ", min.size, " and have been removed.")
1✔
589
        x <- extractArray(x, row.index = -row.rm)
1✔
590
    }
591
    x
1✔
592
}
593

594
#' Removes columns with small sample sizes
595
#' @description Using the 'Column n' (preferred) or 'Base n' statistic
596
#'   this funcion will remove any rows from \code{x}, where all
597
#'   are smaller than \code{min.size}. If any rows/columns are removed
598
#'   then warnings will be given.
599
#' @param x Input table, which must contain the 'Column n' or 'Base n' statistic.
600
#' @param min.size Minimum sample size required.
601
#' @export
602
HideColumnsWithSmallSampleSizes <- function(x, min.size = 30)
603
{
604
    x <- convertTo3dQTable(x)
5✔
605
    size.names <- c("Column Sample Size", "Column n", "Sample Size", "Base n")
5✔
606
    if (!isTableWithStats(x) || !any(size.names %in% dimnames(x)[[3]]))
5✔
607
        stop("Table must have at least one of the following statistics: '",
×
608
             paste(size.names, collapse = "', '", sep=""), "'.")
×
609

610
    j <- 1
5✔
611
    d.ind <- NULL
5✔
612
    while (length(d.ind) == 0)
5✔
613
    {
614
        d.ind <- which(dimnames(x)[[3]] == size.names[j])
8✔
615
        j <- j + 1
8✔
616
    }
617

618
    # Search rows and columns
619
    col.rm <- c()
5✔
620
    for (i in 1:ncol(x))
5✔
621
    {
622
        if (all(as.numeric(x[,i,d.ind]) < min.size))
32✔
623
            col.rm <- c(col.rm, i)
9✔
624
    }
625
    if (length(col.rm) > 0)
5✔
626
    {
627
        warning("Columns ", paste(col.rm, collapse=","), " have sample size less than ", min.size, " and have been removed.")
3✔
628
        x <- extractArray(x, col.index = -col.rm)
3✔
629
    }
630
    x
5✔
631
}
632

633
#' Removes values with small sample sizes
634
#' @description Using the 'Column n' (preferred) or 'Base n' statistic
635
#'   this funcion will remove any values from \code{x}, where all
636
#'   are smaller than \code{min.size}. They will be replaced with \code{NA}.
637
#' @param x Input table, which must contain the 'Column n' or 'Base n' statistic.
638
#' @param min.size Minimum sample size required.
639
#' @export
640
HideValuesWithSmallSampleSizes <- function(x, min.size = 30)
641
{
642
    x <- convertTo3dQTable(x)
6✔
643
    size.names <- c("Column Sample Size", "Column n", "Sample Size", "Base n")
6✔
644
    if (!isTableWithStats(x) || !any(size.names %in% dimnames(x)[[3]]))
6✔
645
        stop("Table must have at least one of the following statistics: '",
×
646
             paste(size.names, collapse = "', '", sep=""), "'.")
×
647

648
    j <- 1
6✔
649
    d.ind <- NULL
6✔
650
    while (length(d.ind) == 0)
6✔
651
    {
652
        d.ind <- which(dimnames(x)[[3]] == size.names[j])
14✔
653
        j <- j + 1
14✔
654
    }
655

656
    sz.dat <- matrix(as.numeric(x[,,d.ind]), nrow(x), ncol(x))
6✔
657
    ind <- which(sz.dat < min.size, arr.ind = TRUE)
6✔
658
    if (NROW(ind) > 0)
6✔
659
    {
660
        for (ii in 1:NROW(ind))
5✔
661
            x[ind[ii,1], ind[ii,2], 1] <- NA
73✔
662
    }
663
    return(x)
6✔
664
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc