How to reproduce the result of pivot_longer with reshape in base R?

Viewed 136

Consider the cprefm object:

library(conjoint)

data(chocolate)

With pivot_longer:

library(dplyr)
library(tidyr)

cprefm %>% 
  pivot_longer(., 1:16, "profile", "rating") %>% 
  head(16)

# A tibble: 16 × 2
   profile   value
   <chr>     <int>
 1 profile1     14
 2 profile2     15
 3 profile3      5
 4 profile4      2
 5 profile5      1
 6 profile6     11
 7 profile7      3
 8 profile8     10
 9 profile9     16
10 profile10    13
11 profile11    12
12 profile12     7
13 profile13     6
14 profile14     9
15 profile15     4
16 profile16     8

I couldn't reproduce this with the reshape function:

cprefm |>
  (\(x) reshape(x, varying = 1:16, times = names(x)[1:16], timevar = "profile", 
                v.names = "values", direction = "long"))() |> 
  (\(x) head(x, 16))()

             profile values id
1.profile1  profile1     14  1
2.profile1  profile1     16  2
3.profile1  profile1      7  3
4.profile1  profile1      9  4
5.profile1  profile1      7  5
6.profile1  profile1     14  6
7.profile1  profile1      3  7
8.profile1  profile1      2  8
9.profile1  profile1      1  9
10.profile1 profile1      4 10
11.profile1 profile1      4 11
12.profile1 profile1     14 12
13.profile1 profile1      7 13
14.profile1 profile1     14 14
15.profile1 profile1     10 15
16.profile1 profile1      4 16

I have tried in many ways to modify varying argument, but I have not been able to reproduce the behavior of tidyr::pivot_longer. Note that even row.names has column names, but I wouldn't like that either.

I would like it to look identical to pivot_longer.

3 Answers

1) post process The reshape command in the question produces the same output except for row names, row order and an extra id column so just fix those up.

At the end we run pivot_longer and converting its output to data.frame show that that is identical to the fixed-up output of reshape.

out <- reshape(cprefm, dir = "long", varying = names(cprefm), 
  v.names = "value", timevar = "profile", times = names(cprefm))
out <- out[order(out$id), 1:2]
rownames(out) <- NULL

out.piv <- cprefm %>% pivot_longer(1:16, "profile", "rating")

identical(out, as.data.frame(out.piv))
## [1] TRUE

2) pre process w transpose Alternately fix it up before passing it to reshape by reshaping the transpose of cprefm. In that case we only have to select out the desired columns while the row order comes out as desired without sorting.

out2 <- reshape(as.data.frame(t(cprefm)), dir = "long",
  varying = 1:nrow(cprefm), idvar = "profile", v.names = "value", 
  ids = names(cprefm), new.row.names = 1:prod(dim(cprefm)))[3:2]

identical(out2, as.data.frame(out.piv))
## [1] TRUE

3) as.data.frame.table w transpose The transpose idea also works with as.data.frame.table:

out3 <- with(as.data.frame.table(t(cprefm), responseName = "value"),
  data.frame(profile = as.character(Var1), value))

identical(out3, as.data.frame(out.piv))
## [1] TRUE

This one can be nicely written in terms of pipes like this:

cprefm |>
  t() |>
  as.data.frame.table(responseName = "value") |>
  with(data.frame(profile = as.character(Var1), value))

4) stack w tranpose and with stack:

out4 <- with(stack(as.data.frame(t(cprefm))), 
  data.frame(profile = names(cprefm), value = values))

identical(out4, as.data.frame(out.piv))
## [1] TRUE

If not using reshape is okay, this might works...

x <- as.data.frame(t(cprefm))

y <- data.frame(
  profile = rownames(x),
  value = unlist(x)
)
head(y,16)

     profile value
1   profile1    14
2   profile2    15
3   profile3     5
4   profile4     2
5   profile5     1
6   profile6    11
7   profile7     3
8   profile8    10
9   profile9    16
10 profile10    13
11 profile11    12
12 profile12     7
13 profile13     6
14 profile14     9
15 profile15     4
16 profile16     8

For a complete pivot in base I'd use stack, no edited to fiddle it into an exact match with pivot_longer:

result = cprefm |> stack() |> setNames(c("value", "profile")) |> rev() 
result = result[order((1:nrow(result) - 1) %% nrow(cprefm), result$profile), ]
rownames(result) = 1:nrow(result)
result
#      profile value
# 1   profile1    14
# 2   profile2    15
# 3   profile3     5
# 4   profile4     2
# 5   profile5     1
# 6   profile6    11
# 7   profile7     3
# 8   profile8    10
# 9   profile9    16
# 10 profile10    13
# 11 profile11    12
# 12 profile12     7
# 13 profile13     6
# 14 profile14     9
# 15 profile15     4
# 16 profile16     8
# 17  profile1    16
# 18  profile2    15
# 19  profile3     7
# 20  profile4    14
# ...
Related