metrumresearchgroup / mrgsolve

Simulate from ODE-based population PK/PD and QSP models in R
https://mrgsolve.org
129 stars 36 forks source link

[DOC] How to annotate blocks with Rmd? #491

Open billdenney opened 5 years ago

billdenney commented 5 years ago

I love the new ability to use Rmd files!

One thing that wasn't clear from the info available is how to add modifiers like @annotated to the block. After a bit of playing, I found that I can just add it to the first line of the block like the following. Can you please add that to the manual?

```{cmt}
@annotated
CENT : Central (mg)
P1   : Peripheral compartment (mg)

My first attempt was to add it to the header like:

or

```{cmt @annotated}
kylebaron commented 5 years ago

Thanks, Bill. I will add it to the documentation.

I did spend some time fiddling around with (better) ways to specify the block options, including what you did in the second example. I ended up ditching the approach and keeping the simple formulation. But hoping to work it out in the future and just pass all the block options in as chunk options.

KTB

billdenney commented 5 years ago

How about this?

modelparse_rmd <- function(txt, split=FALSE, drop_blank=TRUE, 
                           comment_re = "//") {

  if(split) txt <- strsplit(txt,"\n",perl=TRUE)[[1]]

  if(drop_blank) txt <- txt[!grepl("^\\s*$",txt)]

  for(comment in comment_re) {
    m <- as.integer(regexpr(comment,txt,fixed=TRUE))
    w <- m > 0
    txt[w] <- substr(txt[w], 1, m[w]-1)
  }
  start_re <- "^```\\{.*\\}\\s*"
  end_re <- "^\\s*```\\s*$"
  start <- grep(start_re, txt)
  end <- grep(end_re, txt)
  ans <- vector("list", length(start))
  for(i in seq_along(start)) {
    ans[[i]] <- txt[seq(start[i], end[i])]
    ans[[i]] <- gsub("^```\\{\\s*(r|c) +", "\\{", ans[[i]])
    ans[[i]] <- sub("^```\\{", "\\{", ans[[i]])
    ans[[i]] <- sub("```", "", ans[[i]])
  }
  chunk <- sapply(ans, "[[", 1)
  lab <- trimws(gsub("\\{|\\}", "", chunk))
  # Split by any combination of space, comma, space where at least one of those
  # is required, but the rest are not required.
  sp <- strsplit(lab, "(\\s+|,\\s+|\\s+,|\\s+,\\s+)")
  label <- sapply(sp, "[", 1L)
  label <- strsplit(label, "-", fixed = TRUE)
  label <- sapply(label, "[", 1L)
  # Find the options and reconnect them with a space
  opts <- sapply(X=lapply(sp, "[", -1L), FUN=paste, collapse=" ")
  for(i in seq_along(label)) {
    # Drop the start and ending markers
    ans[[i]] <- ans[[i]][-c(1, length(ans[[i]]))]
    # Add the options (if present)
    if (opts[i] != "") {
      ans[[i]] <- c(opts[i], ans[[i]])
    }
  }
  names(ans) <- toupper(label)
  dropR <- names(ans)=="R"
  if(any(dropR)) {
    ans <- ans[!dropR]  
  }
  return(ans)
}

It extracts everything puts the options together with a single space between them, and then puts that as the first row. (I'm happy to make a PR if it is what you're looking for.)

kylebaron commented 5 years ago

Thanks, BIll. That's more or less the code that I had developed to tuck block options into the chunk. The sticky part is what happens after that. There is some nuance that needs to be addressed and I just want to think through some more.

billdenney commented 5 years ago

Fair enough, I did see that you had something very close to that in the code. Let me know if I can help.