Special reshape in R

Viewed 80

Consider a 3x3 char dataframe:

example <- data.frame(one = c("a","b","c"),
             two = c("a","b","b"),
             three = c ("c","a","b"))

I want to resize these data to 6x2 and add the following content:

desired <- data.frame(one = c("a","a","b","b",
                              "c","b"),
                      two = c("a","c","b","a","b","b"))

For the original example dataframe, I want to rbind() the contents of example[,2:3] beneath each row index.

This can be achieved by:

ex <- as.matrix(example)

des <- as.data.frame(rbind(ex[,1:2], ex[,2:3]))

Maybe using library(tidyverse) for an arbitrary number of columns would be nicer?

3 Answers

For each pair of columns, transpose the sub-data.frame defined by them and coerce to vector. Then coerce to data.frame and set the result's names.
The code that follows should be scalable, it does not hard code the number of columns.

desired2 <- as.data.frame(
  lapply(seq(names(example))[-1], \(k) c(t(example[(k-1):k])))
)
names(desired2) <- names(example)[-ncol(example)]

identical(desired, desired2)
#[1] TRUE

The code above rewritten as a function.

reformat <- function(x){
  y <- as.data.frame(
    lapply(seq(names(x))[-1], \(k) c(t(x[(k-1):k])))
  )
  names(y) <- names(x)[-ncol(x)]
  y
}

reformat(example)
example %>% reformat()

Another example, with 6 columns input.

ex1 <- example
ex2 <- example
names(ex2) <- c("fourth", "fifth", "sixth")
ex <- cbind(ex1, ex2)

reformat(ex)
ex %>% reformat()

A tidyverse approach using tidyr::pivot_longer may look like so:

library(dplyr)
library(tidyr)

pivot_longer(example, -one, values_to = "two") %>% 
  select(-name)
#> # A tibble: 6 × 2
#>   one   two  
#>   <chr> <chr>
#> 1 a     a    
#> 2 a     c    
#> 3 b     b    
#> 4 b     a    
#> 5 c     b    
#> 6 c     b

A base-R solution with Map:

#iterate over example$one, example$two, and example$three at the same
#time, creating the output you need.
mylist <- Map(function(x ,y ,z ) {
 data.frame(one = c(x, y), two = c(y, z))
},
example$one #x,
example$two #y,
example$three #z)


do.call(rbind, mylist)
    one two
a.1   a   a
a.2   a   c
b.1   b   b
b.2   b   a
c.1   c   b
c.2   b   b
Related