# Lemon print – pretty printing data frames and tables

#### Stefan McKinnon Edwards sme@iysik.com

#### 2025-09-04

This vignette demonstrates how load the `lemon` package automatically enables pretty printing of data frames with knitr’s `kable`.

The beauty of defining the function `knit_print.data.frame` is that when working with R Notebooks in R, the editor can output the data frame dynamically, while ensuring that the knitted document also has the

First we load some data and show the top.

    data(USArrests)
    head(USArrests)

    ##            Murder Assault UrbanPop Rape
    ## Alabama      13.2     236       58 21.2
    ## Alaska       10.0     263       48 44.5
    ## Arizona       8.1     294       80 31.0
    ## Arkansas      8.8     190       50 19.5
    ## California    9.0     276       91 40.6
    ## Colorado      7.9     204       78 38.7

Now load `lemon` and set the `lemon_print` functions.

    library(lemon)
    knit_print.data.frame <- lemon_print

The same view is now nicely printed!

    ```{r caption="Data frame is now printed using `kable`.",render=lemon_print}
    head(USArrests)
    ```

Data frame is now printed using `kable`.
| Murder | Assault | UrbanPop | Rape
--- | --- | --- | --- | ---
Alabama | 13.2 | 236 | 58 | 21.2
Alaska | 10.0 | 263 | 48 | 44.5
Arizona | 8.1 | 294 | 80 | 31.0
Arkansas | 8.8 | 190 | 50 | 19.5
California | 9.0 | 276 | 91 | 40.6
Colorado | 7.9 | 204 | 78 | 38.7

Notice how we specified `kable` arguments directly as chunk-options.

## How it works

knitr uses a S3 generic function, `knit_print`, to print objects. Method dispatching in R means that by defining a function, say `knit_print.data.frame`, calling `knit_print(df)` will call our function if `df` was a data frame.

To have knitr use our function when outputting a data frame, we define the function `knit_print.data.frame` (and similar for data frames passed through dplyr functions, i.e `tbl_df` and `grouped_df`).

Disabling the function is by usual chunk options, `render = normal_print`. check `?knit_print`.

Disabling the functions.

    ```{r normal_print,render=normal_print}
    head(USArrests)
    ```

    ##            Murder Assault UrbanPop Rape
    ## Alabama      13.2     236       58 21.2
    ## Alaska       10.0     263       48 44.5
    ## Arizona       8.1     294       80 31.0
    ## Arkansas      8.8     190       50 19.5
    ## California    9.0     276       91 40.6
    ## Colorado      7.9     204       78 38.7

You can still ask for `kable`.

    ```{r results='asis',render=normal_print}
    kable(head(USArrests),caption='Normal `kable` usage.')
    ```

Normal `kable` usage.
| Murder | Assault | UrbanPop | Rape
--- | --- | --- | --- | ---
Alabama | 13.2 | 236 | 58 | 21.2
Alaska | 10.0 | 263 | 48 | 44.5
Arizona | 8.1 | 294 | 80 | 31.0
Arkansas | 8.8 | 190 | 50 | 19.5
California | 9.0 | 276 | 91 | 40.6
Colorado | 7.9 | 204 | 78 | 38.7

### It uses kable

We have mentioned `kable` a couple of times. That is because the `lemon_print` for data frames uses `kable` of the knitr package.

Some common arguments:

- `caption`: Table caption. See examples in demonstration.
- `align`: Vector of column alignments with `'l'`, `'c'`, and `'r'`. Or, a single element, i.e. `c('c','l','r') = 'clr')`.
- `row.names`: A logical value indicating whether to include row names.
- `col.names`: A character vector of column names to be used in the table.

## Why does it matter

The second code block, when edited in RStudio, would look like this: And, as demonstrated, the rendered document has the data frame printed nicely.

If we do not wish this behaviour, but still use `kable`,

    kable(head(USArrests))

| Murder | Assault | UrbanPop | Rape
--- | --- | --- | --- | ---
Alabama | 13.2 | 236 | 58 | 21.2
Alaska | 10.0 | 263 | 48 | 44.5
Arizona | 8.1 | 294 | 80 | 31.0
Arkansas | 8.8 | 190 | 50 | 19.5
California | 9.0 | 276 | 91 | 40.6
Colorado | 7.9 | 204 | 78 | 38.7

RStudio would look like this:

## Other objects covered

Summaries are nicely formatted, but these require setting the S3 method for `table`.

    ```{r kable.opts=list(caption='Summary tables are printed with some default options.')}
    knit_print.table <- lemon_print
    summary(USArrests)
    ```

Summary tables are printed with some default options.
Murder | Assault | UrbanPop | Rape
--- | --- | --- | ---
Min. : 0.800 | Min. : 45.0 | Min. :32.00 | Min. : 7.30
1st Qu.: 4.075 | 1st Qu.:109.0 | 1st Qu.:54.50 | 1st Qu.:15.07
Median : 7.250 | Median :159.0 | Median :66.00 | Median :20.10
Mean : 7.788 | Mean :170.8 | Mean :65.54 | Mean :21.23
3rd Qu.:11.250 | 3rd Qu.:249.0 | 3rd Qu.:77.75 | 3rd Qu.:26.18
Max. :17.400 | Max. :337.0 | Max. :91.00 | Max. :46.00

Cross-tabulations are however not nicely formatted.

    head(mtcars)

    ##                    mpg cyl disp  hp drat    wt  qsec vs am gear carb
    ## Mazda RX4         21.0   6  160 110 3.90 2.620 16.46  0  1    4    4
    ## Mazda RX4 Wag     21.0   6  160 110 3.90 2.875 17.02  0  1    4    4
    ## Datsun 710        22.8   4  108  93 3.85 2.320 18.61  1  1    4    1
    ## Hornet 4 Drive    21.4   6  258 110 3.08 3.215 19.44  1  0    3    1
    ## Hornet Sportabout 18.7   8  360 175 3.15 3.440 17.02  0  0    3    2
    ## Valiant           18.1   6  225 105 2.76 3.460 20.22  1  0    3    1

    with(mtcars, table(cyl, gear))

    ##    gear
    ## cyl  3 4 5
    ##   4  1 8 2
    ##   6  2 4 1
    ##   8 12 0 2

### dplyr

    library(dplyr)

    ##
    ## Attaching package: 'dplyr'

    ## The following object is masked from 'package:gridExtra':
    ##
    ##     combine

    ## The following objects are masked from 'package:stats':
    ##
    ##     filter, lag

    ## The following objects are masked from 'package:base':
    ##
    ##     intersect, setdiff, setequal, union

    knit_print.tbl <- lemon_print

    ```{r dplyr,kable.opts=list(caption='Also works on `dplyr` objects.')}
    mtcars %>% group_by(cyl) %>% summarise(mean(disp))
    ```

Also works on `dplyr` objects.
cyl | mean(disp)
--- | ---
4 | 105.1364
6 | 183.3143
8 | 353.1000