Tomas
Tomas

Reputation: 59545

Convert roman numerals to numbers in R

In R, there is a great function as.roman in the very base setup:

as.roman(79)
# [1] LXXIX

Is there an inverse function that would convert roman numerals to numbers?

I know I can write it myself but I'd prefer to use already prepared or preferably standard library functions.

Upvotes: 32

Views: 12421

Answers (3)

Jilber Urbina
Jilber Urbina

Reputation: 61204

From as.roman code you can find .roman2numeric and its code can be seen if you run getAnywhere(".roman2numeric")

The code is:

function (x) 
{
  romans <- c("M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", 
              "IX", "V", "IV", "I")
  numbers <- c(1000L, 900L, 500L, 400L, 100L, 90L, 50L, 40L, 
               10L, 9L, 5L, 4L, 1L)
  out <- integer(length(x))
  ind <- is.na(x)
  out[ind] <- NA
  if (any(!ind)) {
    y <- toupper(x[!ind])
    y <- gsub("CM", "DCCCC", y)
    y <- gsub("CD", "CCCC", y)
    y <- gsub("XC", "LXXXX", y)
    y <- gsub("XL", "XXXX", y)
    y <- gsub("IX", "VIIII", y)
    y <- gsub("IV", "IIII", y)
    ok <- grepl("^M{,3}D?C{,4}L?X{,4}V?I{,4}$", y)
    if (any(!ok)) {
      warning(sprintf(ngettext(sum(!ok), "invalid roman numeral: %s", 
                               "invalid roman numerals: %s"), paste(x[!ind][!ok], 
                                                                    collapse = " ")), domain = NA)
      out[!ind][!ok] <- NA
    }
    if (any(ok)) 
      out[!ind][ok] <- sapply(strsplit(y[ok], ""), function(z) as.integer(sum(numbers[match(z, 
                                                                                            romans)])))
  }
  out
}

You can access to .roman2numeric and convert roman number to decimal numbers the way @rawr suggested in his/her comment.

> utils:::.roman2numeric("III")
[1] 3
> utils:::.roman2numeric("XII")
[1] 12
> utils:::.roman2numeric("LXXIX")
[1] 79

Upvotes: 13

jonrsharpe
jonrsharpe

Reputation: 122095

The roman numbers in R are, according to the docs:

objects of class "roman" which are internally represented as integers, and have suitable methods for printing, formatting, subsetting, and coercion to character.

You should therefore be able to get the integer value back using as.integer():

as.integer(as.roman(79)+as.roman(12))

Upvotes: 9

gung - Reinstate Monica
gung - Reinstate Monica

Reputation: 11903

as.roman() returns an object of class roman, so R recognizes it as such. You can directly turn it back into an Arabic numeral with as.numeric(). If you have a string that meets the criteria such that it could be a valid roman numeral, you can coerce it to a class roman object with as.roman(), and then coerce it into an Arabic numeral by composing the coercion functions. Consider:

> as.roman(79)
[1] LXXIX
> x <- as.roman(79)
> x
[1] LXXIX
> str(x)
Class 'roman'  int 79
> as.roman("LXXIX")
[1] LXXIX
> as.numeric(as.roman("LXXIX"))
[1] 79

Upvotes: 45

Related Questions