rcorty
rcorty

Reputation: 1200

How can I convert between numeral systems in R?

I see that there is a built-in function for the...rather abstract...use of transforming from the a decimal representation to roman numerals (Convert roman numerals to numbers in R), but I'm not able to find a built-in way to convert from decimal to other, similar, systems, like base 3 or base 11. I see that binary and hex (base 16) are well supported, but is there a package for converting to/from an arbitrary positional numeral system.

I could make such a package, but I suspect it already exists and my google-fu is just falling short?

Upvotes: 5

Views: 1368

Answers (1)

Allan Cameron
Allan Cameron

Reputation: 174328

You could write your own S3 class:

base <- function(b, base = 10)
{
  base <- as.integer(base)
  if(base > 36 | base < 2) stop("'base' must be between 2 and 36.")
  
  structure(lapply(b, function(x) 
    {
      n   <- ceiling(log(x, base))
      vec <- numeric()
      val <- x
      
      while(n >= 0)
      {
        rem <- val %/% base^n
        val <- val - rem * base^n
        vec <- c(vec, rem)
        n <- n - 1
      }
      
      while(vec[1] == 0 & length(vec) > 1) vec <- vec[-1]
      structure(x, base = base, representation = vec) 
    }), class = "base")
}

Which will need a format and print method:

format.base <- function(b, ...) 
{
  sapply(b, function(x) 
    {
      glyphs <- c(0:9, LETTERS)
      base   <- attr(x, "base")
      vec    <- attr(x, "representation")
      paste0(glyphs[vec + 1], collapse = "")
    })
}

print.base <- function(b, ...) print(format(b), quote = FALSE)

We also need to make sure that maths operations work properly:

Ops.base <- function(e1, e2) {
  base <- attr(e1[[1]], "base")
  e1   <- unlist(e1)
  e2   <- unlist(e2)
  base(NextMethod(.Generic), base)
}

Math.base <- function(e1, e2) {
  base <- attr(e1[[1]], "base")
  e1   <- unlist(e1)
  e2   <- unlist(e2)
  base(NextMethod(.Generic), base)
}

And if you want to use it inside a data frame you need an as.data.frame method:

as.data.frame.base <- function(b, ...) 
{
  structure(list(b),  
            class = "data.frame", 
            row.names = seq_along(b))
}

Which all allows the following behaviour:

data.frame(binary = base(1:20, 2), hex = base(1:20, 16), oct = base(1:20, 8))
#>    binary hex oct
#> 1       1   1   1
#> 2      10   2   2
#> 3      11   3   3
#> 4     100   4   4
#> 5     101   5   5
#> 6     110   6   6
#> 7     111   7   7
#> 8    1000   8  10
#> 9    1001   9  11
#> 10   1010   A  12
#> 11   1011   B  13
#> 12   1100   C  14
#> 13   1101   D  15
#> 14   1110   E  16
#> 15   1111   F  17
#> 16  10000  10  20
#> 17  10001  11  21
#> 18  10010  12  22
#> 19  10011  13  23
#> 20  10100  14  24

And:

x <- base(67, 11)
y <- base(35, 2)
x + y
#> [1] 93

base(x + y, 10)
#> [1] 102

Upvotes: 11

Related Questions