From 32aab121b317c8cbf110a345ba85d46b0dfc0977 Mon Sep 17 00:00:00 2001 From: Michael Chirico Date: Sun, 4 Dec 2016 19:34:42 -0500 Subject: [PATCH] Closes #592 -- setcolorder now handles incomplete specifications automatically amend tests/implementation per jan --- NEWS.md | 2 ++ R/data.table.R | 24 ++++++++++++++++-------- inst/tests/tests.Rraw | 6 +++++- man/setcolorder.Rd | 5 ++++- 4 files changed, 27 insertions(+), 10 deletions(-) diff --git a/NEWS.md b/NEWS.md index 2c8c57154..b3f9b0de9 100644 --- a/NEWS.md +++ b/NEWS.md @@ -5,6 +5,8 @@ 1. `indices()` function gain new argument `vectors` default `FALSE`, when `TRUE` provided then list of vectors is returned, single vector refers to single index. Closes #1589. +2. `setcolorder` allows incomplete specification of new order (chosen columns are moved to the front), [#592](https://github.com/Rdatatable/data.table/issues/592). Thanks @MichaelChirico for the PR. + #### BUG FIXES #### NOTES diff --git a/R/data.table.R b/R/data.table.R index 97da35eff..647ae37aa 100644 --- a/R/data.table.R +++ b/R/data.table.R @@ -2569,22 +2569,30 @@ setnames <- function(x,old,new) { invisible(x) } -setcolorder <- function(x,neworder) +setcolorder <- function(x, neworder) { + if (any(duplicated(neworder))) stop("neworder contains duplicates") # if (!is.data.table(x)) stop("x is not a data.table") - if (length(neworder)!=length(x)) stop("neworder is length ",length(neworder)," but x has ",length(x)," columns.") + if (length(neworder) != length(x)) { + if (length(neworder) > length(x)) + stop("neworder is length ", length(neworder), + " but x has only ", length(x), " columns.") + #if shorter than length(x), pad by the missing + # elements (checks below will catch other mistakes) + neworder = c(neworder, setdiff(if (is.character(neworder)) names(x) + else seq_along(x), neworder)) + } if (is.character(neworder)) { - if (any(duplicated(neworder))) stop("neworder contains duplicate column names") - if (any(duplicated(names(x)))) stop("x has some duplicated column name(s): ",paste(names(x)[duplicated(names(x))],collapse=","),". Please remove or rename the duplicate(s) and try again.") - o = as.integer(chmatch(neworder,names(x))) - if (any(is.na(o))) stop("Names in neworder not found in x: ",paste(neworder[is.na(o)],collapse=",")) + if (any(duplicated(names(x)))) stop("x has some duplicated column name(s): ", paste(names(x)[duplicated(names(x))], collapse=","), ". Please remove or rename the duplicate(s) and try again.") + o = as.integer(chmatch(neworder, names(x))) + if (any(is.na(o))) stop("Names in neworder not found in x: ", paste(neworder[is.na(o)], collapse=",")) } else { if (!is.numeric(neworder)) stop("neworder is not a character or numeric vector") o = as.integer(neworder) m = !(o %in% seq_len(length(x))) - if (any(m)) stop("Column numbers in neworder out of bounds: ",paste(o[m],collapse=",")) + if (any(m)) stop("Column numbers in neworder out of bounds: ", paste(o[m], collapse=",")) } - .Call(Csetcolorder,x,o) + .Call(Csetcolorder, x, o) invisible(x) } diff --git a/inst/tests/tests.Rraw b/inst/tests/tests.Rraw index a336840a4..83ced2758 100644 --- a/inst/tests/tests.Rraw +++ b/inst/tests/tests.Rraw @@ -1442,7 +1442,6 @@ DT = data.table(a=1:2,b=3:4,c=5:6) test(495, setcolorder(DT,c(2,1,3)), data.table(b=3:4,a=1:2,c=5:6)) test(496, setcolorder(DT,c(2,1,3)), data.table(a=1:2,b=3:4,c=5:6)) test(497, setcolorder(DT,c("c","a","b")), data.table(c=5:6,a=1:2,b=3:4)) -test(498, setcolorder(DT,"a"), error="neworder is length") test(498.1, setcolorder(DT,c("d","a","b")), error="Names in neworder not found in x: d") @@ -9789,6 +9788,11 @@ indices(DT, vectors = TRUE) test(1749.1, indices(DT), c("A__B","A","B")) test(1749.2, indices(DT, vectors = TRUE), list(c("A","B"),"A","B")) +# allow incomplete specification in setcolorder, #592 +DT = data.table(a = 1:3, b = 2:4, c = 3:5) +test(1750.1, names(setcolorder(DT, "b")), c("b", "a", "c")) +test(1750.2, names(setcolorder(DT, c(2, 3))), c("a", "c", "b")) +test(1750.3, setcolorder(DT, 1:4), error = "x has only") ########################## diff --git a/man/setcolorder.Rd b/man/setcolorder.Rd index 48132c5e1..5cdf8bfb5 100644 --- a/man/setcolorder.Rd +++ b/man/setcolorder.Rd @@ -13,7 +13,7 @@ setcolorder(x, neworder) } \arguments{ \item{x}{ A \code{data.table}. } - \item{neworder}{ Character vector of the new column name ordering. May also be column numbers. } + \item{neworder}{ Character vector of the new column name ordering. May also be column numbers. If \code{length(neworder) < length(x)}, the specified columns are moved in order to the "front" of \code{x}. } } \details{ To reorder \code{data.table} columns, the idiomatic way is to use \code{setcolorder(x, neworder)}, instead of doing \code{x <- x[, neworder, with=FALSE]}. This is because the latter makes an entire copy of the \code{data.table}, which maybe unnecessary in most situations. \code{setcolorder} also allows column numbers instead of names for \code{neworder} argument, although we recommend using names as a good programming practice. @@ -30,6 +30,9 @@ DT = data.table(A=sample(3, 10, TRUE), B=sample(letters[1:3], 10, TRUE), C=sample(10)) setcolorder(DT, c("C", "A", "B")) + +#incomplete specification +setcolorder(DT, "A") } \keyword{ data }