Search code examples
rdplyrreplacestringr

str_replace in same column for multiple replacements - dplyr, stringr


I have this dataframe:

df <- structure(list(Stratigraphy = c("B", "B", "B", "B", "B", "B", 
"B", "B", "B", "B", "K", "F", "B", "B", "E", "B-C", "B-C", "B-C", 
"?", "B-C", "C?", "C?", "B-C", "B-C", "B-C", "?", "E-F-", "E-F-", 
"E-F-", "C", "F", "F", "E", "E", "B", "F", "F", "F", "F", "F", 
"F", "B", "D?", "E", "E", "E", "E", "E", "?", "F", "F", "F", 
"F", "F", "F", "F", "F", "F", "F", "F", "F", "F", "F", "E-F", 
"F", "G", "G", "G", "D", "C", "Finf", "Finf", "Finf", "D", "C", 
"C", "C", "C", "C", "C", "C", "C", "C", "C", "D", "C?", "G", 
"G", "G", "G", "G", "G", "G", "G", "G", "G", "D", "D", "Dmoy", 
"G", "G-G", "G-G", "G-G", "B-C", "G", "G-G", "G-G", "G-G", "D", 
"C", "F", "D", "D", "C", "C", "C", "C", "Dupper", "Dupper", "Dupper", 
"B", "B", "B", "F", "G", "G", "G", "G", "G", "G", "G", "G", "G", 
"C", "C", "C", "C", "C", "C", "E", "?", "F-F", "E-E", "C", "C", 
"C", "C", "C", "C", "C", "C", "C", "C?", "C?", "C?", "C?", "G", 
"G", "B", "?", "B", "B", "G", "G", "C", "?", "B-B", "B", "G-G", 
"G", "G", "G", "C(C?)", "F", "F", "F", "F", "F", "F", "F", "F", 
"F", "F", "F", "F", "F", "F", "F", "F", "F", "F", "L", "L", "K", 
"C", "C", "G", "G", "G", "J", "J", "K", "K", "F", NA, "L", "L", 
"L", "L", "L", NA, NA, "E", "E", "G", "G", "G", "G", "G", "?", 
"E", NA, NA, "E", "E", "E", "E", "E", "E", "E", "E", "E", "E", 
"E", "E", "?", "?", "C", "H-H", "G-G", "G-G", "G-G", "G-G", "G-G", 
"G-G", "G-G", "G-G", "G-G", "G-G", "G-G", "G-G", "G-G", "G-G", 
"F-F", "F-F", "F-F", "F-F", "F-F", "C", "C", "L", "G-G", "H", 
"F", "B", "B", "B", "B", "B", "B", "B", "B", "B", "B", "B", NA, 
"B"), MD = c(8.33333333333333, 9.17666666666667, NaN, NaN, 9.17, 
NaN, 8.11, 11.6, 9.17666666666667, 9.03, 16.85, 4.785, 15.01, 
14.85, NaN, 9.34, 16.6633333333333, NaN, NaN, 14.49, NaN, NaN, 
NaN, NaN, NaN, NaN, NaN, NaN, NaN, 13.5833333333333, NaN, 13.25, 
15.1466666666667, NaN, NaN, 7.96333333333333, NaN, NaN, 16.7466666666667, 
NaN, 14.1333333333333, 13.6166666666667, NaN, NaN, 10.0066666666667, 
NaN, 13.0966666666667, NaN, NaN, NaN, NaN, NaN, 8.41, NaN, NaN, 
12.6466666666667, NaN, NaN, NaN, NaN, 13.5233333333333, 17.53, 
NaN, NaN, NaN, 9.53, NaN, 16.1666666666667, 9.39, 13.0133333333333, 
12.9433333333333, NaN, NaN, 13.7633333333333, 13.9466666666667, 
14.4566666666667, 13.21, 12.71, 12.86, 9.03666666666667, NaN, 
NaN, 9.67, NaN, 9.3, 11.3433333333333, 13.54, 14.94, NaN, 9.32333333333333, 
17.9, 9.99, NaN, NaN, NaN, 15.06, 13.11, NaN, 13.7633333333333, 
NaN, 15.1333333333333, 8.67, 13.1066666666667, 17.52, NaN, 19.2133333333333, 
12.49, 18.5033333333333, 12.09, NaN, 11.1533333333333, NaN, NaN, 
NaN, 8.44666666666667, 8.00333333333333, 9.68666666666667, NaN, 
14.2566666666667, NaN, 14.26, 11.0533333333333, 8.70666666666667, 
10.5666666666667, 17.81, 16.5633333333333, NaN, NaN, NaN, NaN, 
NaN, NaN, NaN, 15.2866666666667, NaN, NaN, 14.1466666666667, 
NaN, 14.27, 14.5533333333333, NaN, 13.0933333333333, 10.19, NaN, 
NaN, NaN, NaN, NaN, 12.7133333333333, NaN, 5.84, 14.9133333333333, 
NaN, NaN, NaN, NaN, NaN, 12.91, 9.37333333333333, NaN, 9.24, 
12.54, 9.98, 11.9066666666667, 14.4833333333333, NaN, 18.8, NaN, 
10.6966666666667, 10.5266666666667, 10.4366666666667, 15.53, 
13.26, NaN, NaN, 14.09, NaN, NaN, 9.16666666666667, NaN, NaN, 
NaN, NaN, 8.29666666666667, NaN, 9.73, NaN, 9.97, NaN, 17.53, 
NaN, NaN, 13.175, NaN, NaN, 13.4533333333333, NaN, NaN, NaN, 
16.02, NaN, 14.82, 9.17333333333333, NaN, NaN, NaN, NaN, 12.3233333333333, 
12.3266666666667, 12.47, NaN, NaN, NaN, NaN, NaN, 16.8666666666667, 
NaN, NaN, 16.9566666666667, NaN, 14.9133333333333, NaN, NaN, 
10.42, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, 
NaN, NaN, 8.36666666666667, 9.9, 10.22, 14.1366666666667, 13.93, 
NaN, NaN, 10.67, 17.12, 14.89, 11.5133333333333, 11.6266666666667, 
15.6466666666667, 17.7466666666667, 11.09, 15.21, 17.8533333333333, 
NaN, NaN, 19.7333333333333, NaN, 13.8733333333333, 16.8033333333333, 
15.27, 13.11, NaN, 13.5766666666667, 10.7166666666667, 13.33, 
13.33, NaN, NaN, 12.5833333333333, 12.5833333333333, NaN, NaN, 
14.2266666666667, 10.83, 8.97, 10.0166666666667), BL = c(12.4866666666667, 
13.3666666666667, NaN, 13.1, 12.43, NaN, 12.18, 13.5, NaN, 12.5566666666667, 
15.89, 6.18, 12.86, 12.7933333333333, NaN, 7.845, 14.02, NaN, 
NaN, 13.1566666666667, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, 
NaN, 16.4133333333333, NaN, 16.0733333333333, 13.1266666666667, 
NaN, NaN, 9.41666666666667, NaN, NaN, 12.6, NaN, 14.3233333333333, 
11.54, NaN, NaN, NaN, 16.56, 15.5066666666667, NaN, NaN, NaN, 
NaN, NaN, 11.1766666666667, NaN, NaN, 16.13, NaN, NaN, NaN, NaN, 
16.2466666666667, NaN, NaN, NaN, NaN, 12.61, NaN, 14.7633333333333, 
11.7833333333333, 12.1133333333333, NaN, NaN, NaN, 14.5633333333333, 
12.68, 13.13, 15, 13.9633333333333, 13.74, NaN, NaN, NaN, 10.81, 
NaN, 6.11, 9.3, 15.165, NaN, NaN, NaN, 15.7333333333333, 11.6766666666667, 
NaN, NaN, NaN, 14.16, 12.48, NaN, 14.5633333333333, NaN, 13.73, 
9.92666666666667, 14.0066666666667, 16.3066666666667, NaN, 19.2933333333333, 
19.1, 17.0866666666667, 11.7233333333333, NaN, NaN, NaN, NaN, 
12.43, 10.1466666666667, 9.91666666666667, 12.44, NaN, 16.28, 
NaN, 13.37, 13.08, 12.8466666666667, 11.9633333333333, 15.4066666666667, 
14.54, NaN, NaN, NaN, NaN, NaN, NaN, NaN, 13.26, NaN, NaN, NaN, 
NaN, NaN, 15.4933333333333, NaN, 11.2966666666667, 12.1866666666667, 
NaN, NaN, NaN, NaN, 14, 10.1033333333333, NaN, 5.96, 16.45, NaN, 
NaN, NaN, NaN, 10.155, 11.1833333333333, 10.72, NaN, 13.4966666666667, 
10.7633333333333, 7.505, 10.04, 12.96, NaN, 14.8233333333333, 
NaN, 11.9866666666667, NaN, 17.2833333333333, 16.6566666666667, 
11.41, NaN, NaN, 15.3, NaN, NaN, 12.86, NaN, NaN, NaN, NaN, 11.7466666666667, 
NaN, 11.67, NaN, NaN, NaN, NaN, NaN, NaN, 11.6533333333333, NaN, 
NaN, 12.38, NaN, NaN, NaN, 16.2, NaN, 16.69, 13.2166666666667, 
NaN, NaN, NaN, NaN, 12.67, 13.4666666666667, 11.8166666666667, 
NaN, NaN, NaN, NaN, NaN, 15.7333333333333, NaN, NaN, 14.84, NaN, 
13.37, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, 
NaN, NaN, NaN, NaN, NaN, 11.9866666666667, NaN, NaN, 12.6466666666667, 
12.0533333333333, NaN, NaN, 12.0833333333333, 14.9166666666667, 
13.9666666666667, 11.5333333333333, 12.81, 13.82, 14.9133333333333, 
13.9733333333333, NaN, NaN, NaN, NaN, NaN, NaN, 14.4633333333333, 
NaN, 12.4733333333333, 13.2333333333333, NaN, 14.1033333333333, 
10.4966666666667, 12.22, 12.22, NaN, NaN, 14.66, 14.66, NaN, 
NaN, 12.67, 12.07, 10.25, 13.555)), row.names = c(NA, -277L), class = c("tbl_df", 
"tbl", "data.frame"))

If you see the values of the column Stratigraphy...

sort(unique(df$Stratigraphy))

 [1] "?"      "B"      "B-B"    "B-C"    "C"      "C(C?)"  "C?"     "D"      "D?"     "Dmoy"   "Dupper"
[12] "E"      "E-E"    "E-F"    "E-F-"   "F"      "F-F"    "Finf"   "G"      "G-G"    "H"      "H-H"   
[23] "J"      "K"      "L"   

...you can realize that there are several Strata which might be duplicated. For instance, I would like to convert all B-B in B, all C(C?) in C, etc.

For this reason I used str_replace to accommodate each situation:

df2 <- df %>%
  mutate(Stratigraphy = str_replace(Stratigraphy, "B-B" ,"B")) %>% 
  mutate(Stratigraphy = str_replace(Stratigraphy, "C(C?)", "C")) %>%
  mutate(Stratigraphy = str_replace(Stratigraphy, "C?", "C")) %>%
  mutate(Stratigraphy = str_replace(Stratigraphy, "D?", "D")) %>%
  mutate(Stratigraphy = str_replace(Stratigraphy, "Dmoy", "D")) %>%
  mutate(Stratigraphy = str_replace(Stratigraphy, "Dupper", "D")) %>%
  mutate(Stratigraphy = str_replace(Stratigraphy, "E-E", "E")) %>%
  mutate(Stratigraphy = str_replace(Stratigraphy, "F-F", "F")) %>%
  mutate(Stratigraphy = str_replace(Stratigraphy, "Finf", "F")) %>%
  mutate(Stratigraphy = str_replace(Stratigraphy, "G-G", "G")) %>%
  mutate(Stratigraphy = str_replace(Stratigraphy, "H-H", "H"))

However, the result is not what I want:

df2$Stratigraphy
  [1] "DCB"    "DCB"    "DCB"    "DCB"    "DCB"    "DCB"    "DCB"    "DCB"    "DCB"    "DCB"    "DCK"   
 [12] "DCF"    "DCB"    "DCB"    "DCE"    "DCB-C"  "DCB-C"  "DCB-C"  "DC?"    "DCB-C"  "DC?"    "DC?"   
 [23] "DCB-C"  "DCB-C"  "DCB-C"  "DC?"    "DCE-F-" "DCE-F-" "DCE-F-" "DC"     "DCF"    "DCF"    "DCE"   
 [34] "DCE"    "DCB"    "DCF"    "DCF"    "DCF"    "DCF"    "DCF"    "DCF"    "DCB"    "DCD?"   "DCE"   
 [45] "DCE"    "DCE"    "DCE"    "DCE"    "DC?"    "DCF"    "DCF"    "DCF"    "DCF"    "DCF"    "DCF"   
 [56] "DCF"    "DCF"    "DCF"    "DCF"    "DCF"    "DCF"    "DCF"    "DCF"    "DCE-F"  "DCF"    "DCG"   
 [67] "DCG"    "DCG"    "DCD"    "DC"     "DCF"    "DCF"    "DCF"    "DCD"    "DC"     "DC"     "DC"    
 [78] "DC"     "DC"     "DC"     "DC"     "DC"     "DC"     "DC"     "DCD"    "DC?"    "DCG"    "DCG"   
 [89] "DCG"    "DCG"    "DCG"    "DCG"    "DCG"    "DCG"    "DCG"    "DCG"    "DCD"    "DCD"    "DCD"   
[100] "DCG"    "DCG"    "DCG"    "DCG"    "DCB-C"  "DCG"    "DCG"    "DCG"    "DCG"    "DCD"    "DC"    
[111] "DCF"    "DCD"    "DCD"    "DC"     "DC"     "DC"     "DC"     "DCD"    "DCD"    "DCD"    "DCB"   
[122] "DCB"    "DCB"    "DCF"    "DCG"    "DCG"    "DCG"    "DCG"    "DCG"    "DCG"    "DCG"    "DCG"   
[133] "DCG"    "DC"     "DC"     "DC"     "DC"     "DC"     "DC"     "DCE"    "DC?"    "DCF"    "DCE"   
[144] "DC"     "DC"     "DC"     "DC"     "DC"     "DC"     "DC"     "DC"     "DC"     "DC?"    "DC?"   
[155] "DC?"    "DC?"    "DCG"    "DCG"    "DCB"    "DC?"    "DCB"    "DCB"    "DCG"    "DCG"    "DC"    
[166] "DC?"    "DCB"    "DCB"    "DCG"    "DCG"    "DCG"    "DCG"    "DC(C?)" "DCF"    "DCF"    "DCF"   
[177] "DCF"    "DCF"    "DCF"    "DCF"    "DCF"    "DCF"    "DCF"    "DCF"    "DCF"    "DCF"    "DCF"   
[188] "DCF"    "DCF"    "DCF"    "DCF"    "DCL"    "DCL"    "DCK"    "DC"     "DC"     "DCG"    "DCG"   
[199] "DCG"    "DCJ"    "DCJ"    "DCK"    "DCK"    "DCF"    NA       "DCL"    "DCL"    "DCL"    "DCL"   
[210] "DCL"    NA       NA       "DCE"    "DCE"    "DCG"    "DCG"    "DCG"    "DCG"    "DCG"    "DC?"   
[221] "DCE"    NA       NA       "DCE"    "DCE"    "DCE"    "DCE"    "DCE"    "DCE"    "DCE"    "DCE"   
[232] "DCE"    "DCE"    "DCE"    "DCE"    "DC?"    "DC?"    "DC"     "DCH"    "DCG"    "DCG"    "DCG"   
[243] "DCG"    "DCG"    "DCG"    "DCG"    "DCG"    "DCG"    "DCG"    "DCG"    "DCG"    "DCG"    "DCG"   
[254] "DCF"    "DCF"    "DCF"    "DCF"    "DCF"    "DC"     "DC"     "DCL"    "DCG"    "DCH"    "DCF"   
[265] "DCB"    "DCB"    "DCB"    "DCB"    "DCB"    "DCB"    "DCB"    "DCB"    "DCB"    "DCB"    "DCB"   
[276] NA       "DCB"

How can I proceed? What am I doing wrong?


Solution

  • We could extract uppercase, check, and reconstruct:

    library(dplyr)
    library(tidyr)
    library(stringr)
    
    df2 <-
      df |>
      mutate(elements = str_extract_all(Stratigraphy, "[A-Z]")) |>
      unnest_wider(elements, names_sep = "_") |>
      mutate(newStratigraphy = if_else(elements_1 == elements_2 | is.na(elements_2),
                                       elements_1,
                                       paste0(elements_1, "-", elements_2))) 
    

    Note. I am unnesting the columns for communicative reasons. You could work directly on the nested list elements if you want more compact code.

    To check the output:

    df2 |>
      count(Stratigraphy, newStratigraphy) |>
      print(n = 26)
    

    Output:

    # A tibble: 26 × 3
       Stratigraphy newStratigraphy     n
       <chr>        <chr>           <int>
     1 ?            NA                  9
     2 B            B                  33
     3 B-B          B                   1
     4 B-C          B-C                 8
     5 C            C                  38
     6 C(C?)        C                   1
     7 C?           C                   7
     8 D            D                   8
     9 D?           D                   1
    10 Dmoy         D                   1
    11 Dupper       D                   3
    12 E            E                  24
    13 E-E          E                   1
    14 E-F          E-F                 1
    15 E-F-         E-F                 3
    16 F            F                  46
    17 F-F          F                   6
    18 Finf         F                   3
    19 G            G                  39
    20 G-G          G                  22
    21 H            H                   1
    22 H-H          H                   1
    23 J            J                   2
    24 K            K                   4
    25 L            L                   8
    26 NA           NA                  6
    

    Alternatively, following your attempt with str_replace: When you want to recode categories and not make replacements inside the strings, your friend was recode, from dplyr >= 1.1.0 superseded in favour of case_match:

    library(dplyr)
    
    df |>
      mutate(newStratigraphy = recode(Stratigraphy,
                                      "B-B" = "B",
                                      "C(C?)" = "C",
                                      "C?" = "C",
                                      "D?" = "D",
                                      "Dmoy" = "D",
                                      "Dupper" = "D",
                                      "E-E" = "E",
                                      "F-F" = "F",
                                      "Finf" = "F",
                                      "G-G" = "G",
                                      "H-H" = "H")) 
    
    df |>
      mutate(newStratigraphy = case_match(Stratigraphy,
                                         "B-B" ~ "B",
                                         c("C(C?)", "C?") ~ "C",
                                         c("D?", "Dmoy", "Dupper") ~ "D",
                                         "E-E" ~ "E",
                                         c("F-F", "Finf") ~ "F",
                                         "G-G" ~ "G",
                                         "H-H" ~ "H",
                                         .default = Stratigraphy))
    

    With 26 categories, however, it can be a bit long..