Gsub conditional replacement

I have text data (in R) and you want to replace some characters with other characters in the data frame. I thought this would be a simple task using strsplit in spaces and create a vector that I can then use to match (% in%), which can then be inserted back together. But then I thought about punctuation. There is no place at the end between the last word of the sentence and punctuation.

I believe that there may be an easier way to achieve what I want than the tangled mess that becomes my code. I would appreciate this problem.

#Character String x <- "I like 346 ice cream cones. They're 99 percent good! I ate 46." #Replacement Values Dataframe symbol text 1 "346" "three hundred forty six" 2 "99" "ninety nine" 3 "46" "forty six" #replacement dataframe numDF <- data.frame(symbol = c("346","99", "46"), text = c("three hundred forty six", "ninety nine","forty six"), stringsAsFactors = FALSE) 

Desired Result:

 [1] "I like three hundred forty six ice cream cones. They're ninety nine percent good! You ate forty six?") 

EDIT: I originally called this conditional gsub because it looks like me even if there is no gsub in it.

+6
source share
5 answers

Perhaps this, inspired by Josh O'Brien, replies:

 x <- "I like 346 ice cream cones. They're 99 percent good! I ate 46." numDF <- structure(c("346", "99", "46", "three hundred forty six", "ninety nine", "forty six"), .Dim = c(3L, 2L), .Dimnames = list(c("1", "2", "3"), c("symbol", "text"))) pat <- paste(numDF[,"symbol"], collapse="|") repeat { m <- regexpr(pat, x) if(m==-1) break sym <- regmatches(x,m) regmatches(x,m) <- numDF[match(sym, numDF[,"symbol"]), "text"] } x 
+8
source

This solution uses gsubfn in a package with the same name:

 library(gsubfn) (pat <- paste(numDF$symbol, collapse="|")) # [1] "346|99|46" gsubfn(pattern = pat, replacement = function(x) { numDF$text[match(x, numDF$symbol)] }, x) [1] "I like three hundred forty six ice cream cones. They're ninety nine percent good! I ate forty six." 
+6
source

You can separate spaces or word boundaries (which will match between a word and punctuation):

 > x [1] "I like 346 ice cream cones. They're 99 percent good! I ate 46." > strsplit(x, split='\\s|\\>|\\<') [[1]] [1] "I" "like" "346" "ice" "cream" "cones" "." [8] "" "They" "'re" "99" "percent" "good" "!" [15] "" "I" "ate" "46" "." 

Then you can carry out your replacements.

+4
source

Another solution using Reduce from base .

 list_df <- apply(numDF, 1, as.list) Reduce(function(x, l) gsub(l$symbol, l$text, x), list_df, init = x) 

EDIT. Here is a complete solution using the numbers2words function directly.

 list_df <- as.numeric(regmatches(x, gregexpr('[0-9]+', x))[[1]]) Reduce(function(x, l) gsub(l, numbers2words(l), x), list_df, init = x) 
+3
source

It's not clear if you really wanted to convert the numbers to your alpha equivalents. If so, then there is a much more general strategy. Rhelp archives have (at least) two functions for converting numbers to text: Jim Lemon digits2text and John Fox numberstowords . I also switched to gregexpr to move on to a vectorized approach:

Cut and Paste The Lemon function from the HTML found here was out of the box:

 > m <- gregexpr("[0-9]+", x) > sym <- regmatches(x,m) > regmatches(x,m) <- digits2text(as.numeric(sym[[1]])) illion = 0 digilen = 3 digitext = three hundred forty six [1] 6 4 3 > > x [1] "I like three hundred forty six ice cream cones. They're three hundred forty six percent good! I ate three hundred forty six." 

I needed to do some editing of numberstowords because there were some missing lines that messed up the parsing (and I include the successful version below this demo:

 > m <- gregexpr("[0-9]+", x) > sym <- regmatches(x,m) > regmatches(x,m) <- numbers2words(as.numeric(sym[[1]])) > > x [1] "I like three hundred forty six ice cream cones. They're three hundred forty six percent good! I ate three hundred forty six." 

Fox function edited: http://tolstoy.newcastle.edu.au/R/help/05/04/2715.html

 numbers2words <- function(x){ helper <- function(x){ digits <- rev(strsplit(as.character(x), "")[[1]]) nDigits <- length(digits) if (nDigits == 1) as.vector(ones[digits]) else if (nDigits == 2) if (x <= 19) as.vector(teens[digits[1]]) else trim(paste(tens[digits[2]], Recall(as.numeric(digits[1])))) else if (nDigits == 3) trim(paste(ones[digits[3]], "hundred", Recall(makeNumber(digits[2:1])))) else { nSuffix <- ((nDigits + 2) %/% 3) - 1 if (nSuffix > length(suffixes)) stop(paste(x, "is too large!")) trim(paste(Recall(makeNumber(digits[ nDigits:(3*nSuffix + 1)])), suffixes[nSuffix], Recall(makeNumber(digits[(3*nSuffix):1])))) } } trim <- function(text){ gsub("^\ ", "", gsub("\ *$", "", text)) } makeNumber <- function(...) as.numeric(paste(..., collapse="")) opts <- options(scipen=100) on.exit(options(opts)) ones <- c("", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine") names(ones) <- 0:9 teens <- c("ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen", " seventeen", "eighteen", "nineteen") names(teens) <- 0:9 tens <- c("twenty", "thirty", "forty", "fifty", "sixty", "seventy", "eighty", "ninety") names(tens) <- 2:9 x <- round(x) suffixes <- c("thousand", "million", "billion", "trillion") if (length(x) > 1) return(sapply(x, helper)) helper(x) } 
+2
source

Source: https://habr.com/ru/post/904923/


All Articles