Creating an Expression Tree

hlsmith

Less is more. Stay pure. Stay poor.
#1
I am trying to get this not to look like garbage. Any suggestions? Stole most of the code from the internet (partially from some raptor sympathizer named @trinker )

Code:
parseTree <- function(string, ignore=c('(',')','{','}'), ...) {
  dat <- utils::getParseData(parse(text=string))
  g <- parser2graph(dat[!(dat$text %in% ignore), ])
  leaves <- V(g)[!degree(g, mode='out')]                             # tree leaves
  preds <- sapply(leaves, neighbors, g=g, mode="in")                 # their predecessors
  vertex_attr(g, 'label', preds) <- vertex_attr(g, 'label', leaves)  # bump labels up a level
  g <- g - leaves                                                    # remove the leaves
  gaps <- V(g)[!nchar(vertex_attr(g, 'label'))]                      # gaps where ()/{} were
  nebs <- c(sapply(gaps, neighbors, graph=g, mode='all'))            # neighbors of gaps
  g <- add_edges(g, nebs, color='blue')                              # edges around the gaps
  g <- g - V(g)[!nchar(vertex_attr(g, 'label'))]                     # remove leaves/gaps
  plot(g, layout=layout.reingold.tilford, ...)
  title(string, cex.main=2.5)
}

Code:
string = "(2*Na) + (Glucose / 18) + (BUN / 2.8)"

library(igraph)
string <- "(a/{5})+(2*b+c)"

parseTree(string,  # plus some graphing stuff
          vertex.color="#FCFDBFFF", vertex.frame.color=NA,
          vertex.label.font=2, vertex.label.cex=2,
          vertex.label.color="darkred", vertex.size=25,
          asp=.8, edge.width=2, margin= -0.9)
1629318366164.png
 
Last edited:

hlsmith

Less is more. Stay pure. Stay poor.
#2
I created one of these using daggity as well, but that isn't what it is for and I couldn't use the same text in more than one node, so two '/' would cause issues.

P.S., obviously the code does some weird sizing formatting that I don't understand in the code from Post #1. And that output is from me making the plot window in RStudio shaped like a long rectangle.

P.S.S., This is actually a tame version of some of the other expressions I would like to display.
 

hlsmith

Less is more. Stay pure. Stay poor.
#3
a little better

1629320386118.png


Code:
parseTree <- function(string, ignore=c('(',')','{','}'), ...) {
  dat <- utils::getParseData(parse(text=string))
  g <- parser2graph(dat[!(dat$text %in% ignore), ])
  leaves <- V(g)[!degree(g, mode='out')]                             # tree leaves
  preds <- sapply(leaves, neighbors, g=g, mode="in")                 # their predecessors
  vertex_attr(g, 'label', preds) <- vertex_attr(g, 'label', leaves)  # bump labels up a level
  g <- g - leaves                                                    # remove the leaves
  gaps <- V(g)[!nchar(vertex_attr(g, 'label'))]                      # gaps where ()/{} were
  nebs <- c(sapply(gaps, neighbors, graph=g, mode='all'))            # neighbors of gaps
  g <- add_edges(g, nebs, color='black')                              # edges around the gaps
  g <- g - V(g)[!nchar(vertex_attr(g, 'label'))]                     # remove leaves/gaps
  plot(g, layout=layout.reingold.tilford, ...)
}


string = "(2*Na) + (Glucose / 18) + (BUN / 2.8)"


parseTree(string,  # plus some graphing stuff
          vertex.color="white", 
          vertex.frame.color="black",
          vertex.label.font=1, 
          vertex.label.cex=1.3,
          vertex.label.color="black", 
          vertex.size=40,
          asp=1, 
          edge.width=2, 
          margin= -0.46,
          main=NULL)
 

Attachments

Last edited:

hlsmith

Less is more. Stay pure. Stay poor.
#4
Any suggestions on how to turn Na into Na^+ or Na**ion, with the later parts obviously being superscripts. The above code treats any mathematical operator as a mathematical operator in the tree.
 

Dason

Ambassador to the humans
#6
I think I tracked down where you were pulling from. I modified the parseTree function to take in a named vector of expressions which will do some replacements. There's probably a better way to attack the problem but this seems like a simple enough solution.

C-like:
library(igraph)

parser2graph <- function(y, ...){
  y$new.id <- seq_along(y$id)
  h <- graph.tree(0) + vertices(id = y$id, label= y$text)
  for(i in 1:nrow(y)){
    if(y[i, 'parent'])
      h <- h + edge(c(y[y$id == y[i, 'parent'], 'new.id'], y[i, 'new.id']))
  }
  h <- set_edge_attr(h, 'color', value='black')
  return(h)
}

parseTree <- function(string, ignore=c('(',')','{','}'), replace_values = NULL, ...) {
  dat <- utils::getParseData(parse(text=string))
  g <- parser2graph(dat[!(dat$text %in% ignore), ])
  leaves <- V(g)[!degree(g, mode='out')]                             # tree leaves
  preds <- sapply(leaves, neighbors, g=g, mode="in")                 # their predecessors
  vertex_attr(g, 'label', preds) <- vertex_attr(g, 'label', leaves)  # bump labels up a level
  
  #### This if statement is what I added. 
  #### It's not particularly clean and there's probably a better way 
  #### to do the replace. Just not thinking great today
  if(!is.null(replace_values)){
    lab <- vertex_attr(g, 'label', preds)
    idx <- match(lab, names(replace_values))
    replace_id <- which(!is.na(idx))
    lab[replace_id] <- replace_values[idx[replace_id]]
    vertex_attr(g, 'label', preds) <- lab
  }
  
  g <- g - leaves                                                    # remove the leaves
  gaps <- V(g)[!nchar(vertex_attr(g, 'label'))]                      # gaps where ()/{} were
  nebs <- c(sapply(gaps, neighbors, graph=g, mode='all'))            # neighbors of gaps
  g <- add_edges(g, nebs, color='black')                              # edges around the gaps
  g <- g - V(g)[!nchar(vertex_attr(g, 'label'))]                     # remove leaves/gaps
  plot(g, layout=layout.reingold.tilford, ...)
}


string = "(2*Na) + (Glucose / 18) + (BUN / 2.8)"
#### vals is the named vector of expressions.  The name in the vector will
#### be replaced with whatever expression is given.
vals <- c(Na = expression("Na"**"ion"), Glucose = expression("Glucose"[2]))


parseTree(string,  # plus some graphing stuff
          replace_values = vals,
          vertex.color="white", 
          vertex.frame.color="black",
          vertex.label.font=1, 
          vertex.label.cex=1.3,
          vertex.label.color="black", 
          vertex.size=40,
          asp=1, 
          edge.width=2, 
          margin= -0.46,
          main=NULL)
 

hlsmith

Less is more. Stay pure. Stay poor.
#7
Yeah, I think I was using and defining the function, because the package would not install. I'll check this out this afternoon. I had went to a network analysis workshop once awhile back and remember kind of playing with igraph.