Can data.table assign-by-reference (mutate) preserve vector names

Viewed 127

I have a named vector of colors that I'm using to assign to a new column using :=, based on a column value. If I use a dplyr mutate, the result is different than a data.table style mutate. With dplyr, the vector names are preserved, whereas in data.table the names are lost.

Let me walk you through what I've figured out so far.

# first I make a named vector of colors
movieColors <- c("Aladdin" = "steelblue1",
                 "Beauty" = "gold1",
                 "Brave" = "darkorange1")
# lets create some dummy data
dt <- data.table::data.table(movie = c("Aladdin", "Beauty", "Brave"), 
                             movieNum = 1:3)

# I want a new column that tells me the color of each movie for each row
# a dplyr mutate
dt1 <- dplyr::mutate(.data = dt, 
                     movColor = movieColors[movie])
# a data.table mutate
dt2 <- dt[, movColor := movieColors[movie]]

# check the results and they look the same
dt1
dt2

# check that they are the same
dplyr::all_equal(dt1, dt2)
# they're not the same?

# the dplyr mutate is preserving the named vector
dt1$movColor
# the data.table mutate does not preserve the named vector
dt2$movColor

If you run it, you can see that dt1, the dplyr version, prints:

Aladdin            Beauty         Brave 
"steelblue1"       "gold1"        "darkorange1" 

while the data.table version, dt2 prints:

[1] "steelblue1"   "gold1"        "darkorange1"

Why isn't data.table preserving the named vector? Is there a way to force it to do so?

1 Answers

As @dww pointed out, there is no specific need to keep the names in the movies column. While I do not know why {dplyr} supports this while {data.table} does not, you can use data.table::setattr() to achieve the same result.

# first I make a named vector of colors
movieColors <- c("Aladdin" = "steelblue1",
                 "Beauty" = "gold1",
                 "Brave" = "darkorange1")
# lets create some dummy data
dt <- data.table::data.table(movie = c("Aladdin", "Beauty", "Brave"), 
                             movieNum = 1:3)

# I want a new column that tells me the color of each movie for each row
# a dplyr mutate
dt1 <- dplyr::mutate(.data = dt, 
                     movColor = movieColors[movie])
#> Registered S3 methods overwritten by 'tibble':
#>   method     from  
#>   format.tbl pillar
#>   print.tbl  pillar
# a data.table mutate
dt2 <- dt[, movColor := movieColors[movie]]



# size prior to adding names
object.size(dt2)
#> 2008 bytes

# add names to the movies column in place
data.table::setattr(dt2$movColor, "names", dt2$movie)

#size after adding names
object.size(dt2)
#> 2368 bytes



# check the results and they look the same
dt1
#>      movie movieNum    movColor
#> 1: Aladdin        1  steelblue1
#> 2:  Beauty        2       gold1
#> 3:   Brave        3 darkorange1
dt2
#>      movie movieNum    movColor
#> 1: Aladdin        1  steelblue1
#> 2:  Beauty        2       gold1
#> 3:   Brave        3 darkorange1

# check that they are the same
dplyr::all_equal(dt1, dt2)
#> [1] TRUE

# the dplyr mutate is preserving the named vector
dt1$movColor
#>       Aladdin        Beauty         Brave 
#>  "steelblue1"       "gold1" "darkorange1"
# the data.table mutate does now preserve the named vector
dt2$movColor
#>       Aladdin        Beauty         Brave 
#>  "steelblue1"       "gold1" "darkorange1"

As you see, I just use information that is already in the data.table. Consequently, the size of the object increases. Might be that this is also the answer to why data.table strips the names automatically.

Related