Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,18 @@
# froll[fun] 0.003 0.002 NA
```

28. `setnames()` now accepts functions in `old=` and `new=`, [#3703](https://github.com/Rdatatable/data.table/issues/3703). Thanks @smingerson for the feature request and @shrektan for the PR.

```R
DT = data.table(a=1:3, b=4:6, c=7:9)
setnames(DT, toupper)
names(DT)
# [1] "A" "B" "C"
setnames(DT, 2:3, tolower)
names(DT)
# [1] "A" "b" "c"
```

#### BUG FIXES

1. `first`, `last`, `head` and `tail` by group no longer error in some cases, [#2030](https://github.com/Rdatatable/data.table/issues/2030) [#3462](https://github.com/Rdatatable/data.table/issues/3462). Thanks to @franknarf1 for reporting.
Expand Down
4 changes: 3 additions & 1 deletion R/data.table.R
Original file line number Diff line number Diff line change
Expand Up @@ -2425,6 +2425,7 @@ setnames = function(x,old,new,skip_absent=FALSE) {
stopifnot(isTRUEorFALSE(skip_absent))
if (missing(new)) {
# for setnames(DT,new); e.g., setnames(DT,c("A","B")) where ncol(DT)==2
if (is.function(old)) old = old(names(x))
if (!is.character(old)) stop("Passed a vector of type '",typeof(old),"'. Needs to be type 'character'.")
if (length(old) != ncol) stop("Can't assign ",length(old)," names to a ",ncol," column data.table")
if (anyNA(names(x))) {
Expand All @@ -2438,7 +2439,8 @@ setnames = function(x,old,new,skip_absent=FALSE) {
i = w
} else {
if (missing(old)) stop("When 'new' is provided, 'old' must be provided too")
if (!is.character(new)) stop("'new' is not a character vector")
if (is.function(new)) new = if (is.numeric(old)) new(names(x)[old]) else new(old)
if (!is.character(new)) stop("'new' is not a character vector or a function")
# if (anyDuplicated(new)) warning("Some duplicates exist in 'new': ", brackify(new[duplicated(new)])) # dups allowed without warning; warn if and when the dup causes an ambiguity
if (anyNA(new)) stop("NA in 'new' at positions ", brackify(which(is.na(new))))
if (anyDuplicated(old)) stop("Some duplicates exist in 'old': ", brackify(old[duplicated(old)]))
Expand Down
13 changes: 13 additions & 0 deletions inst/tests/tests.Rraw
Original file line number Diff line number Diff line change
Expand Up @@ -16028,6 +16028,19 @@ a1 <- data.table(
)
test(2106, a1[a == "\U4E2D\U6587\U4E00"], data.table(a = "\U4E2D\U6587\U4E00", b = c(1L, 5L, 9L)))

# setnames() allows function arguments, #3703
DT = data.table(a=1:3, b=4:6, c=7:9)
setnames(DT, base::toupper)
test(2107.1, names(DT), c('A','B','C'))
setnames(DT, c('B','C'), function(x) sprintf('W_%s_W', x))
test(2107.2, names(DT), c('A','W_B_W','W_C_W'))
DT = data.table(a=1:3, b=4:6, c=7:9)
# support numeric old as well
setnames(DT, 1.0, toupper)
test(2107.3, names(DT), c('A','b','c'))
setnames(DT, -(1:2), toupper)
test(2107.4, names(DT), c('A','b','C'))


###################################
# Add new tests above this line #
Expand Down
6 changes: 4 additions & 2 deletions man/setattr.Rd
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ setnames(x,old,new,skip_absent=FALSE)
\item{x}{ \code{setnames} accepts \code{data.frame} and \code{data.table}. \code{setattr} accepts any input; e.g, list, columns of a \code{data.frame} or \code{data.table}. }
\item{name}{ The character attribute name. }
\item{value}{ The value to assign to the attribute or \code{NULL} removes the attribute, if present. }
\item{old}{ When \code{new} is provided, character names or numeric positions of column names to change. When \code{new} is not provided, the new column names, which must be the same length as the number of columns. See examples. }
\item{new}{ Optional. New column names, must be the same length as columns provided to \code{old} argument. }
\item{old}{ When \code{new} is provided, character names or numeric positions of column names to change. When \code{new} is not provided, a function or the new column names. If a function, it will be called with the current column names and is supposed to return the new column names. The new column names must be the same length as the number of columns. See examples. }
\item{new}{ Optional. It can be a function or the new column names. If a function, it will be called with \code{old} and expected to return the new column names. The new column names must be the same length as columns provided to \code{old} argument. }
\item{skip_absent}{ Skip items in \code{old} that are missing (i.e. absent) in `names(x)`. Default \code{FALSE} halts with error if any are missing. }
}

Expand Down Expand Up @@ -59,6 +59,8 @@ setnames(DT,3,"C") # by position with warning if 3 > ncol(DT)
setnames(DT,2:3,c("D","E")) # multiple
setnames(DT,c("a","E"),c("A","F")) # multiple by name (warning if either "a" or "E" is missing)
setnames(DT,c("X","Y","Z")) # replace all (length of names must be == ncol(DT))
setnames(DT,tolower) # replace all names with their lower case
setnames(DT,2:3,toupper) # replace the 2nd and 3rd names with their upper case

DT <- data.table(x = 1:3, y = 4:6, z = 7:9)
setnames(DT, -2, c("a", "b")) # NEW FR #1443, allows -ve indices in 'old' argument
Expand Down