Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

plotEqn() - can't supply labels for lines #68

Open
friendly opened this issue Oct 20, 2024 · 9 comments
Open

plotEqn() - can't supply labels for lines #68

friendly opened this issue Oct 20, 2024 · 9 comments
Labels

Comments

@friendly
Copy link
Owner

friendly commented Oct 20, 2024

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

image

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 
@friendly friendly added the bug label Oct 23, 2024
@john-d-fox
Copy link
Collaborator

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.

@friendly
Copy link
Owner Author

friendly commented Oct 23, 2024

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.

dual-points-lines

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.

@john-d-fox
Copy link
Collaborator

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).

@john-d-fox
Copy link
Collaborator

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).

@friendly
Copy link
Owner Author

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?

@philchalmers
Copy link
Collaborator

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()

image

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()

image

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.

@john-d-fox
Copy link
Collaborator

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.

@friendly
Copy link
Owner Author

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

@john-d-fox
Copy link
Collaborator

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)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants