Skip to content

Commit

Permalink
add_totals functions now work w/ non-numeric cols
Browse files Browse the repository at this point in the history
per #57
  • Loading branch information
sfirke committed Feb 2, 2017
1 parent 8a132d1 commit 3fbd673
Show file tree
Hide file tree
Showing 2 changed files with 60 additions and 18 deletions.
42 changes: 31 additions & 11 deletions R/add_totals.R
Original file line number Diff line number Diff line change
Expand Up @@ -14,22 +14,36 @@
#' add_totals_row


add_totals_row <- function(dat, na.rm = TRUE){
check_all_cols_after_first_are_numeric(dat)
dat[[1]] <- as.character(dat[[1]]) # for binding to the "Total" character value of add-on row
col_totals <- data.frame(x1 = "Total", t(colSums(dat[-1], na.rm = na.rm)), stringsAsFactors = FALSE) %>%
add_totals_row <- function(dat, fill = "-", na.rm = TRUE){
clean_dat <- clean_names(dat) # bad names will make select_if choke

if(dim(select_if(clean_dat, is.numeric))[2] == 0){stop("data.frame must contain at least one column of class numeric")} # chokes on illegal names

# creates the totals row to be appended
col_vec <- function(a_col, na_rm = na.rm){
if(is.numeric(a_col)){ # can't do this with if_else because it doesn't like the sum() of a character vector, even if that clause is not reached
sum(a_col, na.rm = na_rm)
} else {fill}
}

col_totals <- lapply(dat, col_vec) %>%
as.data.frame(stringsAsFactors = FALSE) %>%
stats::setNames(names(dat))
dplyr::bind_rows(dat, col_totals)

col_totals[nrow(col_totals), min(which(!unlist(lapply(col_totals, is.numeric))))] <- "Total" # replace final row, first non-numeric column with "Total"
dplyr::bind_rows(clean_dat %>%
stats::setNames(names(dat)), col_totals)

}

#' @title Append a totals column to a data.frame.
#'
#' @description
#' This function excludes the first column of the input data.frame, assuming that it contains a descriptive variable not to be summed.
#' This function excludes non-numeric columns of the input data.frame, e.g., a first column with a descriptive variable not to be summed.
#'
#' @param dat an input data.frame with numeric values in all columns beyond the first.
#' @param dat an input data.frame with at least one numeric column.
#' @param na.rm should missing values (including NaN) be omitted from the calculations?
#' @return Returns a data.frame with a totals column, consisting of "Total" in the first row and row sums in the others.
#' @return Returns a data.frame with a totals column containing row-wise sums.
#' @export
#' @examples
#' library(dplyr) # for the %>% pipe
Expand All @@ -38,8 +52,14 @@ add_totals_row <- function(dat, na.rm = TRUE){
#' add_totals_col

add_totals_col <- function(dat, na.rm = TRUE){
check_all_cols_after_first_are_numeric(dat)
row_totals <- data.frame(Total = rowSums(dat[-1], na.rm = na.rm))
dplyr::bind_cols(dat, row_totals)

clean_dat <- clean_names(dat) # bad names will make select_if choke
if(dim(dplyr::select_if(clean_dat, is.numeric))[2] == 0){stop("data.frame must contain at least one column of class numeric")}
row_totals <- clean_dat %>%
dplyr::select_if(is.numeric) %>%
dplyr::transmute(Total = rowSums(., na.rm = na.rm))

dplyr::bind_cols(dat, row_totals) %>%
stats::setNames(c(names(dat), "Total")) # put back original names
}

36 changes: 29 additions & 7 deletions tests/testthat/test-add-totals.R
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,6 @@ dat <- data.frame(a = c(rep(c("big", "small", "big"), 3)),
ct <- dat %>%
crosstab(a, b)


test_that("error thrown if column beyond the first is not numeric", {
expect_error(add_totals_row(dat %>% select(b, a)),
"all columns after the first one must be numeric")
expect_error(add_totals_col(dat %>% select(b, a)),
"all columns after the first one must be numeric")
})


test_that("totals row is correct", {
Expand Down Expand Up @@ -74,3 +67,32 @@ test_that("both functions work with a single column", {
expect_error(single_col %>% add_totals_row(), NA) # from http://stackoverflow.com/a/30068233
expect_error(single_col %>% add_totals_row(), NA)
})




dat <- data.frame(
a = c("hi", "lo"),
b = c(1, 2),
c = c(5, 10),
d = c("big", "small"),
e = c(20, NA),
stringsAsFactors = FALSE
)

test_that("na.rm value gets passed through", {

})


test_that("error thrown if no columns are numeric", {

})

test_that("works with non-numeric columns mixed in", {

})

test_that("column names are passed through", {

})

0 comments on commit 3fbd673

Please sign in to comment.