While tidyr's unite is good for small datasets:
library(tidyr)
df |>
unite("X2", X2:X3, sep = "") |>
unite("X4", X4:X5, sep = "") |>
unite("X6", X6:X7, sep = "")
.. we might want to explore another way for general approach. One such is to pivot to a longer format, change all odd numbered columns to the preceeding even number (using the modulo operator) and then pivot longer collapsing the strings with paste0.
library(tidyr)
library(dplyr)
df |>
pivot_longer(-X1,
names_prefix = "X",
names_transform = as.numeric) |>
mutate(name = if_else(name %% 2 == 1, name - 1, name)) |>
pivot_wider(names_from = name,
names_prefix = "X",
values_fn = ~ paste0(., collapse = ""))
Output:
# A tibble: 4 × 4
X1 X2 X4 X6
<chr> <chr> <chr> <chr>
1 p1 HI KJ KH
2 p2 HK JK IJ
3 p3 JK HI JK
4 p4 KI HJ IJ
Data:
library(readr)
df <- read_table("X1 X2 X3 X4 X5 X6 X7
p1 H I K J K H
p2 H K J K I J
p3 J K H I J K
p4 K I H J I J")
Update:
If we want to start from X3 instead you'll need to change the code in two places. First, by not pivoting two columns (-c(X1, X2)) and then by subtracting 1 from the even columns instead (name %% 2 == 0). E.g.
library(tidyr)
library(dplyr)
df |>
pivot_longer(-c(X1, X2),
names_prefix = "X",
names_transform = as.numeric) |>
mutate(name = if_else(name %% 2 == 0, name - 1, name)) |>
pivot_wider(names_from = name,
names_prefix = "X",
values_fn = ~ paste0(., collapse = ""))
Output:
# A tibble: 4 × 5
X1 X2 X3 X5 X7
<chr> <chr> <chr> <chr> <chr>
1 p1 H IK JK H
2 p2 H KJ KI J
3 p3 J KH IJ K
4 p4 K IH JI J
(There is of course no X8 to combine with here.)