1 Quick start

library(dplyr)
data(starwars)
sw <- starwars[, c(1:3, 7:8)]
sw %>% colorDF
# Color data frame (class colorDF) 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 (class colorDF) 5 x 5:
 │Col       │Class│NAs  │unique│Summary                                        
1name      <chr>    0    87All values unique                              
2height    <int>    6    45 66 [167 <180> 191] 264                        
3mass      <dbl>   28    38  15.0 [  55.6 <  79.0>   84.5] 1358.0         
4birth_year<dbl>   44    36  8 [ 35 < 52>  72] 896                        
5gender    <chr>    3     4male: 62, female: 19, none: 2, hermaphrodite: 1

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.1). 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 (class colorDF) 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

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 (class colorDF) 6 x 87:
# (Showing rows 1 - 20 out of 87)
  │name                 │height│mass │birth_year│gender       │probability
 1       Luke Skywalker   172   77        19male         0.073      
 2                C-3PO   167   75       112NA           0.044      
 3                R2-D2    96   32        33NA           0.040      
 4          Darth Vader   202  136        42male         0.022      
 5          Leia Organa   150   49        19female       0.091      
 6            Owen Lars   178  120        52male         0.030      
 7   Beru Whitesun lars   165   75        47female       0.023      
 8                R5-D4    97   32        NANA           0.093      
 9    Biggs Darklighter   183   84        24male         0.022      
10       Obi-Wan Kenobi   182   77        57male         0.013      
11     Anakin Skywalker   188   84        42male         0.057      
12       Wilhuff Tarkin   180   NA        64male         0.028      
13            Chewbacca   228  112       200male         0.070      
14             Han Solo   180   80        29male         0.079      
15               Greedo   173   74        44male         0.050      
16Jabba Desilijic Tiure   175 1358       600hermaphrodite0.058      
17       Wedge Antilles   170   77        21male         0.062      
18     Jek Tono Porkins   180  110        NAmale         0.075      
19                 Yoda    66   17       896male         0.025      
20            Palpatine   170   75        82male         0.048      

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 "class", ".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.

You can also hide a column:

sw <- colorDF(starwars)
col_type(sw, c("vehicles", "films", "starships")) <- "hidden"
sw
# Color data frame (class colorDF) 13 x 87:
# (Showing rows 1 - 20 out of 87)
  │name                 │height│mass │hair_color   │skin_color      │eye_color│birth_year
 1Luke Skywalker          172   77blond        fair            blue             19
 2C-3PO                   167   75NA           gold            yellow          112
 3R2-D2                    96   32NA           white, blue     red              33
 4Darth Vader             202  136none         white           yellow           42
 5Leia Organa             150   49brown        light           brown            19
 6Owen Lars               178  120brown, grey  light           blue             52
 7Beru Whitesun lars      165   75brown        light           blue             47
 8R5-D4                    97   32NA           white, red      red              NA
 9Biggs Darklighter       183   84black        light           brown            24
10Obi-Wan Kenobi          182   77auburn, whitefair            blue-gray        57
11Anakin Skywalker        188   84blond        fair            blue             42
12Wilhuff Tarkin          180   NAauburn, grey fair            blue             64
13Chewbacca               228  112brown        unknown         blue            200
14Han Solo                180   80brown        fair            brown            29
15Greedo                  173   74NA           green           black            44
16Jabba Desilijic Tiure   175 1358NA           green-tan, brownorange          600
17Wedge Antilles          170   77brown        fair            hazel            21
18Jek Tono Porkins        180  110brown        fair            blue             NA
19Yoda                     66   17white        green           brown           896
20Palpatine               170   75grey         pale            yellow           82
  │gender       │homeworld │species       
 1male         Tatooine  Human         
 2NA           Tatooine  Droid         
 3NA           Naboo     Droid         
 4male         Tatooine  Human         
 5female       Alderaan  Human         
 6male         Tatooine  Human         
 7female       Tatooine  Human         
 8NA           Tatooine  Droid         
 9male         Tatooine  Human         
10male         Stewjon   Human         
11male         Tatooine  Human         
12male         Eriadu    Human         
13male         Kashyyyk  Wookiee       
14male         Corellia  Human         
15male         Rodia     Rodian        
16hermaphroditeNal Hutta Hutt          
17male         Corellia  Human         
18male         Bestine IVHuman         
19male         NA        Yoda's species
20male         Naboo     Human         
# Hidden columns (3): vehicles, films, starships

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 (class colorDF) 13 x 87:
# (Showing rows 1 - 20 out of 87)
  │name                 │height│mass │hair_color   │skin_color      │eye_color│birth_year
 1Luke Skywalker       │   172│   77│blond        fair            blue     │        19
 2C-3PO                │   167│   75│NA           gold            yellow   │       112
 3R2-D2                │    96│   32│NA           white, blue     red      │        33
 4Darth Vader          │   202│  136│none         white           yellow   │        42
 5Leia Organa          │   150│   49│brown        light           brown    │        19
 6Owen Lars            │   178│  120│brown, grey  light           blue     │        52
 7Beru Whitesun lars   │   165│   75│brown        light           blue     │        47
 8R5-D4                │    97│   32│NA           white, red      red              NA
 9Biggs Darklighter    │   183│   84│black        light           brown    │        24
10Obi-Wan Kenobi       │   182│   77│auburn, whitefair            blue-gray│        57
11Anakin Skywalker     │   188│   84│blond        fair            blue     │        42
12Wilhuff Tarkin       │   180│   NAauburn, grey fair            blue     │        64
13Chewbacca            │   228│  112│brown        unknown         blue     │       200
14Han Solo             │   180│   80│brown        fair            brown    │        29
15Greedo               │   173│   74│NA           green           black    │        44
16Jabba Desilijic Tiure│   175│ 1358│NA           green-tan, brownorange   │       600
17Wedge Antilles       │   170│   77│brown        fair            hazel    │        21
18Jek Tono Porkins     │   180│  110│brown        fair            blue             NA
19Yoda                 │    66│   17│white        green           brown    │       896
20Palpatine            │   170│   75│grey         pale            yellow   │        82
  │gender       │homeworld │species       
 1male         Tatooine  Human         
 2NA           Tatooine  Droid         
 3NA           Naboo     Droid         
 4male         Tatooine  Human         
 5female       Alderaan  Human         
 6male         Tatooine  Human         
 7female       Tatooine  Human         
 8NA           Tatooine  Droid         
 9male         Tatooine  Human         
10male         Stewjon   Human         
11male         Tatooine  Human         
12male         Eriadu    Human         
13male         Kashyyyk  Wookiee       
14male         Corellia  Human         
15male         Rodia     Rodian        
16hermaphroditeNal Hutta Hutt          
17male         Corellia  Human         
18male         Bestine IVHuman         
19male         NA        Yoda's species
20male         Naboo     Human         
# Hidden columns (3): vehicles, films, starships

Here is an overview of the themes. Some of them are intended for dark background and will not look great on a light background, which is why we use force_bg=TRUE to force black on white background for these themes:

colorDF_themes_show(force_bg=TRUE)
Theme light - Suitable for black on white terminals:
# Color data frame (class colorDF) 7 x 3:
 │ID   │String   │Factor│Number│Integer│Logical│Pvalue
1  ID1foo      foo     12.1     12TRUE   0.001 
2  ID2baz      baz     -3.1    -13FALSE  0.314 
3  ID3highlightboo      2.7     42NA     NA    

Theme minimal - Almost no style:
# Color data frame (class colorDF) 7 x 3:
  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
3 ID3   highlight boo    2.71828182845905  42      NA      NA      

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

Theme tibble - Very much like a tibble:
# Color data frame (class colorDF) 7 x 3:
  ID    String    Factor Number Integer Logical Pvalue
  <fct> <fct>     <fct>  <dbl>  <dbl>   <lgl>   <dbl> 
1 ID1   foo       foo    12.10   12     TRUE    0.001 
2 ID2   baz       baz    -3.14  -13     FALSE   0.314 
3 ID3   highlight boo     2.72   42     NA         NA 

Theme dark - Suitable for white on black terminals:
# Color data frame (class colorDF) 7 x 3:
 │ID   │String   │Factor│Number│Integer│Logical│Pvalue
1  ID1foo      foo     12.1     12TRUE   0.001 
2  ID2baz      baz     -3.1    -13FALSE  0.314 
3  ID3highlightboo      2.7     42NA     NA    

Theme bw - Black and white only. Suitable for black on white terminals:
# Color data frame (class colorDF) 7 x 3:
 │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 
3  ID3│highlight│boo   │   2.7│     42│NA     NA    

Theme wb - Black and white only. Suitable for white on black terminals:
# Color data frame (class colorDF) 7 x 3:
 │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 
3  ID3│highlight│boo   │   2.7│     42│NA     NA    

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

6.1 Summaries

colorDF comes with a couple of 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 (class colorDF) 5 x 13:
  │Col       │Class│NAs  │unique│Summary                                                         
 1name      <chr>    0    87All values unique                                               
 2height    <int>    6    45 66 [167 <180> 191] 264                                         
 3mass      <dbl>   28    38  15.0 [  55.6 <  79.0>   84.5] 1358.0                          
 4hair_color<chr>    5    12none: 37, brown: 18, black: 13, white: 4, blond: 3, auburn: 1, …
 5skin_color<chr>    0    31fair: 17, light: 11, dark: 6, green: 6, grey: 6, pale: 5, brown…
 6eye_color <chr>    0    15brown: 21, blue: 19, yellow: 11, black: 10, orange: 8, red: 5, …
 7birth_year<dbl>   44    36  8 [ 35 < 52>  72] 896                                         
 8gender    <chr>    3     4male: 62, female: 19, none: 2, hermaphrodite: 1                 
 9homeworld <chr>   10    48Naboo: 11, Tatooine: 10, Alderaan: 3, Coruscant: 3, Kamino: 3, …
10species   <chr>    5    37Human: 35, Droid: 5, Gungan: 3, Kaminoan: 2, Mirialan: 2, Twi'l…
11films     <lst>    0    24Attack of the Clones: 40, Revenge of the Sith: 34, The Phantom …
12vehicles  <lst>    0    11Imperial Speeder Bike: 2, Snowspeeder: 2, Tribubble bongo: 2, A…
13starships <lst>    0    17Millennium Falcon: 4, X-wing: 4, Imperial shuttle: 3, Naboo fig…

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

starwars %>% summary_colorDF

As you can see, the summary is much more informative than the default summary.data.frame function. Not only this, but the object does not need to be a data frame – any list can do!

mtcars_cyl <- split(mtcars$mpg, mtcars$cyl)
sapply(mtcars_cyl, length)
 4  6  8 
11  7 14 

The list mtcars_cyl is the miles per gallon column split by number of cylinders. We can use summary_colorDF to create a (semi)graphical summary of this list:

summary_colorDF(mtcars_cyl, numformat="g", width=90)
# Color data frame (class colorDF) 5 x 3:
 │Col  │Class│NAs  │unique│Summary                                                     
14    <dbl>    0     9                            ╾──┤       +          ├────────╼
26    <dbl>    0     6                   ╾─┤ +   ├╼                               
38    <dbl>    0    12╾─────────┤ +  ├──────╼                                     

In fact, this is so useful (especially if an interactive graphic device is not practical, e.g. when running R over ssh/screen) that I implemented a terminal boxplotting function:

term_boxplot(Sepal.Length ~ Species, data=iris, width=90)
# Color data frame (class colorDF) 5 x 4:
 │Col       │Class│NAs  │unique│Summary                                               
1setosa    <dbl>    0    15╾──────┤  +  ├────────╼                               
2versicolor<dbl>    0    21         ╾─────────┤    +    ├──────────╼             
3virginica <dbl>    0    21         ╾──────────────────┤   +     ├──────────────╼
4Range     <chr>    0     1Only one value: Range: 4.3 - 7.9                      

6.2 Highlighting

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

foo <- starwars %>% select(name, species, homeworld) %>% 
  highlight(.$homeworld == "Tatooine")
# Tibble (class tbl_df) 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)

This is a bit more complicated in case of S4 objects. One such object type is a DataFrame defined in the S4Vectors package. It is commonly used in many Bioconductor packages such as DESeq2. Unfortunately, the show method defined for DataFrames is not convenient, for example it always displays a ridiculous number of significant digits, cluttering the output. print_colorDF can print these classes, as it can work on anything that can be cast into a data frame using an as.data.frame method.

To take over the output of DataFrames and all other objects inheriting from it (such as DESeqResults), we need to use the S4 convention of defining the methods:

setMethod("show", "DataFrame", function(object) colorDF::print_colorDF(object))

Since methods can be only defined for existing classes, if you want to put it in your .Rprofile, you need to first load (but not necessarily attach) the S4Vectors package:

loadNamespace("S4Vectors")
setMethod("show", "DataFrame", function(object) colorDF::print_colorDF(object))

8 Global options

There is a number of options which override whatever has been defined in a particular theme / style. You can view them with colorDF_options():

colorDF_options()
# Data frame like object (class data.frame) 4 x 6:
 │Option              │Description                       │Default           │Current
1colorDF_n           Number of rows to show            20                Not set
2colorDF_noitalic    Suppress italics (read on loading)FALSE             Not set
3colorDF_tibble_styleBehave like tibble                FALSE             Not set
4colorDF_sep         Column separator                  " "               Not set
5colorDF_theme       colorDF theme to use              light             light  
6width               Width of the terminal             getOption("width")100    

To change these options, use options() just like with any other global option. For example,

options(colorDF_tibble_style=TRUE)
options(colorDF_sep= " ")
options(colorDF_n=5)
colorDF(starwars)
# Color data frame (class colorDF) 13 x 87:
# (Showing rows 1 - 5 out of 87)
  name           height mass  hair_color skin_color  eye_color birth_year gender homeworld species
  <chr>          <int>  <dbl> <chr>      <chr>       <chr>     <dbl>      <chr>  <chr>     <chr>  
1 Luke Skywalker    172    77 blond      fair        blue              19 male   Tatooine  Human  
2 C-3PO             167    75 NA         gold        yellow           112 NA     Tatooine  Droid  
3 R2-D2              96    32 NA         white, blue red               33 NA     Naboo     Droid  
4 Darth Vader       202   136 none       white       yellow            42 male   Tatooine  Human  
5 Leia Organa       150    49 brown      light       brown             19 female Alderaan  Human  
# 3 more columns: films (<lst>), vehicles (<lst>), starships (<lst>)

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")
```

  1. Strictly speaking, that is not true, as there is a [.colorDF method which serves only as a wrapper around the respective [ of the underlying data frame, tibble or data table. The only reason is exist is that otherwise the style and column type attributes will be lost.