diff --git a/DESCRIPTION b/DESCRIPTION index a2fbdf400..544f96508 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,10 +1,11 @@ Encoding: UTF-8 Package: tibble Type: Package -Version: 0.2-1 +Version: 0.2-2 Date: 2016-03-08 -Title: Simple data frames -Description: Data frames and data sources in "dplyr" style. +Title: Simple Data Frames +Description: Provides a 'tbl_df' class that offers better checking and + printing capabilities than traditional data frames. Authors@R: c( person("Hadley", "Wickham", , "hadley@rstudio.com", role = "aut"), person("Romain", "Francois", , "romain@r-enthusiasts.com", role = "aut"), person("Kirill", diff --git a/NAMESPACE b/NAMESPACE index f0fdfad9c..2d2b3cbf7 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -33,8 +33,8 @@ S3method(type_sum,logical) S3method(type_sum,matrix) S3method(type_sum,numeric) export(add_row) -export(add_rownames) export(as_data_frame) +export(column_to_rownames) export(data_frame) export(data_frame_) export(dim_desc) @@ -43,6 +43,7 @@ export(glimpse) export(knit_print.trunc_mat) export(lst) export(lst_) +export(rownames_to_column) export(tbl_df) export(tibble) export(trunc_mat) diff --git a/NEWS.md b/NEWS.md index 6161f9f62..b4a836fde 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,3 +1,10 @@ +Version 0.2-2 (2016-03-08) +=== + +- `frame_data()` now also creates a list column if one of the entries is a list (#32). +- New `rownames_to_column()` and `column_to_rownames()` functions, replace `add_rownames()` (#11, @zhilongjia). + + Version 0.2-1 (2016-03-08) === diff --git a/R/dataframe.R b/R/dataframe.R index c91856d8f..1350f793b 100644 --- a/R/dataframe.R +++ b/R/dataframe.R @@ -194,25 +194,62 @@ as_data_frame.NULL <- function(x, ...) { as_data_frame(list()) } -#' Convert row names to an explicit variable. +#' Conversion between rownames and a column in data frame +#' +#' \code{rownames_to_column} convert row names to an explicit variable. #' #' @param df Input data frame with rownames. #' @param var Name of variable to use #' @export +#' @rdname rownames #' @importFrom stats setNames #' @examples -#' tbl_df(mtcars) +#' rownames_to_column(mtcars) #' -#' add_rownames(mtcars) -add_rownames <- function(df, var = "rowname") { +#' mtcars_tbl <- rownames_to_column(tbl_df(mtcars)) +#' mtcars_tbl +rownames_to_column <- function(df, var = "rowname") { + stopifnot(is.data.frame(df)) + if (var %in% colnames(df) ) { + stop(paste("There is a column named", var, "already!")) + } + rn <- as_data_frame(setNames(list(rownames(df)), var)) rownames(df) <- NULL - as_data_frame(cbind(rn, df)) + rn_df <- cbind(rn, df) + class(rn_df) <- class(df) + return (rn_df) } +#' \code{column_to_rownames} convert a column variable to row names. This is an +#' inverted operation of \code{rownames_to_column}. +#' +#' @rdname rownames +#' @export +#' @examples +#' +#' column_to_rownames(mtcars_tbl) +column_to_rownames <- function(df, var = "rowname") { + stopifnot(is.data.frame(df)) + + if (!identical(rownames(df), as.character(seq_len(NROW(df))))) { + stop("This data frame already has row names.") + + } else { + if ( !var %in% colnames(df) ) { + stop(paste0("This data frame has no column named ", var, ".") ) + } + + rownames(df) <- df[[var]] + df[, var] <- NULL + return (df) + } +} + + #' Add a row to a data frame #' #' This is a convenient way to add a single row of data to an existing data diff --git a/R/frame-data.R b/R/frame-data.R index d7a2b1197..2a465abd1 100644 --- a/R/frame-data.R +++ b/R/frame-data.R @@ -64,22 +64,19 @@ frame_data <- function(...) { )) } - frame_nrow <- n_elements / frame_ncol - - # Extract the columns from 'frame_rest' - frame_columns <- lapply(seq_len(frame_ncol), function(i) { - indices <- seq.default(from = i, to = length(frame_rest), by = frame_ncol) - col <- frame_rest[indices] - if (!any(vapply(col, function(x) is.list(x) || length(x) != 1L, - logical(1L)))) { - col <- unlist(col) + frame_mat <- matrix(frame_rest, ncol = frame_ncol, byrow = TRUE) + frame_col <- lapply(seq_len(ncol(frame_mat)), function(i) { + col <- frame_mat[, i] + if (any(vapply(col, needs_list_col, logical(1L)))) { + col + } else { + unlist(col) } - col }) # Create a tbl_df and return it - names(frame_columns) <- frame_names - as_data_frame(frame_columns) + names(frame_col) <- frame_names + as_data_frame(frame_col) } #' @rdname frame_data diff --git a/R/utils.r b/R/utils.r index 52821743f..acf7b1f1d 100644 --- a/R/utils.r +++ b/R/utils.r @@ -15,3 +15,7 @@ is_1d <- function(x) { # dimension check is for matrices and data.frames (is_atomic(x) || is.list(x)) && length(dim(x)) <= 1 } + +needs_list_col <- function(x) { + is.list(x) || length(x) != 1L +} diff --git a/man/add_rownames.Rd b/man/add_rownames.Rd deleted file mode 100644 index 3f9b57965..000000000 --- a/man/add_rownames.Rd +++ /dev/null @@ -1,22 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/dataframe.R -\name{add_rownames} -\alias{add_rownames} -\title{Convert row names to an explicit variable.} -\usage{ -add_rownames(df, var = "rowname") -} -\arguments{ -\item{df}{Input data frame with rownames.} - -\item{var}{Name of variable to use} -} -\description{ -Convert row names to an explicit variable. -} -\examples{ -tbl_df(mtcars) - -add_rownames(mtcars) -} - diff --git a/man/rownames.Rd b/man/rownames.Rd new file mode 100644 index 000000000..30fbc6351 --- /dev/null +++ b/man/rownames.Rd @@ -0,0 +1,31 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/dataframe.R +\name{rownames_to_column} +\alias{column_to_rownames} +\alias{rownames_to_column} +\title{Conversion between rownames and a column in data frame} +\usage{ +rownames_to_column(df, var = "rowname") + +column_to_rownames(df, var = "rowname") +} +\arguments{ +\item{df}{Input data frame with rownames.} + +\item{var}{Name of variable to use} +} +\description{ +\code{rownames_to_column} convert row names to an explicit variable. + +\code{column_to_rownames} convert a column variable to row names. This is an +inverted operation of \code{rownames_to_column}. +} +\examples{ +rownames_to_column(mtcars) + +mtcars_tbl <- rownames_to_column(tbl_df(mtcars)) +mtcars_tbl + +column_to_rownames(mtcars_tbl) +} + diff --git a/tests/testthat/test-data_frame.R b/tests/testthat/test-data_frame.R index ebd62312a..259e22e33 100644 --- a/tests/testthat/test-data_frame.R +++ b/tests/testthat/test-data_frame.R @@ -73,17 +73,42 @@ test_that("Zero column list makes 0 x 0 tbl_df", { expect_equal(dim(zero), c(0L, 0L)) }) + +test_that("rownames_to_column keeps the tbl classes (#882)", { + res <- rownames_to_column( mtcars, "Make&Model" ) + expect_equal( class(res), class(mtcars) ) + expect_error(rownames_to_column( mtcars, "wt"), + paste("There is a column named wt already!") ) + res1 <- rownames_to_column( tbl_df(mtcars), "Make&Model" ) + expect_equal( class(res1), class(tbl_df(mtcars)) ) + expect_error(rownames_to_column( mtcars, "wt"), + paste("There is a column named wt already!") ) +}) + +test_that("column_to_rownames returns tbl", { + var <- "car" + mtcars <- tbl_df(mtcars) + res <- column_to_rownames( rownames_to_column( mtcars, var), var) + expect_equal( class(res), class(mtcars) ) + expect_equal(rownames(res), rownames(mtcars)) + expect_equal(res, mtcars) + expect_false(var %in% names(res)) + + mtcars$num <- rev(seq_len(nrow(mtcars))) + res1 <- column_to_rownames( rownames_to_column( mtcars), var="num") + expect_equal(rownames(res1), as.character(mtcars$num) ) + expect_error(column_to_rownames(res1), "This data frame already has row names.") + expect_error(column_to_rownames( rownames_to_column( mtcars, var), "num2"), + paste("This data frame has no column named num2.") ) +}) + + test_that("NULL makes 0 x 0 tbl_df", { nnnull <- as_data_frame(NULL) expect_is(nnnull, "tbl_df") expect_equal(dim(nnnull), c(0L, 0L)) }) -test_that("add_rownames keeps the tbl classes (#882)", { - res <- add_rownames( mtcars, "Make&Model" ) - expect_equal( class(res), c("tbl_df","tbl", "data.frame")) -}) - # Validation -------------------------------------------------------------- test_that("2d object isn't a valid column", {