Sunday, June 3, 2012

Coding a dynamic systems and controlling it via a graphical user interface

My work, in the past year, has consisted mostly of coding dynamic models in R, models which I will soon be exporting to a server-based R implementation, possibly thanks to rApache.

I ususally run my models through an input file where I specify all parameters needed, but for the end users, we felt it may be better to provide a graphical user interface where they could select just a few parameters, with the others defaulting to meaningful values.

Wioth this post I want to quickly illustrate all that's needed to put such a system together, exception made for the rApache part. I've not made any steps in that direction, yet.

So, let's start by defining a dynamic model, which we'll integrate using the deSolve package. We use the Lorentz system, which is far simpler than any of the models I actually work with, and produces much more beautiful and interesting graphics, too.



OK, if you run this you'll obtain a variable called 'out', which contains the X/Y/Z coordinates of your system at different time instants in the phase space. You can look at the values directly, but obviously plotting is a good option. Taking advantage of the 'multiplot' function defined in the Cookbook for R, we can write:



Which will generate the following picture:
I did make use of the alpha channel to give some sense of depth to all pictures. I would love to plot a 3D version of the Lorenz Attractor in the fourth panel, lower right - however, I didn't want to get bogged down in defining a rotation / projection matrix.

Until now, there's no GUI - all this happens within the command line, or if you prefer a simple R script.

Unless, that is, you also define a gWidget which can actualy control your model, like this:

To draw this, you just need to type a few lines of code in your R script, plus some more functions to handle events (that is, you clicking the button or changing parameter values)

As a matter of fact, we can also embed the graphical output within the GUI window, either on the side of the controls, or in another tab. perhaps I'll update the post later on to reflect that.

11 comments:

  1. What a beautiful(!) post Luca, thank you :)

    ReplyDelete
  2. shameless plug: gridExtra::grid.arrange could save you the trouble of source()ing multiplot from a website.

    ReplyDelete
    Replies
    1. Thanks Baptiste!
      I knew there was more than one way to do it, though I didn\'t remember how yesterday. I was sure I used something else before, but I wanted to post this quickly so I relied on the multiplot function (which, in my code, I inserted in my 'useful.R' script I source at the begin.

      Delete
  3. Hi, thought I'd point you to gWidgetsWWW2 for the rapache bit. (install_github("gWidgetsWWW2", "jverzani")). The configuration is a bit of a chore, but here are some modifications to your code to make it run there. (The only real bit is using the gsvg driver, the rest is just a suggestion for shorter code):


    ## File for our grid.
    tmp <- get_tempfile(ext=".svg")

    RunModel <- function(...) {
    ## use Aer, Ber, Cer, timer to make graph
    vals <- lapply(list(Aer, Ber, Cer, timer), svalue) ## also svalue(flyt)

    svg(tmp)
    hist(rnorm(100)) ## your graphic here, I punted
    dev.off()
    svalue(cnv) <- tmp

    }

    ## GUI Layout
    win_ctrls <- gwindow("Lorenz controls")
    pane <- gpanedgroup(container=win_ctrls, default.size=250)

    cnv <- gsvg(container=pane)

    grp_ctrls <- ggroup(container = pane, horizontal = FALSE)

    ## Use form layout, not just a vertical box container
    flyt <- gformlayout(container=grp_ctrls)

    Aer<-gedit(-8/3, label="Aer", cont = flyt)
    Ber<-gedit("-10", label="Ber", cont = flyt)
    Cer<-gedit("28", label="Cer", cont = flyt)
    timer<-gspinbutton(value=100, from = 1, to = 1000, by = 1,
    horizontal = TRUE,
    label="timer", cont = flyt)


    # if you click a button, execute a function which runs the model and hopefully pl\
    ots it.

    run_me <- gbutton("run me!!!", container = grp_ctrls)
    ## add handlers
    sapply(list(Aer, Ber, Cer, timer, run_me), addHandlerChanged, handler=RunModel)

    ## initial graphic
    RunModel()




    You can see this bit running here:

    http://23.21.60.54/cgi-bin/R/app?app=luca1

    ReplyDelete
    Replies
    1. Thanks,

      I've had a look and will certainly try to implement my (real) models using gWidgetsWWW - as I may have mentioned in the post, the ease of transferring from GTK+ to WWW was one of the reason for going gWidgets. Unfortunately, I haven't yet made any attempt to get rApache running on our server...

      Perhaps I'll nag you later on with one or two questions :-)

      Delete
    2. Getting it all set up is a chore, but not too difficult. Note the "2" in the gWidgetsWWW2. It replaces gWidgetsWWW and (hopefully) will be better suited for web deployment. Installation is different, it only uses apache for a server and RServe and FastRWeb to link in R. However, for real performance of scalability you would want to code directly, as the ease of use comes at a big cost in speed.

      Delete
    3. I'm actually mulling over a webserver with computation in R, and visualisation in d3js (http://d3js.org)

      Delete
  4. What a shame, Luca in love with user interfaces! :)

    [guess who wrote this, old buddy!]

    ReplyDelete
  5. This looks great, and really useful, but there seem to be some issues with the code. First, there is at least a right parenthesis missing from the line

    parameters <- c(a = -8/3, b = -10, c = 28

    Second, the line

    times <- seq(0, svalue(timer), by = 0.001)

    generates an error:

    Error in seq.default(0, svalue(timer), by = 0.001) :
    could not find function "svalue"

    So I suppose a function is installed with another package not listed. I didn't try anything further, but I do note that

    source(file.path(Sys.getenv("USERPROFILE"),"Documents/My Dropbox/Software/useful.r"))

    isn't going to work for those of us without such a file or path or both. It may however resolve the svalue issue. Could it be posted?

    ReplyDelete
    Replies
    1. Sorry Michael,

      I fixed it. The svalue() refers to one of the gwidgets which at that early point aren't defined yet.

      I also closed that parenthesis.

      The 'useful.r call was there to define the multiplot function, which I reference to in the text. I'll change tat part too.

      Delete