Home > Software engineering >  tidyverse analog of reshape2::melt that does everything?
tidyverse analog of reshape2::melt that does everything?

Time:01-25

The answers to this question document that the tidyverse analog of reshape2::melt() is tidyr::pivot_longer(). That's true, as far as it goes, but I still find the process of melting a matrix with defined dimnames in tidyverse much less convenient than the tidyverse equivalent.

Example:

z <- matrix(1:12, ncol=3, 
       dimnames = list(a = 1:4, b = LETTERS[1:3]))

reshape2::melt(z) automatically gives me a data frame with the ID columns named "a" and "b", corresponding to the elements of names(dimnames(z)).

I originally had a somewhat clunky solution here but realized it didn't work. I think the required steps are

  • store the row-dimension name and column-dimension names
  • convert to a data frame
  • add the rowname as a column with name "tmp" (with tibble::rownames_to_column("tmp"))
  • pivot_longer() with -tmp and set names_to to the column-dimension name
  • rename "tmp" to the row_dimension name (could save a step if we did NSE magic)

This seems much clunkier than melt(). Obviously I could write a utility function to encapsulate this, but I'm wondering if I'm missing a solution that is more compact and/or tidyverse-idiomatic. What do other people do when the first step in their data pipeline is a matrix?

CodePudding user response:

If you don't mind having a base R function in your pipeline, then you could do:

z %>%
  as.table() %>%
  as_tibble()
#> # A tibble: 12 x 3
#>    a     b         n
#>    <chr> <chr> <int>
#>  1 1     A         1
#>  2 2     A         2
#>  3 3     A         3
#>  4 4     A         4
#>  5 1     B         5
#>  6 2     B         6
#>  7 3     B         7
#>  8 4     B         8
#>  9 1     C         9
#> 10 2     C        10
#> 11 3     C        11
#> 12 4     C        12

Of course, this is just a slightly contrived tidyverse version of the base R equivalent, which is difficult to beat for brevity.

as.data.frame(as.table(z))
#>    a b Freq
#> 1  1 A    1
#> 2  2 A    2
#> 3  3 A    3
#> 4  4 A    4
#> 5  1 B    5
#> 6  2 B    6
#> 7  3 B    7
#> 8  4 B    8
#> 9  1 C    9
#> 10 2 C   10
#> 11 3 C   11
#> 12 4 C   12

And these both give the same result as reshape2::melt:

reshape2::melt(z)
#>    a b value
#> 1  1 A     1
#> 2  2 A     2
#> 3  3 A     3
#> 4  4 A     4
#> 5  1 B     5
#> 6  2 B     6
#> 7  3 B     7
#> 8  4 B     8
#> 9  1 C     9
#> 10 2 C    10
#> 11 3 C    11
#> 12 4 C    12

Created on 2023-01-24 with reprex v2.0.2

  • Related