Can I "automate" uniting columns that are V1:V148?

Viewed 63

I have a dataset that has 148 columns. I need to combine them in groups of 4. For example V1,V2,V3,V4 =X1

V1 <-c(0,3)
V2 <-c(F,F)
V3 <-c (2,4)
V4 <-c(A,C)


X1

0F2A

3F4C

I know I can use

```{r}
    new_data_4 <-new%>%
       unite(V1:V4)%>%
       unite(V5:V8)%>%
       unite(V9:V12)%>%
       unite(V13:V16)

```

with great success but I would like to make this function. My wish is that it can count the number of columns and do it automatically without hardcoding the numbers. I have more files to go over. I have looked around StackOverFlow and found LOTS of examples with specific problems that don't really jive with what I have.

I have tried:

```{r}
     unite_columns <-function(x){
        united_cols <-tidyr::unite(x, seq_along(1, ncol(x), 4), seq_along(4, ncol(x), 4))
        return(united_cols)
     }
```

and

```{r} 
     unite_columns <-function(x){
       united_cols <-unite(x, seq(1, ncol(x), 4), seq(4, ncol(x), 4))
       return(united_cols)
     }
```

I was thinking I could use a similar tactic that is used to merge strings but it did not work. Any help would be greatly appreciated. TIA

4 Answers

You can use split.default to split the columns in every 4 columns and paste the values rowwise using do.call.

result <- data.frame(sapply(split.default(new, ceiling(seq_along(new)/4)),
                     function(x) do.call(paste0, x)))

#    X1   X2
#1 0F2A 8A4R
#2 3F4C 9B5K

data

new <- data.frame(V1 = c(0,3), V2 = c("F","F"), V3 = c(2,4), V4 = c("A","C"), 
                  V5 = c(8, 9), V6 = c("A", "B"), V7 = c(4, 5), V8 = c("R", "K"))
new

#  V1 V2 V3 V4 V5 V6 V7 V8
#1  0  F  2  A  8  A  4  R
#2  3  F  4  C  9  B  5  K

Using Reduce.

sapply(1:(ncol(new)/4), function(f) Reduce(paste0, new[1:4*f]))
#        [,1]   [,2]   [,3]  
# [1,] "0F2A" "FAAR" "2A1D"
# [2,] "3F4C" "FCBK" "4B2S"

If you want a data frame:

as.data.frame(sapply(1:(ncol(new)/4), function(f) Reduce(paste0, new[1:4*f])))
#     V1   V2   V3
# 1 0F2A FAAR 2A1D
# 2 3F4C FCBK 4B2S

Data

new <- structure(list(V1 = c(0, 3), V2 = c("F", "F"), V3 = c(2, 4), 
    V4 = c("A", "C"), V5 = c(8, 9), V6 = c("A", "B"), V7 = c(4, 
    5), V8 = c("R", "K"), V9 = c(1, 2), V10 = c("C", "D"), V11 = c(9, 
    8), V12 = c("D", "S")), class = "data.frame", row.names = c(NA, 
-2L))

You can also do like this, with tidyverse only. I have used purrr::map2dfc to do this.

  • First argument of map is a sequence of length n/4 (needless to say you may use ncol(new) instead of storing n separately.
  • and second argument is names of columns to be generated.
  • At each iteration, the map function will take out four columns as per integer division function used,
  • name it as per second argument
  • and then select that column only.
  • all the lists generated at each iteration of map function will be col-bind and therefore map2_dfc has been used.
  • I think that is pretty clear.
library(tidyverse)

#say n is 148
n <- 148

map2_dfc(seq_len(n/4), paste0("X", seq_len(n/4)), ~new %>%
      unite(!!.y, 
            seq_along(new)[(3 + seq_along(new)) %/% 4 == .x],
            sep = "") %>% select(all_of(.y)) 
    )

Check it for data generated by @Ronak

n <- 8

map2_dfc(seq_len(n/4), paste0("X", seq_len(n/4)), ~new %>%
      unite(!!.y, 
            seq_along(new)[(3 + seq_along(new)) %/% 4 == .x],
            sep = "") %>% select(all_of(.y)) 
    )
    X1   X2
1 0F2A 8A4R
2 3F4C 9B5K

Or on data generated by @jay.sf

n <- 12
map2_dfc(seq_len(n/4), paste0("X", seq_len(n/4)), ~new %>%
      unite(!!.y, 
            seq_along(new)[(3 + seq_along(new)) %/% 4 == .x],
            sep = "") %>% select(all_of(.y)) 
    )

    X1   X2   X3
1 0F2A 8A4R 1C9D
2 3F4C 9B5K 2D8S

Another purrr and tidyr option could be:

imap_dfc(.x = split.default(df, ceiling(1:ncol(df)/4)),
         ~ .x %>%
          unite(col = !!paste0("X", .y), everything(), sep = ""))

    X1   X2
1 0F2A 8A4R
2 3F4C 9B5K
Related