# Introduction to summarytools

#### Dominic Comtois

#### 2025-04-29

# 1. Overview

summarytools provides a coherent set of functions centered on data exploration and simple reporting. At its core reside the following four functions:

Function | Description
--- | ---
`freq()` | Frequency Tables featuring counts, proportions, cumulative statistics as well as missing data reporting
`ctable()` | Cross-Tabulations (joint frequencies) between pairs of discrete/categorical variables, featuring marginal sums as well as row, column or total proportions
`descr()` | Univariate (‘Descriptive’) Statistics for numerical data, with common measures of central tendency and dispersion
`dfSummary()` | Data Frame Summaries featuring type-specific information for all variables: univariate statistics and/or frequency distributions, bar charts or histograms, as well as missing data counts and proportions. Very useful to quickly, detect anomalies and identify trends at a glance

## 1.1 Motivation

The package was developed with the following objectives in mind:

- Provide a coherent set of easy-to-use descriptive functions akin to those included in commercial statistical software suites such as SAS, SPSS, and Stata
- Offer flexibility in terms of output format & content
- Integrate well with commonly used software & tools for reporting (the RStudio IDE, Rmarkdown, Quarto, and knitr) while also allowing for standalone, simple report generation using any R interface

## 1.2 Directing Output

Results can be

- Displayed in the R console as plain text
- Rendered as html and shown in a Web browser or in RStudio’s Viewer Pane
- Written / appended to plain text, markdown, or html files

When creating R Markdown documents, make sure to

- Use chunk option `results = "asis"`
- Une the function argument `plain.ascii = FALSE`
- Set the style parameter to “rmarkdown”, or “grid” for `dfSummary()`

## 1.3 Other Characteristics

- Weights-enabled: `freq()`, `ctable()` and `descr()` support sampling weights
- Multilingual:
  - Built-in translations exist for French, Portuguese, Spanish, Russian, and Turkish. Users can easily add custom translations or modify existing languages at will
- Flexible and extensible:
  - Pipe operators from magrittr (`%>%`, `%$%`) and pipeR (`%>>%`) are fully supported; the native `|>` introduced in R 4.0 is supported as well and in most cases is a safer bet as far as objects’ name searching goes.
  - Default values for a good number of function arguments can be modified using `st_options()` to minimize redundancy in function calls
  - By-group processing is easily achieved using the package’s `stby()` function which is a slightly modified version of `base::by()`, but `dplyr::group_by()` is also supported
  - Version 1.1 introduced support for labelled vectors (classes labelled / haven_labelled), which are being treated as factors in `freq()`, and in `dfSummary()` when all values are labelled. A future release will have `ctable()` behave similarly.
  - Parameter `na.val` allows treating a special value as `NA` in `freq()`, `ctable()` and `dfSummary()` (feature introduced in version 1.1.0).
  - Pander options can be used to customize or enhance plain text and markdown tables
  - Base R’s `format()` arguments are also supported by summarytools’ `print` method / `view()` function; this can be used to set a thousands separator or modify the decimal separator, among other possibilities (see `help("format")`)
  - Bootstrap CSS is used by default with html output, and user-defined classes can be added at will

<< 1. Overview | TOC | 3. Cross-Tabulations: ctable() >>

# 2. Frequency Tables: freq()

The `freq()` function generates frequency tables with counts, proportions, as well as missing data information. Side note: the very idea for creating this package stemmed from the absence of such a function in base R.

    freq(iris$Species, plain.ascii = FALSE, style = "rmarkdown")

### Frequencies

iris$Species
Type: Factor

| Freq | % Valid | % Valid Cum. | % Total | % Total Cum.
--- | --- | --- | --- | --- | ---
setosa | 50 | 33.33 | 33.33 | 33.33 | 33.33
versicolor | 50 | 33.33 | 66.67 | 33.33 | 66.67
virginica | 50 | 33.33 | 100.00 | 33.33 | 100.00
<NA> | 0 |  |  | 0.00 | 100.00
Total | 150 | 100.00 | 100.00 | 100.00 | 100.00

In this first example, the `plain.ascii` and `style` arguments were specified. However, since we have defined them globally for this document using `st_options()`, they are redundant and will be omitted from hereon (section 16 contains a detailed description of this vignette’s configuration).

## 2.1 Missing Data

One of summarytools’ main purposes is to help cleaning and preparing data for further analysis. But in some circumstances, we don’t need (or already have) information about missing data. Using `report.nas = FALSE` makes the output table smaller by one row and two columns:

    freq(iris$Species, report.nas = FALSE, headings = FALSE)

| Freq | % | % Cum.
--- | --- | --- | ---
setosa | 50 | 33.33 | 33.33
versicolor | 50 | 33.33 | 66.67
virginica | 50 | 33.33 | 100.00
Total | 150 | 100.00 | 100.00

| The `headings = FALSE` parameter suppresses the heading section.

## 2.2 Simplest Expression

By “switching off” all optional elements, a much simpler table will be produced:

    freq(iris$Species,
         report.nas = FALSE,
         totals     = FALSE,
         cumul      = FALSE,
         headings   = FALSE)

| Freq | %
--- | --- | ---
setosa | 50 | 33.33
versicolor | 50 | 33.33
virginica | 50 | 33.33

While the output is much simplified, the syntax is not; I blame it on Tesler’s law of conservation of complexity! Thankfully, `st_options()` is there to accommodate everyone’s preferences (see the section on package options).

## 2.3 Multiple Frequency Tables At Once

To generate frequency tables for all variables in a data frame, we could (and in the earliest versions, needed to) use `lapply()`. However, this is not required since `freq()` accepts data frames as the main argument:

    freq(tobacco)

To avoid cluttering the results, numerical columns having more than 25 distinct values are ignored. This threshold value can be changed via `st_options()` (example: `st_options(freq.ignore.threshold = 10)`).

| The tobacco data frame contains simulated data and is included in the package. Another simulated data frame is included: exams. Both have French versions (tabagisme, examens).

## 2.4 Subsetting (Filtering) Frequency Tables

The `rows` argument allows subsetting frequency tables; we can use it in different ways:

- To filter rows by their order of appearance, we use a numerical vector; `rows = 1:10` will show the frequencies for the first 10 values only. An “(Other)” row is automatically added to account for the frequency of other values
- To filter rows by name, we can use either
  - a character vector specifying all the row names we wish to keep
  - a single character string, which will be used as a regular expression (see `?regex` for more information on this topic)

### Showing The Most Common Values

By combining the `order` and `rows` parameters, we can easily filter the results to show, for example, the 5 most common values in a factor:

    freq(tobacco$disease,
         order    = "freq",
         rows     = 1:5,
         headings = FALSE)

| Freq | % Valid | % Valid Cum. | % Total | % Total Cum.
--- | --- | --- | --- | --- | ---
Hypertension | 36 | 16.22 | 16.22 | 3.60 | 3.60
Cancer | 34 | 15.32 | 31.53 | 3.40 | 7.00
Cholesterol | 21 | 9.46 | 40.99 | 2.10 | 9.10
Heart | 20 | 9.01 | 50.00 | 2.00 | 11.10
Pulmonary | 20 | 9.01 | 59.01 | 2.00 | 13.10
(Other) | 91 | 40.99 | 100.00 | 9.10 | 22.20
<NA> | 778 |  |  | 77.80 | 100.00
Total | 1000 | 100.00 | 100.00 | 100.00 | 100.00

Instead of `"freq"`, we can use `"-freq"` to reverse the ordering and get results ranked from lowest to highest in frequency.

| Notice the “(Other)” row, which is automatically added

## 2.5 Collapsible Sections

When generating html results, use the `collapse = TRUE` argument with `print()` or `view()` / `stview()` to get collapsible sections; clicking on the variable name in the heading section will collapse / reveal the frequency table (results not shown).

    view(freq(tobacco), collapse = TRUE)

<< 2. Frequency Tables: freq() | TOC | 4. Descriptive (Univariate) Statistics: descr() >>

# 3. Cross-Tabulations: ctable()

`ctable()` generates cross-tabulations (joint frequencies) for pairs of categorical variables.

Using the tobacco simulated data frame, we’ll cross-tabulate the two categorical variables smoker and diseased.

    ctable(x = tobacco$smoker,
           y = tobacco$diseased,
           prop = "r")   # Show row proportions

### Cross-Tabulation, Row Proportions

smoker * diseased
Data Frame: tobacco

| diseased | Yes | No | Total
smoker |  |  |  |
Yes |  | 125 (41.9%) | 173 (58.1%) | 298 (100.0%)
No |  | 99 (14.1%) | 603 (85.9%) | 702 (100.0%)
Total |  | 224 (22.4%) | 776 (77.6%) | 1000 (100.0%)

As can be seen, since markdown does not fully support multiline table headings, pander does what it can to display this particular type of table. To get better results, the “render” method is recommended and will be used in the next examples.

## 3.1 Row, Column, or Total Proportions

Row proportions are shown by default. To display column or total proportions, use `prop = "c"` or `prop = "t"`, respectively. To omit proportions altogether, use `prop = "n"`.

## 3.2 Minimal Cross-Tabulations

By “switching off” all optional features, we get a simple 2 x 2 table:

    with(tobacco,
         print(ctable(x = smoker,
                      y = diseased,
                      prop     = 'n',
                      totals   = FALSE,
                      headings = FALSE),
               method = "render")
    )

| diseased
--- | ---
smoker | Yes | No
Yes | 125 | 173
No | 99 | 603

## 3.3 Chi-Square (𝛘2), Odds Ratio and Risk Ratio

To display the chi-square statistic, set `chisq = TRUE`. For 2 x 2 tables, use `OR` and `RR` to show odds ratio and risk ratio (also called relative risk), respectively. Those can be set to `TRUE`, in which case 95% confidence intervals are shown; to use different confidence levels, use for example `OR = .90`.

| Using pipes generally makes it easier to generate `ctable()` results.

    library(magrittr)
    tobacco %$%  # Acts like with(tobacco, ...)
      ctable(x = smoker, y = diseased,
             chisq = TRUE,
             OR    = TRUE,
             RR    = TRUE,
             headings = FALSE) %>%
      print(method = "render")

| diseased |
--- | --- | ---
smoker | Yes | No | Total
Yes | 125 | ( | 41.9% | ) | 173 | ( | 58.1% | ) | 298 | ( | 100.0% | )
No | 99 | ( | 14.1% | ) | 603 | ( | 85.9% | ) | 702 | ( | 100.0% | )
Total | 224 | ( | 22.4% | ) | 776 | ( | 77.6% | ) | 1000 | ( | 100.0% | )
Χ2 = 91.7088 df = 1 p = .0000 O.R. (95% C.I.) = 4.40 (3.22 - 6.02) R.R. (95% C.I.) = 2.97 (2.37 - 3.73)

<< 3. Cross-Tabs: ctable() | TOC | 5. Data Frame Summaries: dfSummary() >>

# 4. Descriptive (Univariate) Statistics: descr()

`descr()` generates descriptive / univariate statistics, i.e. common central tendency statistics and measures of dispersion. It accepts single vectors as well as data frames; in the latter case, all non-numerical columns are ignored, with a message to that effect.

    descr(iris)

    Non-numerical variable(s) ignored: Species

### Descriptive Statistics

iris
N: 150

| Petal.Length | Petal.Width | Sepal.Length | Sepal.Width
--- | --- | --- | --- | ---
Mean | 3.76 | 1.20 | 5.84 | 3.06
Std.Dev | 1.77 | 0.76 | 0.83 | 0.44
Min | 1.00 | 0.10 | 4.30 | 2.00
Q1 | 1.60 | 0.30 | 5.10 | 2.80
Median | 4.35 | 1.30 | 5.80 | 3.00
Q3 | 5.10 | 1.80 | 6.40 | 3.30
Max | 6.90 | 2.50 | 7.90 | 4.40
MAD | 1.85 | 1.04 | 1.04 | 0.44
IQR | 3.50 | 1.50 | 1.30 | 0.50
CV | 0.47 | 0.64 | 0.14 | 0.14
Skewness | -0.27 | -0.10 | 0.31 | 0.31
SE.Skewness | 0.20 | 0.20 | 0.20 | 0.20
Kurtosis | -1.42 | -1.36 | -0.61 | 0.14
N.Valid | 150.00 | 150.00 | 150.00 | 150.00
N | 150.00 | 150.00 | 150.00 | 150.00
Pct.Valid | 100.00 | 100.00 | 100.00 | 100.00

To turn off the variable-type messages, use the package’s `print()` method `silent = TRUE` parameter. It is possible to set that option globally, which we will do here, so it won’t be displayed in the remaining portion of this vignette.

    st_options(descr.silent = TRUE)

## 4.1 Transposing and Selecting Statistics

Results can be transposed by using `transpose = TRUE`, and statistics can be selected using the `stats` argument:

    descr(iris,
          stats     = c("mean", "sd"),
          transpose = TRUE,
          headings  = FALSE)

| Mean | Std.Dev
--- | --- | ---
Petal.Length | 3.76 | 1.77
Petal.Width | 1.20 | 0.76
Sepal.Length | 5.84 | 0.83
Sepal.Width | 3.06 | 0.44

See `?descr` for a list of all available statistics. Special values “all”, “fivenum”, and “common” are also valid, and can be used in conjunction with specific statistics, to add or remove them. For example, `stats= c("common", "", "-pct.valid")` adds N to, and excludes Pct. Valid from, common statistics. The default value is “all”, and it can be modified using `st_options()`:

    st_options(descr.stats = "common")

keywords (all, fivenum, and common) can be used in conjunction with statistics, to add or remove them. `stats= c("common", "n", "-pct.valid")` adds N to, and excludes Pct. Valid from, common statistics.

<< 4. Descriptive Statistics with descr() | TOC | 6. Grouped Statistics: stby() >>

# 5. Data Frame Summaries: dfSummary()

`dfSummary()` creates a summary table with statistics, frequencies and graphs for all variables in a data frame. The information displayed is type-specific (character, factor, numeric, date) and also varies according to the number of distinct values.

To see the results in RStudio’s Viewer (or in the default Web browser if working in another IDE or from a terminal window), use the `view()` function, or its twin `stview()` in case of name conflicts:

    view(dfSummary(iris))

| Be careful to use `view()` (or `stview()`) and not `View()` with capital `V’. Otherwise, results will be shown in the data viewer. Also, be mindful of the order in which the packages are loaded. Some packages redefine `view()` to point to `View()`; loading summarytools after these packages will ensure its own `view()` function works properly. Otherwise, `stview()` is always there as a foolproof alternative.

## 5.1 Using dfSummary() in R Markdown Documents

When using `dfSummary()` in R Markdown documents, it is generally a good idea to exclude a column or two to avoid margin overflow. Since the Valid and Missing columns are complementary (and therefore redundant), we can safely drop either one of them.

    dfSummary(tobacco,
              plain.ascii  = FALSE,
              style        = "grid",
              graph.magnif = 0.82,
              varnumbers   = FALSE,
              valid.col    = FALSE,
              tmp.img.dir  = "/tmp")

The `tmp.img.dir` parameter is mandatory when generating dfSummaries in R Markdown documents, except for method “render”. The explanation for this can be found further below.

| Some users reported repeated X11 warnings; those can be avoided by setting the `warning` chunk option to `FALSE`: `{r chunk_name, results="asis", warning=FALSE}`.

## 5.2 Optional Statistics

Introduced in version 1.0.0 in response to feature requests, a mechanism provides control over which statistics to shown in the Stats/Values column. The third row, which displays `IQR (CV)`, can be modified to show any available statistics in R. An additional “slot” (unused by default) is also made available. To use this feature, define `dfSummary.custom.1` and/or `dfSummary.custom.2` using `st_options()` in the following way, encapsulating the code in an `expression()`:

    st_options(
      dfSummary.custom.1 =
        expression(
          paste(
            "Q1 - Q3 :",
            round(
              quantile(column_data, probs = .25, type = 2,
                       names = FALSE, na.rm = TRUE), digits = 1
            ), " - ",
            round(
              quantile(column_data, probs = .75, type = 2,
                       names = FALSE, na.rm = TRUE), digits = 1
            )
          )
        )
    )

    print(
      dfSummary(iris,
                varnumbers   = FALSE,
                na.col       = FALSE,
                style        = "multiline",
                plain.ascii  = FALSE,
                headings     = FALSE,
                graph.magnif = .82),
      method = "render"
    )

Variable | Stats / Values | Freqs (% of Valid) | Graph | Valid
--- | --- | --- | --- | ---
Sepal.Length [numeric] |
Mean (sd) : 5.8 (0.8)
min ≤ med ≤ max:
4.3 ≤ 5.8 ≤ 7.9
Q1 - Q3 : 5.1 - 6.4
| 35 distinct values |  | 150 (100.0%)
Sepal.Width [numeric] |
Mean (sd) : 3.1 (0.4)
min ≤ med ≤ max:
2 ≤ 3 ≤ 4.4
Q1 - Q3 : 2.8 - 3.3
| 23 distinct values |  | 150 (100.0%)
Petal.Length [numeric] |
Mean (sd) : 3.8 (1.8)
min ≤ med ≤ max:
1 ≤ 4.3 ≤ 6.9
Q1 - Q3 : 1.6 - 5.1
| 43 distinct values |  | 150 (100.0%)
Petal.Width [numeric] |
Mean (sd) : 1.2 (0.8)
min ≤ med ≤ max:
0.1 ≤ 1.3 ≤ 2.5
Q1 - Q3 : 0.3 - 1.8
| 22 distinct values |  | 150 (100.0%)
Species [factor] |
1. setosa
2. versicolor
3. virginica
50 | ( | 33.3% | )
50 | ( | 33.3% | )
50 | ( | 33.3% | )
|  | 150 (100.0%)

If we had used `dfSummary.custom.2` instead of `dfSummary.custom.1`, a fourth line would have been added under `IQR (CV)`.

Note that instead of `round()`, it is possible to use the internal function `format_number()`, which will ensure correct formatting of numbers according to all specified arguments (rounding digits, decimal mark, big/small mark, etc.). The variable `round.digits`, which contains the value of `st_options("round.digits")` can also be used.

This is how the default `IQR (CV)` is defined – here we set the first custom stat back to its default value and then display its definition (`formatR::tidy_source()` is used to format the expression):

    library(formatR)
    st_options(dfSummary.custom.1 = "default")
    formatR::tidy_source(
      text   = deparse(st_options("dfSummary.custom.1")),
      indent = 2,
      args.newline = TRUE
    )

    expression(
      paste(
        paste0(
          trs("iqr"),
          " (", trs("cv"),
          ") : "
        ),
        format_number(
          IQR(column_data, na.rm = TRUE),
          round.digits
        ),
        " (", format_number(
          sd(column_data, na.rm = TRUE)/mean(column_data, na.rm = TRUE),
          round.digits
        ),
        ")", collapse = "", sep = ""
      )
    )

| Don’t forget to specify `na.rm = TRUE` for all functions that use this parameter (most of base R functions do).

## 5.3 Other Notable Features

The `dfSummary()` function also

- Reports the number of duplicate records in the heading section
- Detects UPC/EAN codes (barcode numbers) and doesn’t calculate irrelevant statistics for them
- Detects email addresses and reports counts of valid, invalid and duplicate addresses; note that the proportions of valid and invalid sum up to 100%; the duplicates proportion is calculated independently, which is why in the bar chart (html version), the bar for this category is shown with a different color
- Allows the display of “windowed” results by using the `max.tbl.height` parameter; This is especially convenient if the analyzed data frame has numerous variables; see `vignette("rmarkdown", package = "summarytools")` for more details

## 5.4 Excluding Columns

Although most columns can be excluded using the function’s parameters, it is also possible to delete them with the following syntax (results not shown):

    dfs <- dfSummary(iris)
    dfs$Variable <- NULL # This deletes the Variable column

<< 5. Data Frame Summaries | TOC | 7. Grouped Statistics: group_by() >>

# 6. Grouped Statistics: stby()

To produce optimal results, summarytools has its own version of the base `by()` function. It’s called `stby()`, and we use it as we would `by()`, with a notable difference: set the `useNA` parameter to `TRUE` to create an additional group for observations containing `NA`s on the grouping variable(s) (see example in section 6.2).

    (iris_stats_by_species <- stby(data      = iris,
                                   INDICES   = iris$Species,
                                   FUN       = descr,
                                   stats     = "common",
                                   transpose = TRUE))

### Descriptive Statistics

iris
Group: Species = setosa
N: 50

| Mean | Std.Dev | Min | Median | Max | N.Valid | N | Pct.Valid
--- | --- | --- | --- | --- | --- | --- | --- | ---
Petal.Length | 1.46 | 0.17 | 1.00 | 1.50 | 1.90 | 50.00 | 50.00 | 100.00
Petal.Width | 0.25 | 0.11 | 0.10 | 0.20 | 0.60 | 50.00 | 50.00 | 100.00
Sepal.Length | 5.01 | 0.35 | 4.30 | 5.00 | 5.80 | 50.00 | 50.00 | 100.00
Sepal.Width | 3.43 | 0.38 | 2.30 | 3.40 | 4.40 | 50.00 | 50.00 | 100.00

Group: Species = versicolor
N: 50

| Mean | Std.Dev | Min | Median | Max | N.Valid | N | Pct.Valid
--- | --- | --- | --- | --- | --- | --- | --- | ---
Petal.Length | 4.26 | 0.47 | 3.00 | 4.35 | 5.10 | 50.00 | 50.00 | 100.00
Petal.Width | 1.33 | 0.20 | 1.00 | 1.30 | 1.80 | 50.00 | 50.00 | 100.00
Sepal.Length | 5.94 | 0.52 | 4.90 | 5.90 | 7.00 | 50.00 | 50.00 | 100.00
Sepal.Width | 2.77 | 0.31 | 2.00 | 2.80 | 3.40 | 50.00 | 50.00 | 100.00

Group: Species = virginica
N: 50

| Mean | Std.Dev | Min | Median | Max | N.Valid | N | Pct.Valid
--- | --- | --- | --- | --- | --- | --- | --- | ---
Petal.Length | 5.55 | 0.55 | 4.50 | 5.55 | 6.90 | 50.00 | 50.00 | 100.00
Petal.Width | 2.03 | 0.27 | 1.40 | 2.00 | 2.50 | 50.00 | 50.00 | 100.00
Sepal.Length | 6.59 | 0.64 | 4.90 | 6.50 | 7.90 | 50.00 | 50.00 | 100.00
Sepal.Width | 2.97 | 0.32 | 2.20 | 3.00 | 3.80 | 50.00 | 50.00 | 100.00

## 6.1 Special Case of descr() with stby()

When used to produce split-group statistics for a single variable, `stby()` assembles everything into a single table instead of displaying a series of one-column tables.

    with(tobacco,
         stby(data    = BMI,
              INDICES = age.gr,
              FUN     = descr,
              stats   = c("mean", "sd", "min", "med", "max"),
              useNA   = TRUE)
    )

### Descriptive Statistics

BMI by age.gr
Data Frame: tobacco
N: 1000

| 18-34 | 35-50 | 51-70 | 71 + | NA
--- | --- | --- | --- | --- | ---
Mean | 23.84 | 25.11 | 26.91 | 27.45 | 25.17
Std.Dev | 4.23 | 4.34 | 4.26 | 4.37 | 3.64
Min | 8.83 | 10.35 | 9.01 | 16.36 | 18.84
Median | 24.04 | 25.11 | 26.77 | 27.52 | 24.91
Max | 34.84 | 39.44 | 39.21 | 38.37 | 31.83

## 6.2 Using stby() with ctable()

The syntax is a little trickier for this combination, so here is an example (results not shown):

    stby(data    = list(x = tobacco$smoker, y = tobacco$diseased),
         INDICES = tobacco$gender,
         FUN     = ctable)

    # or equivalently
    with(tobacco,
         stby(data    = list(x = smoker, y = diseased),
              INDICES = gender,
              FUN     = ctable))

<< 6. Grouped Statistics : group_by() | TOC | 8. Tidy Tables : tb() >>

# 7. Grouped Statistics: group_by()

To create grouped statistics with `freq()`, `descr()` or `dfSummary()`, it is possible to use dplyr’s `group_by()` as an alternative to `stby()`. Usings `forcats::fct_na_value_to_level` to make `NA`’s explicit in factors is recommended:

    library(dplyr)
    tobacco$gender %<>% forcats::fct_na_value_to_level()
    tobacco %>%
      group_by(gender) %>%
      descr(stats = "fivenum")

### Descriptive Statistics

tobacco
Group: gender = F
N: 489

| BMI | age | cigs.per.day | samp.wgts
--- | --- | --- | --- | ---
Min | 9.01 | 18.00 | 0.00 | 0.86
Q1 | 22.98 | 34.00 | 0.00 | 0.86
Median | 25.87 | 50.00 | 0.00 | 1.04
Q3 | 29.48 | 66.00 | 10.50 | 1.05
Max | 39.44 | 80.00 | 40.00 | 1.06

Group: gender = M
N: 489

| BMI | age | cigs.per.day | samp.wgts
--- | --- | --- | --- | ---
Min | 8.83 | 18.00 | 0.00 | 0.86
Q1 | 22.52 | 34.00 | 0.00 | 0.86
Median | 25.14 | 49.50 | 0.00 | 1.04
Q3 | 27.96 | 66.00 | 11.00 | 1.05
Max | 36.76 | 80.00 | 40.00 | 1.06

Group: gender = (Missing)
N: 22

| BMI | age | cigs.per.day | samp.wgts
--- | --- | --- | --- | ---
Min | 20.24 | 19.00 | 0.00 | 0.86
Q1 | 24.97 | 36.00 | 0.00 | 1.04
Median | 27.16 | 55.50 | 0.00 | 1.05
Q3 | 30.23 | 64.00 | 10.00 | 1.05
Max | 32.43 | 80.00 | 28.00 | 1.06

<< 7. Grouped Statistics : group_by() | TOC | 9. Directing Output to Files >>

# 8. Tidy Tables : tb()

When generating `freq()` or `descr()` tables, it is possible to turn the results into “tidy” tables with the use of the `tb()` function (think of tb as a diminutive for tibble). For example:

    library(magrittr)
    iris %>%
      descr(stats = "common") %>%
      tb() %>%
      knitr::kable()

variable | mean | sd | min | med | max | n.valid | n | pct.valid
--- | --- | --- | --- | --- | --- | --- | --- | ---
Petal.Length | 3.758000 | 1.7652982 | 1.0 | 4.35 | 6.9 | 150 | 150 | 100
Petal.Width | 1.199333 | 0.7622377 | 0.1 | 1.30 | 2.5 | 150 | 150 | 100
Sepal.Length | 5.843333 | 0.8280661 | 4.3 | 5.80 | 7.9 | 150 | 150 | 100
Sepal.Width | 3.057333 | 0.4358663 | 2.0 | 3.00 | 4.4 | 150 | 150 | 100

    iris$Species %>%
      freq(cumul = FALSE, report.nas = FALSE) %>%
      tb() %>%
      knitr::kable()

Species | freq | pct
--- | --- | ---
setosa | 50 | 33.33333
versicolor | 50 | 33.33333
virginica | 50 | 33.33333

By definition, no total rows are part of tidy tables, and the row names are turned into a regular column.

| When displaying tibbles using rmarkdown, the knitr chunk option `results` should be set to ‘markup’ instead of ‘asis’.

Not all tables generated by tb() are strictly speaking tidy; you can choose for instance to not recalculate proportions/valid proportions of grouped freq outputs, using `recalculate = FALSE` (TRUE by default).

## 8.1 Tidy Split-Group Statistics

Here are some examples showing how lists created using `stby()` or `group_by()` can be transformed into tidy tibbles.

    grouped_descr <- stby(data    = exams,
                          INDICES = exams$gender,
                          FUN     = descr,
                          stats   = "common")

    grouped_descr %>% tb()

    # A tibble: 12 × 10
       gender variable   mean    sd   min   med   max n.valid     n pct.valid
       <fct>  <chr>     <dbl> <dbl> <dbl> <dbl> <dbl>   <dbl> <dbl>     <dbl>
     1 Girl   economics  72.5  7.79  62.3  70.2  89.6      14    15      93.3
     2 Girl   english    73.9  9.41  58.3  71.8  93.1      14    15      93.3
     3 Girl   french     71.1 12.4   44.8  68.4  93.7      14    15      93.3
     4 Girl   geography  67.3  8.26  50.4  67.3  78.9      15    15     100
     5 Girl   history    71.2  9.17  53.9  72.9  86.4      15    15     100
     6 Girl   math       73.8  9.03  55.6  74.8  86.3      14    15      93.3
     7 Boy    economics  75.2  9.40  60.5  71.7  94.2      15    15     100
     8 Boy    english    77.8  5.94  69.6  77.6  90.2      15    15     100
     9 Boy    french     76.6  8.63  63.2  74.8  94.7      15    15     100
    10 Boy    geography  73   12.4   47.2  71.2  96.3      14    15      93.3
    11 Boy    history    74.4 11.2   54.4  72.6  93.5      15    15     100
    12 Boy    math       73.3  9.68  60.5  72.2  93.2      14    15      93.3

The `order` parameter controls row ordering:

    grouped_descr %>% tb(order = 2)

    # A tibble: 12 × 10
       gender variable   mean    sd   min   med   max n.valid     n pct.valid
       <fct>  <chr>     <dbl> <dbl> <dbl> <dbl> <dbl>   <dbl> <dbl>     <dbl>
     1 Girl   economics  72.5  7.79  62.3  70.2  89.6      14    15      93.3
     2 Boy    economics  75.2  9.40  60.5  71.7  94.2      15    15     100
     3 Girl   english    73.9  9.41  58.3  71.8  93.1      14    15      93.3
     4 Boy    english    77.8  5.94  69.6  77.6  90.2      15    15     100
     5 Girl   french     71.1 12.4   44.8  68.4  93.7      14    15      93.3
     6 Boy    french     76.6  8.63  63.2  74.8  94.7      15    15     100
     7 Girl   geography  67.3  8.26  50.4  67.3  78.9      15    15     100
     8 Boy    geography  73   12.4   47.2  71.2  96.3      14    15      93.3
     9 Girl   history    71.2  9.17  53.9  72.9  86.4      15    15     100
    10 Boy    history    74.4 11.2   54.4  72.6  93.5      15    15     100
    11 Girl   math       73.8  9.03  55.6  74.8  86.3      14    15      93.3
    12 Boy    math       73.3  9.68  60.5  72.2  93.2      14    15      93.3

Setting `order = 3` changes the order of the sort variables exactly as with `order = 2`, but it also reorders the columns:

    grouped_descr %>% tb(order = 3)

    # A tibble: 12 × 10
       variable  gender  mean    sd   min   med   max n.valid     n pct.valid
       <chr>     <fct>  <dbl> <dbl> <dbl> <dbl> <dbl>   <dbl> <dbl>     <dbl>
     1 economics Girl    72.5  7.79  62.3  70.2  89.6      14    15      93.3
     2 economics Boy     75.2  9.40  60.5  71.7  94.2      15    15     100
     3 english   Girl    73.9  9.41  58.3  71.8  93.1      14    15      93.3
     4 english   Boy     77.8  5.94  69.6  77.6  90.2      15    15     100
     5 french    Girl    71.1 12.4   44.8  68.4  93.7      14    15      93.3
     6 french    Boy     76.6  8.63  63.2  74.8  94.7      15    15     100
     7 geography Girl    67.3  8.26  50.4  67.3  78.9      15    15     100
     8 geography Boy     73   12.4   47.2  71.2  96.3      14    15      93.3
     9 history   Girl    71.2  9.17  53.9  72.9  86.4      15    15     100
    10 history   Boy     74.4 11.2   54.4  72.6  93.5      15    15     100
    11 math      Girl    73.8  9.03  55.6  74.8  86.3      14    15      93.3
    12 math      Boy     73.3  9.68  60.5  72.2  93.2      14    15      93.3

Note that percentages will be recalculated, unless setting `tb()`’s `recalculate` argument to `FALSE`, in which case the results won’t comply with the tidy principles.

    tobacco %>% dplyr::group_by(gender) %>% freq(smoker) %>% tb()

    # A tibble: 9 × 7
      gender    smoker  freq pct_valid pct_valid_cum pct_tot pct_tot_cum
      <fct>     <fct>  <dbl>     <dbl>         <dbl>   <dbl>       <dbl>
    1 F         Yes      147      14.7          14.7    14.7        14.7
    2 F         No       342      34.2          48.9    34.2        48.9
    3 F         <NA>       0      NA            NA       0          48.9
    4 M         Yes      143      14.3          63.2    14.3        63.2
    5 M         No       346      34.6          97.8    34.6        97.8
    6 M         <NA>       0      NA            NA       0          97.8
    7 (Missing) Yes        8       0.8          98.6     0.8        98.6
    8 (Missing) No        14       1.4         100       1.4       100
    9 (Missing) <NA>       0      NA            NA       0         100

For more details, see `?tb`.

## 8.2 A Bridge to Other Packages

summarytools objects are not always compatible with packages focused on table formatting, such as formattable or kableExtra. However, `tb()` can be used as a “bridge”, an intermediary step turning `freq()` and `descr()` objects into simple tables that any package can work with. Here is an example using kableExtra:

    library(kableExtra)
    library(magrittr)
    stby(data    = iris,
         INDICES = iris$Species,
         FUN     = descr,
         stats   = "fivenum") %>%
      tb(order = 3) %>%
      kable(format = "html", digits = 2) %>%
      collapse_rows(columns = 1, valign = "top")

variable | Species | min | q1 | med | q3 | max
--- | --- | --- | --- | --- | --- | ---
Petal.Length | setosa | 1.0 | 1.4 | 1.50 | 1.6 | 1.9
versicolor | 3.0 | 4.0 | 4.35 | 4.6 | 5.1
virginica | 4.5 | 5.1 | 5.55 | 5.9 | 6.9
Petal.Width | setosa | 0.1 | 0.2 | 0.20 | 0.3 | 0.6
versicolor | 1.0 | 1.2 | 1.30 | 1.5 | 1.8
virginica | 1.4 | 1.8 | 2.00 | 2.3 | 2.5
Sepal.Length | setosa | 4.3 | 4.8 | 5.00 | 5.2 | 5.8
versicolor | 4.9 | 5.6 | 5.90 | 6.3 | 7.0
virginica | 4.9 | 6.2 | 6.50 | 6.9 | 7.9
Sepal.Width | setosa | 2.3 | 3.2 | 3.40 | 3.7 | 4.4
versicolor | 2.0 | 2.5 | 2.80 | 3.0 | 3.4
virginica | 2.2 | 2.8 | 3.00 | 3.2 | 3.8

<< 8. Tidy Tables : tb() | TOC | 10. Global Options >>

# 9. Directing Output to Files

Using the `file` argument with `print()` or `view()` / `stview()`, we can write outputs to a file, be it html, Rmd, md, or just plain text (txt). The file extension is used to determine the type of content to write out.

    view(iris_stats_by_species, file = "~/iris_stats_by_species.html")
    view(iris_stats_by_species, file = "~/iris_stats_by_species.md")

A Note on PDF documents

There is no direct way to create a PDF file with summarytools. One option is to generate an html file and convert it to PDF using Pandoc or WK<html>TOpdf (the latter gives better results than Pandoc with `dfSummary()` output).

Another option is to create an Rmd document using PDF as the output format. See `vignette("rmarkdown", package = "summarytools")` for the details on how to proceed.

## 9.1 Appending Output Files

The `append` argument allows adding content to existing files generated by summarytools. This is useful if we wish to include several statistical tables in a single file. It is a quick alternative to creating an Rmd document.

<< 9. Directing Output to Files | TOC | 11. Format Attributes >>

# 10. Package Options

The following options can be set globally with `st_options()`:

## 10.1 General Options

Option name | Default | Note
--- | --- | ---
style (1) | “simple” | Set to “rmarkdown” in .Rmd documents
plain.ascii | TRUE | Set to FALSE in .Rmd documents
round.digits (2) | 2 | Number of decimals to show
headings | TRUE | Formerly “omit.headings”
footnote | “default” | Customize or set to NA to omit
display.labels | TRUE | Show variable / data frame labels in headings
na.val | NULL | Value to treat as NA in factor / char variables
bootstrap.css (3) | TRUE | Include Bootstrap 4 CSS in html output files
custom.css | NA | Path to your own CSS file
escape.pipe | FALSE | Useful for some Pandoc conversions
char.split (4) | 12 | Threshold for line-wrapping in column headings
subtitle.emphasis | TRUE | Controls headings formatting
lang | “en” | Language (always 2-letter, lowercase)

1 Does not apply to `dfSummary()`, which has its own style option (see next table)
2 Does not apply to `ctable()`, which has its own `round.digits` option (see next table)
3 Set to `FALSE` in Shiny apps
4 Affects only html outputs for `descr()` and `ctable()`

## 10.2 Function-Specific Options

Option name | Default | Note
--- | --- | ---
freq.cumul | TRUE | Display cumulative proportions in freq()
freq.totals | TRUE | Display totals row in freq()
freq.report.nas | TRUE | Display row and “valid” columns
freq.ignore.threshold (1) | 25 | Used to determine which vars to ignore
freq.silent | FALSE | Hide console messages
ctable.prop | “r” | Display row proportions by default
ctable.totals | TRUE | Show marginal totals
ctable.round.digits | 1 | Number of decimals to show in `ctable()`
ctable.silent | FALSE | Hide console messages
descr.stats | “all” | “fivenum”, “common” or vector of stats
descr.transpose | FALSE | Display stats in columns instead of rows
descr.silent | FALSE | Hide console messages
dfSummary.style | “multiline” | Can be set to “grid” as an alternative
dfSummary.varnumbers | TRUE | Show variable numbers in 1st col.
dfSummary.labels.col | TRUE | Show variable labels when present
dfSummary.graph.col | TRUE | Show graphs
dfSummary.valid.col | TRUE | Include the Valid column in the output
dfSummary.na.col | TRUE | Include the Missing column in the output
dfSummary.graph.magnif | 1 | Zoom factor for bar plots and histograms
dfSummary.silent | FALSE | Hide console messages
tmp.img.dir (2) | NA | Directory to store temporary images
use.x11 (3) | TRUE | Allow creation of Base64-encoded graphs

1 See section 2.3 for details
2 Applies to `dfSummary()` only
3 Set to FALSE in text-only environments

### Examples

    st_options()                      # Display all global options values
    st_options('round.digits')        # Display the value of a specific option
    st_options(style = 'rmarkdown',   # Set the value of one or several options
               footnote = NA)         # Turn off the footnote for all html output

<< 10. Global Options | TOC | 12. Fine-Tuning Looks : CSS >>

# 11. Format Attributes

When a summarytools object is created, its formatting attributes are stored within it. However, we can override most of them when using `print()` or `view()` / `stview()`.

## 11.1 Overriding Function-Specific Arguments

The following table indicates what arguments can be used with `print()` or `view()` / `stview()` to override formatting attributes. Base R’s `format()` arguments can also be used (they are not listed here).

Argument | freq | ctable | descr | dfSummary
--- | --- | --- | --- | ---
style | x | x | x | x
round.digits | x | x | x |
plain.ascii | x | x | x | x
justify | x | x | x | x
headings | x | x | x | x
display.labels | x | x | x | x
varnumbers |  |  |  | x
labels.col |  |  |  | x
graph.col |  |  |  | x
valid.col |  |  |  | x
na.col |  |  |  | x
col.widths |  |  |  | x
totals | x | x |  |
report.nas | x |  |  |
display.type | x |  |  |
missing | x |  |  |
split.tables (1) | x | x | x | x
caption (1) | x | x | x | x

1 pander options

## 11.2 Overriding Heading Contents

To change the information shown in the heading section, use the following arguments with `print()` or `view()`:

Argument | freq | ctable | descr | dfSummary
--- | --- | --- | --- | ---
Data.frame | x | x | x | x
Data.frame.label | x | x | x | x
Variable | x | x | x |
Variable.label | x | x | x |
Group | x | x | x | x
date | x | x | x | x
Weights | x |  | x |
Data.type | x |  |  |
Row.variable |  | x |  |
Col.variable |  | x |  |

### Example

In the following example, we will create and display a `freq()` object, and then display it again, this time overriding three of its formatting attributes, as well as one of its heading attributes.

    (age_stats <- freq(tobacco$age.gr))

### Frequencies

tobacco$age.gr
Type: Factor

| Freq | % Valid | % Valid Cum. | % Total | % Total Cum.
--- | --- | --- | --- | --- | ---
18-34 | 258 | 26.46 | 26.46 | 25.80 | 25.80
35-50 | 241 | 24.72 | 51.18 | 24.10 | 49.90
51-70 | 317 | 32.51 | 83.69 | 31.70 | 81.60
71 + | 159 | 16.31 | 100.00 | 15.90 | 97.50
<NA> | 25 |  |  | 2.50 | 100.00
Total | 1000 | 100.00 | 100.00 | 100.00 | 100.00

    print(age_stats,
          report.nas     = FALSE,
          totals         = FALSE,
          display.type   = FALSE,
          Variable.label = "Age Group")

### Frequencies

tobacco$age.gr
Label: Age Group

| Freq | % | % Cum.
--- | --- | --- | ---
18-34 | 258 | 26.46 | 26.46
35-50 | 241 | 24.72 | 51.18
51-70 | 317 | 32.51 | 83.69
71 + | 159 | 16.31 | 100.00

## 11.3 Order of Priority for Parameters / Options

1. `print()` or `view()` parameters have precedence (overriding feature)
2. `freq() / ctable() / descr() / dfSummary()` parameters come second
3. Global options set with `st_options()` come third and act as default

The logic for the evaluation of the various parameter values can be summarized as follows:

- If an argument is explicitly supplied in the function call, it will have precedence.

- If both the core function and the `print()` / `view()` / `stview()` function are called at once and have conflicting parameter values, the latter has precedence (they always win the argument!).

- If the parameter values cannot be found in the function calls, the stored defaults (which can be modified with `st_options()`) will be applied.

<< 11. Format Attributes | TOC | 13. Shiny Apps >>

# 12. Fine-Tuning Looks : CSS

When creating html reports, both Bootstrap’s CSS and summarytools.css are included by default. For greater control on the looks of html content, it is also possible to add class definitions in a custom CSS file.

### Example

We need to use a very small font size for a simple html report containing a `dfSummary()`. For this, we create a .css file (with the name of our choosing) which contains the following class definition:

    .tiny-text {
      font-size: 8px;
    }

Then we use `print()`’s `custom.css` argument to specify to location of our newly created CSS file (results not shown):

    print(dfSummary(tobacco),
          custom.css    = 'path/to/custom.css',
          table.classes = 'tiny-text',
          file          = "tiny-tobacco-dfSummary.html")

<< 12. Fine-Tuning Looks : CSS | TOC | 14. Graphs in R Markdown >>

# 13. Shiny Apps

To successfully include summarytools functions in Shiny apps,

- use html rendering
- set `bootstrap.css = FALSE` to avoid interacting with the app’s layout
- set `headings = FALSE` in case problems arise
- adjust graph sizes with the `graph.magnif` parameter or with the `dfSummary.graph.magnif` global option
- if `dfSummary()` tables are too wide, omit a column or two (`valid.col` and `varnumbers`, for instance)
- if the results are still unsatisfactory, set column widths manually with the `col.widths` parameter
- if `col.widths` or `graph.magnig` do not seem to work, try using them as parameters for `print()` rather than `dfSummary()`

### Example (results not shown)

    print(dfSummary(somedata,
                    varnumbers   = FALSE,
                    valid.col    = FALSE,
                    graph.magnif = 0.8),
          method   = 'render',
          headings = FALSE,
          bootstrap.css = FALSE)

<< 13. Shiny Apps | TOC | 15. Languages & Term Customization >>

# 14. Graphs in R Markdown

When using `dfSummary()` in an Rmd document using markdown styling (as opposed to html rendering), three elements are needed in order to display the png graphs properly:

1 - `plain.ascii` must be set to FALSE
2 - `style` must be set to “grid”
3 - `tmp.img.dir` must be defined and be at most 5 characters wide

Note that as of version 0.9.9, setting `tmp.img.dir` is no longer required when using `method = "render"`. It is only necessary when a transitory markdown output is created.

In markdown tables, Pandoc uses string lengths (number of characters in the cells) to set minimal column widths. A wide image can cause a column to expand as needed, but a narrow image won’t make it “shrink”. This is why it is important to keep the link / path as succinct as possible.

    +---------------+--------|----------------------+---------+
    | Variable      | stats  |  Graph               | Valid   |
    +===============+========|======================+=========+
    | age\          |  ...   | ![](/tmp/ds0001.png) | 978\    |
    | [numeric]     |  ...   |                      | (97.8%) |
    +---------------+--------+----------------------+---------+

CRAN policies are really strict when it comes to writing content in the user directories, or anywhere outside R’s temporary zone (for good reasons). So users need to set this temporary location themselves.

On Mac OS and Linux, using “/tmp” makes a lot of sense: it’s a short path, and the directory is purged automatically. On Windows, there is no such convenient directory, so we need to pick one – be it absolute (“/tmp”) or relative (“img”, or simply “.”).

<< 14. Graphs in R Markdown | TOC | 16. Vignette Setup >>

# 15. Languages & Term Customization

Thanks to the R community’s efforts, the following languages can be used, in addition to English (default):

- French (fr)
- Portuguese (pt)
- Russian (ru)
- Spanish (es)
- Turkish (tr)

To switch languages, simply use

    st_options(lang = "fr")

All output from the core functions will now use that language:

    freq(iris$Species)

### Tableau de fréquences

iris$Species
Type: Facteur

| Fréq. | % Valide | % Valide cum. | % Total | % Total cum.
--- | --- | --- | --- | --- | ---
setosa | 50 | 33.33 | 33.33 | 33.33 | 33.33
versicolor | 50 | 33.33 | 66.67 | 33.33 | 66.67
virginica | 50 | 33.33 | 100.00 | 33.33 | 100.00
<NA> | 0 |  |  | 0.00 | 100.00
Total | 150 | 100.00 | 100.00 | 100.00 | 100.00

## 15.1 Non-UTF-8 Locales

On most Windows systems, it is necessary to change the `LC_CTYPE` element of the locale settings if the character set is not included in the system’s default locale. For instance, in order to get good results with the Russian language in a “latin1” environment, use the following settings:

    Sys.setlocale("LC_CTYPE", "russian")
    st_options(lang = 'ru')

To go back to default settings…

    Sys.setlocale("LC_CTYPE", "")
    st_options(lang = "en")

## 15.2 Defining and Using Custom Terms

Using the function `use_custom_lang()`, it is possible to add your own set of translations or custom terms. To achieve this, get the csv template, customize one, many or all of the +/- 80 terms, and call `use_custom_lang()` with the path to the edited csv template.

## 15.3 Defining Specific Keywords

The `define_keywords()` makes it easy to change just one or a few terms. For instance, you might prefer using “N” or “Count” rather than “Freq” in the title row of `freq()` tables. Or you might want to generate a document which uses the tables’ titles as sections titles.

For this, call `define_keywords()` and feed it the term(s) you wish to modify (which can themselves be stored in predefined variables). Here, the terms we need to change are `freq.title` and `freq`:

    section_title <- "**Species of Iris**"
    define_keywords(title.freq = section_title,
                    freq = "N")
    freq(iris$Species)

### Species of Iris

iris$Species
Type: Factor

| N | % Valid | % Valid Cum. | % Total | % Total Cum.
--- | --- | --- | --- | --- | ---
setosa | 50 | 33.33 | 33.33 | 33.33 | 33.33
versicolor | 50 | 33.33 | 66.67 | 33.33 | 66.67
virginica | 50 | 33.33 | 100.00 | 33.33 | 100.00
<NA> | 0 |  |  | 0.00 | 100.00
Total | 150 | 100.00 | 100.00 | 100.00 | 100.00

Calling `define_keywords()` without any arguments will bring up, on systems that support graphical devices (the vast majority, that is), a window from which you can edit all terms.

After closing the edit window, a dialogue box gives the option to save the newly created custom language to a csv file. (Even if we only modify one or two keywords, the package considers the terms as part of a whole “language”). We can later reload into memory the custom language file by calling `use_custom_lang("language-file.csv")`.

See `?define_keywords` for a list of all customizable terms.

To revert all changes, simply use `st_options(lang = "en")`.

## 15.4 Power-Tweaking Headings

It is possible to further customize the headings by adding arguments to the `print()` function. Here, we use an empty string to override the value of `Variable`; this causes the second line of the heading to disappear altogether.

    define_keywords(title.freq = "Types and Counts, Iris Flowers")
    print(
      freq(iris$Species,
           display.type = FALSE), # Variable type won't be displayed...
      Variable = ""               # and neither will the variable name
      )

### Types and Counts, Iris Flowers

| N | % Valid | % Valid Cum. | % Total | % Total Cum.
--- | --- | --- | --- | --- | ---
setosa | 50 | 33.33 | 33.33 | 33.33 | 33.33
versicolor | 50 | 33.33 | 66.67 | 33.33 | 66.67
virginica | 50 | 33.33 | 100.00 | 33.33 | 100.00
<NA> | 0 |  |  | 0.00 | 100.00
Total | 150 | 100.00 | 100.00 | 100.00 | 100.00

<< 15. Translations & Term Customization | TOC | 17. Conclusion >>

# 16. Vignette Setup

Knowing how this vignette is configured can help you get started with summarytools in R Markdown.

## 16.1 The YAML Section

The output element is the one that matters:

    ---
    output:
     rmarkdown::html_vignette:
       css:
       - !expr system.file("rmarkdown/templates/html_vignette/resources/vignette.css", package = "rmarkdown")
    ---

## 16.2 The Setup Chunk

    ```{r setup, include=FALSE}
    library(knitr)
    opts_chunk$set(results = 'asis',     # Can also be set at chunk level
                  comment = NA,
                  prompt  = FALSE,
                  cache   = FALSE)
    library(summarytools)
    st_options(plain.ascii = FALSE,       # Always use in Rmd documents
               style       = "rmarkdown", # Always use in Rmd documents
               subtitle.emphasis = FALSE) # Improves layout w/ some themes
    ```

## 16.3 Including summarytools’ CSS

The needed CSS is automatically added to html files created using `print()` or `view()` with the `file` argument. But in R Markdown documents, this needs to be done explicitly in a setup chunk just after the YAML header (or following a first setup chunk specifying knitr and summarytools options):

    ```{r, echo=FALSE}
    st_css(main = TRUE, global = TRUE)
    ```

<< 16. Vignette Setup | TOC

# 17. Conclusion

The package comes with no guarantees. It is a work in progress and feedback is welcome. Please open an issue on GitHub if you find a bug or wish to submit a feature request.

summarytools is the result of many hours of work. If it brings value to your work, please consider making a small donation using this Paypal link, or buy me a coffee.

### Stay Up to Date

Check out the GitHub project’s page; from there you can see the latest updates and also submit feature requests.

For a preview of what’s coming in the next release, have a look at the development branch.

TOC