How to convert equation from R code into readable text?

Viewed 66

I have defined multiple different functions, each containing one equation each, like so:

catalanFormula <- function(n){
    return( (factorial(2 * n)) / ((factorial(n + 1)) * factorial(n)) )
  }
triangularFormula <- function(n){
    return( (n * (n+1)) / 2 )
}

I am then plotting each function using the plot() function from base R. What I want to do is be able to include the equation in my plot as a label or text, but in a way that is easily understandable. I know I can use LaTeX to write each equation manually, but I was wondering if there is a package or method anyone knows of that can deparse a mathematical equation in R and turn it into a standard readable mathematical equation. The only math occurring is factorials, multiplication, division, exponents, addition, and subtraction.

For example, I want to convert

(factorial(2 * n)) / ((factorial(n + 1)) * factorial(n))

into something that looks like this. Then, be able to use the output within the text() function of base R plotting.

3 Answers

With the Ryacas package you can transform a math expression to its corresponding LaTeX code. But the factorial must be given as !:

library(Ryacas)
eq <- yac_symbol("(2 * n)! / ((n + 1)! * n!)")
tex(eq)
# "\\frac{\\left( 2 n\\right) !}{\\left( n + 1\\right) ! n!}"

Another possibility is to call Python from R and use the pytexit library:

from pytexit import py2tex
py2tex("math.factorial(2 * n) / ((factorial(n + 1)) * factorial(n))")
# $$\frac{\operatorname{factorial}\left(2n\right)}{\operatorname{factorial}\left(n+1\right) \operatorname{factorial}\left(n\right)}$$

You can also take a look at PyLaTeX and lax.

The expr2latex() function from simsalapar parses expressions to LaTeX, and can be extended to include the factorial symbol with a minor addition to the code:

expr2latex2 <- function(expr) {
  L <- length(expr)
  c.BinTable <- simsalapar:::c.BinTable
  if(!L) "" else {
    Symb <- is.symbol(expr)
    F <- if(Symb) expr else expr[[1]]
    cF <- simsalapar:::mDeparse(F)
    FF <- simsalapar:::renderAtom(F, Len=L, d.a = cF)
    if(Symb && L != 1)
      stop("is.symbol(.), but length(.) = ", L, " != 1")
    else if(!Symb && typeof(expr) != "language" && L != 1)
      stop("is not language nor symbol), but length(.) = ", L, " != 1")
     switch(L,
           ## length 1:
           FF,
           
           { ## length 2: e.g.  "- 1", "+ x", "!TRUE",  "~ ff",
             #browser()
             rhs <- expr2latex2(expr[[2]])
             if       (cF == "bold") paste0("\\mathbf{", rhs, "}")
             else if(cF == "italic") paste0("\\mathit{", rhs, "}")
             else if(cF == "factorial")  paste0("(",rhs,")!")   #extra case added in for factorial
             else if(!simsalapar:::isOp(cF)) # not a binary operator ==> "function call":
               paste0(FF,"(",rhs,")") ## e.g. "O(n)"
             else if(cF == "{") paste0("{", rhs, "}")
             else if(cF == "(") paste0("(", rhs, ")")
             else paste(FF, rhs)
           },
           
           { ## length 3:
             lhs <- expr2latex2(expr[[2]])
             rhs <- expr2latex2(expr[[3]])
             if(cF == "[") ## subscript
               paste0(lhs, "_{", rhs, "}")
             else if(cF == "~") ## space
               paste(lhs, "\\", rhs)
             ## not treated, as plotmath() does neither :
             ## else if(cF == "[[")
             ##     paste0(lhs, "[[", rhs, "]]")
             else if(cF %in% c.BinTable)
               paste(lhs, simsalapar:::getTab(cF, simsalapar:::BinTable), rhs)
             else if(cF %in% c.RelTable)
               paste(lhs, simsalapar:::getTab(cF, simsalapar:::RelTable), rhs)
             else if(simsalapar:::isOp(cF)) ## e.g.   U + x
               paste(lhs, FF, rhs)
             else ## log(x, 2)
               paste0(FF, "(", lhs, ",", rhs, ")")
           },
           
           ## length >=4 : F(a, b, c, ...)
           stop("length(expr) = ",L," (>= 4);  not yet implemented") # TODO MM
    )## end{switch}
  }
}

original function

expr2latex( quote( (factorial(2 * n)) / ((factorial(n + 1)) * factorial(n)) ) )
#[1] "(factorial(2   n)) / ((factorial(n + 1))   factorial(n))"

revised treatment of factorials

expr2latex2( quote( (factorial(2 * n)) / ((factorial(n + 1)) * factorial(n)) ) )
#[1] "((2   n)!) / (((n + 1)!)   (n)!)"

Here's a base R function that will walk the abstract syntax tree to replace the factorial and / functions with the corresponding ?plotmath markup so you can add them to R plots.


returnToPlotmath <- function(fun) {
  swap <- function(x) {
    if (class(x) %in% c("call","(")) {
      x <- as.list(x)
      if (as.character(x[[1]])=="/") {
        x[[1]] = quote(frac)
        x[[2]] = swap(x[[2]])
        x[[3]] = swap(x[[3]])
      } else if (as.character(x[[1]])=="factorial") {
        x[[1]] = quote(`*`)
        if (is.call(x[[2]])) {
          x[[2]] = as.call(list(quote(`(`), x[[2]]))
        } else {
          x[[2]] = swap(x[[2]])
        }
        x[[3]] = "!"
      } else if (as.character(x[[1]])=="*") {
        if(is.call(x[[2]]) | is.call(x[[3]])) {
          x[[1]] = quote(`%.%`)
          x[[2]] = swap(x[[2]])
          x[[3]] = swap(x[[3]])
        }
      } else {
        x[[2]] = swap(x[[2]])
        if (length(x)==3) x[[3]] = swap(x[[3]])
      }
      return(as.call(x))
    } else {
      return(x)
    }
  }
  body_exprs <- body(fun)[[2]]
  swap(body_exprs[[length(body_exprs)]])
}

This does assume that the return is the last statement in the function body {} block. You can get the expression with

returnToPlotmath(catalanFormula)
# frac(((2 * n) * "!"), (((n + 1) * "!") %.% (n * "!")))
returnToPlotmath(triangularFormula)
# frac((n %.% (n + 1)), 2)

And you can add them to plot titles and such

plot(main=returnToPlotmath(catalanFormula), 1, 1)
plot(main=returnToPlotmath(triangularFormula), 1, 1)

catalanFormula triangularFormula

This solution is highly specific to the functions you need to transform. But it could be extended if needed.

Related