forked from tidyverse/dplyr
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathbind.r
163 lines (143 loc) · 4.4 KB
/
bind.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
150
151
152
153
154
155
156
157
158
159
160
161
162
163
#' Efficiently bind multiple data frames by row and column.
#'
#' This is an efficient implementation of the common pattern of
#' \code{do.call(rbind, dfs)} or \code{do.call(cbind, dfs)} for binding many
#' data frames into one. \code{combine()} acts like \code{\link{c}()} or
#' \code{\link{unlist}()} but uses consistent dplyr coercion rules.
#'
#' @section Deprecated functions:
#' \code{rbind_list()} and \code{rbind_all()} have been deprecated. Instead use
#' \code{bind_rows()}.
#'
#' @param ... Data frames to combine.
#'
#' Each argument can either be a data frame, a list that could be a data
#' frame, or a list of data frames.
#'
#' When column-binding, rows are matched by position, not value so all data
#' frames must have the same number of rows. To match by value, not
#' position, see \code{left_join} etc. When row-binding, columns are
#' matched by name, and any values that don't match will be filled with NA.
#' @param .id Data frames identifier.
#'
#' When \code{.id} is supplied, a new column of identifiers is
#' created to link each row to its original data frame. The labels
#' are taken from the named arguments to \code{bind_rows()}. When a
#' list of data frames is supplied, the labels are taken from the
#' names of the list. If no names are found a numeric sequence is
#' used instead.
#' @return \code{bind_rows} and \code{bind_cols} return the same type as
#' the first input, either a data frame, \code{tbl_df}, or \code{grouped_df}.
#' @aliases rbind_all rbind_list
#' @examples
#' one <- mtcars[1:4, ]
#' two <- mtcars[11:14, ]
#'
#' # You can either supply data frames as arguments
#' bind_rows(one, two)
#' # Or a single argument containing a list of data frames
#' bind_rows(list(one, two))
#' bind_rows(split(mtcars, mtcars$cyl))
#'
#' # When you supply a column name with the `.id` argument, a new
#' # column is created to link each row to its original data frame
#' bind_rows(list(one, two), .id = "id")
#' bind_rows(list(a = one, b = two), .id = "id")
#' bind_rows("group 1" = one, "group 2" = two, .id = "groups")
#'
#' # Columns don't need to match when row-binding
#' bind_rows(data.frame(x = 1:3), data.frame(y = 1:4))
#' \dontrun{
#' # Rows do need to match when column-binding
#' bind_cols(data.frame(x = 1), data.frame(y = 1:2))
#' }
#'
#' bind_cols(one, two)
#' bind_cols(list(one, two))
#'
#' # combine applies the same coercion rules
#' f1 <- factor("a")
#' f2 <- factor("b")
#' c(f1, f2)
#' unlist(list(f1, f2))
#'
#' combine(f1, f2)
#' combine(list(f1, f2))
#' @name bind
NULL
#' @export
#' @rdname bind
bind_rows <- function(..., .id = NULL) {
x <- list_or_dots(...)
if (!is.null(.id)) {
if (!(is.character(.id) && length(.id) == 1)) {
stop(".id is not a string", call. = FALSE)
}
names(x) <- names(x) %||% seq_along(x)
}
bind_rows_(x, .id)
}
#' @export
#' @rdname bind
bind_cols <- function(...) {
x <- list_or_dots(...)
cbind_all(x)
}
#' @export
#' @rdname bind
combine <- function(...) {
args <- list(...)
if (length(args) == 1 && is.list(args[[1]])) {
combine_all(args[[1]])
} else {
combine_all(args)
}
}
list_or_dots <- function(...) {
dots <- list(...)
# Need to ensure that each component is a data list:
data_lists <- vapply(dots, is_data_list, logical(1))
dots[data_lists] <- lapply(dots[data_lists], list)
unlist(dots, recursive = FALSE)
}
# Is this object a
is_data_list <- function(x) {
# data frames are trivially data list, and so are nulls
if (is.data.frame(x) || is.null(x))
return(TRUE)
# Must be a list
if (!is.list(x))
return(FALSE)
# 0 length named list (#1515)
if( !is.null(names(x)) && length(x) == 0)
return(TRUE)
# With names
if (any(!has_names(x)))
return(FALSE)
# Where each element is an 1d vector or list
is_1d <- vapply(x, is_1d, logical(1))
if (any(!is_1d))
return(FALSE)
# All of which have the same length
n <- vapply(x, length, integer(1))
if (any(n != n[1]))
return(FALSE)
TRUE
}
# Deprecated functions ----------------------------------------------------
#' @export
#' @rdname bind
#' @usage NULL
rbind_list <- function(...){
warning("`rbind_list()` is deprecated. Please use `bind_rows()` instead.",
call. = FALSE)
rbind_list__impl(environment())
}
#' @export
#' @rdname bind
#' @usage NULL
rbind_all <- function(x, id = NULL) {
warning("`rbind_all()` is deprecated. Please use `bind_rows()` instead.",
call. = FALSE)
bind_rows_(x, id = id)
}