13

The default behavior of melt.data.frame is to return the "variable" column in "factor" class. Here is an example:

> head(airquality)

  ozone solar.r wind temp month day
1    41     190  7.4   67     5   1
2    36     118  8.0   72     5   2
3    12     149 12.6   74     5   3
4    18     313 11.5   62     5   4
5    NA      NA 14.3   56     5   5
6    28      NA 14.9   66     5   6

> x = melt(head(airquality))
Using  as id variables

> head(x)
  variable value
1    ozone    41
2    ozone    36
3    ozone    12
4    ozone    18
5    ozone    NA
6    ozone    28

> class(x$variable)
[1] "factor"

The question is that is there any parameter to change the class from factor to character? I tried options(stringsAsFactors = FALSE) but it is not working.

A5C1D2H2I1M1N2O1R2T1
  • 190,393
  • 28
  • 405
  • 485
rninja
  • 540
  • 1
  • 4
  • 12

2 Answers2

5

With most users heading over to either the "tidyverse" or "data.table" for reshaping data these days, your options have improved.

In the "tidyverse", the default behavior is to keep the molten variable as characters:

library(tidyverse)
airquality %>% gather(var, val, everything()) %>% str()
# 'data.frame': 918 obs. of  2 variables:
#  $ var: chr  "Ozone" "Ozone" "Ozone" "Ozone" ...
#  $ val: num  41 36 12 18 NA 28 23 19 8 NA ...

In the "data.table" implementation of melt, a few new arguments have been added, one of which is variable.factor which can be set to FALSE. It's set to TRUE by default for, I believe, consistency with the "reshape2" implementation of melt.

library(data.table)
str(melt(as.data.table(airquality), variable.factor = FALSE))
# Classes ‘data.table’ and 'data.frame':    36 obs. of  2 variables:
#  $ variable: chr  "Ozone" "Ozone" "Ozone" "Ozone" ...
#  $ value   : num  41 36 12 18 NA 28 190 118 149 313 ...
#  - attr(*, ".internal.selfref")=<externalptr> 
A5C1D2H2I1M1N2O1R2T1
  • 190,393
  • 28
  • 405
  • 485
5

I don't believe there is such an option built into melt.data.frame. However, if you inspect the code, it's not hard to change. We can define a new function melt.df that replaces the relevant line with a quick check to see if the user has set stringsAsFactors = FALSE:

if (getOption("stringsAsFactors")){
    df[[variable_name]] <- factor(df[[variable_name]], 
                                   unique(df[[variable_name]]))
}
else{
   df[[variable_name]] <- as.character(factor(df[[variable_name]],         
                                   unique(df[[variable_name]])))
}

I checked this on your simple example and it worked as expected, but I haven't checked it more generally, so beware. I'm not convinced this modification won't produce surprising behavior in other circumstances.

joran
  • 169,992
  • 32
  • 429
  • 468
  • 5
    I think you'd be better off wrapping melt in another function that does `df$variable <- as.character(df$variable)` – hadley Sep 15 '11 at 20:17
  • @hadley, this is an inconvenience when working with dates. Works, though. – dmvianna Dec 09 '13 at 03:46
  • @dmvianna please file an issue with small example at https://github.com/hadley/reshape/issues – hadley Dec 12 '13 at 12:29
  • 4
    @hadley It’s not straightforward to have a generic wrapper: `melt.data.frame` has a different argument name for the variables column than `melt.matrix` (`variable.name` vs `varnames`). It would be better if the actual `melt` could be made to use characters instead of a factor. – Konrad Rudolph Feb 15 '15 at 22:03