1

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.

Tobias Dekker
  • 980
  • 8
  • 19
  • 1
    options(scipen=99) will resolve the e+20 issue – joel.wilson Nov 14 '16 at 11:42
  • the 0's at the start will rip off if its a numeric column. I don't think we can avoid it. If you were importing data from csv files etc then specify this column as a character one there itself to avoid this issue – joel.wilson Nov 14 '16 at 12:23

1 Answers1

2

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):

head(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

EDIT

In the development version of reshape2 (devtools::install_github("hadley/reshape"), melt.array is differently defined and you can use parameter as.is to avoid the conversion:

melt(test, as.is=TRUE)

will give you the same result as above (with Var1 etc instead of X1 etc).

Cath
  • 23,906
  • 5
  • 52
  • 86