How do I collapse certain strings with a specific pattern from a text?

Viewed 126

I have some strings of text (example below actual text is an entire book). As you can see each string was split at a period or question mark.

   [1]"I am a Mr."
   [2]"asking for help."
   [3]"Can you help?"
   [4]"Thank you ms."
   [5]"or mr."

I want to collapse where the string ends with an abbreviation like mr., mrs. so the end result would be the desired output below.

    [1]"I am a Mr. asking for help."
    [2]"Can you help?"
    [3]"Thank you ms. or mr."

I already created a vector (called abbr) containing all my abbreviations in the following format:

> abbr
[1] "Mr|Mrs|Ms|Dr|Ave|Blvd|Rd|Mt|Capt|Maj"

but I can't figure out how to use it in paste function to collapse. I have also tried using gsub (didn't work) to replace \n following abbreviation with a period with a space like this:

lines<-gsub('(?<=abbr\\.\\n)(?=[A-Z])', ' ', lines, perl=FALSE)
3 Answers

One way to do it is to use strsplit, with the following logic * Split on empty space * Space preceded by punctuation * Punctuation NOT preceded by vector of abbreviations

I put the string into 1 character vector like so:

lines2 <- paste(lines, collapse = ' ')
lines2
[1] "I am a Mr. asking for help. Can you help? Thank you ms. or mr."

Heres how to accomplish that with regex. Importantly, your attempt used "abbr" in quotes in a way that wont find the vector you define, you have to include it with a paste. I also added "mr" and "ms" to abbr. I include several lines that build up the ideas. I use look-behinds, you can learn more here

# Split on punctuation
strsplit(lines2, "[[:punct:]]", perl = T)

# Split on punctuation, not preceded by abbr
strsplit(lines2, paste("(?<!",abbr,")[[:punct:]]"), perl = T)

# Split on space after punctuation, not preceded by abbr
strsplit(lines2, paste("(?<=(?<!", abbr,")[[:punct:]])[[:space:]]"), perl = T)

[[1]]
[1] "I am a Mr. asking for help." "Can you help?"              
[3] "Thank you ms. or mr." 

How about this?

library(tidyverse)

lines <- c("I am a Mr.", "asking for help.", "Can you help?", "Thank you ms.", "or mr.")
abbr <- "Mr|Mrs|Ms|Dr|Ave|Blvd|Rd|Mt|Capt|Maj"

paste(lines, collapse = " ") %>%
  str_split(paste0("(?<=(?<!",abbr,"|", tolower(abbr),")\\.|\\?)\\s")) %>%
  unlist()
#> [1] "I am a Mr. asking for help." "Can you help?"              
#> [3] "Thank you ms. or mr."

First I undo the original spllit to get one big string, then I split the text by a space that is preceded by a question mark or a period, but only if the period is not preceded by an abbreviation.

Kind of hacky, but this works:

texts <- c("I am a Mr.", "asking for help.", "Can you help?", "Thank you ms.", "or mr.")
abbr <- "Mr|Mrs|Ms|Dr|Ave|Blvd|Rd|Mt|Capt|Maj"
texts2 <- c()
to_skip <- c()
for (i in seq_along(texts)) {
  if (i %in% to_skip) next
  if (grepl(abbr, texts[i], ignore.case = TRUE)) {
    texts2[i] <- paste(texts[i], texts[i+1], collapse = " ")
    to_skip <- c(to_skip, i+1)
  } else {
    texts2[i] <- texts[i]
  }
}
texts2[!is.na(texts2)]
#> [1] "I am a Mr. asking for help." "Can you help?"              
#> [3] "Thank you ms. or mr."

Created on 2020-04-08 by the reprex package (v0.3.0)

Related