Hmisc examples using plotly interactive graphics in an html report

Department of Biostatistics
Vanderbilt University School of Medicine

April 28, 2025

NOTE: remove the csl and bibliography lines above if you don’t have any bibliographic citations.

Introduction

This is a set of reproducible examples for the R1 Hmisc package2, put together in an rmarkdown html document using RStudio and knitr. When viewing the resulting html file you can see all the code, and there exist options to download the entire rmarkdown script, which is especially helpful for seeing how knitr chunks are specified. Graphics that have a plotly method for them are rendered using plotly instead of using defaults such as base graphics, lattice, or ggplot2. That way the plots are somewhat interactive, e.g., allow for drill-down for more information without the viewer needing to install R.

Much of the tabular output produced here was created using html methods, which are especially easy to implement with rmarkdown and make for output that can be directly opened using word processors. Jump to Computing Environment for a list of packages used here, and their version numbers.

Rmarkdown themes such as bookdown and rmdformats::readthedown (the latter is used here) allow one to number figures and the symbolically reference them. The knitrSet capfile='captions.md' argument below capitalizes on this to make it easy for the user to insert a table of figures anywhere in the report. Here we place it at the end. A caption listed in the table of figures is the short caption (scap= or fig.scap in the chunk header) if there is one, otherwise the long caption is used. If neither caption is used, that figure will not appear in the table of figures.

The full script for this report may be found here.

Setup

Code
require(Hmisc)
knitrSet(lang='markdown', h=4.5, fig.path='png/', fig.align='left',
         capfile='captions.md')  # + sometimes ,cache=TRUE
# knitrSet redirects all messages to messages.txt
options(grType='plotly') # for certain graphics functions
mu <- markupSpecs$html   # markupSpecs is in Hmisc
cap  <- mu$cap           # function to output html caption
lcap <- mu$lcap          # for continuation for long caption
# These last 2 functions are used by the putHfig function in Hmisc
cat('', file='captions.md')   # initialize table of short captions
The following (r mu$styles()) defines HTML styles and javascript functions, such as ffig, smg and the function for expanding and collapsing text as done by the expcoll function. `

`{=html}

Here is an example using expcoll. Click on the down arrow to expand; it turns to an up arrow which can be clicked to contract.


Example table

Fetching Data, Modifying Variables, and Printing Data Dictionary

The getHdata function is used to fetch a dataset from the Vanderbilt DataSets web site hbiostat.org/data. The upData function is used to

  • create a new variable from an old one
  • add labels to 2 variables
  • add units to the new variable
  • remove the old variable
  • automatically move units of measurements from parenthetical expressions in labels to separate units attributed used by Hmisc and rms functions for table making and graphics

contents is used to print a data dictionary, run through an html method for nicer output. Information about the data source may be found here. Click on the number of levels in the contents table to jump to the value labels for the variable.

Code
getHdata(pbc)

# Have upData move units from labels to separate attribute
pbc <- upData(pbc,
              fu.yrs = fu.days / 365.25,
              labels = c(fu.yrs = 'Follow-up Time',
                         status = 'Death or Liver Transplantation'),
              units = c(fu.yrs = 'year'),
              drop  = 'fu.days',
              moveUnits=TRUE, html=TRUE)
Input object size:   80952 bytes;    19 variables    418 observations
 Label for bili changed from Serum Bilirubin (mg/dl) to Serum Bilirubin 
    units set to mg/dl 
 Label for albumin changed from Albumin (gm/dl) to Albumin 
    units set to gm/dl 
 Label for protime changed from Prothrombin Time (sec.) to Prothrombin Time 
    units set to sec. 
 Label for alk.phos changed from Alkaline Phosphatase (U/liter) to Alkaline Phosphatase 
    units set to U/liter 
 Label for sgot changed from SGOT (U/ml) to SGOT 
    units set to U/ml 
 Label for chol changed from Cholesterol (mg/dl) to Cholesterol 
    units set to mg/dl 
 Label for trig changed from Triglycerides (mg/dl) to Triglycerides 
    units set to mg/dl 
 Label for platelet changed from Platelets (per cm^3/1000) to Platelets 
    units set to per cm^3/1000 
 Label for copper changed from Urine Copper (ug/day) to Urine Copper 
    units set to ug/day 
 Added variable     fu.yrs
 Dropped variable   fu.days
 New object size:   84744 bytes;    19 variables    418 observations
Code
# The following can also be done by running this command
# to put the results in a new browser tab:
# getHdata(pbc, 'contents')
html(contents(pbc), maxlevels=10, levelType='table')
pbc Contents

Data frame:pbc

418 observations and 19 variables, maximum # NAs:136  
Name Labels Units Levels Storage NAs
bili Serum Bilirubin mg/dl double 0
albumin Albumin gm/dl double 0
stage Histologic Stage, Ludwig Criteria integer 6
protime Prothrombin Time sec. double 2
sex 2 integer 0
age Age double 0
spiders 2 integer 106
hepatom 2 integer 106
ascites 2 integer 106
alk.phos Alkaline Phosphatase U/liter double 106
sgot SGOT U/ml double 106
chol Cholesterol mg/dl integer 134
trig Triglycerides mg/dl integer 136
platelet Platelets per cm^3/1000 integer 110
drug 3 integer 0
status Death or Liver Transplantation integer 0
edema 3 integer 0
copper Urine Copper ug/day integer 108
fu.yrs Follow-up Time year double 0

Variable Levels
sex male
female
spiders, hepatom absent
 ascites present
drug D-penicillamine
placebo
not randomized
edema no edema
edema, no diuretic therapy
edema despite diuretic therapy

Descriptive Statistics Without Stratification

The html method is used for the describe function, and the output is put in a scrollable box. Other than for the overall title and variable names and labels, the output size used here is 80 (0.8 × the usual font size1). But the graphical display of the descriptives statistics that follows this is probably better.

1 The default is 75% size.

Code
# did have results='asis' above
d <- describe(pbc)
html(d, size=80, scroll=TRUE)
pbc Descriptives
pbc

19 Variables   418 Observations

bili: Serum Bilirubin mg/dl
image
nmissingdistinctInfoMeanpMedianGmd.05.10.25.50.75.90.95
4180980.9983.22123.742 0.50 0.60 0.80 1.40 3.40 8.0314.00
lowest : 0.3 0.4 0.5 0.6 0.7 , highest: 21.6 22.5 24.5 25.5 28
albumin: Albumin gm/dl
image
nmissingdistinctInfoMeanpMedianGmd.05.10.25.50.75.90.95
418015413.4973.5150.4732.7502.9673.2433.5303.7704.0104.141
lowest : 1.96 2.1 2.23 2.27 2.31 , highest: 4.3 4.38 4.4 4.52 4.64
stage: Histologic Stage, Ludwig Criteria
image
nmissingdistinctInfoMeanpMedianGmd
412640.8933.02430.9519
 Value          1     2     3     4
 Frequency     21    92   155   144
 Proportion 0.051 0.223 0.376 0.350 
For the frequency table, variable is rounded to the nearest 0
protime: Prothrombin Time sec.
image
nmissingdistinctInfoMeanpMedianGmd.05.10.25.50.75.90.95
4162480.99810.7310.61.029 9.60 9.8010.0010.6011.1012.0012.45
lowest : 9 9.1 9.2 9.3 9.4 , highest: 13.8 14.1 15.2 17.1 18
sex
nmissingdistinct
41802
 Value        male female
 Frequency      44    374
 Proportion  0.105  0.895 

age: Age
image
nmissingdistinctInfoMeanpMedianGmd.05.10.25.50.75.90.95
4180345150.7450.6811.9633.8436.3742.8351.0058.2464.3067.92
lowest : 26.2779 28.8843 29.5551 30.2752 30.5736 , highest: 74.5243 75 75.0116 76.7091 78.4394
spiders
nmissingdistinct
3121062
 Value       absent present
 Frequency      222      90
 Proportion   0.712   0.288 

hepatom
nmissingdistinct
3121062
 Value       absent present
 Frequency      152     160
 Proportion   0.487   0.513 

ascites
nmissingdistinct
3121062
 Value       absent present
 Frequency      288      24
 Proportion   0.923   0.077 

alk.phos: Alkaline Phosphatase U/liter
image
nmissingdistinctInfoMeanpMedianGmd.05.10.25.50.75.90.95
3121062951198314341760 599.6 663.0 871.51259.01980.03826.46669.9
lowest : 289 310 369 377 414 , highest: 11046.6 11320.2 11552 12258.8 13862.4
sgot: SGOT U/ml
image
        n  missing distinct     Info     Mean  pMedian      Gmd      .05      .10 
      312      106      179        1    122.6    117.6    60.45    54.25    60.45 
      .25      .50      .75      .90      .95 
    80.60   114.70   151.90   196.47   219.25  
lowest : 26.35 28.38 41.85 43.4 45 , highest: 288 299.15 328.6 338 457.25
chol: Cholesterol mg/dl
image
nmissingdistinctInfoMeanpMedianGmd.05.10.25.50.75.90.95
2841342011369.5324.5194.5188.4213.6249.5309.5400.0560.8674.0
lowest : 120 127 132 149 151 , highest: 1336 1480 1600 1712 1775
trig: Triglycerides mg/dl
image
        n  missing distinct     Info     Mean  pMedian      Gmd      .05      .10 
      282      136      146        1    124.7      116    64.07    56.00    63.10 
      .25      .50      .75      .90      .95 
    84.25   108.00   151.00   195.00   230.95  
lowest : 33 44 46 49 50 , highest: 319 322 382 432 598
platelet: Platelets per cm3/1000
image
nmissingdistinctInfoMeanpMedianGmd.05.10.25.50.75.90.95
3081102101261.9259107.8117.7139.7199.8257.0322.5386.5430.6
lowest : 62 70 71 79 80 , highest: 493 514 518 539 563
drug
image
nmissingdistinct
41803
 Value      D-penicillamine         placebo  not randomized
 Frequency              154             158             106
 Proportion           0.368           0.378           0.254 

status: Death or Liver Transplantation
nmissingdistinctInfoSumMean
418020.711610.3852

edema
image
nmissingdistinct
41803
 Value                            no edema     edema, no diuretic therapy
 Frequency                             354                             44
 Proportion                          0.847                          0.105
                                          
 Value      edema despite diuretic therapy
 Frequency                              20
 Proportion                          0.048 

copper: Urine Copper ug/day
image
        n  missing distinct     Info     Mean  pMedian      Gmd      .05      .10 
      310      108      158        1    97.65     82.5    83.16    17.45    24.00 
      .25      .50      .75      .90      .95 
    41.25    73.00   123.00   208.10   249.20  
lowest : 4 9 10 11 12 , highest: 412 444 464 558 588
fu.yrs: Follow-up Time year
image
        n  missing distinct     Info     Mean  pMedian      Gmd      .05      .10 
      418        0      399        1    5.251    5.099    3.429    0.671    1.661 
      .25      .50      .75      .90      .95 
    2.992    4.736    7.155    9.649   11.063  
lowest : 0.112252 0.117728 0.13963 0.194387 0.210815 , highest: 12.3203 12.345 12.3833 12.4736 13.128
Code
# prList is in Hmisc; useful for plotting or printing a list of objects
# Can just use plot(d) if don't care about the mess
# If using html output these 2 images would not be rendered no matter what
p <- plot(d)
# The option htmlfig=2 causes markupSpecs$html$cap() to be used to
# HTML-typeset as a figure caption and to put the sub-sub section
# marker ### in front of the caption.  htmlfig is the only reason
# results='asis' was needed in the chunk header
# We define a long caption for one of the plots, which does not appear
# in the table of contents
# prList works for html notebooks but not html documents
# prList(p, lcap=c('', 'These are spike histograms'), htmlfig=2)

You can also re-form multiple plotly graphs into a single HTML object. If you want to have total control over long and short figure captions, use the Hmisc putHfig function to render the result, with a caption and a short caption for the table of contents. That would have fixed a problem with the chunk below: when plotly graphics are not rendered in the usual way, the figure is not numbered and no caption appears.

Code
htmltools::tagList(p)    # lapply(p, plotly::as.widget)

This used the htmltools tagList function.

You can also create figure captions outside of R code by using the smg, fcap HTML tags defined in markupSpecs. The long caption not appearing in the table of contents will be in a separate line without ###.

Stratified Descriptive Statistics

Produce stratified quantiles, means/SD, and proportions by treatment group. Plot the results before rendering as an advanced html table:

  • categorical variables: a single dot chart
  • continuous variables: a series of extended box plots
Code
s <- summaryM(bili + albumin + stage + protime + sex + age + spiders +
              alk.phos + sgot + chol ~ drug, data=pbc,
                            overall=FALSE, test=TRUE)
plot(s, which='categorical')

Proportions and \(\chi^{2}\) tests for categorical variables

To construct the caption outside of the code chunk use e.g. ### r cap(‘Proportions and’, mu$chisq(), ‘tests for categorical variables’) where a backtick is placed before r and after the last ).

Code
plot(s, which='continuous', vars=1 : 4)

Extended box plots for the first 4 continuous variables

Code
plot(s, which='continuous', vars=5 : 7)

Extended box plots for the remaining continuous variables

Code
html(s, caption='Baseline characteristics by randomized treatment',
     exclude1=TRUE, npct='both', digits=3, middle.bold=TRUE,
     prmsd=TRUE, brmsd=TRUE, msdsize=mu$smaller2)
Baseline characteristics by randomized treatment.
N
D-penicillamine
N=154
placebo
N=158
not randomized
N=106
Test Statistic
Serum Bilirubin
mg/dl
418 0.725 1.300 3.600
3.649 ± 5.282
0.800 1.400 3.200
2.873 ± 3.629
0.725 1.400 3.075
3.117 ± 4.043
F2 415=0.03, P=0.9721
Albumin
gm/dl
418 3.342 3.545 3.777
3.524 ± 0.396
3.212 3.565 3.830
3.516 ± 0.443
3.125 3.470 3.720
3.431 ± 0.435
F2 415=2.13, P=0.121
Histologic Stage, Ludwig Criteria : 1 412 0.03 4154 0.08 12158 0.05 5100 χ26=5.33, P=0.5022
  2 0.21 32154 0.22 35158 0.25 25100
  3 0.42 64154 0.35 56158 0.35 35100
  4 0.35 54154 0.35 55158 0.35 35100
Prothrombin Time
sec.
416 10.000 10.600 11.400
10.800 ±  1.138
10.025 10.600 11.000
10.653 ±  0.851
10.100 10.600 11.000
10.750 ±  1.078
F2 413=0.23, P=0.7951
sex : female 418 0.90 139154 0.87 137158 0.92 98106 χ22=2.38, P=0.3042
Age 418 41.43 48.11 55.80
48.58 ±  9.96
42.98 51.93 58.90
51.42 ± 11.01
46.00 53.00 61.00
52.87 ±  9.78
F2 415=6.1, P=0.0021
spiders 312 0.29 45154 0.28 45158 χ21=0.02, P=0.8852
Alkaline Phosphatase
U/liter
312 922 1283 1950
1943 ± 2102
841 1214 2028
2021 ± 2183
F1 310=0.06, P=0.8123
SGOT
U/ml
312 83.8 117.4 151.9
125.0 ±  58.9
76.7 111.6 151.5
120.2 ±  54.5
F1 310=0.55, P=0.463
Cholesterol
mg/dl
284 254 304 377
374 ± 252
248 316 417
365 ± 210
F1 282=0.37, P=0.5453
a b c represent the lower quartile a, the median b, and the upper quartile c for continuous variables. x ± s represents X ± 1 SD.   N is the number of non-missing values.
Tests used: 1Kruskal-Wallis test; 2Pearson test; 3Wilcoxon test .

Now show almost the full raw data for one continuous variable stratified by treatment. This display spike histograms using at most 100 bins, and also shows the mean and quantiles similar to what is in an extended box plot: 0.05, 0.25, 0.5, 0.75, 0.95. Measures of spread are also shown if the user clicks on their legend entries: Gini’s mean difference (mean absolute difference between any two values) and the SD. These can be seen as horizontal lines up against the minimum x-value.

Code
with(pbc, histboxp(x=sgot, group=drug, sd=TRUE))

Spike histograms, means, quantiles, Gini’s mean difference, and SD stratified by treatment

The following is a better way to display proportions, for categorical variables. If computing marginal statistics by running the dataset through the Hmisc addMarginal function, the plot method with options(grType='plotly') is especially useful.

Code
pbcm <- addMarginal(pbc, drug)
s <- summaryP(stage + sex + spiders ~ drug, data=pbc)
# putHcap('Proportions stratified by treatment')
plot(s, groups='drug')

Proportions (large symbols) and proportions stratified by treatment (small symbols)

Code
s <- summaryP(stage + sex + spiders ~ drug, data=pbcm)
plot(s, marginVal='All', marginLabel='All Treatment Groups')

Proportions (large symbols) and proportions stratified by treatment (small symbols)

Better Demonstration of Boxplot Replacement

Code
getHdata(support2)
with(support2, histboxp(x=meanbp, group=dzgroup, sd=TRUE, bins=200))

Spike histograms, means, quantiles, Gini’s mean difference, and SD for MAP stratified by diagnosis

Changing Size of Figure Captions

As explained here, one can place captions under figures using ordinary knitr capabilities, and one can change the size of captions. The following example defines a CSS style to make captions small (here 0.6em), and produces a plot with a caption. Unlike using putHfig captions given in knitr chunks do not also appear in the table of contents.

Code
# Note: in the chunk header cap is an alias for fig.cap defined by knitrSet
plot(runif(10))

This is a simple figure caption

Computing Environment2

Code
grateful::cite_packages(pkgs='Session', output='paragraph', out.dir='.',
    cite.tidyverse=FALSE, omit=c('grateful', 'ggplot2'))

We used R version 4.4.23 and the following R packages: Hmisc v. 5.2.42.

The code was run on macOS Sequoia 15.4.1 on a Macbook Pro M2 Max.

2 mu is a copy of the part of the Hmisc package object markupSpecs that is for html. It includes a function session that renders the session environment (including package versions) in html.

Bibliographic File Managament

Find and Install .csl Reference Style Files

Code
# Note: mu was defined in an earlier code chunk
# Only need to install .csl file once.
mu$installcsl(rec=TRUE)   # get list of recommended styles
mu$installcsl()     # web search of styles meeting your criteria
# Install a .csl file to your project directory:
mu$installcsl('american-medical-association')

List of Figures

Figure Description
@ref(fig:plotlym) Two plotly graphics combined into one
@ref(fig:summaryM) Proportions and \(\chi^{2}\) tests for categorical variables
@ref(fig:summaryM2) Extended box plots for the first 4 continuous variables
@ref(fig:summaryM3) Extended box plots for the remaining continuous variables
@ref(fig:histbox) Stratified spike histograms and quantiles
@ref(fig:summaryM5) Proportions with and without stratification by treatment
@ref(fig:support) Stratified spike histograms and quantiles for MAP
@ref(fig:simplecap) This is a simple figure caption

Note: the hidden R command that rendered the table of figures (including short captions) was markupSpecs$markdown$tof().

References

1.
R Development Team. R: A Language and Environment for Statistical Computing. R Foundation for Statistical Computing; www.r-project.org; 2020. http://www.R-project.org
2.
Harrell FE. Hmisc: A package of miscellaneous R functions. Published online 2020. https://hbiostat.org/R/Hmisc
3.
R Core Team. R: A Language and Environment for Statistical Computing. R Foundation for Statistical Computing; 2024. https://www.R-project.org/