The hardware and bandwidth for this mirror is donated by METANET, the Webhosting and Full Service-Cloud Provider.
If you wish to report a bug, or if you are interested in having us mirror your free-software or open-source project, please feel free to contact us at mirror[@]metanet.ch.

Introduction to ttt: Formatted Tables the Easy Way

Benjamin Rich

2021-05-06

Introduction

ttt stands for “The Table Tool” (or, if you prefer “Tables! Tables! Tables!”). It allows you to creates formatted HTML tables of in a flexible and convenient way.

Creating nice formatted tables has traditionally been a pain points with R. Over the years, however, things have gotten a lot better, with the emergence of packages that can produce nice looking tables in HTML (or LaTex or even Microsoft® Word), or which there are now a large number.1 But most of those packages treat tables just like data.frames, i.e. a grid of rows and columns, with very little structure. While some packages do have constructs that allow you to group columns or rows with additional headers and labels, that structure is basically superficial, tacked onto the data.frame after the fact. And while it may achieve the desired result it tends to require more code and by consequence, effort. Another thing that some of these packages do is they give you the flexibility to control the visual appearance or styling of the tables (fonts, colors, grid lines, spacing, etc.) directly from the R code. This is nice, but typically achieving that level of flexibility requires a pretty complex interface of functions and arguments dedicated to styling, and achieving the desired result can take a considerable amount of code and hence again, effort.

This package takes a different approach. It focuses on the table structure and content, leaving the formatting duties to CSS, a dedicated language that was designed specifically for this purpose (the downside is that it only works for HTML, but we accept this inconvenience). Also, the package follows the philosophy of not trying to solve all problems, but solving some problems well. Design decisions have been made to make some things easy, at the expense of limiting the package’s generality (while still keeping in it some sense quite general, as this vignette will demonstrate). That it is not possible with this package to produce all conceivable tables is a given; that was never the intention.

Basic Examples

Before we start, let’s load a couple of packages that we will be using:

library(table1, quietly=TRUE)
## 
## Attaching package: 'table1'
## The following objects are masked from 'package:base':
## 
##     units, units<-
library(magrittr, quietly=TRUE)

It is worth taking a minute to comment on these packages. The first, table1, is like a “sister” package of ttt (they are both written by the same author). While not a strict requirement, table1 contains some utility functions that can also be quite useful in conjunction with ttt, so most of the time it is a good idea to load table1 along with ttt. The magrittr package contains the well-known “pipe” operator that we will make use of at some point in this vignette, so we load that, too.

With that out of the way, let’s start looking at what ttt can do, and how to use it. The types of tables that can be produced are many and varied. At its simplest, the ttt() function can turn a data.frame into an HTML table:

ttt(mtcars)
mpg cyl disp hp drat wt qsec vs am gear carb
Mazda RX4 21.0 6 160.0 110 3.90 2.620 16.46 0 1 4 4
Mazda RX4 Wag 21.0 6 160.0 110 3.90 2.875 17.02 0 1 4 4
Datsun 710 22.8 4 108.0 93 3.85 2.320 18.61 1 1 4 1
Hornet 4 Drive 21.4 6 258.0 110 3.08 3.215 19.44 1 0 3 1
Hornet Sportabout 18.7 8 360.0 175 3.15 3.440 17.02 0 0 3 2
Valiant 18.1 6 225.0 105 2.76 3.460 20.22 1 0 3 1
Duster 360 14.3 8 360.0 245 3.21 3.570 15.84 0 0 3 4
Merc 240D 24.4 4 146.7 62 3.69 3.190 20.00 1 0 4 2
Merc 230 22.8 4 140.8 95 3.92 3.150 22.90 1 0 4 2
Merc 280 19.2 6 167.6 123 3.92 3.440 18.30 1 0 4 4
Merc 280C 17.8 6 167.6 123 3.92 3.440 18.90 1 0 4 4
Merc 450SE 16.4 8 275.8 180 3.07 4.070 17.40 0 0 3 3
Merc 450SL 17.3 8 275.8 180 3.07 3.730 17.60 0 0 3 3
Merc 450SLC 15.2 8 275.8 180 3.07 3.780 18.00 0 0 3 3
Cadillac Fleetwood 10.4 8 472.0 205 2.93 5.250 17.98 0 0 3 4
Lincoln Continental 10.4 8 460.0 215 3.00 5.424 17.82 0 0 3 4
Chrysler Imperial 14.7 8 440.0 230 3.23 5.345 17.42 0 0 3 4
Fiat 128 32.4 4 78.7 66 4.08 2.200 19.47 1 1 4 1
Honda Civic 30.4 4 75.7 52 4.93 1.615 18.52 1 1 4 2
Toyota Corolla 33.9 4 71.1 65 4.22 1.835 19.90 1 1 4 1
Toyota Corona 21.5 4 120.1 97 3.70 2.465 20.01 1 0 3 1
Dodge Challenger 15.5 8 318.0 150 2.76 3.520 16.87 0 0 3 2
AMC Javelin 15.2 8 304.0 150 3.15 3.435 17.30 0 0 3 2
Camaro Z28 13.3 8 350.0 245 3.73 3.840 15.41 0 0 3 4
Pontiac Firebird 19.2 8 400.0 175 3.08 3.845 17.05 0 0 3 2
Fiat X1-9 27.3 4 79.0 66 4.08 1.935 18.90 1 1 4 1
Porsche 914-2 26.0 4 120.3 91 4.43 2.140 16.70 0 1 5 2
Lotus Europa 30.4 4 95.1 113 3.77 1.513 16.90 1 1 5 2
Ford Pantera L 15.8 8 351.0 264 4.22 3.170 14.50 0 1 5 4
Ferrari Dino 19.7 6 145.0 175 3.62 2.770 15.50 0 1 5 6
Maserati Bora 15.0 8 301.0 335 3.54 3.570 14.60 0 1 5 8
Volvo 142E 21.4 4 121.0 109 4.11 2.780 18.60 1 1 4 2

But this is far from the typical use case. More typically, there is some structure to the data, in the sense that some columns contain values, while others contain keys that are used to group values according to some common characteristic. We will refer to these latter columns as facets, borrowing a term from the ggplot2 package. We will assume that the data are in a “tidy” format, by which we mean that all the values have been placed in a single column (if this is not the case, there are many functions that will allow you to “reshape” the data accordingly).

For the second example, continuing with the mtcars data, we would like to tabulate the average mpg (miles per gallon) by number of gears (rows) and cylinders (columns). Here is the code:

ttt(mpg ~ gear | cyl, data=mtcars, lab="Number of Cylinders", render=mean)
gear Number of Cylinders
4 6 8
3 21.5 19.75 15.05
4 26.925 19.75 NaN
5 28.2 19.7 15.4

Let’s break this down. The first argument is a formula with 3 parts: <values> ~ <row facets> | <column facets>. The first part, to the left of the symbol ~, is the name of the column that contains the values (recall that in the “tidy” format there is only one such column). The second part, between the symbols ~ and | contains the row facets, one or more variables that define how the values should be split into rows. The third part, to the right of the symbol | is the column facets, one or more variables that define how the values should be split into columns.

Following the formula comes the data argument, which is the data.frame that contains the data that the formula refers to. The next argument lab is an optional label placed over all the columns. The last argument, render, is a function. This function is called for each grouping of data defined by unique combinations of the row and column facets, and produces the value that appears in the corresponding cell of the table. Hopefully this is fairly intuitive.

Now, the above table is nice, but still not quite what we want. Here are the issues that need to be addressed:

  1. The label “gear” should be changed to “Number of Gears”.

  2. One table cell contains the cryptic value “NaN” because there aren’t any cars with 8 cylinders and 4 gears in our dataset; we would like this cell to remain empty instead.

  3. The number of decimal digits is different in each cell; we would like it to be the same throughout the table (1 decimal digit).

Let us now address these issues.

label(mtcars$gear) <- "Number of<br/>Gears"

rndr <- function(x, ...) {
    if (length(x) == 0) return("")
    round_pad(mean(x), 1)
}

ttt(mpg ~ gear | cyl, data=mtcars, lab="Number of Cylinders", render=rndr)
Number of
Gears
Number of Cylinders
4 6 8
3 21.5 19.8 15.1
4 26.9 19.8
5 28.2 19.7 15.4

The way we addressed the first issue was to add a label to the variable gear using the label() function (one of the useful utility functions from the table1 package). The two other issues were fixed by defining a function rndr() to do the rendering.

The ttt() function allows the order of the formula data data arguments to be switched, so that an alternative syntax using the magrittr “pipe” operator may be used:

mtcars %>% ttt(mpg ~ gear | cyl, lab="Number of Cylinders", render=rndr)
Number of
Gears
Number of Cylinders
4 6 8
3 21.5 19.8 15.1
4 26.9 19.8
5 28.2 19.7 15.4

Facets

The facets allow you to “slide-&-dice” the data however you want. The column facet is optional; it can be omitted:

ttt(mpg ~ gear, data=mtcars, render=rndr)
Number of
Gears
mpg
3 16.1
4 24.5
5 21.4

The row facet is required by the formula syntax, but the “magic” value 1 may be used to indicate no splitting by rows:

ttt(mpg ~ 1 | cyl, data=mtcars, lab="Number of Cylinders", render=rndr)
Number of Cylinders
4 6 8
26.7 19.7 15.1

Both row and column facets may consist of more than one variable, joined together by the symbol +. Here is an example with 2 row facets:

label(mtcars$cyl) <- "Number of<br/>Cylinders"
ttt(mpg ~ gear + cyl, data=mtcars, render=rndr)
Number of
Gears
Number of
Cylinders
mpg
3 4 21.5
6 19.8
8 15.1
4 4 26.9
6 19.8
5 4 28.2
6 19.7
8 15.4

And, similarly for 2 column facets:

ttt(mpg ~ 1 | gear + cyl, data=mtcars, lab="Number of Cylinders/Gears", render=rndr)
Number of Cylinders/Gears
3 4 5
4 6 8 4 6 8 4 8
21.5 26.9 28.2 19.8 19.8 19.7 15.1 15.4

The order of the variables is obviously important, as they define a nesting structure. If we think of the | that separates the row and column facets as the “values” (i.e. the central part of the table), then the order makes sense: variables closer to the center (|) are grouped within variables that are farther away.

Just to demonstrate, here is a synthetic example of a large table where both row and column facets are nested 3 levels deep:

bigtable <- expand.grid(
    R1=LETTERS[1:3],
    R2=LETTERS[4:6],
    R3=LETTERS[7:9],
    C1=LETTERS[10:12],
    C2=LETTERS[13:15],
    C3=LETTERS[16:18])

bigtable$x <- 1:nrow(bigtable)
ttt(x ~ R3 + R2 + R1 | C1 + C2 + C3, data=bigtable)
R3 R2 R1 P Q R
M N O M N O M N O
J K L J K L J K L J K L J K L J K L J K L J K L J K L
G D A 1 28 55 82 109 136 163 190 217 244 271 298 325 352 379 406 433 460 487 514 541 568 595 622 649 676 703
B 2 29 56 83 110 137 164 191 218 245 272 299 326 353 380 407 434 461 488 515 542 569 596 623 650 677 704
C 3 30 57 84 111 138 165 192 219 246 273 300 327 354 381 408 435 462 489 516 543 570 597 624 651 678 705
E A 4 31 58 85 112 139 166 193 220 247 274 301 328 355 382 409 436 463 490 517 544 571 598 625 652 679 706
B 5 32 59 86 113 140 167 194 221 248 275 302 329 356 383 410 437 464 491 518 545 572 599 626 653 680 707
C 6 33 60 87 114 141 168 195 222 249 276 303 330 357 384 411 438 465 492 519 546 573 600 627 654 681 708
F A 7 34 61 88 115 142 169 196 223 250 277 304 331 358 385 412 439 466 493 520 547 574 601 628 655 682 709
B 8 35 62 89 116 143 170 197 224 251 278 305 332 359 386 413 440 467 494 521 548 575 602 629 656 683 710
C 9 36 63 90 117 144 171 198 225 252 279 306 333 360 387 414 441 468 495 522 549 576 603 630 657 684 711
H D A 10 37 64 91 118 145 172 199 226 253 280 307 334 361 388 415 442 469 496 523 550 577 604 631 658 685 712
B 11 38 65 92 119 146 173 200 227 254 281 308 335 362 389 416 443 470 497 524 551 578 605 632 659 686 713
C 12 39 66 93 120 147 174 201 228 255 282 309 336 363 390 417 444 471 498 525 552 579 606 633 660 687 714
E A 13 40 67 94 121 148 175 202 229 256 283 310 337 364 391 418 445 472 499 526 553 580 607 634 661 688 715
B 14 41 68 95 122 149 176 203 230 257 284 311 338 365 392 419 446 473 500 527 554 581 608 635 662 689 716
C 15 42 69 96 123 150 177 204 231 258 285 312 339 366 393 420 447 474 501 528 555 582 609 636 663 690 717
F A 16 43 70 97 124 151 178 205 232 259 286 313 340 367 394 421 448 475 502 529 556 583 610 637 664 691 718
B 17 44 71 98 125 152 179 206 233 260 287 314 341 368 395 422 449 476 503 530 557 584 611 638 665 692 719
C 18 45 72 99 126 153 180 207 234 261 288 315 342 369 396 423 450 477 504 531 558 585 612 639 666 693 720
I D A 19 46 73 100 127 154 181 208 235 262 289 316 343 370 397 424 451 478 505 532 559 586 613 640 667 694 721
B 20 47 74 101 128 155 182 209 236 263 290 317 344 371 398 425 452 479 506 533 560 587 614 641 668 695 722
C 21 48 75 102 129 156 183 210 237 264 291 318 345 372 399 426 453 480 507 534 561 588 615 642 669 696 723
E A 22 49 76 103 130 157 184 211 238 265 292 319 346 373 400 427 454 481 508 535 562 589 616 643 670 697 724
B 23 50 77 104 131 158 185 212 239 266 293 320 347 374 401 428 455 482 509 536 563 590 617 644 671 698 725
C 24 51 78 105 132 159 186 213 240 267 294 321 348 375 402 429 456 483 510 537 564 591 618 645 672 699 726
F A 25 52 79 106 133 160 187 214 241 268 295 322 349 376 403 430 457 484 511 538 565 592 619 646 673 700 727
B 26 53 80 107 134 161 188 215 242 269 296 323 350 377 404 431 458 485 512 539 566 593 620 647 674 701 728
C 27 54 81 108 135 162 189 216 243 270 297 324 351 378 405 432 459 486 513 540 567 594 621 648 675 702 729

Render functions

The render function gives a lot of flexibility. For example, instead of the mean mpg, we can list the cars according to their gear/cylinder combination:

ttt(rownames(mtcars) ~ gear | cyl, data=mtcars, lab="Number of Cylinders",
  render=paste, collapse="<br/>")
Number of
Gears
Number of Cylinders
4 6 8
3 Toyota Corona Hornet 4 Drive
Valiant
Hornet Sportabout
Duster 360
Merc 450SE
Merc 450SL
Merc 450SLC
Cadillac Fleetwood
Lincoln Continental
Chrysler Imperial
Dodge Challenger
AMC Javelin
Camaro Z28
Pontiac Firebird
4 Datsun 710
Merc 240D
Merc 230
Fiat 128
Honda Civic
Toyota Corolla
Fiat X1-9
Volvo 142E
Mazda RX4
Mazda RX4 Wag
Merc 280
Merc 280C
5 Porsche 914-2
Lotus Europa
Ferrari Dino Ford Pantera L
Maserati Bora

Note that additional arguments can be passed to the render function through ... (as in this case, collapse).

Furthermore, a render function can return more than one value in a named vector. In this case, the argument expand.along also comes into play. It determines how the multiple values are laid out, either vertically (along rows), or the horizontally (along columns).

For example, here we define a function that computes both the mean and standard deviation (SD), each to 3 significant digits, and apply it to the response variable in the OrchardSprays dataset (i.e., decrease) according to treatment:

rndr.meansd <- function(x) signif_pad(c(Mean=mean(x), SD=sd(x)), digits=3)

ttt(decrease ~ treatment, data=OrchardSprays, render=rndr.meansd)
treatment Statistic decrease
A Mean 4.63
SD 3.20
B Mean 7.63
SD 3.29
C Mean 25.3
SD 24.4
D Mean 35.0
SD 13.4
E Mean 63.1
SD 26.9
F Mean 69.0
SD 29.2
G Mean 68.5
SD 20.1
H Mean 90.3
SD 24.2

The default is expand.along="rows", which produces the result above. As we can see, the table contains an additional column with the values “Mean” and “SD”, and each value is displayed in its own row. By default, the extra column is labeled “Statistic”, but to change the label to “Blah” we can specify a named vector as follows:

ttt(decrease ~ treatment, data=OrchardSprays, render=rndr.meansd, expand.along=c(Blah="rows"))
treatment Blah decrease
A Mean 4.63
SD 3.20
B Mean 7.63
SD 3.29
C Mean 25.3
SD 24.4
D Mean 35.0
SD 13.4
E Mean 63.1
SD 26.9
F Mean 69.0
SD 29.2
G Mean 68.5
SD 20.1
H Mean 90.3
SD 24.2

The other option is expand.along="columns", which produces this result:

ttt(decrease ~ treatment, data=OrchardSprays, render=rndr.meansd, expand.along="columns")
treatment decrease
Mean SD
A 4.63 3.20
B 7.63 3.29
C 25.3 24.4
D 35.0 13.4
E 63.1 26.9
F 69.0 29.2
G 68.5 20.1
H 90.3 24.2

Now “Mean” and “SD” each have their own column.

Captions and footnotes

A caption and one or more footnotes can be added to the table by specifying string values to the caption and footnote arguments, respectively:

ttt(decrease ~ 1 | treatment, data=OrchardSprays, render=rndr.meansd, lab="Treatment",
    caption="Mean and SD of Decrease by Treatment",
    footnote=c("Data: OrchardSprays", "Comment: <code>ttt</code> is cool!"))
Mean and SD of Decrease by Treatment
Statistic Treatment
A B C D E F G H

Data: OrchardSprays

Comment: ttt is cool!

Mean 4.63 7.63 25.3 35.0 63.1 69.0 68.5 90.3
SD 3.20 3.29 24.4 13.4 26.9 29.2 20.1 24.2

There’s not much more to say about this.

Styling

The appearance of the tables produced by ttt can be changed in 2 ways: using themes, or custom styling. Themes are easier, but don’t give much flexibility. For fine-level control, custom styling must be used.

Themes

The ttt package comes with 2 themes at this time: the default theme that has been used throughout this vignette so far, and the booktabs theme. (More themes may be added later.) Selecting the theme can be done using the ttt.theme global option:

options(ttt.theme="booktabs")  # Select the "booktabs" theme

If we select the booktabs theme, our large table looks like this:

ttt(x ~ R3 + R2 + R1 | C1 + C2 + C3, data=bigtable)
R3 R2 R1 P Q R
M N O M N O M N O
J K L J K L J K L J K L J K L J K L J K L J K L J K L
G D A 1 28 55 82 109 136 163 190 217 244 271 298 325 352 379 406 433 460 487 514 541 568 595 622 649 676 703
B 2 29 56 83 110 137 164 191 218 245 272 299 326 353 380 407 434 461 488 515 542 569 596 623 650 677 704
C 3 30 57 84 111 138 165 192 219 246 273 300 327 354 381 408 435 462 489 516 543 570 597 624 651 678 705
E A 4 31 58 85 112 139 166 193 220 247 274 301 328 355 382 409 436 463 490 517 544 571 598 625 652 679 706
B 5 32 59 86 113 140 167 194 221 248 275 302 329 356 383 410 437 464 491 518 545 572 599 626 653 680 707
C 6 33 60 87 114 141 168 195 222 249 276 303 330 357 384 411 438 465 492 519 546 573 600 627 654 681 708
F A 7 34 61 88 115 142 169 196 223 250 277 304 331 358 385 412 439 466 493 520 547 574 601 628 655 682 709
B 8 35 62 89 116 143 170 197 224 251 278 305 332 359 386 413 440 467 494 521 548 575 602 629 656 683 710
C 9 36 63 90 117 144 171 198 225 252 279 306 333 360 387 414 441 468 495 522 549 576 603 630 657 684 711
H D A 10 37 64 91 118 145 172 199 226 253 280 307 334 361 388 415 442 469 496 523 550 577 604 631 658 685 712
B 11 38 65 92 119 146 173 200 227 254 281 308 335 362 389 416 443 470 497 524 551 578 605 632 659 686 713
C 12 39 66 93 120 147 174 201 228 255 282 309 336 363 390 417 444 471 498 525 552 579 606 633 660 687 714
E A 13 40 67 94 121 148 175 202 229 256 283 310 337 364 391 418 445 472 499 526 553 580 607 634 661 688 715
B 14 41 68 95 122 149 176 203 230 257 284 311 338 365 392 419 446 473 500 527 554 581 608 635 662 689 716
C 15 42 69 96 123 150 177 204 231 258 285 312 339 366 393 420 447 474 501 528 555 582 609 636 663 690 717
F A 16 43 70 97 124 151 178 205 232 259 286 313 340 367 394 421 448 475 502 529 556 583 610 637 664 691 718
B 17 44 71 98 125 152 179 206 233 260 287 314 341 368 395 422 449 476 503 530 557 584 611 638 665 692 719
C 18 45 72 99 126 153 180 207 234 261 288 315 342 369 396 423 450 477 504 531 558 585 612 639 666 693 720
I D A 19 46 73 100 127 154 181 208 235 262 289 316 343 370 397 424 451 478 505 532 559 586 613 640 667 694 721
B 20 47 74 101 128 155 182 209 236 263 290 317 344 371 398 425 452 479 506 533 560 587 614 641 668 695 722
C 21 48 75 102 129 156 183 210 237 264 291 318 345 372 399 426 453 480 507 534 561 588 615 642 669 696 723
E A 22 49 76 103 130 157 184 211 238 265 292 319 346 373 400 427 454 481 508 535 562 589 616 643 670 697 724
B 23 50 77 104 131 158 185 212 239 266 293 320 347 374 401 428 455 482 509 536 563 590 617 644 671 698 725
C 24 51 78 105 132 159 186 213 240 267 294 321 348 375 402 429 456 483 510 537 564 591 618 645 672 699 726
F A 25 52 79 106 133 160 187 214 241 268 295 322 349 376 403 430 457 484 511 538 565 592 619 646 673 700 727
B 26 53 80 107 134 161 188 215 242 269 296 323 350 377 404 431 458 485 512 539 566 593 620 647 674 701 728
C 27 54 81 108 135 162 189 216 243 270 297 324 351 378 405 432 459 486 513 540 567 594 621 648 675 702 729

Note that a theme can only apply to a whole document; it is not possible to selectively style different tables within the same document differently using different themes, as we appear to have done here (but it can be done with custom styling, which is how it was done).

options(ttt.theme="default")  # Change back to the "default" theme

Custom styling

As mentioned in the introduction, changing the table’s appearance is accomplished using CSS. In order to make this possible, ttt places “hooks” in the table in the form of class attributes on various HTML elements.

The first thing to know is that the whole table is enclosed in a <div class="Rttt"> element. The allows specific formatting to be applied to tables output by ttt without interfering with other tables in the same document.

The next thing is that all row labels have the class Rttt-rl, and all column labels have the class Rttt-cl. Furthermore, there are different classes for each level or nesting: Rttt-rl-lvl1 for the first (i.e. innermost) level or row labels, Rttt-rl-lvl2 for the second level, and so on, and similarly for the column labels with cl instead of rl.

Finally, it is possible to add a class attribute or id attribute to the whole table, so that it may be targetted with specific CSS selectors. We can also pass CSS code directly to the ttt() function to be included with the table.

For example, can can specify that our table has the ID bigtable, and then give it a particular (and particularly weird) style:

css <- '
#bigtable {
  font-family: "Lucida Console", Monaco, monospace;
}
#bigtable td {
  background-color: #eee;
}
#bigtable th {
  color: blue;
  background-color: lightblue;
}
#bigtable th, #bigtable td {
  border: 2px dashed orange;
}
#bigtable .Rttt-rl {
  background-color: #fff;
  font-style: italic;
  font-weight: bold;
}
#bigtable .Rttt-rl-lvl1 {
  font-size: 12pt;
  color: pink;
  background-color: yellow;
}
#bigtable .Rttt-rl-lvl2 {
  font-size: 14pt;
  color: green;
}
#bigtable .Rttt-rl-lvl3 {
  font-size: 18pt;
  color: red;
}
'

ttt(x ~ R3 + R2 + R1 | C1 + C2 + C3, data=bigtable, id="bigtable", css=css)
R3 R2 R1 P Q R
M N O M N O M N O
J K L J K L J K L J K L J K L J K L J K L J K L J K L
G D A 1 28 55 82 109 136 163 190 217 244 271 298 325 352 379 406 433 460 487 514 541 568 595 622 649 676 703
B 2 29 56 83 110 137 164 191 218 245 272 299 326 353 380 407 434 461 488 515 542 569 596 623 650 677 704
C 3 30 57 84 111 138 165 192 219 246 273 300 327 354 381 408 435 462 489 516 543 570 597 624 651 678 705
E A 4 31 58 85 112 139 166 193 220 247 274 301 328 355 382 409 436 463 490 517 544 571 598 625 652 679 706
B 5 32 59 86 113 140 167 194 221 248 275 302 329 356 383 410 437 464 491 518 545 572 599 626 653 680 707
C 6 33 60 87 114 141 168 195 222 249 276 303 330 357 384 411 438 465 492 519 546 573 600 627 654 681 708
F A 7 34 61 88 115 142 169 196 223 250 277 304 331 358 385 412 439 466 493 520 547 574 601 628 655 682 709
B 8 35 62 89 116 143 170 197 224 251 278 305 332 359 386 413 440 467 494 521 548 575 602 629 656 683 710
C 9 36 63 90 117 144 171 198 225 252 279 306 333 360 387 414 441 468 495 522 549 576 603 630 657 684 711
H D A 10 37 64 91 118 145 172 199 226 253 280 307 334 361 388 415 442 469 496 523 550 577 604 631 658 685 712
B 11 38 65 92 119 146 173 200 227 254 281 308 335 362 389 416 443 470 497 524 551 578 605 632 659 686 713
C 12 39 66 93 120 147 174 201 228 255 282 309 336 363 390 417 444 471 498 525 552 579 606 633 660 687 714
E A 13 40 67 94 121 148 175 202 229 256 283 310 337 364 391 418 445 472 499 526 553 580 607 634 661 688 715
B 14 41 68 95 122 149 176 203 230 257 284 311 338 365 392 419 446 473 500 527 554 581 608 635 662 689 716
C 15 42 69 96 123 150 177 204 231 258 285 312 339 366 393 420 447 474 501 528 555 582 609 636 663 690 717
F A 16 43 70 97 124 151 178 205 232 259 286 313 340 367 394 421 448 475 502 529 556 583 610 637 664 691 718
B 17 44 71 98 125 152 179 206 233 260 287 314 341 368 395 422 449 476 503 530 557 584 611 638 665 692 719
C 18 45 72 99 126 153 180 207 234 261 288 315 342 369 396 423 450 477 504 531 558 585 612 639 666 693 720
I D A 19 46 73 100 127 154 181 208 235 262 289 316 343 370 397 424 451 478 505 532 559 586 613 640 667 694 721
B 20 47 74 101 128 155 182 209 236 263 290 317 344 371 398 425 452 479 506 533 560 587 614 641 668 695 722
C 21 48 75 102 129 156 183 210 237 264 291 318 345 372 399 426 453 480 507 534 561 588 615 642 669 696 723
E A 22 49 76 103 130 157 184 211 238 265 292 319 346 373 400 427 454 481 508 535 562 589 616 643 670 697 724
B 23 50 77 104 131 158 185 212 239 266 293 320 347 374 401 428 455 482 509 536 563 590 617 644 671 698 725
C 24 51 78 105 132 159 186 213 240 267 294 321 348 375 402 429 456 483 510 537 564 591 618 645 672 699 726
F A 25 52 79 106 133 160 187 214 241 268 295 322 349 376 403 430 457 484 511 538 565 592 619 646 673 700 727
B 26 53 80 107 134 161 188 215 242 269 296 323 350 377 404 431 458 485 512 539 566 593 620 647 674 701 728
C 27 54 81 108 135 162 189 216 243 270 297 324 351 378 405 432 459 486 513 540 567 594 621 648 675 702 729

Example: conditional formatting

A render function can actually add a html.class attribute to its return value. The value of this attribute will be assigned to the resulting HTML element’s class attribute, allowing you to target that element with specific formatting.

For example, suppose we have a data.frame that contains some numeric values, and we want to put them in a table:

dat <- expand.grid(row=LETTERS[1:5], column=LETTERS[1:5])
dat$value <- rnorm(nrow(dat))

ttt(value ~ row | column, data=dat, render=round_pad, digits=2)
row A B C D E
A -0.56 1.72 1.22 1.79 -1.07
B -0.23 0.46 0.36 0.50 -0.22
C 1.56 -1.27 0.40 -1.97 -1.03
D 0.07 -0.69 0.11 0.70 -0.73
E 0.13 -0.45 -0.56 -0.47 -0.63

Furthermore, suppose we want the cells that contain negative values to be red, and those that contain positive values to be green. (Note: this can actually be easily accomplished using JavaScript, but that’s not the point of this example). Let’s define a render function for this:

rndr <- function(x, ...) {
    y <- round_pad(x, 2)
    attr(y, "html.class") <- ifelse(x < 0, "neg", "pos")
    y
}

(since there are no zeros in this example, the code here cheats and treats zero as positive; if you don’t like this, think of it as non-negative).

The render function sets the desired class on the elements. Now, we can add some CSS code to obtain the desired colors:

.neg {
  color: #990000;
  background-color: #ff000030;
}
.pos {
  color: #007700;
  background-color: #00ff0030;
}

Finally, we generate the table:

ttt(value ~ row | column, data=dat, render=rndr)
row A B C D E
A -0.56 1.72 1.22 1.79 -1.07
B -0.23 0.46 0.36 0.50 -0.22
C 1.56 -1.27 0.40 -1.97 -1.03
D 0.07 -0.69 0.11 0.70 -0.73
E 0.13 -0.45 -0.56 -0.47 -0.63

  1. A non-exhaustive list includes: flextable, kableExtra, huxtable, htmlTable, tableHTML, ztable, formattable, pixiedust, basictabler, mmtable2, gt, DT, tables, xtable.↩︎

These binaries (installable software) and packages are in development.
They may not be fully stable and should be used with caution. We make no claims about them.