Skip to content

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

Open

Description

@friendly

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 

Activity

  1. john-d-fox commented on Oct 23, 2024

    @john-d-fox
    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.

  2. friendly commented on Oct 23, 2024

    @friendly
    OwnerAuthor

    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.

  3. john-d-fox commented on Oct 23, 2024

    @john-d-fox
    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).

  4. john-d-fox commented on Oct 23, 2024

    @john-d-fox
    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).

  5. friendly commented on Oct 23, 2024

    @friendly
    OwnerAuthor

    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?

  6. philchalmers commented on Oct 23, 2024

    @philchalmers
    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.

  7. john-d-fox commented on Oct 24, 2024

    @john-d-fox
    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.

  8. friendly commented on Oct 24, 2024

    @friendly
    OwnerAuthor

    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

  9. john-d-fox commented on Oct 24, 2024

    @john-d-fox
    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

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions