Make duplicate rows but change specific characters to lowercase in duplicates - R

Viewed 61

I have a dataframe that looks like this:

+------------+
|site        |
+------------+
|JPN Tokyo   |
|AUS Sydney  |
|CHN Beijing |

But I'd like to make duplicate rows of the existing rows but with the 2nd and 3rd character changed to lowercase such that the dataframe becomes like this:

+------------+
|site        |
+------------+
|JPN Tokyo   |
|Jpn Tokyo   |
|AUS Sydney  |
|Aus Sydney  |
|CHN Beijing |
|Chn Beijing |

Would anyone have an idea how to do that?

3 Answers

We expand the rows with uncount, then create a logical condition with duplicated on the 'site', replace the substring values to lower case using sub within case_when

library(dplyr)
library(tidyr)
library(stringr)
df1 <- df1 %>%
   uncount(2) %>%   
   mutate(site = case_when(duplicated(site)
    ~ sub("^(.)(\\w+)", "\\1\\L\\2", site, perl = TRUE),
        TRUE ~ site)) 

-output

df1
# A tibble: 6 x 1
  site       
  <chr>      
1 JPN Tokyo  
2 Jpn Tokyo  
3 AUS Sydney 
4 Aus Sydney 
5 CHN Beijing
6 Chn Beijing

data

df1 <- structure(list(site = c("JPN Tokyo", "AUS Sydney", "CHN Beijing"
)), class = "data.frame", row.names = c(NA, -3L))

edit: @AnilGoyal suggested the use of map_dfr, that reduced the call to only one line.

library(tidyverse)    

data <- 
    tribble(
        ~site,      
        
        'JPN Tokyo',   
        'AUS Sydney', 
        'CHN Beijing' )

#option1
map_dfr(data$site, ~list(sites = c(.x, str_to_title(.x))))
#> # A tibble: 6 x 1
#>   sites      
#>   <chr>      
#> 1 JPN Tokyo  
#> 2 Jpn Tokyo  
#> 3 AUS Sydney 
#> 4 Aus Sydney 
#> 5 CHN Beijing
#> 6 Chn Beijing

#option2
map(data$site, ~rbind(.x, str_to_title(.x))) %>%
    reduce(rbind) %>%
    tibble(site = .)
#> # A tibble: 6 x 1
#>   site[,1]   
#>   <chr>      
#> 1 JPN Tokyo  
#> 2 Jpn Tokyo  
#> 3 AUS Sydney 
#> 4 Aus Sydney 
#> 5 CHN Beijing
#> 6 Chn Beijing

Created on 2021-06-08 by the reprex package (v2.0.0)

You can use substr to replace characters at specific position.

df1 <- df
substr(df1$site, 2, 3) <- tolower(substr(df1$site, 2, 3))
df1

#         site
#1   Jpn Tokyo
#2  Aus Sydney
#3 Chn Beijing

res <- rbind(df1, df)
res[order(res$site), , drop = FALSE]

#         site
#2  Aus Sydney
#5  AUS Sydney
#3 Chn Beijing
#6 CHN Beijing
#1   Jpn Tokyo
#4   JPN Tokyo
Related