1 Quick start

data(starwars)
sw <- starwars[, c(1:3, 7:8)]
sw %>% colorDF
# Color data frame 5 x 87:
(Showing rows 1 - 20 out of 87)
  │name                 │height│mass │birth_year│gender       
 1Luke Skywalker          172   77        19male         
 2C-3PO                   167   75       112NA           
 3R2-D2                    96   32        33NA           
 4Darth Vader             202  136        42male         
 5Leia Organa             150   49        19female       
 6Owen Lars               178  120        52male         
 7Beru Whitesun lars      165   75        47female       
 8R5-D4                    97   32        NANA           
 9Biggs Darklighter       183   84        24male         
10Obi-Wan Kenobi          182   77        57male         
11Anakin Skywalker        188   84        42male         
12Wilhuff Tarkin          180   NA        64male         
13Chewbacca               228  112       200male         
14Han Solo                180   80        29male         
15Greedo                  173   74        44male         
16Jabba Desilijic Tiure   175 1358       600hermaphrodite
17Wedge Antilles          170   77        21male         
18Jek Tono Porkins        180  110        NAmale         
19Yoda                     66   17       896male         
20Palpatine               170   75        82male         
colorDF(sw) %>% summary
# Color data frame 5 x 5:
 │Col       │Class│NAs  │unique│Summary                           
1name      <chr>    0    87All values unique                 
2height    <int>    6    46 66 [167 <180> 191] 264           
3mass      <dbl>   28    39  15 [  56 <  79>   84] 1358      
4birth_year<dbl>   44    37  8 [ 35 < 52>  72] 896           
5gender    <chr>    3     5male: 62, female: 19, none: 2, ...

2 Colorful data frames

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:

  1. colorDF never modifies the behavior of the data frame like object or its contents (i.e. it does not redefine methods like [<-, removes row names etc.). The only two things that change are (i) the default print method (visualization), and (ii) the “.style” and “.coltp” attributes of the object, and that only if you really change the class of the object, which is often unnecessary.
  2. Any data frame like object can be used in colorDF, and you don’t need to modify these objects to use the colorful visualizations.

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 
          Mazda RX4   21    6  160  110  3.9  2.6   16    0    1    4
      Mazda RX4 Wag   21    6  160  110  3.9  2.9   17    0    1    4
         Datsun 710   23    4  108   93  3.9  2.3   19    1    1    4
     Hornet 4 Drive   21    6  258  110  3.1  3.2   19    1    0    3
  Hornet Sportabout   19    8  360  175  3.1  3.4   17    0    0    3
            Valiant   18    6  225  105  2.8  3.5   20    1    0    3
         Duster 360   14    8  360  245  3.2  3.6   16    0    0    3
          Merc 240D   24    4  147   62  3.7  3.2   20    1    0    4
           Merc 230   23    4  141   95  3.9  3.1   23    1    0    4
           Merc 280   19    6  168  123  3.9  3.4   18    1    0    4
          Merc 280C   18    6  168  123  3.9  3.4   19    1    0    4
         Merc 450SE   16    8  276  180  3.1  4.1   17    0    0    3
         Merc 450SL   17    8  276  180  3.1  3.7   18    0    0    3
        Merc 450SLC   15    8  276  180  3.1  3.8   18    0    0    3
 Cadillac Fleetwood   10    8  472  205  2.9  5.2   18    0    0    3
Lincoln Continental   10    8  460  215  3.0  5.4   18    0    0    3
  Chrysler Imperial   15    8  440  230  3.2  5.3   17    0    0    3
           Fiat 128   32    4   79   66  4.1  2.2   19    1    1    4
        Honda Civic   30    4   76   52  4.9  1.6   19    1    1    4
     Toyota Corolla   34    4   71   65  4.2  1.8   20    1    1    4

                   │carb 
          Mazda RX4    4
      Mazda RX4 Wag    4
         Datsun 710    1
     Hornet 4 Drive    1
  Hornet Sportabout    2
            Valiant    1
         Duster 360    4
          Merc 240D    2
           Merc 230    2
           Merc 280    4
          Merc 280C    4
         Merc 450SE    3
         Merc 450SL    3
        Merc 450SLC    3
 Cadillac Fleetwood    4
Lincoln Continental    4
  Chrysler Imperial    4
           Fiat 128    1
        Honda Civic    2
     Toyota Corolla    1

3 Column types

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). Column types are stored in the .coltp attribute of the colorDF object.

colorDF tries to guess how each column should be displayed. First it checks whether any column types have been assigned explicitely using the col_type<- function and stored in the .coltp attribute of the object. Next, it looks up whether it can guess the contents of the column by looking at the column name (ID, p-value). Finally, it determines the class of the column (character, integer, numeric, logical, factor).

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 6 x 87:
(Showing rows 1 - 20 out of 87)
  │name                 │height│mass │birth_year│gender       │probability
 1       Luke Skywalker   172   77        19male         0.0917     
 2                C-3PO   167   75       112NA           0.0432     
 3                R2-D2    96   32        33NA           0.0929     
 4          Darth Vader   202  136        42male         0.0471     
 5          Leia Organa   150   49        19female       0.0460     
 6            Owen Lars   178  120        52male         0.0496     
 7   Beru Whitesun lars   165   75        47female       0.0032     
 8                R5-D4    97   32        NANA           4e-05      
 9    Biggs Darklighter   183   84        24male         0.0013     
10       Obi-Wan Kenobi   182   77        57male         0.0288     
11     Anakin Skywalker   188   84        42male         0.0497     
12       Wilhuff Tarkin   180   NA        64male         0.0265     
13            Chewbacca   228  112       200male         0.0807     
14             Han Solo   180   80        29male         0.0396     
15               Greedo   173   74        44male         0.0533     
16Jabba Desilijic Tiure   175 1358       600hermaphrodite0.0136     
17       Wedge Antilles   170   77        21male         0.0890     
18     Jek Tono Porkins   180  110        NAmale         0.0606     
19                 Yoda    66   17       896male         0.0877     
20            Palpatine   170   75        82male         0.0945     

Note that changing the column type does not change the class of the column in the data frame! colorDF never touches the data frame contents, the only operations concern the “.style” and “.coltp” attributes. So while you may set a column type to “character” instead of “factor”, even though it will be looking like a character type on the terminal output, the column class will still be a factor.

4 Styles and Themes

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 6 x 87:
(Showing rows 1 - 20 out of 87)
  │name                 │height│mass │birth_year│gender       │probability
 1       Luke Skywalker│   172│   77│        19│male         │0.0917     
 2                C-3PO│   167│   75│       112│NA           0.0432     
 3                R2-D2│    96│   32│        33│NA           0.0929     
 4          Darth Vader│   202│  136│        42│male         │0.0471     
 5          Leia Organa│   150│   49│        19│female       │0.0460     
 6            Owen Lars│   178│  120│        52│male         │0.0496     
 7   Beru Whitesun lars│   165│   75│        47│female       │0.0032     
 8                R5-D4│    97│   32│        NANA           4e-05      
 9    Biggs Darklighter│   183│   84│        24│male         │0.0013     
10       Obi-Wan Kenobi│   182│   77│        57│male         │0.0288     
11     Anakin Skywalker│   188│   84│        42│male         │0.0497     
12       Wilhuff Tarkin│   180│   NA│        64│male         │0.0265     
13            Chewbacca│   228│  112│       200│male         │0.0807     
14             Han Solo│   180│   80│        29│male         │0.0396     
15               Greedo│   173│   74│        44│male         │0.0533     
16Jabba Desilijic Tiure│   175│ 1358│       600│hermaphrodite│0.0136     
17       Wedge Antilles│   170│   77│        21│male         │0.0890     
18     Jek Tono Porkins│   180│  110│        NA│male         │0.0606     
19                 Yoda│    66│   17│       896│male         │0.0877     
20            Palpatine│   170│   75│        82│male         │0.0945     

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 light - Suitable for black on white terminals:
# Color data frame 7 x 2:
 │ID   │String│Factor│Number│Integer│Logical│Pvalue
1  ID1foo   foo     12.1     12TRUE   0.001 
2  ID2baz   baz     -3.1    -13FALSE  0.314 


Theme minimal - Almost no style:
# 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.14159265358979 -13     FALSE   0.314159


Theme universal - Suitable for all terminals:
# Color data frame 7 x 2:
 │ID   │String│Factor│Number│Integer│Logical│Pvalue
1  ID1foo   foo     12.1     12TRUE   0.001 
2  ID2baz   baz     -3.1    -13FALSE  │0.314 


Theme tibble - Very much like a tibble:
# Color data frame 7 x 2:
  ID    String Factor Number Integer Logical Pvalue
  <fct> <fct>  <fct>  <dbl>  <int>   <lgl>   <dbl> 
1 ID1   foo    foo    12.10   12     TRUE    0.001 
2 ID2   baz    baz    -3.14  -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  ID1foo   foo     12.1     12TRUE   0.001 
2  ID2baz   baz     -3.1    -13FALSE  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="<theme name>")`

You can add your own themes using add_colorDF_theme() (see the example section on the help page).

5 Column styles

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().

6 Utilities

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
 1name      <chr>    0    87
 2height    <int>    6    46
 3mass      <dbl>   28    39
 4hair_color<chr>    5    13
 5skin_color<chr>    0    31
 6eye_color <chr>    0    15
 7birth_year<dbl>   44    37
 8gender    <chr>    3     5
 9homeworld <chr>   10    49
10species   <chr>    5    38
11films     <lst>    0    24
12vehicles  <lst>    0    11
13starships <lst>    0    17

  │Summary                                        
 1All values unique                              
 2 66 [167 <180> 191] 264                        
 3  15 [  56 <  79>   84] 1358                   
 4none: 37, brown: 18, black: 13, white: 4, ...  
 5fair: 17, light: 11, dark: 6, green: 6, ...    
 6brown: 21, blue: 19, yellow: 11, black: 10, ...
 7  8 [ 35 < 52>  72] 896                        
 8male: 62, female: 19, none: 2, ...             
 9Naboo: 11, Tatooine: 10, ...                   
10Human: 35, Droid: 5, Gungan: 3, ...            
11Attack of th...: 13, ...                       
12character(0): 76, ...                          
13character(0): 67, X-wing: 3, ...               

There is a directly visible (exported) version of the colorful summary called summary_colorDF:

starwars %>% summary_colorDF

The highlight() function allows to mark selected rows from the table:

foo <- starwars %>% select(name, species, homeworld) %>% 
  highlight(.$homeworld == "Tatooine")
# Tibble 3 x 87:
(Showing rows 1 - 20 out of 87)
  │name                 │species       │homeworld 
 1Luke Skywalker       Human         Tatooine  
 2C-3PO                Droid         Tatooine  
 3R2-D2                Droid         Naboo     
 4Darth Vader          Human         Tatooine  
 5Leia Organa          Human         Alderaan  
 6Owen Lars            Human         Tatooine  
 7Beru Whitesun lars   Human         Tatooine  
 8R5-D4                Droid         Tatooine  
 9Biggs Darklighter    Human         Tatooine  
10Obi-Wan Kenobi       Human         Stewjon   
11Anakin Skywalker     Human         Tatooine  
12Wilhuff Tarkin       Human         Eriadu    
13Chewbacca            Wookiee       Kashyyyk  
14Han Solo             Human         Corellia  
15Greedo               Rodian        Rodia     
16Jabba Desilijic TiureHutt          Nal Hutta 
17Wedge Antilles       Human         Corellia  
18Jek Tono Porkins     Human         Bestine IV
19Yoda                 Yoda's speciesNA        
20Palpatine            Human         Naboo     

(Unfortunately, the HTML representation of the ANSI terminal doesn’t show that one correctly).

7 Setting up colorDF as the default data frame print method

You can use colorDF as the default method for displaying data frames and similar objects. For this, you need to use the colorDF:::print.colorDF function:

## for regular data frames
print.data.frame <- colorDF:::print.colorDF

## for tidyverse tibbles
print.tbl        <- colorDF:::print.colorDF

## for data.tables
print.data.table <- colorDF:::print.colorDF

This will not replace or modify the original functions from data.table or tibble packages, but merely mask these. And from now on, every data frame like object will be shown in color, but otherwise, its behavior will not change.

Should you want to go back to the original print functions, just remove these new functions:

rm(print.data.frame, print.tbl, print.data.table)

8 Global options

There are two global options which override whatever has been defined in a particular theme / style.

  • colorDF_tibble_style: if not NULL, tibble conventions will be used (i.e., show only columns which fit on the screen and create a subheader with column classes)
  • colorDF_sep: column separator string
  • colorDF_n: how many rows to show on the screen

For example,

options(colorDF_tibble_style="yes please")
options(colorDF_sep= " ")
options(colorDF_n=5)
colorDF(starwars)
# Color data frame 13 x 87:
(Showing rows 1 - 5 out of 87)
  name           height mass  hair_color skin_color  eye_color birth_year
1 Luke Skywalker    172    77 blond      fair        blue              19
2 C-3PO             167    75 NA         gold        yellow           112
3 R2-D2              96    32 NA         white, blue red               33
4 Darth Vader       202   136 none       white       yellow            42
5 Leia Organa       150    49 brown      light       brown             19

  gender homeworld species films        vehicles     starships   
1 male   Tatooine  Human   <list: 5 el> <list: 2 el> <list: 2 el>
2 NA     Tatooine  Droid   <list: 6 el> <list: 0 el> <list: 0 el>
3 NA     Naboo     Droid   <list: 7 el> <list: 0 el> <list: 0 el>
4 male   Tatooine  Human   <list: 4 el> <list: 0 el> <list: 1 el>
5 female Alderaan  Human   <list: 5 el> <list: 1 el> <list: 0 el>

9 Rmarkdown

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}
options(crayon.enabled = TRUE)
knitr::knit_hooks$set(output = function(x, options){
  paste0(
    '<pre class="r-output"><code>',
    fansi::sgr_to_html(x = htmltools::htmlEscape(x), warn = FALSE),
    '</code></pre>'
  )
})
```

10 Issues

Currently, colorDF relies on the crayon library to generate the ANSI escape codes. Unfortunately, crayon is peculiar about trying to guess the terminal type. Without going into details, there are situations in which it is not possible to force crayon into using 256 colors, even if you know that it really works. One such example is this vignette: if an rmarkdown file is built from command line, crayon will ignore any setting of the crayon.colors option and use only the base colors.

The reason that the colors in this vignette appear correct is that I used an ugly hack to substitute the crayon::num_colors() function by a function that always returns 256:

```{r echo=FALSE}
num_colors <- function(forget=TRUE) 256
library(crayon)
assignInNamespace("num_colors", num_colors, pos="package:crayon")
```