Duplicate first occurence of string into column of data frame

Viewed 81

In a data frame, I am attempting to duplicate the first occurence of a string into the same column, but also into the neighbouring column. More specifically, I want the first occurence of a string in column v1 to be duplicated and inserted above itself and above the same row in column v2, as exemplified in the mock data frame below:

Input:

df_1<-data.frame("v1"=c(rep("a",times=3),rep("aa",times=4)),"v2"=c(c("b","c","d"),c("bb","cc","dd","ee")))
df_1
      v1 v2
    1  a  b
    2  a  c
    3  a  d
    4 aa bb
    5 aa cc
    6 aa dd
    7 aa ee

Expected output:

df_2<-data.frame("v1"=c(rep("a",times=4),rep("aa",times=5)),"v2"=c(c("a","b","c","d"),c("aa","bb","cc","dd","ee")))
df_2
    v1 v2
    1  a  a
    2  a  b
    3  a  c
    4  a  d
    5 aa aa
    6 aa bb
    7 aa cc
    8 aa dd
    9 aa ee

So in this case, the first occurence of "a" and "aa" has been duplicated and inserted into the same data frame above it's first occurence.

I hope my question makes sense.

Best, Rikki

4 Answers

One dplyr option could be:

df_1 %>%
 group_by(v1) %>%
 uncount((row_number() == 1) + 1) %>%
 mutate(v2 = if_else(row_number() == 1, first(v1), v2))

  v1    v2   
  <chr> <chr>
1 a     a    
2 a     b    
3 a     c    
4 a     d    
5 aa    aa   
6 aa    bb   
7 aa    cc   
8 aa    dd   
9 aa    ee   

Here is a base R idea:

 do.call(rbind, lapply(split(df_1, df_1$v1), function(i)
                                 rbind(data.frame(v1 = i$v1[1], v2 = i$v1[1]), i)))
#     v1 v2
#a.1   a  a
#a.2   a  b
#a.3   a  c
#a.4   a  d
#aa.1 aa aa
#aa.4 aa bb
#aa.5 aa cc
#aa.6 aa dd
#aa.7 aa ee

NOTE: You can use rownames() <- NULL to remove the rownames If they bother you.

EDIT Apparently there is a make.row.names arguments in the data.frame-method of rbind as provided in comments by @Jaap:

do.call(rbind, c(lapply(split(df_1, df_1$v1),
                        function(i) rbind(data.frame(v1 = i$v1[1], v2 = i$v1[1]), i)),
                 make.row.names = FALSE)
        )

You can use rep to copy the matching rows and then overwrite v2:

i <- !duplicated(df_1$v1)
df_2 <- df_1[rep(seq_len(length(i)), 1+i),]
i <- which(i)
i <- i + seq(0, length.out=length(i))
df_2$v2[i] <- df_2$v1[i]
#df_2[i,] <- df_2$v1[i]   #Alternative
#df_2[i,-1] <- df_2$v1[i] #Alternative
df_2
#    v1 v2
#1    a  a
#1.1  a  b
#2    a  c
#3    a  d
#4   aa aa
#4.1 aa bb
#5   aa cc
#6   aa dd
#7   aa ee

Here's one dplyr solution:

library(dplyr) 

df_1 %>% 
  select(v1) %>% 
  mutate(v2 = v1) %>% 
  unique() %>% 
  rbind(df_1) %>% 
  arrange(v1)

Which gives:

  v1 v2
1   a  a
11  a  b
2   a  c
3   a  d
4  aa aa
41 aa bb
5  aa cc
6  aa dd
7  aa ee
Related