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 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210
|
#' Get data from a ggvis object
#'
#' This function is useful for inspecting the data in a ggvis object.
#' @param vis A ggvis object.
#' @examples
#' p <- cocaine %>% ggvis(~price) %>% layer_bars()
#' get_data(p)
#'
#' @export
get_data <- function(vis) {
if (!is.ggvis(vis)) stop("vis must be a ggvis object.")
lapply(vis$data, function(x) shiny::isolate(x()))
}
data_id <- function(x) {
return(attr(x, "data_id", TRUE))
}
`data_id<-` <- function(x, value) {
attr(x, "data_id") <- value
x
}
# Convenience function to remove missing values from a data.frame
# Remove all non-complete rows, with a warning if \code{warn_na = FALSE}.
#
# @param x An input data frame or split_df
# @param warn_na Warn when rows are removed?
# @param finite When FALSE, remove NA and NaN. When TRUE, also remove Inf and -Inf.
# @param vars Columns to check for missing values (for data.frame and split_df)
remove_missing <- function(x, warn_na = TRUE, finite = FALSE, ...) {
UseMethod("remove_missing")
}
#' @export
remove_missing.default <- function(x, warn_na = TRUE, finite = FALSE) {
if (!is.atomic(x)) {
stop("Input to remove_missing.default must be a vector.")
}
if (finite) {
missing <- !is.finite(x) | is.na(x)
str <- "non-finite"
} else {
missing <- is.na(x)
str <- "missing"
}
if (any(missing)) {
x <- x[!missing]
if (warn_na) warning("Removed ", sum(missing), " rows containing ", str,
" values", ".", call. = FALSE)
}
x
}
#' @export
remove_missing.data.frame <- function(x, warn_na = TRUE, finite = FALSE,
vars = names(x)) {
vars <- intersect(vars, names(x))
if (finite) {
missing <- !finite.cases(x[, vars, drop = FALSE])
str <- "non-finite"
} else {
missing <- !stats::complete.cases(x[, vars, drop = FALSE])
str <- "missing"
}
if (any(missing)) {
x <- x[!missing, , drop = FALSE]
if (warn_na) warning("Removed ", sum(missing), " rows containing ", str,
" values", ".", call. = FALSE)
}
x
}
#' @export
remove_missing.split_df <- function(x, warn_na = TRUE, finite = FALSE,
vars = names(x)) {
x[] <- lapply(x, remove_missing, warn_na, vars, finite)
x
}
# Returns a logical vector of same length as nrow(x). If all data on a row
# is finite (not NA, NaN, Inf, or -Inf) return TRUE; otherwise FALSE.
finite.cases <- function(x) {
finite_cases <- vapply(x, is.finite, logical(nrow(x)))
# Need a special case test when x has exactly one row, because rowSums
# doesn't respect dimensions for 1x1 matrices. vapply returns a vector (not
# a matrix when the input has one row.
if (is.vector(finite_cases)) {
all(finite_cases)
} else {
# Find all the rows where all are TRUE
rowSums(as.matrix(finite_cases)) == ncol(x)
}
}
to_csv <- function(x, header = TRUE, ...) UseMethod("to_csv")
#' @export
to_csv.data.frame <- function(x, header = TRUE) {
x <- lapply(x, format_vec_csv)
# Collapse across rows, yielding each row of CSV text
rows <- do.call(paste, c(x, sep = ","))
rows <- paste0(rows, collapse = "\n")
if (header) {
rows <- paste(
paste(quote_text(names(x)), collapse = ","),
rows,
sep = "\n"
)
}
rows
}
# Format a vector for csv output
format_vec_csv <- function(vec) UseMethod("format_vec_csv")
#' @export
format_vec_csv.numeric <- function(vec) vec
#' @export
format_vec_csv.character <- function(vec) quote_text(vec)
#' @export
format_vec_csv.factor <- function(vec) quote_text(vec)
# Represent dates and times as numbers (ms from epoch). If they're represented
# as date-time strings, this will cause problems when the client is in a
# different time zone from the server.
#' @export
format_vec_csv.POSIXt <- function(vec) floor(as.numeric(vec) * 1000)
#' @export
format_vec_csv.Date <- function(vec) as.numeric(as.POSIXct(vec)) * 1000
# Format a vector for d3 json output
format_vec_d3json <- function(vec) UseMethod("format_vec_d3json")
#' @export
format_vec_d3json.numeric <- function(vec) vec
#' @export
format_vec_d3json.character <- function(vec) vec
#' @export
format_vec_d3json.factor <- function(vec) as.character(vec)
#' @export
format_vec_d3json.POSIXt <- function(vec) floor(as.numeric(vec) * 1000)
#' @export
format_vec_d3json.Date <- function(vec) as.numeric(as.POSIXct(vec)) * 1000
# Replace \. with . , " with \", and add " to start and end
quote_text <- function(txt) {
if (length(txt) == 0)
return(txt)
txt <- gsub("\\.", ".", txt, fixed = TRUE)
txt <- gsub('"', '\\\\"', txt, fixed = TRUE)
paste0('"', txt, '"')
}
cur_data <- function(x) shiny::isolate(x$cur_data())
cur_props <- function(x) x$cur_props
eval_vector <- function(x, f) UseMethod("eval_vector")
eval_vector.data.frame <- function(x, f) {
eval(f[[2]], x, environment(f))
}
# Find the range of values for a vector
data_range <- function(x) UseMethod("data_range")
#' @export
data_range.default <- function(x) range2(x, na.rm = TRUE)
#' @export
data_range.character <- function(x) unique(stats::na.omit(x))
#' @export
data_range.factor <- function(x) levels(x)
# Takes a list of vectors, and puts them all together into one vector.
# For POSIXct, this preserves time zone.
# For factors, this preserves all levels (but not necessarily order)
concat <- function(x) {
x <- drop_nulls(x)
if (length(x) == 0) {
return(NULL)
}
if (inherits(x[[1]], "POSIXct")) {
vec <- do_call(c, .args = x)
structure(vec, tzone = attr(x[[1]], "tzone"))
} else if (inherits(x[[1]], "Date")) {
structure(unlist(x, recursive = FALSE), class = "Date")
} else {
unlist(x, recursive = FALSE)
}
}
# Does the same as base::range, except that for for 0-length vectors, it returns
# a zero-length vector of appropriate type, instead of throwing an error.
range2 <- function(..., na.rm = FALSE) {
vals <- c(...)
if (length(vals) == 0) {
return(vals)
}
range(..., na.rm = na.rm)
}
|