Create Menu Dialogs for Data Entry and Computing Predicted Values

DESCRIPTION:

Dialog.data.frame takes as input a data frame, and uses details about all the variables in the data frame to call S-PLUS 4.5 or later graphical user interface functions to enter one observation into that data frame. An optional fun argument is used to add read-only fields to show derived values in the dialog. For factor or character variables, a pop-up list of possible values is set up, and "label" attributes of variables are used as field prompts. For variables having "units" attributes for units of measurement, these units are added to the end of the prompt, enclosed in brackets. The vectors in the input data frame can optionally be used to also determine legal ranges for data, and default values. Depending on defaultAuto , the observed data can be used to derive default values for categorical variables (mode category), binary or logical variables (mode), and continuous variables (median). If an input data frame is not specified, other parameters specifying the data attributes must be given: types, varnames, prompts, values, limits, and these parameters can also be given to override characteristics of the observed data.

fitPar is a utility function that collects information for one of the model fits that are passed to Dialog.Design.

Dialog.Design retrieves information from one or more model fits that were created with Design in effect. These fits must have been run through the fitPar function to collect additional information about each fit such as functions that transform predicted values. Dialog.Design obtains all the information about the predictors such as ranges and default values from the fit object, assuming that datadist was in effect when the fit was made. Dialog.Design creates functions to add predicted values and confidence limits to the menu as read-only fields. It also adds dataRep information at the end of the menu. The created functions are passed, along with information about the predictors, to Dialog.data.frame. The different fit objects passed to Dialog.Design through fitPar need not have the same predictors. All unique predictors are found from all fits, and these are placed at the beginning of the created menu, in the order encountered. If vary is specified, not all fits need have the predictor specified in vary. When a fit did not contain this variable, only a single predicted value will be obtained for that model.

plotDialogResults is a function that is used in conjunction with auxFun and Dialog.Design (see below). It makes sense to pass plotDialogResults or "plotDialogResults" as the auxFun argument to Dialog.Design if conf.type is not "none" for at least some of the fits ( fitPar objects) given to Dialog.Design.

USAGE:

Dialog(data, types=rep('',length(data)),
       varnames=names(data), prompts=NULL, values=NULL,
       basename=deparse(substitute(data)),
       prefix=paste(basename,'.',sep=''),
       Name=paste('menu',basename, sep='.'),
       resultName=paste('Result',basename,sep='.'),
       callbackName=paste('CallBack',basename,sep='.'),
       runmenuName=paste('runmenu',basename,sep='.'),
       required=T, limits=NULL,
       defaultAuto=c('factor','none','binfactor','all'), defaultValues=NULL,
       fun=NULL, funlabel=NULL, funArgType=c('list','data.frame'),
       funExtra=NULL, fungroups=NULL, auxFun=NULL, auxExtra=NULL, 
       header=basename, helpCommand='page(DialogHelpText,multi=T)', 
       where=1)

Dialog(fitinfo, ..., vary=NULL, varyPrefix=F,
       basename=fitinfo[[1]],
       prefix=paste(basename,'.',sep=''),
       prompts=NULL, required=T, limits=NULL,
       defaultAuto=c('factor','none','binfactor','all'), 
       defaultValues=NULL,
       funExtra=NULL, datarep=NULL, 
       auxFun='plotDialogResults', auxExtra=NULL,
       header=basename, helpCommand='page(DialogHelpText,multi=T)', 
       where=1)

fitPar(fitname, label='Predicted Value', lp=T,
       lplabel=label, lp.round=2,
       conf.int=.95,
       conf.type=c('mean','individual','both','none'),
       fun=NULL, fun.round=lp.round)

plotDialogResults(results, extra)

ARGUMENTS:

fitinfo
an object created by fitPar
fitname
a string specifying the name of a fit object created by Design
results
an object containing k lists where k is the number of fitPar objects passed to Dialog.Design, plus one for an optional element named vary plus one if a datarep object was also passed to Dialog.Design. Aside from the datarep object (named "datarep") and the vary object, the lists contain the fitPar information along with a list named estimates that contain the predicted values and confidence limits.
data
a data frame used as a prototype for variables to appear in the menu. Must be specified to Dialog.data.frame if types is not.
types
a vector of character strings representing variable types, specified if data is not. Values are "logical", "binary", "factor", "string", "integer", "float".
varnames
string vector of variable names, the same length as types. Specified if data is not.
prompts
for Dialog.data.frame is a string vector specifying field prompts in the same order as variables in data or varnames. For Dialog.Design this is a named vector specifying overrides to predictor variable labels as they were used in the model fits. For example, to override labels for age, sex to specify customized prompts, use prompts=c(age=Age [years]', sex='Sex')'.
values
for Dialog.data.frame when data is omitted, you must specify values of categorical predictors using for example values=list(sex=c("female","male"), treatment=c("placebo","drug").
basename
the base name used in forming the default value of prefix. The default is the name of the data argument given to Dialog.data.frame or the name of the first fit object given to Dialog.Design. It is best specified by the user, and basename or prefix should be chosen so that GUI objects stored in _Prefs should be uniquely named.
prefix
the base name used in all created GUI objects and functions. GUI objects corresponding to data fields have field names appended to prefix when their names are formed. The default for prefix is basename followed by a period.
Name
the name of the function to be created that is called to create the data frame of values defined when the menu is filled
resultName
the name of a temporary object in frame 0 to hold the value from the Name function.
callbackName
the name of the GUI callback function to be created
runmenuName
the name of the function to be created that will invoke the menu
required
By default, values of all predictor variables are required to be entered by the user. You can specify required=F to not require any values to be entered, or specify a vector of predictor variable names corresponding to a subset you wish to be required.
limits
By default, any values of variables other than categorical, logical, or binary variables are allowed to be entered. Specify limits="data" to restrict values to be in the range of the observed data. Specify for example limits=list(age=c(0,100), height=c(30,84)) to have control over the limits to be imposed during data entry.
defaultAuto
Unless defaultValues is specified and is not "none", default values for factor variables will appear by their prompts, and unless defaultValues is given for such variables, the values that pop up are the most frequent categories. Specify defaultAuto="binfactor" to also use modal values for binary or logical variables. Specify "all" to use automatic default values for all predictors. For continuous predictors, the median values are the default values.
defaultValues
a list specifying default values for selected predictors, which will override those determined through defaultAuto, for example defaultValues=list(age=50, sex="male").
fun
For Dialog.Design, fun is a list of (usually) simple functions along with labels (prompts) for them, that are used to transform linear predictors. For example, fun=list("Predicted Probability"=plogis, "Predicted Odds"=exp) are useful for binary logistic models.
funlabel
a string vector of labels (prompts) corresponding to the vector of string values returned by fun, for Dialog.data.frame.
funArgType
In the callback function, input variables are combined into a list that is passed to fun functions for obtaining derived values. Specify funArgType="data.frame" to Dialog.data.frame to convert the list to a data frame before calling the functions. The list of data frame is the first argument given to these functions.
funExtra
an optional second argument passed to the functions called in the callback function for the GUI. This will often be a list so that several arguments may be passed.
fungroups
a named integer vector specifying group names and how many individual function values from fun and funlabel there are per group. For example, if fun produces a vector of 7 values and the first 3 pertain to "Speed" and the last 4 to "Size", specify fungroups=c(Speed=3, Size=4). Specify e.g. c("Speed of Travel"=3) when group names are not legal S-PLUS names. These group names are used to organize values into groups, with overall labels.
auxFun
a function invoked after all derived quantities (from fun) are displayed. The argument passed to this function is the "results" attribute returned by fun. For Dialog.Design, this "results" object is created automatically. It is a list of lists containing the fitPar objects augmented with an estimates object containing predictions ( linear.predictors) and confidence limits ( upper, lower, iupper, ilower, where i stands for "individual" and others are for the mean). There are additional element of results. The data element contains a data frame of the predictor values entered by the user. A vary list contains the variable and its settings that were specified to Dialog as vary. A datarep element contains the result of predict(dataRep object) if datarep was given. auxFun can be a character string containing the name of the function, or the actual function itself. If the former case it is assumed that the function is available in the search list when the menu is run. auxFun is useful for plotting results that are presented in printed form in the menu. The plotDialogResults (the default value of auxFun for Dialog.Design) function is set up to plot the results object. Specify auxFun=NULL to suppress invocation of any auxiliary function when Apply is pressed.
auxExtra
a list of extra argument values passed to the function specified in auxFun
header
a string specifying the title line for the menu. Default is basename.
helpCommand
a string specifying an S-PLUS command to be executed if the menu Help button is pressed. The default helpCommand uses the page command to display general help text found in a character vector DialogHelpText.
where
position on the search list where created functions are stored. Default is 1.
...
additional fitPar objects, to allow for more than one model to be evaluated in the menu
vary
a list containing a single numeric or character vector representing values of a variable that is not to be entered by the user but instead is to be varied automatically, creating a vector of predicted values and several pairs of confidence limits. vary is of the form list(treatment=levels(treatment).
varyPrefix
By default, the vary variable name will not appear as a label in the menu. Set varyPrefix=T to use the variable name as a prefix to the vary values, or set varyPrefix to a single character string to use that prefix instead.
datarep
an optional object produced by dataRep
label
a generic label for all of the predicted values arising from a specific fit. Used only as a label for grouping multiple predictions for a single subject when vary is not present. Defaults to lplabel or the name of the first fun if these are specified and label is not.
lp
set to F to prevent the ordinary predicted value (linear predictors) from being displayed. This is useful for example when fun specifies the logistic transformation and you want to specify predicted probabilities but not predicted logits.
lplabel
string specifying the prompt for the linear predictor if lp=T
lp.round
causes linear predictor values to be rounded to lp.round decimal places (default is 2) before being displayed.
conf.int
confidence level for confidence limits (default is 0.95)
conf.type
type of confidence limits to display. Default is "mean" to display ordinary limits. Use "individual" to display confidence limits for individual responses when using ols. Specify conf.type="both" to display both confidence intervals for means and intervals for individuals.
fun.round
causes values of functions specified to Dialog.Design in fun to be rounded to fun.round decimal places before being displayed. Default uses two digits to the right. If there is more than one function in fun, elements of fun.round are assumed to be specified in the order of the functions. If only one fun.round is given it is assumed to apply to all of the functions.
extra
a list containing other arguments to plotDialogResults, including many graphical parameters. See the function for details.

DETAILS:

When vary and dataRep are both used, the frequencies reported regarding the dataRep information are the minimum number of matches over all levels of vary.

VALUE:

fitPar returns a list containing the name of the model fit along with all of the information given to fitPar, with unspecified options set to defaults. A vector of names of predictors used in the fit is also contained in the result. Dialog.data.frame and Dialog.Design return a function definition that will start the menu. You can use for example .guiFirst <- Dialog(...) to set up for automatic invocation of the menu the next time you start S-PLUS.

AUTHOR(S):

Frank Harrell
Department of Biostatistics
Vanderbilt University School of Medicine
f.harrell@vanderbilt.edu

SEE ALSO:

, , , , ,

EXAMPLES:

# Create a data frame, and generate a GUI to input data like that
# Display the square root of one of the variables plus the
# value of a binary variable
test <- expand.grid(age=21:50, sex=c('female','male'), sick=0:1)
Dialog(test, fun=function(z) round(sqrt(z$age)+z$sick,1),
       funlabel='Square root of age, +sick', limits=list(age=c(10,70)),
       fungroups=c('Calculated Values'=1))
runmenu.test()

# Use a linear model in log(total cost) for the support dataset available
# from hesweb1.med.virginia.edu/s/data, display predictions from a
# binary logistic model for hospital death, and display various
# predictions from a fitted Cox proportional hazards survival model
# (1- and 2-year survival probability and median survival time).
# The models do not use the
# same predictors, so all the predictors from all models are first combined.
# Prompts for all but two variables come from variable labels that were
# orginally stored in the data frame.  Prompts for two variables are defined
# in the call to Dialog.
# In this example, there is no prompt for the race variable as race is
# automatically varied over all its levels, for models that have race
# as one of the predictors.

dd <- datadist(support)
options(datadist='dd')
costfit <- ols(log(totcst) ~ dzgroup + rcs(age,4) + race + sex +
               rcs(meanbp,5) + pol(scoma,2), data=support, subset=totcst>0)
hospmortfit <- lrm(hospdead ~ rcs(age,4) + sex + rcs(meanbp,5) + rcs(crea,4),
                   data=support)
survivalfit <- cph(Surv(d.time, death) ~ dzgroup*rcs(age,4),
                   surv=T, data=support)

drep <- dataRep(~ dzgroup + roundN(age,20), data=support)
drep

Data Representativeness    n=1000

dataRep(formula =  ~ dzgroup + roundN(age, 20), data = support)

Specifications for Matching

                     Type 
dzgroup exact categorical
    age             round
                                                                             Parameters 
dzgroup ARF/MOSF w/Sepsis COPD CHF Cirrhosis Coma Colon Cancer Lung Cancer MOSF w/Malig
    age                                                                   to nearest 20

 33 unique combinations of variable values were found.



surv <- Survival(survivalfit)         # Derive function to estimate S(t|Xbeta)
surv1 <- function(lp) surv(365, lp)   # Derive function to get S(1|Xbeta)
surv2 <- function(lp) surv(365*2, lp) # Function to get S(2|Xbeta)
survq <- Quantile(survivalfit)        # Derive function to get S inverse 
survmed <- function(lp) survq(.5, lp) # Derive function for S inverse(.5)
# At present, Dialog does not support stratification in cph when
# predicting survival probabilities or survival times, unless strata
# are specified explicitly when surv or survmed are called.

Dialog(fitPar('costfit', lp=F, conf.type='both', 
              fun=list('Median Cost ($/1000)'=function(y)exp(y)/1000), 
                       fun.round=0),
       fitPar('hospmortfit', lp=F, conf.type='mean',
              fun=list('Probability of Death in Hospital'=plogis),label=''),
       fitPar('survivalfit', lp=F, conf.type='none',
              fun=list('1-Year Survival'=surv1,
                       '2-Year Survival'=surv2,
                       'Median Survival Time [days]'=survmed),
              fun.round=c(2,2,0), label='Survival Predictions'),
       vary=list(race=levels(support$race)),
       basename='SUPPORT', header='SUPPORT Predictive Models',
       prompts=c(scoma='SUPPORT Coma Score', sex='Sex'),
       defaultAuto='binfactor', limits='data', datarep=drep)
# lp=F prevents predicted log(totcst) and logit(prob of death)
# from being displayed
runmenu.SUPPORT()

# Generate data and fit two ols models for a clinical trial in which
# we are predicting follow-up values of two baseline variables.
# Graphically display predictions for both treatments, along with
# baseline values (using a triangle, between the two lines for treatment)

n <- 500
set.seed(17)        # to reproduce results
treatment <- sample(c('Drug','Placebo'), n, T)
label(treatment) <- 'Treatment'
age <- rnorm(n, 50, 10)
sex <- sample(c('female','male'), n, T)
sys.bp <- rnorm(n, 120, 8)
dias.bp <- rnorm(n, 80, 8)
label(sys.bp) <- 'Systolic Blood Pressure [mmHg]'
label(dias.bp) <- 'Diastolic Blood Pressure [mmHg]'
drep <- dataRep(~ roundN(age,2.5) + sex)
# Now simulate blood pressures after one year of follow-up
sys.bp1 <- sys.bp + (age-50)/5 + (sex=='male') - 3*(treatment=='Drug')+
  rnorm(n, 0, 4)
dias.bp1<- dias.bp+ (age-50)/5 + (sex=='male') - 3.5*(treatment=='Drug')+
  rnorm(n, 0, 4)
dd <- datadist(age, sex, treatment, sys.bp, dias.bp)
options(datadist='dd')
fitsys <- ols(sys.bp1 ~ sys.bp + age + sex + treatment)
fitdias <- ols(dias.bp1 ~ dias.bp + age + sex + treatment)

Dialog(fitPar('fitsys', lplabel='Systolic BP at Year 1', lp.round=1,
              conf.type='both'),
       fitPar('fitdias',lplabel='Diastolic BP at Year 1', lp.round=1,
              conf.type='both'),
       vary=list(treatment=c('Placebo','Drug')),
       basename='BP', header='Hypertension Trial Predictions',
       limits='data', datarep=drep, 
       auxExtra=list(baseline.vars=c('sys.bp','dias.bp')))

# Set up a function that will cause the menu to be displayed
# as soon as S-PLUS is started.  Some options are also given.
 .guiFirst <- function() {
   guiSetOption('AutoAddPages','Every Graph')
   graphsheet(Name='Hypertension Trial', background.color='user9')
   # or guiSetOption('BackColorStd','user9') and wait for first
   # plot to open a graphsheet
   guiExecuteBuiltIn(
    '$$SPlusMenuBar$Object_Browser$Window$Tile_Vertical')
   # Can add library() commands here as needed (or put in .First)

   # Install button to allow menu to be easily invoked in the future
   if(!length(locate.menu.item('Hypertension Predictions'))) 
     add.menu.item(name="Hypertension Predictions", 
                   action='runmenu.BP()')
   # Type delete.menu.item('Hypertension Predictions') to remove button

   # Invoke menu now
   runmenu.BP()
   invisible()
 }