Tobias Dekker Tobias Dekker - 24 days ago 6
R Question

Melt a array and make numeric values character

I have a array and I want to melt it based on the dimnames. The problem is that the dimension names are large numeric values and therefore making them character would convert them to a wrong ID see the example:

test <- array(1:18, dim = c(3,3,2), dimnames = list(c(00901291282245454545454,329293929929292,2929992929922929),
c("a", "b", "c"),
c("d", "e")))

library(reshape2)
library(data.table)
test2 <- data.table(melt(test))
test2[, Var1 := as.character(Var1)]

> test2
Var1 Var2 Var3 value
1: 9.01291282245455e+20 a d 1
2: 329293929929292 a d 2
3: 2929992929922929 a d 3
4: 9.01291282245455e+20 b d 4
5: 329293929929292 b d 5
6: 2929992929922929 b d 6
7: 9.01291282245455e+20 c d 7
8: 329293929929292 c d 8
9: 2929992929922929 c d 9
10: 9.01291282245455e+20 a e 10
11: 329293929929292 a e 11
12: 2929992929922929 a e 12
13: 9.01291282245455e+20 b e 13
14: 329293929929292 b e 14
15: 2929992929922929 b e 15
16: 9.01291282245455e+20 c e 16
17: 329293929929292 c e 17
18: 2929992929922929 c e 18


How could I make the first column with the large IDs character? What I am currently doing is pasting a character letter to the dimnames and then melt, making it a character and then take a substring, which is really inefficient. It is important that it is an efficient solution because the dataset is millions of rows. There are two problems,first the 0's are deleted if they are in front of the ID and it is converted to a e+20 character.

Answer

You need to define your dimnames as character and then slighly modify melt.array which is called when you do melt on your array:

test <- array(1:18, dim = c(3,3,2), dimnames = list(c("00901291282245454545454", "329293929929292", "2929992929922929"),
                                                    c("a", "b", "c"),
                                                    c("d", "e")))

Customise melt.array to add a parameter which permits to decide wether you want the conversion or not:

melt.array2 <- function (data, varnames = names(dimnames(data)), conv=TRUE, ...) 
{
    values <- as.vector(data)
    dn <- dimnames(data)
    if (is.null(dn)) 
        dn <- vector("list", length(dim(data)))
    dn_missing <- sapply(dn, is.null)
    dn[dn_missing] <- lapply(dim(data), function(x) 1:x)[dn_missing]
    if(conv){ # conv is the new parameter to know if conversion needs to be done
        char <- sapply(dn, is.character)
        dn[char] <- lapply(dn[char], type.convert)
    }
    indices <- do.call(expand.grid, dn)
    names(indices) <- varnames
    data.frame(indices, value = values)
}

Try the new function on your example (with conv=FALSE):

melt.array2(test, conv=FALSE)
                      # X1 X2 X3 value
# 1  00901291282245454545454  a  d     1
# 2          329293929929292  a  d     2
# 3         2929992929922929  a  d     3
# 4  00901291282245454545454  b  d     4
# 5          329293929929292  b  d     5
# 6         2929992929922929  b  d     6
# 7  00901291282245454545454  c  d     7
# 8          329293929929292  c  d     8
# 9         2929992929922929  c  d     9
# 10 00901291282245454545454  a  e    10
# 11         329293929929292  a  e    11
# 12        2929992929922929  a  e    12
# 13 00901291282245454545454  b  e    13
# 14         329293929929292  b  e    14
# 15        2929992929922929  b  e    15
# 16 00901291282245454545454  c  e    16
# 17         329293929929292  c  e    17
# 18        2929992929922929  c  e    18