Skip to content

Instantly share code, notes, and snippets.

@psychemedia
Created July 12, 2015 00:12
Show Gist options
  • Save psychemedia/150cb9901529da58124a to your computer and use it in GitHub Desktop.
Save psychemedia/150cb9901529da58124a to your computer and use it in GitHub Desktop.
R function to convert numbers to words
#https://github.com/ateucher/useful_code/blob/master/R/numbers2words.r
numbers2words <- function(x){
## Function by John Fox found here:
## http://tolstoy.newcastle.edu.au/R/help/05/04/2715.html
## Tweaks by AJH to add commas and "and"
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 and",
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){
#Tidy leading/trailing whitespace, space before comma
text=gsub("^\ ", "", gsub("\ *$", "", gsub("\ ,",",",text)))
#Clear any trailing " and"
text=gsub(" and$","",text)
#Clear any trailing comma
gsub("\ *,$","",text)
}
makeNumber <- function(...) as.numeric(paste(..., collapse=""))
#Disable scientific notation
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(trim(sapply(x, helper)))
helper(x)
}
@psychemedia
Copy link
Author

See also: CRAN toOrdinal package. Would be handy to have something like inflect.py that allows combinations of ordinal and toText conversion.

@camkay
Copy link

camkay commented Nov 7, 2018

Does as it says. Great work!

@partha6369
Copy link

The program works. However, I found one flaw.

If x = 0, then the output is not correct.

@partha6369
Copy link

Customised for Indian Requirement

numbers2words <- function(x){
  helper <- function(x){
    if (x < 0) { print(paste(x, "is negative!")); return }
    
    digits <- rev(strsplit(as.character(x), "")[[1]])
    nDigits <- length(digits)
    if (nDigits == 1) as.vector(ones[digits])
    else if (nDigits == 2)
      convert2DigitNumbers(x)
    else if (nDigits == 3) 
      convert3DigitNumbers(x)
    else if (nDigits == 4 || nDigits == 5) 
      convertThousands(x)
    else if (nDigits == 6 || nDigits == 7) 
      convertLakhs(x)
    else if (nDigits == 8 || nDigits == 9) 
      convertCrores(x)
    else
      trim(paste(numbers2words(floor(x/10000000)), "crore", convertCrores(x %% 10000000)))
  }

  convert2DigitNumbers <- function(x) {
    if ( x > 0 && x <= 99 ) {
      digits2DigitNumber <- rev(strsplit(as.character(x), "")[[1]])
      if (x <= 9) as.vector(ones[digits2DigitNumber])
      else if (x <= 19) as.vector(teens[digits2DigitNumber[1]])
      else {
        if (digits2DigitNumber[1][1] == "0") {
          trim(paste(tens[digits2DigitNumber[2]]))
        }
        else {
          trim(paste(tens[digits2DigitNumber[2]], as.vector(ones[digits2DigitNumber[1]])))
        }
      } 
    }
  } 
  
  convert3DigitNumbers <- function(x) {
    if ( x > 0 && x <= 999 ) {
      if ( x < 100 ) convert2DigitNumbers(x)
      else {
        digits3DigitNumber <- rev(strsplit(as.character(x), "")[[1]])
        trim(paste(ones[digits3DigitNumber[3]], "hundred and", 
                   convert2DigitNumbers(makeNumber(digits3DigitNumber[2:1]))))
      }
    }
  } 
  
  convertThousands <- function(x) {
    if ( x > 0 && x <= 99999 ) {
      if ( x < 1000 ) convert3DigitNumbers(x)
      else {
        digitsThousands <- rev(strsplit(as.character(x), "")[[1]])
        if ( x <= 9999 )
          trim(paste(ones[digitsThousands[4]], "thousand", 
                     convert3DigitNumbers(makeNumber(digitsThousands[3:1]))))
        else 
          trim(paste(convert2DigitNumbers(makeNumber(digitsThousands[5:4])), "thousand", 
                     convert3DigitNumbers(makeNumber(digitsThousands[3:1]))))  
      }
    }
  } 
  
  convertLakhs <- function(x) {
    if ( x > 0 && x <= 9999999 ) {
      if ( x < 100000 ) convertThousands(x)
      else {
        digitsLakhs <- rev(strsplit(as.character(x), "")[[1]])
        if ( x <= 999999 )
          trim(paste(ones[digitsLakhs[6]], "lakh", 
                     convertThousands(makeNumber(digitsLakhs[5:1]))))
        else 
          trim(paste(convert2DigitNumbers(makeNumber(digitsLakhs[7:6])), "lakh", 
                     convertThousands(makeNumber(digitsLakhs[5:1]))))  
      }
    }
  } 
  
  convertCrores <- function(x) {
    if ( x > 0 && x <= 999999999 ) {
      if ( x < 10000000 ) convertLakhs(x)
      else {
        digitsCrores <- rev(strsplit(as.character(x), "")[[1]])
        if ( x <= 99999999 )
          trim(paste(ones[digitsCrores[8]], "crore", 
                     convertLakhs(makeNumber(digitsCrores[7:1]))))
        else 
          trim(paste(convert2DigitNumbers(makeNumber(digitsCrores[9:8])), "crore", 
                     convertLakhs(makeNumber(digitsCrores[7:1]))))  
      }
    }
  } 
  
  trim <- function(text){
    #Tidy leading/trailing whitespace, space before comma
    text=gsub("^\ ", "", gsub("\ *$", "", gsub("\ ,",",",text)))

    #Clear any trailing " and"
    gsub(" and$","",text)
  }
  
  makeNumber <- function(...) as.numeric(paste(..., collapse=""))     
  
  #Disable scientific notation
  opts <- options(scipen=100) 
  on.exit(options(opts)) 
  
  ones <- c("zero", "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)
  if (length(x) > 1 && x >= 0) return(trim(sapply(x, helper)))

  helper(x)
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment