forked from tidyverse/dplyr
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathselect-utils.R
149 lines (124 loc) · 4.03 KB
/
select-utils.R
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
#' Select helpers
#'
#' These functions allow you to select variables based on their names.
#' \itemize{
#' \item \code{starts_with()}: starts with a prefix
#' \item \code{ends_with()}: ends with a prefix
#' \item \code{contains()}: contains a literal string
#' \item \code{matches()}: matches a regular expression
#' \item \code{num_range()}: a numerical range like x01, x02, x03.
#' \item \code{one_of()}: variables in character vector.
#' \item \code{everything()}: all variables.
#' }
#'
#' @param match A string.
#' @param ignore.case If \code{TRUE}, the default, ignores case when matching
#' names.
#' @param vars A character vector of variable names. When called from inside
#' \code{\link{select}()} these are automatically set to the names of the
#' table.
#' @name select_helpers
#' @return An integer vector given the position of the matched variables.
#' @examples
#' iris <- tbl_df(iris) # so it prints a little nicer
#' select(iris, starts_with("Petal"))
#' select(iris, ends_with("Width"))
#' select(iris, contains("etal"))
#' select(iris, matches(".t."))
#' select(iris, Petal.Length, Petal.Width)
#' select(iris, everything())
#' vars <- c("Petal.Length", "Petal.Width")
#' select(iris, one_of(vars))
NULL
cur_vars_env <- new.env()
set_current_vars <- function(x) {
stopifnot(is.character(x))
cur_vars_env$selected <- x
}
reset_current_vars <- function() {
set_current_vars(character())
}
#' @export
#' @rdname select_helpers
current_vars <- function() cur_vars_env$selected
#' @export
#' @rdname select_helpers
starts_with <- function(match, ignore.case = TRUE, vars = current_vars()) {
stopifnot(is.string(match), !is.na(match), nchar(match) > 0)
if (ignore.case) match <- tolower(match)
n <- nchar(match)
if (ignore.case) vars <- tolower(vars)
which_vars(match, substr(vars, 1, n))
}
#' @export
#' @rdname select_helpers
ends_with <- function(match, ignore.case = TRUE, vars = current_vars()) {
stopifnot(is.string(match), !is.na(match), nchar(match) > 0)
if (ignore.case) match <- tolower(match)
n <- nchar(match)
if (ignore.case) vars <- tolower(vars)
length <- nchar(vars)
which_vars(match, substr(vars, pmax(1, length - n + 1), length))
}
#' @export
#' @rdname select_helpers
contains <- function(match, ignore.case = TRUE, vars = current_vars()) {
stopifnot(is.string(match), nchar(match) > 0)
if (ignore.case) {
vars <- tolower(vars)
match <- tolower(match)
}
grep_vars(match, vars, fixed = TRUE)
}
#' @export
#' @rdname select_helpers
matches <- function(match, ignore.case = TRUE, vars = current_vars()) {
stopifnot(is.string(match), nchar(match) > 0)
grep_vars(match, vars, ignore.case = ignore.case)
}
#' @export
#' @rdname select_helpers
#' @param prefix A prefix that starts the numeric range.
#' @param range A sequence of integers, like \code{1:5}
#' @param width Optionally, the "width" of the numeric range. For example,
#' a range of 2 gives "01", a range of three "001", etc.
num_range <- function(prefix, range, width = NULL, vars = current_vars()) {
if (!is.null(width)) {
range <- sprintf(paste0("%0", width, "d"), range)
}
match_vars(paste0(prefix, range), vars)
}
#' @export
#' @rdname select_helpers
#' @param ... One or more character vectors.
one_of <- function(..., vars = current_vars()) {
keep <- c(...)
if (!is.character(keep)) {
stop("`c(...)` must be a character vector", call. = FALSE)
}
if (!all(keep %in% vars)) {
bad <- setdiff(keep, vars)
warning("Unknown variables: ", paste0("`", bad, "`", collapse = ", "))
}
match_vars(keep, vars)
}
#' @export
#' @rdname select_helpers
everything <- function(vars = current_vars()) {
seq_along(vars)
}
match_vars <- function(needle, haystack) {
x <- match(needle, haystack)
x <- x[!is.na(x)]
fill_out(x, haystack)
}
grep_vars <- function(needle, haystack, ...) {
fill_out(grep(needle, haystack, ...), haystack)
}
which_vars <- function(needle, haystack) {
fill_out(which(needle == haystack), haystack)
}
fill_out <- function(x, haystack) {
if (length(x) > 0) return(x)
-seq_along(haystack)
}