Remove run-length duplicate numbers and NA in strings

Viewed 47

I have columns with large strings of decimal numbers and NA:

df <- data.frame(
  A_gsr =c("2.752,2.752,2.752,2.752,2.752,2.752,2.752,2.911,2.911,3.555",
           "2.999,2.999,2.999,2.752,2.752,2.752,2.752"),
  B_gsr = c("1.34,1.34,1.34,1.55,1.55,1.55,1.55,1.55,1.55,1.55",
            "1.56,1.56,1.56,1.55,1.55,1.55,1.55,NA,NA,NA,NA,1.34,1.34,1.34"),
  C_gsr = c("NA,NA,NA,0.147,0.147,0.147,0.147,0.147,NA",
            "0.146,0.146,0.146,0.146,0.146,0.146,0.146,0.146,0.146,0.146")
)

I want to remove all run-length duplicates. Using gsub and backreference, I'm getting pretty close to what I want to have:

lapply(df[,1:3], function(x) gsub("((\\d\\.\\d+,)|(NA,))\\1+", "\\1", x))
$A_gsr
[1] "2.752,2.911,3.555" "2.999,2.752,2.752"

$B_gsr
[1] "1.34,1.55,1.55"         "1.56,1.55,NA,1.34,1.34"

$C_gsr
[1] "NA,0.147,NA" "0.146,0.146"

However, not close enough - there are still some run-length dups, all at the end of the strings. The expected result is this:

$A_gsr
[1] "2.752,2.911,3.555" "2.999,2.752"

$B_gsr
[1] "1.34,1.55"         "1.56,1.55,NA,1.34"

$C_gsr
[1] "NA,0.147,NA" "0.146"
1 Answers

You can use

lapply(df[,1:3], function(x) gsub("\\b(\\d+\\.\\d+|NA)(?:,\\1)+\\b", "\\1", x))
## => $A_gsr
##   [1] "2.752,2.911,3.555" "2.999,2.752"      
##   
##   $B_gsr
##   [1] "1.34,1.55"         "1.56,1.55,NA,1.34"
##   
##   $C_gsr
##   [1] "NA,0.147,NA" "0.146"    

See the regex demo and the R demo online.

Details:

  • \b - a word boundary
  • (\d+\.\d+|NA) - Group 1: one or more digits, ., one or more digits, OR NA string
  • (?:,\1)+ - one or more repetitions of a comma and the value in Group 1
  • \b - a word boundary
Related