(Please note that the HTML colors below are only approximate rendering of how the output will look on a terminal).
data(starwars)
sw <- starwars[, c(1:4, 7:8)]
sw %>% colorDF
Color data frame 6 x 87:
(Showing rows 1 - 20 out of 87)
│name │height│mass│hair_color │birth_year│gender
1│Luke Skywalker │ 172│ 77│blond │ 19│male
2│C-3PO │ 167│ 75│NA │ 112│NA
3│R2-D2 │ 96│ 32│NA │ 33│NA
4│Darth Vader │ 202│ 136│none │ 42│male
5│Leia Organa │ 150│ 49│brown │ 19│female
6│Owen Lars │ 178│ 120│brown, grey │ 52│male
7│Beru Whitesun lars │ 165│ 75│brown │ 47│female
8│R5-D4 │ 97│ 32│NA │ NA│NA
9│Biggs Darklighter │ 183│ 84│black │ 24│male
10│Obi-Wan Kenobi │ 182│ 77│auburn, white│ 57│male
11│Anakin Skywalker │ 188│ 84│blond │ 42│male
12│Wilhuff Tarkin │ 180│ NA│auburn, grey │ 64│male
13│Chewbacca │ 228│ 112│brown │ 200│male
14│Han Solo │ 180│ 80│brown │ 29│male
15│Greedo │ 173│ 74│NA │ 44│male
16│Jabba Desilijic Tiure│ 175│1358│NA │ 600│hermaphrodite
17│Wedge Antilles │ 170│ 77│brown │ 21│male
18│Jek Tono Porkins │ 180│ 110│brown │ NA│male
19│Yoda │ 66│ 17│white │ 896│male
20│Palpatine │ 170│ 75│grey │ 82│male
as.colorDF(sw) %>% summary
Color data frame 5 x 6:
│Col │Class │NAs│unique
1│name │character│ 0│ 87
2│height │integer │ 6│ 46
3│mass │numeric │ 28│ 39
4│hair_color│character│ 5│ 13
5│birth_year│numeric │ 44│ 37
6│gender │character│ 3│ 5
│Summary
1│All values unique
2│ 66 [167 <180> 191] 264
3│ 15 [ 56 < 79> 84] 1358
4│none: 37, brown: 18, black: 13, white: 4, blond: 3, auburn: 1, ...
5│ 8 [ 35 < 52> 72] 896
6│male: 62, female: 19, none: 2, hermaphrodite: 1
Your average terminal in which you run R is capable of displaying colors, styles and unicode characters. Wouldn’t it be nice to add some color to the data frames you are displaying? For example, that factors are shown in a distinct color (no confusing of strings and factors any more!) or that significant p-values are colored in red?
This was my motivation when writing this tiny package. Of course, changing default method for printing data frames is nothing a package is allowed to do (but read on!). However, this package defines everything you need to get dynamic, colorful output when viewing data frames. There are two things about colorDF which are important:
Yes, you can color any object that can be cast into a data frame with this or related functions! For example, you can apply it to both tibbles and data.table objects:
## works with standard data.frames
colorDF(mtcars)
## works with tidyverse tibbles
mtcars %>% as_tibble %>% colorDF
## works with data.table
colorDF(data.table(mtcars))
The output of these three commands is identical:
Color data frame 11 x 32:
(Showing rows 1 - 20 out of 32)
│mpg│cyl│disp│hp │drat│wt │qsec│vs│am│gear│carb
Mazda RX4│ 21│ 6│ 160│110│ 3.9│2.6│ 16│ 0│ 1│ 4│ 4
Mazda RX4 Wag│ 21│ 6│ 160│110│ 3.9│2.9│ 17│ 0│ 1│ 4│ 4
Datsun 710│ 23│ 4│ 108│ 93│ 3.9│2.3│ 19│ 1│ 1│ 4│ 1
Hornet 4 Drive│ 21│ 6│ 258│110│ 3.1│3.2│ 19│ 1│ 0│ 3│ 1
Hornet Sportabout│ 19│ 8│ 360│175│ 3.1│3.4│ 17│ 0│ 0│ 3│ 2
Valiant│ 18│ 6│ 225│105│ 2.8│3.5│ 20│ 1│ 0│ 3│ 1
Duster 360│ 14│ 8│ 360│245│ 3.2│3.6│ 16│ 0│ 0│ 3│ 4
Merc 240D│ 24│ 4│ 147│ 62│ 3.7│3.2│ 20│ 1│ 0│ 4│ 2
Merc 230│ 23│ 4│ 141│ 95│ 3.9│3.1│ 23│ 1│ 0│ 4│ 2
Merc 280│ 19│ 6│ 168│123│ 3.9│3.4│ 18│ 1│ 0│ 4│ 4
Merc 280C│ 18│ 6│ 168│123│ 3.9│3.4│ 19│ 1│ 0│ 4│ 4
Merc 450SE│ 16│ 8│ 276│180│ 3.1│4.1│ 17│ 0│ 0│ 3│ 3
Merc 450SL│ 17│ 8│ 276│180│ 3.1│3.7│ 18│ 0│ 0│ 3│ 3
Merc 450SLC│ 15│ 8│ 276│180│ 3.1│3.8│ 18│ 0│ 0│ 3│ 3
Cadillac Fleetwood│ 10│ 8│ 472│205│ 2.9│5.2│ 18│ 0│ 0│ 3│ 4
Lincoln Continental│ 10│ 8│ 460│215│ 3.0│5.4│ 18│ 0│ 0│ 3│ 4
Chrysler Imperial│ 15│ 8│ 440│230│ 3.2│5.3│ 17│ 0│ 0│ 3│ 4
Fiat 128│ 32│ 4│ 79│ 66│ 4.1│2.2│ 19│ 1│ 1│ 4│ 1
Honda Civic│ 30│ 4│ 76│ 52│ 4.9│1.6│ 19│ 1│ 1│ 4│ 2
Toyota Corolla│ 34│ 4│ 71│ 65│ 4.2│1.8│ 20│ 1│ 1│ 4│ 1
Column types are mostly like classes, but colorDF introduces some additional distinctions, specifically “identifier” (such that character columns which contain identifiers can be shown with a particular, distinct style) and “pval”, to show significant p-values in a different color (and use format.pval()
for formatting).
colorDF tries to guess how each column should be displayed. First of all, it determines the class of the column (character, integer, numeric, logical, factor). Next, it looks up whether it can guess the contents of the column by looking at the column name (ID, p-value). Finally, it checks whether any columns have been implicitely been assigned a column type.
To assign a particular column type, you need first to turn a data frame colorful and then modify the column type:
sw <- sw %>% as.colorDF
col_type(sw, "name") <- "identifier"
col_type(sw, "gender") <- "factor"
sw$probability <- runif(nrow(sw), 0, 0.1)
col_type(sw, "probability") <- "pval"
sw
Color data frame 7 x 87:
(Showing rows 1 - 20 out of 87)
│name │height│mass│hair_color │birth_year│gender
1│ Luke Skywalker│ 172│ 77│blond │ 19│male
2│ C-3PO│ 167│ 75│NA │ 112│NA
3│ R2-D2│ 96│ 32│NA │ 33│NA
4│ Darth Vader│ 202│ 136│none │ 42│male
5│ Leia Organa│ 150│ 49│brown │ 19│female
6│ Owen Lars│ 178│ 120│brown, grey │ 52│male
7│ Beru Whitesun lars│ 165│ 75│brown │ 47│female
8│ R5-D4│ 97│ 32│NA │ NA│NA
9│ Biggs Darklighter│ 183│ 84│black │ 24│male
10│ Obi-Wan Kenobi│ 182│ 77│auburn, white│ 57│male
11│ Anakin Skywalker│ 188│ 84│blond │ 42│male
12│ Wilhuff Tarkin│ 180│ NA│auburn, grey │ 64│male
13│ Chewbacca│ 228│ 112│brown │ 200│male
14│ Han Solo│ 180│ 80│brown │ 29│male
15│ Greedo│ 173│ 74│NA │ 44│male
16│Jabba Desilijic Tiure│ 175│1358│NA │ 600│hermaphrodite
17│ Wedge Antilles│ 170│ 77│brown │ 21│male
18│ Jek Tono Porkins│ 180│ 110│brown │ NA│male
19│ Yoda│ 66│ 17│white │ 896│male
20│ Palpatine│ 170│ 75│grey │ 82│male
│probability
1│0.09100
2│0.04949
3│0.03106
4│0.07788
5│0.05289
6│0.02460
7│0.04182
8│0.04714
9│0.02727
10│0.04489
11│0.06712
12│0.00054
13│0.05606
14│0.01441
15│0.04311
16│0.00457
17│0.08491
18│0.01783
19│0.07835
20│0.09416
Note that changing the column type does not change the class in the data frame! colorDF never touches the data frame object itself, the only operations concern the “.style” attribute. So while you may set a column type to “character” instead of “factor”, it will be looking like a character type on the terminal output, but the column class will still be a factor.
I am a bit confused when it comes to distinguishing the two. Themes are basically internally predefined styles. Styles are simply lists that hold information how different columns, column and row headers, separators between the columns and highlighted rows are displayed.
Themes can be set using the options(colorDF_theme="<theme name>")
command or by directly specifying the option in a call to colorDF
:
colorDF(sw, theme="bw")
Color data frame 7 x 87:
(Showing rows 1 - 20 out of 87)
│name │height│mass│hair_color │birth_year│gender
1│ Luke Skywalker│ 172│ 77│blond │ 19│male
2│ C-3PO│ 167│ 75│NA │ 112│NA
3│ R2-D2│ 96│ 32│NA │ 33│NA
4│ Darth Vader│ 202│ 136│none │ 42│male
5│ Leia Organa│ 150│ 49│brown │ 19│female
6│ Owen Lars│ 178│ 120│brown, grey │ 52│male
7│ Beru Whitesun lars│ 165│ 75│brown │ 47│female
8│ R5-D4│ 97│ 32│NA │ NA│NA
9│ Biggs Darklighter│ 183│ 84│black │ 24│male
10│ Obi-Wan Kenobi│ 182│ 77│auburn, white│ 57│male
11│ Anakin Skywalker│ 188│ 84│blond │ 42│male
12│ Wilhuff Tarkin│ 180│ NA│auburn, grey │ 64│male
13│ Chewbacca│ 228│ 112│brown │ 200│male
14│ Han Solo│ 180│ 80│brown │ 29│male
15│ Greedo│ 173│ 74│NA │ 44│male
16│Jabba Desilijic Tiure│ 175│1358│NA │ 600│hermaphrodite
17│ Wedge Antilles│ 170│ 77│brown │ 21│male
18│ Jek Tono Porkins│ 180│ 110│brown │ NA│male
19│ Yoda│ 66│ 17│white │ 896│male
20│ Palpatine│ 170│ 75│grey │ 82│male
│probability
1│0.09100
2│0.04949
3│0.03106
4│0.07788
5│0.05289
6│0.02460
7│0.04182
8│0.04714
9│0.02727
10│0.04489
11│0.06712
12│0.00054
13│0.05606
14│0.01441
15│0.04311
16│0.00457
17│0.08491
18│0.01783
19│0.07835
20│0.09416
Here is an overview of the themes. Some of them are intended for dark background and will not look great below:
colorDF_themes_show()
Theme universal - Suitable for all terminals:
Color data frame 7 x 2:
│ID │String│Factor│Number│Integer│Logical│Pvalue
1│ID1│foo │foo │ 12.1│ 12│TRUE │0.001
2│ID2│baz │baz │ -3.1│ -13│FALSE │0.314
Theme light - Suitable for black on white terminals:
Color data frame 7 x 2:
│ID │String│Factor│Number│Integer│Logical│Pvalue
1│ID1│foo │foo │ 12.1│ 12│TRUE │0.001
2│ID2│baz │baz │ -3.1│ -13│FALSE │0.314
Theme dark - Suitable for white on black terminals:
Color data frame 7 x 2:
│ID │String│Factor│Number│Integer│Logical│Pvalue
1│ID1│foo │foo │ 12.1│ 12│TRUE │0.001
2│ID2│baz │baz │ -3.1│ -13│FALSE │0.314
Theme bw - Black and white only. Suitable for black on white terminals:
Color data frame 7 x 2:
│ID │String│Factor│Number│Integer│Logical│Pvalue
1│ID1│foo │foo │ 12.1│ 12│TRUE │0.001
2│ID2│baz │baz │ -3.1│ -13│FALSE │0.314
Theme wb - Black and white only. Suitable for white on black terminals:
Color data frame 7 x 2:
│ID │String│Factor│Number│Integer│Logical│Pvalue
1│ID1│foo │foo │ 12.1│ 12│TRUE │0.001
2│ID2│baz │baz │ -3.1│ -13│FALSE │0.314
Default theme: light
Change it with `options(colorDF_theme="")`
You can add your own themes using add_colorDF_theme()
(see the example section on the help page).
Styles of a colorDF object can be directly manipulated using df_style
:
mtcars.c <- colorDF(mtcars)
df_style(mtcars.c, "sep") <- "; "
If interested, read the help file for df_style()
.
colorDF comes with two utility functions. Firstly, it defines a summary method for colorful data frames which can also be used for any other data frame like object and which I find much more useful than the regular summary:
starwars %>% as.colorDF %>% summary
Color data frame 5 x 13:
│Col │Class │NAs│unique
1│name │character│ 0│ 87
2│height │integer │ 6│ 46
3│mass │numeric │ 28│ 39
4│hair_color│character│ 5│ 13
5│skin_color│character│ 0│ 31
6│eye_color │character│ 0│ 15
7│birth_year│numeric │ 44│ 37
8│gender │character│ 3│ 5
9│homeworld │character│ 10│ 49
10│species │character│ 5│ 38
11│films │list │ 0│ 24
12│vehicles │list │ 0│ 11
13│starships │list │ 0│ 17
│Summary
1│All values unique
2│ 66 [167 <180> 191] 264
3│ 15 [ 56 < 79> 84] 1358
4│none: 37, brown: 18, black: 13, white: 4, blond: 3, auburn: 1, ...
5│fair: 17, light: 11, dark: 6, green: 6, grey: 6, pale: 5, ...
6│brown: 21, blue: 19, yellow: 11, black: 10, orange: 8, red: 5, ...
7│ 8 [ 35 < 52> 72] 896
8│male: 62, female: 19, none: 2, hermaphrodite: 1
9│Naboo: 11, Tatooine: 10, Alderaan: 3, Coruscant: 3, ...
10│Human: 35, Droid: 5, Gungan: 3, Kaminoan: 2, ...
11│Attack of th...: 13, The Phantom ...: 13, ...
12│character(0): 76, Tribubble bongo: 2, ...
13│character(0): 67, X-wing: 3, ...
There is a directly visible (exported) version of the colorful summary called summaryColorDF
:
starwars %>% summaryColorDF
The highlight()
function allows to mark selected rows from the table:
foo <- starwars %>% select(name, species, homeworld) %>%
highlight(.$homeworld == "Tatooine")
(Unfortunately, the HTML representation of the ANSI terminal doesn’t show that one correctly).
The package is intended to be used in terminal. However, as you see above, it is possible to get the colored tables also in an rmarkdown document. For this, include the following chunk at the beginning of your document:
```{r echo=FALSE,include=FALSE}
options(crayon.enabled = TRUE)
knitr::knit_hooks$set(output = function(x, options){
paste0(
'<pre class="r-output"><code>',
fansi::sgr_to_html(x = x, warn = FALSE),
'</code></pre>'
)
})
```
The main problem is that it relies on the crayon
package, which unfortunately is sometimes wrong about whether the terminal has 255 colors or 16 (at least in the version on CRAN). Case in point, the colors do not render correctly in rstudio, despite the fact that the rstudio terminal is of course capable of showing all ANSI colors.