Ryacas substitute but don't simplify or evaluate - r

I am using the Ryacas package in R and am trying to substitute variables for values but not simplify or solve the equation to show the working out. I have searched Stackoverflow, read the Ryacas documentation and have attempted to find this in the Yacas manual but have so far come up short. I think I am looking to turn simplification off to get the substituted equation and then on again to provide the final result.
Here is an example which provides only the result rather than the working:
library(Ryacas)
# a <- 2
# b <- 3
# c <- 4
eq <- ysym(('(a * b) / c'))
solution <- with_value(with_value(with_value(eq, 'a', 2), 'b', 3), 'c', 4)
tex(solution)
# "\\frac{3}{2}"
What I am trying to get as an output is:
# working out
# "\\frac{2 \times 3}{4}
as well as the actual solution:
# solution
# "\\frac{3}{2}"
Does anyone know whether there is a solution to this problem such as passing a command to yacas through yac_str or similar. I have tried translating to latex using the tex() command and then substituting after though the multiplication operators are removed and this means I need to find and replace them which becomes nasty when dealing with symbolics:
tex(eq)
# "\\frac{a b}{c}"
# substituting string values using stringi requires additional
# work to deal with the missing `*` between `a` and `b`
# "\\frac{2 3}{4}"
Whilst this can be done for simple expressions, there are numerous exceptions such as the variables a and c being present in \\frac etc.
I have also tried the TexForm command and substitute in various guises but am still not able to capture the unsimplified and unevaluated equation:
y_fn(substitute(with_value(eq, 'a', 2)), "TeXForm")
# "\\frac{2 b}{c}"
eval(substitute(with_value(eval(substitute(with_value(eq, 'a', 2))), 'b', 3)))
# y: 6/c
Any help appreciated.

You might need to customize this some more but this general approach can work if you are willing to do that.
We translate words to their values using gsubfn and handle the \times part at the end with gsub. Note that frac is not modified because we are matching words and frac it is not a word in the list given in the second arg to gsubfn.
library(magrittr)
library(gsubfn)
library(Ryacas)
eq <- ysym(('(a * b) / c'))
eq %>%
tex %>%
gsubfn("(\\w+)", list(a = 2, b = 3, c = 4), .) %>%
gsub("(\\d) (\\d)", "\\1 \\\\times \\2", .)
## [1] "\\frac{2 \\times 3}{4}"
Added
Take the first three code examples when searching for [r] Ryacas in stackoverflow and it worked on all of them. Note that these used the original version of Ryacas which is currently called Ryacas0 so I used that.
library(Ryacas0)
library(gsubfn)
library(magrittr)
tex_sub <- function(.x, ...) {
.x %>%
gsubfn("(\\w+)", list(...), .) %>%
gsub("(\\d) (\\d)", "\\1 \\\\times \\2", .)
}
# https://stackoverflow.com/questions/21858668/symbolic-matrix-multiplication-by-ryacas
x <- Sym("x")
mat1 <- List(
List(x, 2),
List(x^3, x))
mat2 <- List(
List(x, x),
List(3, 6 * x))
tt <- TeXForm(mat1 * mat2)
tex_sub(tt, x = 1)
###
# https://stackoverflow.com/questions/22739173/matrix-transpose-in-ryacas
u=Sym("u")
v=Sym("v")
w=Sym("w")
DG=List(List(w-v), List(u-w), List(v-u))
tt2 <- TeXForm(DG)
tex_sub(tt2, u = 2, v = 3, w = 4)
###
# https://stackoverflow.com/questions/49572184/how-to-derivate-using-ryacas
x <- Sym("x")
P <- Sym(1)
for (k in 1:3) {
P <- Simplify((1+k*x)*P + x*(1-x)*deriv(P, x))
print(P)
}
tt3 <- TeXForm(P)
tex_sub(tt3, x = 10)

Related

How to use character as variable name in arguments? [R]

I am wondering how to tell R the string I used in the arguments of a function stands for a variable instead of the string itself. Specifically, I am dealing with 'Dict' package and run the following code as a trial.
library(Dict)
x = c(1,2)
d = dict(x = 1)
d$keys
# 'x'
What I want is c(1,2) to be the key instead of 'x'. The same problem goes for 'list', for example
y = 'happy'
l = list(y = 1)
names(l)
The result would be 'y'. Moreover, one can solve this situation by
names(l) <- y, but I don't think this is very efficient and clean.
Anyone has any idea how to do this in a one-line code? Thanks!
We could use setNames
out <- do.call(dict, setNames(rep(list(1), length(x)), x))
out$keys
[1] "1" "2"
Or we may use invoke or exec
library(purrr)
out <- invoke(dict, setNames(rep(1, length(x)), x))
out <- exec(dict, !!!setNames(rep(1, length(x)), x))
For the second case, also setNames works
setNames(list(1), y)
$happy
[1] 1
or we can use dplyr::lst
dplyr::lst(!! y := 1)
$happy
[1] 1

Margrittr pipe with matrix operations in R

I'm working on some functions that take a matrix as input and provide a matrix as output. Is it possible to use the magrittr pipe with matrices without using the . placeholder? Ideally, I'd like these functions to be piped into each other like a dplyr chain. The issue is that I'm constantly forgetting to specify the . placeholder and getting errors.
library(magrittr)
set.seed(123)
m <- matrix(rnorm(10), ncol = 2)
# This works perfectly:
layout_align_x <- function(n = nodes, anchor, m = matrix){
m[n, 1] <- m[anchor, 1]
return(m)}
# This also works perfectly:
layout_align_x(c(1,2), 3, m)
# And this also:
m %>% layout_align_x(c(1,2), 3, .)
# This returns error:
m %>% layout_align_x(c(1,2), 3)
#Error in m[anchor, 1] : incorrect number of dimensions
# The goal is:
m %>%
layout_align_x(c(1,2), 3) %>%
layout_align_x(c(3,4), 5)
Change your function to
layout_align_x <- function(m = matrix, n = nodes, anchor){
m[n, 1] <- m[anchor, 1]
return(m)
}

using expand.grid with objects

I have the following vectors and a combined data frame which are objects feed to the expresion below.
x <- c(1,2,3,4)
y <- c(5,6,7,8)
z <- c(9,10,11,12)
h <- data.frame(x,y,z)
D <- print (( rep ( paste ( "h[,3]" ) , nrow(h) )) , quote=FALSE )
# [1] h[,3] h[,3] h[,3] h[,3]
DD <- c ( print ( paste ( (D) , collapse=",")))
# "[1] h[,3],h[,3],h[,3],h[,3]"
DDD <- print ( DD, quote = FALSE )
# However when I place DDD in expand.grid it does not work
is(DDD)
[1] "character" "vector" "data.frameRowLabels" "SuperClassMethod"
Thus the expresion expand.grid(DDD) does not work. How could I get a process where I repeat n times a character element which represents an object as to obtain a vector of the number of repeated character elements which when placed in expand.grid works.
It looks like you are trying to generate some R code then execute it. For your case, this will work:
# From your question
DDD
# [1] "h[,3],h[,3],h[,3],h[,3]"
# The code that you wish to execute, as a string
my_code <- paste("expand.grid(", DDD, ")")
# [1] "expand.grid( h[,3],h[,3],h[,3],h[,3] )"
# Execute the code
eval(parse(text = my_code))
I really recommend against doing this. See here for some good reasons why eval(parse(text = ...)) is a bad idea.
A more "R" solution to accomplish your task:
# Generate the data.frame, h
x <- c(1,2,3,4)
y <- c(5,6,7,8)
z <- c(9,10,11,12)
h <- data.frame(x,y,z)
# Repeat the 3rd column 3 times, then call expand.grid
expand.grid(rep(list(h[,3]), times = 3))
# Alternatively, access the column by name
expand.grid(rep(list(h$z), times = 3))
By the way, I recommend looking at the help files for expand.grid - they helped me reach a solution to your problem quite quickly after understanding the arguments for expand.grid.

Getting an R expression from a value (similar to enquote)

Assume I have a value x which is of some (unknown) type (especially: scalar, vector or list). I would like to get the R expression representing this value. If x == 1 then this function should simply return expression(1). For x == c(1,2)) this function should return expression(c(1,2)). The enquote function is quite near to that what I want, but not exactly.
By some playing around I found the following "solution" to my problem:
get_expr <- function(val) {
tmp_expr <- enquote(val)
tmp_expr[1] <- quote(expression())
return(eval(tmp_expr))
}
get_expr(1) # returns expression(1)
get_expr(c(1, 2)) # returns expression(c(1, 2))
get_expr(list(x = 1)) # returns expression(list(x = 1))
But I think my get_expr function is some kind of hack. Logically, the evaluation should not be necessary.
Is there some more elegant way to do this? As far as I see, substitute does not really work for me, because the parameter of my get_expr function may be the result of an evaluation (and substitute(eval(expr)) does not do the evaluation).
I found another way via parse(text = deparse(val)), but this is even more a bad hack...
as.expression(list(...)) seems to do it:
> get_expr <- function(val) as.expression(list(val))
> str(get_expr(1))
expression(1)
> str(get_expr(c(1, 2)))
expression(c(1, 2))
> str(get_expr(list(x=1)))
expression(list(x = 1))
> val <- list(x=1, y=2)
> str(get_expr(val))
expression(list(x = 1, y = 2))
You can use substitute(), and just need to call it a bit differently:
express <- function(e) substitute(expression(x), env = list(x=e))
v1 <- c(1, 2)
express(v1)
# expression(c(1, 2))
v2 <- list(a = 1, b = 2)
express(v2)
# expression(list(a = 1, b = 2))

Alternatives to paste( ) to concatenate formatted text expressions on a graph?

I'm using pretty10exp() from the sfsmisc package to make the scientific notation look better. For example:
library(sfsmisc)
a <- 0.000392884
The output of pretty10exp() looks like this:
> pretty10exp(a, digits.fuzz=3) #round to display a certain number of digits
expression(3.93 %*% 10^-4)
I can use this to display the pretty version of a on a graph's title or axes as described in this post:
Force R to write scientific notations as n.nn x 10^-n with superscript
However, things get ugly again when I try to combine it with paste() to write a sequence of character strings like this:
# some data
x <- seq(1, 100000, len = 10)
y <- seq(1e-5, 1e-4, len = 10)
# default plot
plot(x, y)
legend("topleft", bty="n",legend=paste("p =", pretty10exp(a, digits.fuzz=3)))
Which gives me the following graph, so I suppose paste() is not able to handle formatted expressions of the kind that can be found in the output of pretty10exp():
Is there an alternative to paste() that I could use to combine the expressions "p =" and the scientific notation of pretty10exp()?
One solution is just to copy what pretty10exp() does, which for a single numeric, a, and the options you set/defaults, is essentially:
a <- 0.00039288
digits.fuzz <- 3
eT <- floor(log10(abs(a)) + 10^-digits.fuzz)
mT <- signif(a/10^eT, digits.fuzz)
SS <- substitute(p == A %*% 10^E, list(A = mT, E = eT))
plot(1:10)
legend("topleft", bty = "n", legend = SS)
The equivalent using bquote() would be
SS <- bquote(p == .(mT) %*% 10^.(eT))
Definitely not a precise answer, but I just play around with things for a while. bquote is nice once you get a feel for it.
> call("rep", 10, 7)
## rep(10, 7)
> bquote(.(call("rep", 10, 7)) * q^5)
## rep(10, 7) * q^5
> sprintf("paste('%smm'^'%s')", 5, 5)
## [1] "paste('5mm'^'5')"
And my personal favorite, which will of course return TRUE...
just as soon as I someone writes is.awesome.
> bquote( f <- function(x) { is.awesome(R) })
## f <- function(x) {
## is.awesome(R)
## }

Resources