Home > Back-end >  How can I create a df/dt where each column is the value of a row of an existing df/dt without loops?
How can I create a df/dt where each column is the value of a row of an existing df/dt without loops?

Time:03-13

I have a dataframe

data <- data.frame(v=c(15,25,24), x_val=c(12,7,2), y_val=c(6,6,18)) 

I want the resulting data to look like this with the data repeated in rows a specified number of times (here 2 times).

v1  x1  y1  v2  x2  y2  v3  x3  y3
15  12   6  25   7   6  24   2  18
15  12   6  25   7   6  24   2  18

I managed to get the data all in one row with the right column names but I'm not sure how to extend the column to a specified length with the values repeated. Further, how can I do this without loops? I want to run this with a larger dataset which can be quite slow with loops.

My code is below which gives the values in a single row.

r=NULL
  r<- as.data.frame(matrix(nrow=1, ncol=1))
  n<-2
    
  for (i in 1:nrow(data_subset)){
      
         datainarow <- data_subset[i,]
         r=cbind(r,as.data.frame(datainarow))
         
         colnames(r)[n] <- paste0("v",i)
         colnames(r)[n 1] <- paste0("x",i)
         colnames(r)[n 2] <- paste0("y",i)
         n <- n 3
         
  }

Thank you!

CodePudding user response:

You can use uncount in the tidyr package

If you already have your data in the single row format, just do:

n=4
data %>% tidyr::uncount(n)

# A tibble: 4 x 9
     v1    v2    v3    x1    x2    x3    y1    y2    y3
  <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1    15    25    24    12     7     2     6     6    18
2    15    25    24    12     7     2     6     6    18
3    15    25    24    12     7     2     6     6    18
4    15    25    24    12     7     2     6     6    18

Here is one way to get that result from initial three row data frame

library(tidyverse)

n=4

data %>%
  rename_all(~c("v","x","y")) %>% 
  mutate(id = row_number()) %>%
  pivot_wider(names_from = id, values_from = v:y,names_sep = "") %>% 
  uncount(n)

CodePudding user response:

This is a one-liner in base R

 as.data.frame(t(as.vector(t(data))))[rep(1, 2),]
#>     V1 V2 V3 V4 V5 V6 V7 V8 V9
#> 1   15 12  6 25  7  6 24  2 18
#> 1.1 15 12  6 25  7  6 24  2 18

Or if you wish to use the naming convention described, and have a more generalizable solution, you could use the following function:

expand_data <- function(data, reps) {
  df <- as.data.frame(t(as.vector(t(data))))[rep(1, reps),]
  names(df) <- paste(names(data), rep(seq(nrow(data)), each = nrow(data)), sep = "_")
  rownames(df) <- NULL
  df
}

which allows:

expand_data(data, 10)
   v_1 x_val_1 y_val_1 v_2 x_val_2 y_val_2 v_3 x_val_3 y_val_3
1   15      12       6  25       7       6  24       2      18
2   15      12       6  25       7       6  24       2      18
3   15      12       6  25       7       6  24       2      18
4   15      12       6  25       7       6  24       2      18
5   15      12       6  25       7       6  24       2      18
6   15      12       6  25       7       6  24       2      18
7   15      12       6  25       7       6  24       2      18
8   15      12       6  25       7       6  24       2      18
9   15      12       6  25       7       6  24       2      18
10  15      12       6  25       7       6  24       2      18
  • Related