43

I just want to understand if there is a difference between names and colnames when working with data.frame. Both seems to behave the same way. Can I subsitute one by the other?

Arun kumar mahesh
  • 2,289
  • 2
  • 14
  • 22
Manu H
  • 523
  • 1
  • 5
  • 17
  • 3
    From `?colnames`: "For a data frame, `rownames` and `colnames` eventually call `row.names` and `names` respectively, but the latter are preferred." – Henrik Jul 17 '14 at 09:12

4 Answers4

42

Are they the same for data.frames? YES

Are they the same in general? Not quite--the big difference is that colnames also works for matrices, whereas names does not (just dataframes).

In addition, you can use names to set/get the names of vectors (and, for obvious reasons, you can't do this with colnames--the result is NULL for getting and an error for setting).

Steve S
  • 1,023
  • 7
  • 19
  • `names` works on vectors too. And it actually does work on matrices... it just does not do what you expect it to do! – nico Jul 17 '14 at 09:15
  • 2
    Re: "names" and matrices--that's kind of a loose notion of "working"... Nevertheless, point taken. – Steve S Jul 17 '14 at 09:34
  • 1
    just kidding, was mostly to point out that it won't throw an error, and in some cases it may slip through :) – nico Jul 17 '14 at 11:01
  • 1
    @SteveS Can you precise "for obvious reasons"? Doesn't seem so obvious to me ;) – Aurélien Gasser Feb 04 '17 at 17:31
  • 3
    @AurélienGasser, you're right: I'm not even sure myself why I thought that was so obvious at the time... But to answer your question, **colnames** only works for matrix-like objects with at least two dimensions (since it must have rows *and* columns) and, as a result, it doesn't work for vectors (since they don't have columns (or even a 'dim' attribute)). – Steve S Feb 09 '17 at 18:06
  • `names()` does not work for just data.frames but also lists (no?). Example: `x <- list(); x[["a"]] <- 2; x[["b"]] <- "foo"; names(x) <- c("c", "d")` – s_baldur Mar 01 '18 at 10:27
  • A data.frame is a list, and that's likely why `names<-` works on data.frames – alan ocallaghan Dec 16 '19 at 14:53
14

If you look at the beginning of the colnames and colnames<- functions source code :

R> colnames
function (x, do.NULL = TRUE, prefix = "col") 
{
    if (is.data.frame(x) && do.NULL) 
        return(names(x))
(...)


R> `colnames<-`
function (x, value) 
{
    if (is.data.frame(x)) {
        names(x) <- value
    }
(...)

You can see that for data frames, colnames just call the names function. So yes, they are strictly equivalent.

juba
  • 47,631
  • 14
  • 113
  • 118
5

names() creates name attributes where as colnames()simply names the columns.

i.e.

Create a temp variable.

> temp <- rbind(cbind(1,2,3,4,5),
+               cbind(6,7,8,9,10))

> temp
     [,1] [,2] [,3] [,4] [,5]
[1,]    1    2    3    4    5
[2,]    6    7    8    9   10

Create the names.temp object.

> names.temp <- temp

Use names() on names.temp

> names(names.temp) <- paste(c("First col", "Second col", "Third col",
 "Fourth Col", "Fifth col"))

> names.temp
     [,1] [,2] [,3] [,4] [,5]
[1,]    1    2    3    4    5
[2,]    6    7    8    9   10
attr(,"names")
 [1] "First col"  "Second col" "Third col"  "Fourth Col" "Fifth col"  
 NA NA NA          
 [9] NA NA      

We see here we can actually call the 5th name attribute in names.temp.

> names(names.temp)[5]
[1] "Fifth col"    

Repeat with a second object but this time create the colnames.temp object.

> colnames.temp <- temp

Use colnames() on colnames.temp

> colnames(colnames.temp) <- paste(c("First col", "Second col", "Third col",
 "Fourth Col", "Fifth col"))

> colnames.temp
     First col Second col Third col Fourth Col Fifth col
[1,]         1          2         3          4         5
[2,]         6          7         8          9        10

Now name attribute is NULL.

> names(colnames.temp)[5]
NULL

FINALLY. Let's look at our trusty str() command. We can see there is a structural difference between names.temp and colnames.temp. Specifically, colnames.temp has dimnames attributes not names attributes.

> str(names.temp)
 num [1:2, 1:5] 1 6 2 7 3 8 4 9 5 10
 - attr(*, "names")= chr [1:10] "First col" "Second col" "Thrid col" "Fourth     
Col" ...
> str(colnames.temp)
 num [1:2, 1:5] 1 6 2 7 3 8 4 9 5 10
 - attr(*, "dimnames")=List of 2
  ..$ : NULL
  ..$ : chr [1:5] "First col" "Second col" "Thrid col" "Fourth Col" ...
Sam Firke
  • 21,571
  • 9
  • 87
  • 105
  • 2
    Nice first answer, but `names()` does not create names. `names<-` does. It is a different function. – Frank Feb 03 '17 at 18:20
  • 1
    I stand corrected. The implication (read assumption) was to invoke `names()` implies the desire to name which is poor logic. Thanks Frank. – eyeknownothing Feb 03 '17 at 18:24
1

As far as I am concerned, the only difference between names() and colnames() with respect to a data.frame input is that they allocated memory slightly differently. For instance, consider the code chunk below:

    df <- data.frame(x=1:5, y=6:10, z=11:15)
    tracemem(df)
    names(df) <- c("A", "B", "C")
    colnames(df) <- c('a','b','c') 

If you run this code, you will see that the copying of df only occurs once during the names() call, whereas the copying of df occurs twice during the colnames() call.

atallcosts
  • 11
  • 4
  • 3
    If you don't like copying, use `data.table::setnames` (which you can use on data.frames). – Frank Feb 03 '17 at 18:21