```{css,echo=FALSE} .main-container { max-width: unset; margin: 50px; }
.column-container{ / background-color: hotpink / display:flex; flex-direction:row; justify-content:space-between;
} .column-container__column{ / background-color:green; / width:calc(25% - 24px); }
.Code { / background-color: red; / / background-color: #a3edd5; / background-color: #c4ffec; }
<!-- Todo: --> <!-- merge.by.row=F --> <!-- better explanation for flagsAssign --> ```r ##knitr::opts_chunk$set(dev = "cairo_pdf") knitr::opts_chunk$set( collapse = TRUE ,comment = "#>" ,fig.width=7 ,cache=FALSE ,class.source="Code" ) library(data.table) library(NMdata) ## library(devtools) ## load_all() NMdataConf(as.fun="data.table" ,check.time=F) library(ggplot2) theme_set(theme_bw()+theme(legend.position="bottom")) ## this change data.table syntax. I think we can do without. ## knitr::opts_chunk$set(tidy.opts=list(width.cutoff=60), tidy=TRUE) pk <- readRDS(file=system.file("examples/data/xgxr2.rds",package="NMdata")) ## pk[,trtact:=NULL] covs <- unique(pk[,.(ID,WEIGHTB)]) pk[,WEIGHTB:=NULL] set.seed(1) covs2 <- covs[,.(ID,race=sample(c("caucasian","black"),size=.N,replace=T))]
htmltools::img(src = knitr::image_uri("apple-touch-icon-180x180.png"), alt = 'logo', style = 'position:absolute; top:15px; right:70px; padding:0px; width:150px')
:::: {style="display: flex;" class="column-container"} ::: {class="column-container__column"}
Built r Sys.Date()
using NMdata r packageVersion("NMdata")
.
Please make sure to see latest version available here.
This cheat sheet is intented to provide an overview and remind of command names. Please refer to other vignettes for more details on specific topics and individual manual pages for details on the functions.
The order of the contents loosely follows a workflow example (except for the configuration part). The steps can be applied in any order and independently from each other.
install.packages("NMdata") library(NMdata)
We have read some source data files and need to combine them into one data set for NONMEM. Key steps are stacking data sets (like doses, samples, and simulation records) and adding additional information such as covariates. We often use rbind
and merge or join operations for these steps. NMdata helps explore how to do these steps and ensure that merge/join results are as expected.
compareCols
- Compare presence and classes of columns across data sets before merging or stacking.
compareCols(covs,covs2)
Use the cols.wanted
argument for the overview to especially focus on the columns you need in your final data set.
mergeCheck(x,y,...)
- Merges data and only accept results if all that happened was that columns from y
were added to x
. Row order of x
is retained. Arguments are passed to data.table which does the actual merge. This completely automates the necessary checks when say merging covariates onto data.
pk2 <- mergeCheck(pk,covs2,by="ID")
We did not get an error from mergeCheck
so we know that the rows in
pk2
are exactly identical to those in pk
, except the addition of a
column called cov2
. If rows duplicate or disappear mergeCheck
does
a good job telling you where in data to address the issues.
renameByContents
- Keep track of what columns are compatible with
NONMEM by renaming columns accordingly. NMisNumeric
evaluates
whether NONMEM can interpret contents as numeric (different from
is.numeric
):
## Example 1: Append an "N" to columns that NONMEM _can_ read (as numeric) pk <- renameByContents(data=pk, fun.test = NMisNumeric, fun.rename = function(x)paste0(x,"N")) ## Example 2: lowercase names of columns that NONMEM _cannot_ read pk <- renameByContents(data=pk, fun.test = NMisNumeric, fun.rename = tolower, invert.test = TRUE)
addTAPD
- Add time since previous dose to data, time of previous
dose, most recent dose amount, cumulative number of doses, and
cumulative dose amount based on time since first dose. It supports
repeated dosing defined in ADDL
and II
.
flagsAssign
- Sequentially assign exclusion flags to a dataset based
on a set of user-specified exclusion criteria.
flagsCount
- Create an overview of number of retained and discarded
datapoints.
Example with only two exclusion flags applied to samples. If time is
negative, we assign exclusion flag FLAG=100
. If (time is
non-negative and) BLQ==1
we assign FLAG=10
. If none of these
conditions are met, FLAG=0
, and the row is to be included in the
analysis. (fread
is just for row-wise readability.)
pk[,(cc(FLAG,flag)):=NULL]
dt.flags <- fread(text="FLAG,flag,condition 10,Below LLOQ,BLQ==1 100,Negative time,TIME<0") pk <- flagsAssign(pk,tab.flags=dt.flags,subset.data="EVID==0") pk <- flagsAssign(pk,subset.data="EVID==1",flagc.0="Dosing") flagsCount(pk[EVID==0],tab.flags=dt.flags)[,.( flag, N.left, Nobs.left, N.discard, Nobs.discard)]
You may also want to apply a couple of exclusion criteria to dose records (for missing time, zero or missing amounts?) by modifying the steps above and applying to EVID==1
.
NMorderColumns
- Standardize column order. Columns that can be read
by NONMEM are prioritized. Row identifier and standard column names have special priorities.
pk <- NMorderColumns(pk)
NMcheckData
- Extensive data checks for NONMEM compatibility and common issues. Should be run before saving data but see the "Debugging..." section for example on output.
:::
::: {class="column-container__column"}
NMwriteData
- Write data ensuring compatibility with NONMEM. By defaults saves both a csv (for NONMEM) and an rds (for R, retaining factor levels etc). Text for optional use in $INPUT
and $DATA
NONMEM sections is returned. script
and args.stamp
are optional arguments, see "Traceability" section for their purpose.
text.nm <- NMwriteData(pk,file="derived/pkdata.csv",script="NMdata-cheat.Rmd",args.stamp=list(Description="PK data for the NMdata Cheatsheet"))
## writing version 2 rds so we don't need to depend on R 3.5 NMwriteData(pk,file="derived/pkdata.csv",script="NMdata-cheat.Rmd",args.stamp=list(Description="PK data for the NMdata Cheatsheet"),args.rds=list(version=2))
NMwriteSection
- Replace sections of a NONMEM control stream. Can use the text generated by NMwriteData
to update NONMEM runs to match the newly generated input data. Update INPUT section (and not DATA) for all control streams in directory "nonmem" which file names start with "run1" and end in ".mod" (say "run101.mod" to "run199.mod"):
NMwriteSection(dir="nonmem", file.pattern="run1.*\\.mod", list.sections=text.nm["INPUT"])
NMwriteSection
has the argument data.file
to further limit the scope of files to update based on what data file the control streams use. It only makes sense to use the auto-generated text for control streams that use this data set.
The text for NONMEM is generated by NMgenText
. Use that to generate alternative $INPUT
sections (e.g. for models that use other columns as dependent variables) without saving data again. You can tailor the generation of the text to copy (DV=CONC)
, drop (COL=DROP)
, rename (DV
instead of CONC
) and more.
NMcheckData
can check a data.frame
. However, it can also be run on a path to a control stream, in which case it provides a full check of how data is read by NONMEM and then checks the data as read by NONMEM. It checks column names in INPUT section against data and then runs a full check of the data set as read by NONMEM (according to column names in $INPUT and ACCEPT/IGNORE statements in $DATA). We suppress the default print to terminal (quiet=T
) and provide selected parts of the results here.
res.debug <- NMcheckData(file="nonmem/run201.mod",quiet=T) ## we will only show some of what is available here names(res.debug) ## Meta data on input data file: res.debug$tables
In this model we forgot to update the control stream INPUT section after adding a column to data ("off" means that INPUT text can be reorganized to match data file better):
## Comparison of variable naming: res.debug$input.colnames[c(1:2)] res.debug$input.colnames[c(9:12)]
We have some findings on the data set too. But since res.debug$input.colnames
tells us we are reading the data incorrectly, we have to address that before interpreting findings on the data.
res.debug$NMcheckData$summary
If you are preparing a data set, run NMcheckData
directly on the data (using the data
argument) instead of on a control stream.
::: ::: {class="column-container__column"}
NMscanData
- Automatically find NONMEM input and output tables and organize data. By default, available column names are taken from the NONMEM control stream. Additional column names (columns not read by NONMEM) are taken from input data file.
##NMscanData <- function(x)NMdata::NMscanData(file.path(system.file(paste0("examples/nonmem/",x), package="NMdata"))) ## res1 <- NMscanData(system.file("examples/nonmem/xgxr001.lst", package="NMdata"))
res1 <- NMscanData("nonmem/run101.lst")
## rm(NMscanData)
The following plot serves to illustrate that the obtained data set combines output tables (PRED
is from a $TABLE statement) with input data (exclusion flags are represented as character variables). Moreover, the "below LLOQ" samples are included in the result even though they were not in the analysis (excluded using IGNORE
in control stream, recovered in NMscanData
using recover.rows=TRUE
)
## Recover rows that were not read by NONMEM (due to ACCEPT/IGNORE) res2 <- NMscanData("nonmem/run101.lst",recover.rows=TRUE)
library(ggplot2) res2.plot <- subset(res2,ID==135&EVID==0) ggplot(res2.plot,aes(TIME))+ geom_point(aes(y=DV,colour=flag))+ geom_line(aes(y=PRED))+ labs(y="Concentration (unit)",subtitle=unique(res2$model))
Read the messages from NMwriteData
and NMscanData
carefully and notice that an rds file was written and read. This bypasses the loss of information caused by writing and reading csv, and so we have kept factor levels from the input data we generated:
levels(res1$trtact)
:::
::: {class="column-container__column"}
Use the many options in NMdataConf
to tailor NMdata behaviour to your setup and preferences. Make NMdata functions return data.tables or tibbles:
NMdataConf(as.fun=tibble::as_tibble) NMdataConf(as.fun="data.table")
By default, NMdata functions will look for a unique row identifier in columns called ROW
. If you call this column REC
, do
NMdataConf(col.row="REC")
By default, NMdata is configured to read files from PSN in which case the input control stream is needed to find the input data. Do this if you don't use PSN:
NMdataConf(file.mod=identity)
Loosely speaking, NMdataConf
changes default values of NMdata
function arguments. Many options can be configured this way so you
don't have to remember to type in those arguments every time you call
an NMdata function.
NMinfo
- Get metadata from an NMdata object. This will show where and when input data was created, when model was run, results of consistency checks, what tables were read, how they were combined and a complete list of data columns and their origin.
A list of the available elements:
names(NMinfo(res1))
The information recorded during saving of the input data:
NMinfo(res1,"dataCreate")
A full list of columns in all columns in output and input data is included. The source data file and the column number in the result (COLNUM
) are listed.
NMinfo(res1,"columns")[1:8]
We saw earlier that we got "30+2" columns back. We see that the additional two were added by NMscanData (source
). DV
was already included from another table so the redundant DV
column is omitted.
NMinfo(res1,"columns")[30:33]
NMscanTables
- Find and read all output data tables based on a NONMEM control stream file. A list of tables is returned.
NMreadTab
- Read an output table file from NONMEM based on path to output data file
NMscanInput
- Read input data based on NONMEM control stream and optionally translate column names according to the $INPUT
NONMEM section
NMreadCsv
- Read input data formatted for NONMEM
::: ::::
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.