matlib
matlib copied to clipboard
plotEqn() - can't supply labels for lines
In dev/plotEqn-test.R, I've tried to supply my own labels for lines, b/c I want to use x and y for the variables and simplify the equations to the form y = a + b * x. I have a test version in dev/plotEqn.R, where I'd like to also add options to control the solution points, allowing the argument solution = list(pch =, cex=, col=)
(This is for an example where I'd like to show the duality of points and lines in data / beta space.)
A <- matrix(c( 1, 2, 0,
-1, 2, 1), 3, 2) |> print()
b <- c(2, 1, 1)
# works OK
plotEqn(A, b, vars = c("x", "y"))
# try to change the labels: doesn't work
plotEqn(A, b, vars = c("x", "y"),
labels = c("y = x - 2",
"y = 1/2 - x",
"y = 1"))
The lines appear, but not the labels
I can't see what is wrong with the relevant code in the function (around line 132)
if (!is.null(labels)) {
xl <- if(A[i, 2] == 0) b[i] else x[1]
label <- parse(text=sub("=", "==", labels[i]))
text(xl, y[1], label, col=col[i], pos=4)
}
Related to this is the fact that the simplify argument doesn't simplify as much as I'd like:
> showEqn(A, b, vars = c("x", "y"), simplify = TRUE)
x - 1*y = 2
2*x + 2*y = 1
0*x + y = 1
It would be nicer if this gave (aligned)
x - y = 2
2*x + 2*y = 1
y = 1
I took a look at this a couple of days ago and have thought about it a bit since. I think that the simplify option could use a complete rewrite, perhaps separating the signs, coefficients, and variables. I don't know when I'd have a chance to attempt that.
Just to be clear: the figure I'm trying to create is the left panel of that below, illustrating the duality between lines in data space and points in $\beta$ space.
I did this from scratch, starting from
# Equations as intercepts & slopes in data space
x <- c(-2, .5, 1)
y <- c( 1, -1, 0)
# plot lines in data space
plot(0,0, type ="n",
xlab = "x",
ylab = "y",
xlim = c(-1, 4), ylim = c(-3, 2),
cex.lab = 2, asp = 1,
main = "Data space")
abline(h = 0, v = 0, col = "gray")
for (i in seq_along(x)) {
abline(x[i], y[i], col = col[i], lwd = 2)
}
...
then adding annotations of the simplified y = ... form of the equations, calculating the intersections of lines, etc., all because I couldn't do this with plotEqn().
Not a big hurry to fix the simplify problem, now that I've done this graph, but it would be nice to fix at least the bug with labels.
Yes, the two problems were clear to me: (1) You can't get user-supplied labels with plotEqn(); (2) the automatic labels are poorly simplified. I was addressing (2), which I think I could improve (but don't know when). I didn't see the source of (1).
Here's a first attempt at simplifying 2-variable equations, which could be used in showEqn() and plotEqn():
simplifyEqn <- function(A, b,
digits=min(3, getOption("digits") - 3),
vars = c("x1", "x2"),
align=TRUE){
signs <- ifelse(A[, 2] < 0, " - ",
ifelse(A[, 2] == 0, " ", " + "))
signs[A[, 1] == 0 & A[, 2] > 0] <- " "
A[, 2] <- abs(A[, 2])
vars1 <- rep(vars[1], nrow(A))
vars2 <- rep(vars[2], nrow(A))
vars1[A[, 1] == 0] <- paste(rep(" ", nchar(vars[1])), collapse="")
vars2[A[, 2] == 0] <- paste(rep(" ", nchar(vars[2])), collapse="")
AA <- format(signif(A, digit=digits))
AA[A[, 1] == 0, 1] <- paste(rep(" ", nchar(A[1, 1])), collapse="")
AA[A[, 2] == 0, 2] <- paste(rep(" ", nchar(A[2, 1])), collapse="")
AA[A[, 1] == 1, 1] <- paste(rep(" ", nchar(A[1, 1])), collapse="")
AA[A[, 2] == 1, 2] <- paste(rep(" ", nchar(A[2, 1])), collapse="")
bb <- format(signif(b, digits==digits))
eqns <- paste0(AA[, 1], " ", vars1, signs, AA[, 2],
" ", vars2, " = ", bb)
if (align) eqns else gsub(" +", " ", eqns)
}
For example,
> A <- matrix(c(0, 1, -2, 0, -3, -2, 10, -20, 5, 5), 5, 2, byrow=TRUE)
> b <- c(-1, 0, 2, 4, -10)
> simplifyEqn(A, b)
[1] " x2 = -1" "-2 x1 = 0"
[3] "-3 x1 - 2 x2 = 2" "10 x1 - 20 x2 = 4"
[5] " 5 x1 + 5 x2 = -10"
> simplifyEqn(A, b, align=FALSE)
[1] " x2 = -1" "-2 x1 = 0" "-3 x1 - 2 x2 = 2"
[4] "10 x1 - 20 x2 = 4" " 5 x1 + 5 x2 = -10"
I don't see how you can make something like this work with expression(), which can't simply take a character string as an argument (and produce a proper graphed equation) and must follow proper R syntax (which, I suspect, is why the current version doesn't work right).
This looks nice, and would help certainly in showEqn().
It would also help for my use case, but I think I see where the expression() problem comes from:
if (!is.null(labels)) {
xl <- if(A[i, 2] == 0) b[i] else x[1]
label <- parse(text=sub("=", "==", labels[i]))
text(xl, y[1], label, col=col[i], pos=4)
}
This assumes that the variables have been mapped from x1 to expressions, x[1], which is desirable in the case that
(a) no vars= has been given, so the variables become expressions, if (missing(vars)) vars <- c(expression(x[1]), expression(x[2]))
(b) no labels= has been given, so the equation labels become the result of showEqn(A, b, vars, simplify=TRUE)
> A<- matrix(c(1,2,3, -1, 2, 1),3,2)
> b <- c(2,1,3)
> showEqn(A, b)
1*x1 - 1*x2 = 2
2*x1 + 2*x2 = 1
3*x1 + 1*x2 = 3
> plotEqn(A,b)
x[1] - 1*x[2] = 2
2*x[1] + 2*x[2] = 1
3*x[1] + x[2] = 3
Unless @philchalmers objects, I suggest to add your simplifyEqn(), then consider how to fix plotEqn() in the case that triggered this issue, namely when equation labels are specified.
In this case, the function could just treat them as given; it would be up to the user to decide whether to use expressions in them.
Does that sound workable?
I don't have an objection to improving the simply logic as I agree there's room for improvement, though I'm wondering about changing the output focus of showEqn() now that TeX rendering is available and has become a major focus. For instance, a more natural rendering approach at this point is to use
A <- matrix(c( 1, 2, 0,
-1, 2, 1), 3, 2)
b <- c(2, 1, 1)
showEqn(A,b,latex=TRUE) |> Eqn()
which looks fine (though I'd prefer if Eqn() were called within showEqn() when latex=TRUE to avoid the pipe), but then the simplify argument makes the output awful.
showEqn(A,b,latex=TRUE,simplify=TRUE) |> Eqn()
Obviously this needs to be fixed too, though given the new focus on TeX instead of ASCII outputs should showEqn() be modified to be TeX driven instead? The ASCII and TeX approaches are not particularly complimentary.... just trying to get ahead of future issues.
Maybe I'm missing something, but isn't the fundamental problem that although character strings can occur within an expression, character strings aren't expressions? Try, e.g.,
plot(0:1, 0:1, type="n")
text(0.1, 0.7, paste("0 ==", expression(x[2])), adj=0)
text(0.1, 0.3, expression("0" == x[2]), adj=0)
It is possible to render LaTeX in plots.
I agree with @philchalmers that Eqn() makes the rendering of showEqn() nicer, but that's not the point here.
The issue is being able to use equations as line labels in plotEqn(), particularly when they are supplied as input to the labels arg
To elaborate my previous examples slightly, compare
plot(0:1, 0:1, type="n")
text(0.1, 0.1, paste("0 ==", expression(x[2])), adj=0)
text(0.1, 0.3, expression("0" == x[2]), adj=0)
text(0.1, 0.5, eval(parse(text="expression(0 == x[2])")), adj=0)
text(0.1, 0.7, latex2exp::TeX("$0 = x_2$"), adj=0)